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