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