add svi_column so svi can return sc_col to vi/v_ntext.c
[nvi.git] / common / mark.c
blobcb5b667f9bbef19d54cd73073f789a27685f8f14
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: mark.c,v 8.8 1993/11/18 08:17:07 bostic Exp $ (Berkeley) $Date: 1993/11/18 08:17:07 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include "vi.h"
20 static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T));
23 * Marks are maintained in a key sorted doubly linked list. We can't
24 * use arrays because we have no idea how big an index key could be.
25 * The underlying assumption is that users don't have more than, say,
26 * 10 marks at any one time, so this will be is fast enough.
28 * Marks are fixed, and modifications to the line don't update the mark's
29 * position in the line. This can be hard. If you add text to the line,
30 * place a mark in that text, undo the addition and use ` to move to the
31 * mark, the location will have disappeared. It's tempting to try to adjust
32 * the mark with the changes in the line, but this is hard to do, especially
33 * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
34 * would move to the first non-blank on the line when the mark location was
35 * past the end of the line. This can be complicated by deleting to a mark
36 * that has disappeared using the ` command. Historic vi vi treated this as
37 * a line-mode motion and deleted the line. This implementation complains to
38 * the user.
40 * In historic vi, marks returned if the operation was undone, unless the
41 * mark had been subsequently reset. Tricky. This is hard to start with,
42 * but in the presence of repeated undo it gets nasty. When a line is
43 * deleted, we delete (and log) any marks on that line. An undo will create
44 * the mark. Any mark creations are noted as to whether the user created
45 * it or if it was created by an undo. The former cannot be reset by another
46 * undo, but the latter may.
48 * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
49 * the absolute mark locations sets both, so that "m'" and "m`" work like
50 * they, ah, for lack of a better word, "should".
54 * mark_init --
55 * Set up the marks.
57 int
58 mark_init(sp, ep)
59 SCR *sp;
60 EXF *ep;
62 MARK *mp;
65 * Make sure the marks have been set up. If they
66 * haven't, do so, and create the absolute mark.
68 if ((mp = malloc(sizeof(MARK))) == NULL) {
69 msgq(sp, M_SYSERR, NULL);
70 return (1);
72 mp->lno = 1;
73 mp->cno = 0;
74 mp->name = ABSMARK1;
75 mp->flags = 0;
76 LIST_INSERT_HEAD(&ep->marks, mp, q);
77 return (0);
81 * mark_end --
82 * Free up the marks.
84 int
85 mark_end(sp, ep)
86 SCR *sp;
87 EXF *ep;
89 MARK *mp;
91 while ((mp = ep->marks.lh_first) != NULL) {
92 LIST_REMOVE(mp, q);
93 FREE(mp, sizeof(MARK));
95 return (0);
99 * mark_get --
100 * Get the location referenced by a mark.
102 MARK *
103 mark_get(sp, ep, key)
104 SCR *sp;
105 EXF *ep;
106 ARG_CHAR_T key;
108 MARK *mp;
109 size_t len;
110 char *p;
112 if (key == ABSMARK2)
113 key = ABSMARK1;
115 if ((mp = mark_find(sp, ep, key)) == NULL)
116 return (NULL);
117 if (mp->name != key) {
118 msgq(sp, M_BERR, "Mark %s: not set.", sp->cname[key].name);
119 return (NULL);
121 if (F_ISSET(mp, MARK_DELETED)) {
122 msgq(sp, M_BERR,
123 "Mark %s: the line was deleted.", sp->cname[key].name);
124 return (NULL);
126 if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL ||
127 mp->cno > len || mp->cno == len && len != 0) {
128 msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.",
129 sp->cname[key].name);
130 return (NULL);
132 return (mp);
136 * mark_set --
137 * Set the location referenced by a mark.
140 mark_set(sp, ep, key, value, userset)
141 SCR *sp;
142 EXF *ep;
143 ARG_CHAR_T key;
144 MARK *value;
145 int userset;
147 MARK *mp, *mt;
149 if (key == ABSMARK2)
150 key = ABSMARK1;
152 if ((mp = mark_find(sp, ep, key)) == NULL)
153 return (1);
155 * The rules are simple. If the user is setting a mark (if it's a
156 * new mark this is always true), it always happens. If not, it's
157 * an undo, and we set it if it's not already set or if it was set
158 * by a previous undo.
160 if (mp->name != key) {
161 if ((mt = malloc(sizeof(MARK))) == NULL) {
162 msgq(sp, M_SYSERR, NULL);
163 return (1);
165 LIST_INSERT_AFTER(mp, mt, q);
166 mp = mt;
167 } else if (!userset &&
168 !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET))
169 return (0);
171 mp->lno = value->lno;
172 mp->cno = value->cno;
173 mp->name = key;
174 mp->flags = userset ? MARK_USERSET : 0;
175 return (0);
179 * mark_find --
180 * Find the requested mark, or, the slot immediately before
181 * where it would go.
183 static MARK *
184 mark_find(sp, ep, key)
185 SCR *sp;
186 EXF *ep;
187 ARG_CHAR_T key;
189 MARK *mp, *lastmp;
192 * Return the requested mark or the slot immediately before
193 * where it should go.
195 for (lastmp = NULL,
196 mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
197 if (mp->name >= key)
198 return (mp->name == key ? mp : lastmp);
199 return (lastmp);
203 * mark_delete --
204 * Update the marks based on a deletion.
206 void
207 mark_delete(sp, ep, lno)
208 SCR *sp;
209 EXF *ep;
210 recno_t lno;
212 MARK *mp;
214 for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
215 if (mp->lno >= lno)
216 if (mp->lno == lno) {
217 F_SET(mp, MARK_DELETED);
218 (void)log_mark(sp, ep, mp);
219 } else
220 --mp->lno;
224 * mark_insert --
225 * Update the marks based on an insertion.
227 void
228 mark_insert(sp, ep, lno)
229 SCR *sp;
230 EXF *ep;
231 recno_t lno;
233 MARK *mp;
235 for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next)
236 if (mp->lno >= lno)
237 ++mp->lno;