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