GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../common/mark.c Lines: 0 64 0.0 %
Date: 2017-11-07 Branches: 0 68 0.0 %

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