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