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