1 |
|
|
/* $OpenBSD: mark.c,v 1.14 2016/05/27 09:18:11 martijn Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
|
17 |
|
|
#include <bitstring.h> |
18 |
|
|
#include <errno.h> |
19 |
|
|
#include <limits.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
#include <stdlib.h> |
22 |
|
|
#include <string.h> |
23 |
|
|
|
24 |
|
|
#include "common.h" |
25 |
|
|
|
26 |
|
|
static LMARK *mark_find(SCR *, CHAR_T); |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
* Marks are maintained in a key sorted doubly linked list. We can't |
30 |
|
|
* use arrays because we have no idea how big an index key could be. |
31 |
|
|
* The underlying assumption is that users don't have more than, say, |
32 |
|
|
* 10 marks at any one time, so this will be is fast enough. |
33 |
|
|
* |
34 |
|
|
* Marks are fixed, and modifications to the line don't update the mark's |
35 |
|
|
* position in the line. This can be hard. If you add text to the line, |
36 |
|
|
* place a mark in that text, undo the addition and use ` to move to the |
37 |
|
|
* mark, the location will have disappeared. It's tempting to try to adjust |
38 |
|
|
* the mark with the changes in the line, but this is hard to do, especially |
39 |
|
|
* if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi |
40 |
|
|
* would move to the first non-blank on the line when the mark location was |
41 |
|
|
* past the end of the line. This can be complicated by deleting to a mark |
42 |
|
|
* that has disappeared using the ` command. Historic vi treated this as |
43 |
|
|
* a line-mode motion and deleted the line. This implementation complains to |
44 |
|
|
* the user. |
45 |
|
|
* |
46 |
|
|
* In historic vi, marks returned if the operation was undone, unless the |
47 |
|
|
* mark had been subsequently reset. Tricky. This is hard to start with, |
48 |
|
|
* but in the presence of repeated undo it gets nasty. When a line is |
49 |
|
|
* deleted, we delete (and log) any marks on that line. An undo will create |
50 |
|
|
* the mark. Any mark creations are noted as to whether the user created |
51 |
|
|
* it or if it was created by an undo. The former cannot be reset by another |
52 |
|
|
* undo, but the latter may. |
53 |
|
|
* |
54 |
|
|
* All of these routines translate ABSMARK2 to ABSMARK1. Setting either of |
55 |
|
|
* the absolute mark locations sets both, so that "m'" and "m`" work like |
56 |
|
|
* they, ah, for lack of a better word, "should". |
57 |
|
|
*/ |
58 |
|
|
|
59 |
|
|
/* |
60 |
|
|
* mark_init -- |
61 |
|
|
* Set up the marks. |
62 |
|
|
* |
63 |
|
|
* PUBLIC: int mark_init(SCR *, EXF *); |
64 |
|
|
*/ |
65 |
|
|
int |
66 |
|
|
mark_init(SCR *sp, EXF *ep) |
67 |
|
|
{ |
68 |
|
|
/* |
69 |
|
|
* !!! |
70 |
|
|
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. |
71 |
|
|
* |
72 |
|
|
* Set up the marks. |
73 |
|
|
*/ |
74 |
|
|
LIST_INIT(&ep->marks); |
75 |
|
|
return (0); |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
/* |
79 |
|
|
* mark_end -- |
80 |
|
|
* Free up the marks. |
81 |
|
|
* |
82 |
|
|
* PUBLIC: int mark_end(SCR *, EXF *); |
83 |
|
|
*/ |
84 |
|
|
int |
85 |
|
|
mark_end(SCR *sp, EXF *ep) |
86 |
|
|
{ |
87 |
|
|
LMARK *lmp; |
88 |
|
|
|
89 |
|
|
/* |
90 |
|
|
* !!! |
91 |
|
|
* ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. |
92 |
|
|
*/ |
93 |
|
|
while ((lmp = LIST_FIRST(&ep->marks)) != NULL) { |
94 |
|
|
LIST_REMOVE(lmp, q); |
95 |
|
|
free(lmp); |
96 |
|
|
} |
97 |
|
|
return (0); |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
/* |
101 |
|
|
* mark_get -- |
102 |
|
|
* Get the location referenced by a mark. |
103 |
|
|
* |
104 |
|
|
* PUBLIC: int mark_get(SCR *, CHAR_T, MARK *, mtype_t); |
105 |
|
|
*/ |
106 |
|
|
int |
107 |
|
|
mark_get(SCR *sp, CHAR_T key, MARK *mp, mtype_t mtype) |
108 |
|
|
{ |
109 |
|
|
LMARK *lmp; |
110 |
|
|
|
111 |
|
|
if (key == ABSMARK2) |
112 |
|
|
key = ABSMARK1; |
113 |
|
|
|
114 |
|
|
lmp = mark_find(sp, key); |
115 |
|
|
if (lmp == NULL || lmp->name != key) { |
116 |
|
|
msgq(sp, mtype, "Mark %s: not set", KEY_NAME(sp, key)); |
117 |
|
|
return (1); |
118 |
|
|
} |
119 |
|
|
if (F_ISSET(lmp, MARK_DELETED)) { |
120 |
|
|
msgq(sp, mtype, |
121 |
|
|
"Mark %s: the line was deleted", KEY_NAME(sp, key)); |
122 |
|
|
return (1); |
123 |
|
|
} |
124 |
|
|
|
125 |
|
|
/* |
126 |
|
|
* !!! |
127 |
|
|
* The absolute mark is initialized to lno 1/cno 0, and historically |
128 |
|
|
* you could use it in an empty file. Make such a mark always work. |
129 |
|
|
*/ |
130 |
|
|
if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) { |
131 |
|
|
msgq(sp, mtype, |
132 |
|
|
"Mark %s: cursor position no longer exists", |
133 |
|
|
KEY_NAME(sp, key)); |
134 |
|
|
return (1); |
135 |
|
|
} |
136 |
|
|
mp->lno = lmp->lno; |
137 |
|
|
mp->cno = lmp->cno; |
138 |
|
|
return (0); |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
/* |
142 |
|
|
* mark_set -- |
143 |
|
|
* Set the location referenced by a mark. |
144 |
|
|
* |
145 |
|
|
* PUBLIC: int mark_set(SCR *, CHAR_T, MARK *, int); |
146 |
|
|
*/ |
147 |
|
|
int |
148 |
|
|
mark_set(SCR *sp, CHAR_T key, MARK *value, int userset) |
149 |
|
|
{ |
150 |
|
|
LMARK *lmp, *lmt; |
151 |
|
|
|
152 |
|
|
if (key == ABSMARK2) |
153 |
|
|
key = ABSMARK1; |
154 |
|
|
|
155 |
|
|
/* |
156 |
|
|
* The rules are simple. If the user is setting a mark (if it's a |
157 |
|
|
* new mark this is always true), it always happens. If not, it's |
158 |
|
|
* an undo, and we set it if it's not already set or if it was set |
159 |
|
|
* by a previous undo. |
160 |
|
|
*/ |
161 |
|
|
lmp = mark_find(sp, key); |
162 |
|
|
if (lmp == NULL || lmp->name != key) { |
163 |
|
|
MALLOC_RET(sp, lmt, sizeof(LMARK)); |
164 |
|
|
if (lmp == NULL) { |
165 |
|
|
LIST_INSERT_HEAD(&sp->ep->marks, lmt, q); |
166 |
|
|
} else |
167 |
|
|
LIST_INSERT_AFTER(lmp, lmt, q); |
168 |
|
|
lmp = lmt; |
169 |
|
|
} else if (!userset && |
170 |
|
|
!F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET)) |
171 |
|
|
return (0); |
172 |
|
|
|
173 |
|
|
lmp->lno = value->lno; |
174 |
|
|
lmp->cno = value->cno; |
175 |
|
|
lmp->name = key; |
176 |
|
|
lmp->flags = userset ? MARK_USERSET : 0; |
177 |
|
|
return (0); |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
/* |
181 |
|
|
* mark_find -- |
182 |
|
|
* Find the requested mark, or, the slot immediately before |
183 |
|
|
* where it would go. |
184 |
|
|
*/ |
185 |
|
|
static LMARK * |
186 |
|
|
mark_find(SCR *sp, CHAR_T key) |
187 |
|
|
{ |
188 |
|
|
LMARK *lmp, *lastlmp; |
189 |
|
|
|
190 |
|
|
/* |
191 |
|
|
* Return the requested mark or the slot immediately before |
192 |
|
|
* where it should go. |
193 |
|
|
*/ |
194 |
|
|
for (lastlmp = NULL, lmp = LIST_FIRST(&sp->ep->marks); |
195 |
|
|
lmp != NULL; lastlmp = lmp, lmp = LIST_NEXT(lmp, q)) |
196 |
|
|
if (lmp->name >= key) |
197 |
|
|
return (lmp->name == key ? lmp : lastlmp); |
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 |
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 |
|
|
LIST_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 |
|
|
LIST_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 |
|
|
} |