1 /*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "$Id: mark.c,v 10.14 2011/07/04 14:42:58 zy Exp $";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
19
20 #include <bitstring.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "common.h"
28
29 static LMARK *mark_find(SCR *, ARG_CHAR_T);
30
31 /*
32 * Marks are maintained in a key sorted singly linked list. We can't
33 * use arrays because we have no idea how big an index key could be.
34 * The underlying assumption is that users don't have more than, say,
35 * 10 marks at any one time, so this will be is fast enough.
36 *
37 * Marks are fixed, and modifications to the line don't update the mark's
38 * position in the line. This can be hard. If you add text to the line,
39 * place a mark in that text, undo the addition and use ` to move to the
40 * mark, the location will have disappeared. It's tempting to try to adjust
41 * the mark with the changes in the line, but this is hard to do, especially
42 * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
43 * would move to the first non-blank on the line when the mark location was
44 * past the end of the line. This can be complicated by deleting to a mark
45 * that has disappeared using the ` command. Historic vi treated this as
46 * a line-mode motion and deleted the line. This implementation complains to
47 * the user.
48 *
49 * In historic vi, marks returned if the operation was undone, unless the
50 * mark had been subsequently reset. Tricky. This is hard to start with,
51 * but in the presence of repeated undo it gets nasty. When a line is
52 * deleted, we delete (and log) any marks on that line. An undo will create
53 * the mark. Any mark creations are noted as to whether the user created
54 * it or if it was created by an undo. The former cannot be reset by another
55 * undo, but the latter may.
56 *
57 * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
58 * the absolute mark locations sets both, so that "m'" and "m`" work like
59 * they, ah, for lack of a better word, "should".
60 */
61
62 /*
63 * mark_init --
64 * Set up the marks.
65 *
66 * PUBLIC: int mark_init(SCR *, EXF *);
67 */
68 int
mark_init(SCR * sp,EXF * ep)69 mark_init(
70 SCR *sp,
71 EXF *ep)
72 {
73 /*
74 * !!!
75 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
76 *
77 * Set up the marks.
78 */
79 SLIST_INIT(ep->marks);
80 return (0);
81 }
82
83 /*
84 * mark_end --
85 * Free up the marks.
86 *
87 * PUBLIC: int mark_end(SCR *, EXF *);
88 */
89 int
mark_end(SCR * sp,EXF * ep)90 mark_end(
91 SCR *sp,
92 EXF *ep)
93 {
94 LMARK *lmp;
95
96 /*
97 * !!!
98 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
99 */
100 while ((lmp = SLIST_FIRST(ep->marks)) != NULL) {
101 SLIST_REMOVE_HEAD(ep->marks, q);
102 free(lmp);
103 }
104 return (0);
105 }
106
107 /*
108 * mark_get --
109 * Get the location referenced by a mark.
110 *
111 * PUBLIC: int mark_get(SCR *, ARG_CHAR_T, MARK *, mtype_t);
112 */
113 int
mark_get(SCR * sp,ARG_CHAR_T key,MARK * mp,mtype_t mtype)114 mark_get(
115 SCR *sp,
116 ARG_CHAR_T key,
117 MARK *mp,
118 mtype_t mtype)
119 {
120 LMARK *lmp;
121
122 if (key == ABSMARK2)
123 key = ABSMARK1;
124
125 lmp = mark_find(sp, key);
126 if (lmp == NULL || lmp->name != key) {
127 msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
128 return (1);
129 }
130 if (F_ISSET(lmp, MARK_DELETED)) {
131 msgq(sp, mtype,
132 "018|Mark %s: the line was deleted", KEY_NAME(sp, key));
133 return (1);
134 }
135
136 /*
137 * !!!
138 * The absolute mark is initialized to lno 1/cno 0, and historically
139 * you could use it in an empty file. Make such a mark always work.
140 */
141 if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
142 msgq(sp, mtype,
143 "019|Mark %s: cursor position no longer exists",
144 KEY_NAME(sp, key));
145 return (1);
146 }
147 mp->lno = lmp->lno;
148 mp->cno = lmp->cno;
149 return (0);
150 }
151
152 /*
153 * mark_set --
154 * Set the location referenced by a mark.
155 *
156 * PUBLIC: int mark_set(SCR *, ARG_CHAR_T, MARK *, int);
157 */
158 int
mark_set(SCR * sp,ARG_CHAR_T key,MARK * value,int userset)159 mark_set(
160 SCR *sp,
161 ARG_CHAR_T key,
162 MARK *value,
163 int userset)
164 {
165 LMARK *lmp, *lmt;
166
167 if (key == ABSMARK2)
168 key = ABSMARK1;
169
170 /*
171 * The rules are simple. If the user is setting a mark (if it's a
172 * new mark this is always true), it always happens. If not, it's
173 * an undo, and we set it if it's not already set or if it was set
174 * by a previous undo.
175 */
176 lmp = mark_find(sp, key);
177 if (lmp == NULL || lmp->name != key) {
178 MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
179 if (lmp == NULL) {
180 SLIST_INSERT_HEAD(sp->ep->marks, lmt, q);
181 } else
182 SLIST_INSERT_AFTER(lmp, lmt, q);
183 lmp = lmt;
184 } else if (!userset &&
185 !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
186 return (0);
187
188 lmp->lno = value->lno;
189 lmp->cno = value->cno;
190 lmp->name = key;
191 lmp->flags = userset ? MARK_USERSET : 0;
192 return (0);
193 }
194
195 /*
196 * mark_find --
197 * Find the requested mark, or, the slot immediately before
198 * where it would go.
199 */
200 static LMARK *
mark_find(SCR * sp,ARG_CHAR_T key)201 mark_find(
202 SCR *sp,
203 ARG_CHAR_T key)
204 {
205 LMARK *lmp, *lastlmp = NULL;
206
207 /*
208 * Return the requested mark or the slot immediately before
209 * where it should go.
210 */
211 SLIST_FOREACH(lmp, sp->ep->marks, q) {
212 if (lmp->name >= key)
213 return (lmp->name == key ? lmp : lastlmp);
214 lastlmp = lmp;
215 }
216 return (lastlmp);
217 }
218
219 /*
220 * mark_insdel --
221 * Update the marks based on an insertion or deletion.
222 *
223 * PUBLIC: int mark_insdel(SCR *, lnop_t, recno_t);
224 */
225 int
mark_insdel(SCR * sp,lnop_t op,recno_t lno)226 mark_insdel(
227 SCR *sp,
228 lnop_t op,
229 recno_t lno)
230 {
231 LMARK *lmp;
232 recno_t lline;
233
234 switch (op) {
235 case LINE_APPEND:
236 /* All insert/append operations are done as inserts. */
237 abort();
238 case LINE_DELETE:
239 SLIST_FOREACH(lmp, sp->ep->marks, q)
240 if (lmp->lno >= lno)
241 if (lmp->lno == lno) {
242 F_SET(lmp, MARK_DELETED);
243 (void)log_mark(sp, lmp);
244 } else
245 --lmp->lno;
246 break;
247 case LINE_INSERT:
248 /*
249 * XXX
250 * Very nasty special case. If the file was empty, then we're
251 * adding the first line, which is a replacement. So, we don't
252 * modify the marks. This is a hack to make:
253 *
254 * mz:r!echo foo<carriage-return>'z
255 *
256 * work, i.e. historically you could mark the "line" in an empty
257 * file and replace it, and continue to use the mark. Insane,
258 * well, yes, I know, but someone complained.
259 *
260 * Check for line #2 before going to the end of the file.
261 */
262 if (!db_exist(sp, 2)) {
263 if (db_last(sp, &lline))
264 return (1);
265 if (lline == 1)
266 return (0);
267 }
268
269 SLIST_FOREACH(lmp, sp->ep->marks, q)
270 if (lmp->lno >= lno)
271 ++lmp->lno;
272 break;
273 case LINE_RESET:
274 break;
275 }
276 return (0);
277 }
278