1 /* $NetBSD: mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
3 * Copyright (c) 1992, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
13 #include <sys/cdefs.h>
16 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 ";
19 __RCSID("$NetBSD: mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
22 #include <sys/types.h>
23 #include <sys/queue.h>
25 #include <bitstring.h>
34 static LMARK
*mark_find
__P((SCR
*, ARG_CHAR_T
));
37 * Marks are maintained in a key sorted doubly linked list. We can't
38 * use arrays because we have no idea how big an index key could be.
39 * The underlying assumption is that users don't have more than, say,
40 * 10 marks at any one time, so this will be is fast enough.
42 * Marks are fixed, and modifications to the line don't update the mark's
43 * position in the line. This can be hard. If you add text to the line,
44 * place a mark in that text, undo the addition and use ` to move to the
45 * mark, the location will have disappeared. It's tempting to try to adjust
46 * the mark with the changes in the line, but this is hard to do, especially
47 * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
48 * would move to the first non-blank on the line when the mark location was
49 * past the end of the line. This can be complicated by deleting to a mark
50 * that has disappeared using the ` command. Historic vi treated this as
51 * a line-mode motion and deleted the line. This implementation complains to
54 * In historic vi, marks returned if the operation was undone, unless the
55 * mark had been subsequently reset. Tricky. This is hard to start with,
56 * but in the presence of repeated undo it gets nasty. When a line is
57 * deleted, we delete (and log) any marks on that line. An undo will create
58 * the mark. Any mark creations are noted as to whether the user created
59 * it or if it was created by an undo. The former cannot be reset by another
60 * undo, but the latter may.
62 * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
63 * the absolute mark locations sets both, so that "m'" and "m`" work like
64 * they, ah, for lack of a better word, "should".
71 * PUBLIC: int mark_init __P((SCR *, EXF *));
74 mark_init(SCR
*sp
, EXF
*ep
)
78 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
82 LIST_INIT(&ep
->marks
);
90 * PUBLIC: int mark_end __P((SCR *, EXF *));
93 mark_end(SCR
*sp
, EXF
*ep
)
99 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
101 while ((lmp
= LIST_FIRST(&ep
->marks
)) != NULL
) {
110 * Get the location referenced by a mark.
112 * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
115 mark_get(SCR
*sp
, ARG_CHAR_T key
, MARK
*mp
, mtype_t mtype
)
122 lmp
= mark_find(sp
, key
);
123 if (lmp
== NULL
|| (ARG_CHAR_T
)lmp
->name
!= key
) {
124 msgq(sp
, mtype
, "017|Mark %s: not set", KEY_NAME(sp
, key
));
127 if (F_ISSET(lmp
, MARK_DELETED
)) {
129 "018|Mark %s: the line was deleted", KEY_NAME(sp
, key
));
135 * The absolute mark is initialized to lno 1/cno 0, and historically
136 * you could use it in an empty file. Make such a mark always work.
138 if ((lmp
->lno
!= 1 || lmp
->cno
!= 0) && !db_exist(sp
, lmp
->lno
)) {
140 "019|Mark %s: cursor position no longer exists",
151 * Set the location referenced by a mark.
153 * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
156 mark_set(SCR
*sp
, ARG_CHAR_T key
, MARK
*value
, int userset
)
164 * The rules are simple. If the user is setting a mark (if it's a
165 * new mark this is always true), it always happens. If not, it's
166 * an undo, and we set it if it's not already set or if it was set
167 * by a previous undo.
169 lmp
= mark_find(sp
, key
);
170 if (lmp
== NULL
|| (ARG_CHAR_T
)lmp
->name
!= key
) {
171 MALLOC_RET(sp
, lmt
, LMARK
*, sizeof(LMARK
));
173 LIST_INSERT_HEAD(&sp
->ep
->marks
, lmt
, q
);
175 LIST_INSERT_AFTER(lmp
, lmt
, q
);
177 } else if (!userset
&&
178 !F_ISSET(lmp
, MARK_DELETED
) && F_ISSET(lmp
, MARK_USERSET
))
181 lmp
->lno
= value
->lno
;
182 lmp
->cno
= value
->cno
;
184 lmp
->flags
= userset
? MARK_USERSET
: 0;
190 * Find the requested mark, or, the slot immediately before
194 mark_find(SCR
*sp
, ARG_CHAR_T key
)
196 LMARK
*lmp
, *lastlmp
;
199 * Return the requested mark or the slot immediately before
200 * where it should go.
202 for (lastlmp
= NULL
, lmp
= LIST_FIRST(&sp
->ep
->marks
);
203 lmp
!= NULL
; lastlmp
= lmp
, lmp
= LIST_NEXT(lmp
, q
))
204 if ((ARG_CHAR_T
)lmp
->name
>= key
)
205 return ((ARG_CHAR_T
)lmp
->name
== key
? lmp
: lastlmp
);
211 * Update the marks based on an insertion or deletion.
213 * PUBLIC: int mark_insdel __P((SCR *, lnop_t, db_recno_t));
216 mark_insdel(SCR
*sp
, lnop_t op
, db_recno_t lno
)
223 /* All insert/append operations are done as inserts. */
226 LIST_FOREACH(lmp
, &sp
->ep
->marks
, q
)
227 if (lmp
->lno
>= lno
) {
228 if (lmp
->lno
== lno
) {
229 F_SET(lmp
, MARK_DELETED
);
230 (void)log_mark(sp
, lmp
);
238 * Very nasty special case. If the file was empty, then we're
239 * adding the first line, which is a replacement. So, we don't
240 * modify the marks. This is a hack to make:
242 * mz:r!echo foo<carriage-return>'z
244 * work, i.e. historically you could mark the "line" in an empty
245 * file and replace it, and continue to use the mark. Insane,
246 * well, yes, I know, but someone complained.
248 * Check for line #2 before going to the end of the file.
250 if (!db_exist(sp
, 2)) {
251 if (db_last(sp
, &lline
))
257 LIST_FOREACH(lmp
, &sp
->ep
->marks
, q
)