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