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