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