GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../common/line.c Lines: 0 198 0.0 %
Date: 2017-11-13 Branches: 0 138 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: line.c,v 1.15 2016/01/06 22:28:52 millert 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
#include <sys/time.h>
17
18
#include <bitstring.h>
19
#include <errno.h>
20
#include <limits.h>
21
#include <stdio.h>
22
#include <string.h>
23
24
#include "common.h"
25
#include "../vi/vi.h"
26
27
static int scr_update(SCR *, recno_t, lnop_t, int);
28
29
/*
30
 * db_eget --
31
 *	Front-end to db_get, special case handling for empty files.
32
 *
33
 * PUBLIC: int db_eget(SCR *, recno_t, char **, size_t *, int *);
34
 */
35
int
36
db_eget(SCR *sp, recno_t lno, char **pp, size_t *lenp, int *isemptyp)
37
{
38
	recno_t l1;
39
40
	if (isemptyp != NULL)
41
		*isemptyp = 0;
42
43
	/* If the line exists, simply return it. */
44
	if (!db_get(sp, lno, 0, pp, lenp))
45
		return (0);
46
47
	/*
48
	 * If the user asked for line 0 or line 1, i.e. the only possible
49
	 * line in an empty file, find the last line of the file; db_last
50
	 * fails loudly.
51
	 */
52
	if ((lno == 0 || lno == 1) && db_last(sp, &l1))
53
		return (1);
54
55
	/* If the file isn't empty, fail loudly. */
56
	if ((lno != 0 && lno != 1) || l1 != 0) {
57
		db_err(sp, lno);
58
		return (1);
59
	}
60
61
	if (isemptyp != NULL)
62
		*isemptyp = 1;
63
64
	return (1);
65
}
66
67
/*
68
 * db_get --
69
 *	Look in the text buffers for a line, followed by the cache, followed
70
 *	by the database.
71
 *
72
 * PUBLIC: int db_get(SCR *, recno_t, u_int32_t, char **, size_t *);
73
 */
74
int
75
db_get(SCR *sp, recno_t lno, u_int32_t flags, char **pp, size_t *lenp)
76
{
77
	DBT data, key;
78
	EXF *ep;
79
	TEXT *tp;
80
	recno_t l1, l2;
81
82
	/*
83
	 * The underlying recno stuff handles zero by returning NULL, but
84
	 * have to have an OOB condition for the look-aside into the input
85
	 * buffer anyway.
86
	 */
87
	if (lno == 0)
88
		goto err1;
89
90
	/* Check for no underlying file. */
91
	if ((ep = sp->ep) == NULL) {
92
		ex_emsg(sp, NULL, EXM_NOFILEYET);
93
		goto err3;
94
	}
95
96
	if (LF_ISSET(DBG_NOCACHE))
97
		goto nocache;
98
99
	/*
100
	 * Look-aside into the TEXT buffers and see if the line we want
101
	 * is there.
102
	 */
103
	if (F_ISSET(sp, SC_TINPUT)) {
104
		l1 = TAILQ_FIRST(&sp->tiq)->lno;
105
		l2 = TAILQ_LAST(&sp->tiq, _texth)->lno;
106
		if (l1 <= lno && l2 >= lno) {
107
#if defined(DEBUG) && 0
108
	TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno);
109
#endif
110
			TAILQ_FOREACH(tp, &sp->tiq, q) {
111
				if (tp->lno == lno)
112
					break;
113
			}
114
			if (lenp != NULL)
115
				*lenp = tp->len;
116
			if (pp != NULL)
117
				*pp = tp->lb;
118
			return (0);
119
		}
120
		/*
121
		 * Adjust the line number for the number of lines used
122
		 * by the text input buffers.
123
		 */
124
		if (lno > l2)
125
			lno -= l2 - l1;
126
	}
127
128
	/* Look-aside into the cache, and see if the line we want is there. */
129
	if (lno == ep->c_lno) {
130
#if defined(DEBUG) && 0
131
	TRACE(sp, "retrieve cached line %lu\n", (u_long)lno);
132
#endif
133
		if (lenp != NULL)
134
			*lenp = ep->c_len;
135
		if (pp != NULL)
136
			*pp = ep->c_lp;
137
		return (0);
138
	}
139
	ep->c_lno = OOBLNO;
140
141
nocache:
142
	/* Get the line from the underlying database. */
143
	key.data = &lno;
144
	key.size = sizeof(lno);
145
	switch (ep->db->get(ep->db, &key, &data, 0)) {
146
        case -1:
147
		goto err2;
148
	case 1:
149
err1:		if (LF_ISSET(DBG_FATAL))
150
err2:			db_err(sp, lno);
151
err3:		if (lenp != NULL)
152
			*lenp = 0;
153
		if (pp != NULL)
154
			*pp = NULL;
155
		return (1);
156
	}
157
158
	/* Reset the cache. */
159
	ep->c_lno = lno;
160
	ep->c_len = data.size;
161
	ep->c_lp = data.data;
162
163
#if defined(DEBUG) && 0
164
	TRACE(sp, "retrieve DB line %lu\n", (u_long)lno);
165
#endif
166
	if (lenp != NULL)
167
		*lenp = data.size;
168
	if (pp != NULL)
169
		*pp = ep->c_lp;
170
	return (0);
171
}
172
173
/*
174
 * db_delete --
175
 *	Delete a line from the file.
176
 *
177
 * PUBLIC: int db_delete(SCR *, recno_t);
178
 */
179
int
180
db_delete(SCR *sp, recno_t lno)
181
{
182
	DBT key;
183
	EXF *ep;
184
185
#if defined(DEBUG) && 0
186
	TRACE(sp, "delete line %lu\n", (u_long)lno);
187
#endif
188
	/* Check for no underlying file. */
189
	if ((ep = sp->ep) == NULL) {
190
		ex_emsg(sp, NULL, EXM_NOFILEYET);
191
		return (1);
192
	}
193
194
	/* Update marks, @ and global commands. */
195
	if (mark_insdel(sp, LINE_DELETE, lno))
196
		return (1);
197
	if (ex_g_insdel(sp, LINE_DELETE, lno))
198
		return (1);
199
200
	/* Log change. */
201
	log_line(sp, lno, LOG_LINE_DELETE);
202
203
	/* Update file. */
204
	key.data = &lno;
205
	key.size = sizeof(lno);
206
	if (ep->db->del(ep->db, &key, 0) == 1) {
207
		msgq(sp, M_SYSERR,
208
		    "unable to delete line %lu", (u_long)lno);
209
		return (1);
210
	}
211
212
	/* Flush the cache, update line count, before screen update. */
213
	if (lno <= ep->c_lno)
214
		ep->c_lno = OOBLNO;
215
	if (ep->c_nlines != OOBLNO)
216
		--ep->c_nlines;
217
218
	/* File now modified. */
219
	if (F_ISSET(ep, F_FIRSTMODIFY))
220
		(void)rcv_init(sp);
221
	F_SET(ep, F_MODIFIED);
222
223
	/* Update screen. */
224
	return (scr_update(sp, lno, LINE_DELETE, 1));
225
}
226
227
/*
228
 * db_append --
229
 *	Append a line into the file.
230
 *
231
 * PUBLIC: int db_append(SCR *, int, recno_t, char *, size_t);
232
 */
233
int
234
db_append(SCR *sp, int update, recno_t lno, char *p, size_t len)
235
{
236
	DBT data, key;
237
	EXF *ep;
238
	int rval;
239
240
#if defined(DEBUG) && 0
241
	TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
242
#endif
243
	/* Check for no underlying file. */
244
	if ((ep = sp->ep) == NULL) {
245
		ex_emsg(sp, NULL, EXM_NOFILEYET);
246
		return (1);
247
	}
248
249
	/* Update file. */
250
	key.data = &lno;
251
	key.size = sizeof(lno);
252
	data.data = p;
253
	data.size = len;
254
	if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
255
		msgq(sp, M_SYSERR,
256
		    "unable to append to line %lu", (u_long)lno);
257
		return (1);
258
	}
259
260
	/* Flush the cache, update line count, before screen update. */
261
	if (lno < ep->c_lno)
262
		ep->c_lno = OOBLNO;
263
	if (ep->c_nlines != OOBLNO)
264
		++ep->c_nlines;
265
266
	/* File now dirty. */
267
	if (F_ISSET(ep, F_FIRSTMODIFY))
268
		(void)rcv_init(sp);
269
	F_SET(ep, F_MODIFIED);
270
271
	/* Log change. */
272
	log_line(sp, lno + 1, LOG_LINE_APPEND);
273
274
	/* Update marks, @ and global commands. */
275
	rval = 0;
276
	if (mark_insdel(sp, LINE_INSERT, lno + 1))
277
		rval = 1;
278
	if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
279
		rval = 1;
280
281
	/*
282
	 * Update screen.
283
	 *
284
	 * XXX
285
	 * Nasty hack.  If multiple lines are input by the user, they aren't
286
	 * committed until an <ESC> is entered.  The problem is the screen was
287
	 * updated/scrolled as each line was entered.  So, when this routine
288
	 * is called to copy the new lines from the cut buffer into the file,
289
	 * it has to know not to update the screen again.
290
	 */
291
	return (scr_update(sp, lno, LINE_APPEND, update) || rval);
292
}
293
294
/*
295
 * db_insert --
296
 *	Insert a line into the file.
297
 *
298
 * PUBLIC: int db_insert(SCR *, recno_t, char *, size_t);
299
 */
300
int
301
db_insert(SCR *sp, recno_t lno, char *p, size_t len)
302
{
303
	DBT data, key;
304
	EXF *ep;
305
	int rval;
306
307
#if defined(DEBUG) && 0
308
	TRACE(sp, "insert before %lu: len %lu {%.*s}\n",
309
	    (u_long)lno, (u_long)len, MIN(len, 20), p);
310
#endif
311
	/* Check for no underlying file. */
312
	if ((ep = sp->ep) == NULL) {
313
		ex_emsg(sp, NULL, EXM_NOFILEYET);
314
		return (1);
315
	}
316
317
	/* Update file. */
318
	key.data = &lno;
319
	key.size = sizeof(lno);
320
	data.data = p;
321
	data.size = len;
322
	if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
323
		msgq(sp, M_SYSERR,
324
		    "unable to insert at line %lu", (u_long)lno);
325
		return (1);
326
	}
327
328
	/* Flush the cache, update line count, before screen update. */
329
	if (lno >= ep->c_lno)
330
		ep->c_lno = OOBLNO;
331
	if (ep->c_nlines != OOBLNO)
332
		++ep->c_nlines;
333
334
	/* File now dirty. */
335
	if (F_ISSET(ep, F_FIRSTMODIFY))
336
		(void)rcv_init(sp);
337
	F_SET(ep, F_MODIFIED);
338
339
	/* Log change. */
340
	log_line(sp, lno, LOG_LINE_INSERT);
341
342
	/* Update marks, @ and global commands. */
343
	rval = 0;
344
	if (mark_insdel(sp, LINE_INSERT, lno))
345
		rval = 1;
346
	if (ex_g_insdel(sp, LINE_INSERT, lno))
347
		rval = 1;
348
349
	/* Update screen. */
350
	return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
351
}
352
353
/*
354
 * db_set --
355
 *	Store a line in the file.
356
 *
357
 * PUBLIC: int db_set(SCR *, recno_t, char *, size_t);
358
 */
359
int
360
db_set(SCR *sp, recno_t lno, char *p, size_t len)
361
{
362
	DBT data, key;
363
	EXF *ep;
364
365
#if defined(DEBUG) && 0
366
	TRACE(sp, "replace line %lu: len %lu {%.*s}\n",
367
	    (u_long)lno, (u_long)len, MIN(len, 20), p);
368
#endif
369
370
	/* Check for no underlying file. */
371
	if ((ep = sp->ep) == NULL) {
372
		ex_emsg(sp, NULL, EXM_NOFILEYET);
373
		return (1);
374
	}
375
376
	/* Log before change. */
377
	log_line(sp, lno, LOG_LINE_RESET_B);
378
379
	/* Update file. */
380
	key.data = &lno;
381
	key.size = sizeof(lno);
382
	data.data = p;
383
	data.size = len;
384
	if (ep->db->put(ep->db, &key, &data, 0) == -1) {
385
		msgq(sp, M_SYSERR,
386
		    "unable to store line %lu", (u_long)lno);
387
		return (1);
388
	}
389
390
	/* Flush the cache, before logging or screen update. */
391
	if (lno == ep->c_lno)
392
		ep->c_lno = OOBLNO;
393
394
	/* File now dirty. */
395
	if (F_ISSET(ep, F_FIRSTMODIFY))
396
		(void)rcv_init(sp);
397
	F_SET(ep, F_MODIFIED);
398
399
	/* Log after change. */
400
	log_line(sp, lno, LOG_LINE_RESET_F);
401
402
	/* Update screen. */
403
	return (scr_update(sp, lno, LINE_RESET, 1));
404
}
405
406
/*
407
 * db_exist --
408
 *	Return if a line exists.
409
 *
410
 * PUBLIC: int db_exist(SCR *, recno_t);
411
 */
412
int
413
db_exist(SCR *sp, recno_t lno)
414
{
415
	EXF *ep;
416
417
	/* Check for no underlying file. */
418
	if ((ep = sp->ep) == NULL) {
419
		ex_emsg(sp, NULL, EXM_NOFILEYET);
420
		return (1);
421
	}
422
423
	if (lno == OOBLNO)
424
		return (0);
425
426
	/*
427
	 * Check the last-line number cache.  Adjust the cached line
428
	 * number for the lines used by the text input buffers.
429
	 */
430
	if (ep->c_nlines != OOBLNO)
431
		return (lno <= (F_ISSET(sp, SC_TINPUT) ?
432
		    ep->c_nlines + (TAILQ_LAST(&sp->tiq, _texth)->lno
433
		    - TAILQ_FIRST(&sp->tiq)->lno) : ep->c_nlines));
434
435
	/* Go get the line. */
436
	return (!db_get(sp, lno, 0, NULL, NULL));
437
}
438
439
/*
440
 * db_last --
441
 *	Return the number of lines in the file.
442
 *
443
 * PUBLIC: int db_last(SCR *, recno_t *);
444
 */
445
int
446
db_last(SCR *sp, recno_t *lnop)
447
{
448
	DBT data, key;
449
	EXF *ep;
450
	recno_t lno;
451
452
	/* Check for no underlying file. */
453
	if ((ep = sp->ep) == NULL) {
454
		ex_emsg(sp, NULL, EXM_NOFILEYET);
455
		return (1);
456
	}
457
458
	/*
459
	 * Check the last-line number cache.  Adjust the cached line
460
	 * number for the lines used by the text input buffers.
461
	 */
462
	if (ep->c_nlines != OOBLNO) {
463
		*lnop = ep->c_nlines;
464
		if (F_ISSET(sp, SC_TINPUT))
465
			*lnop += TAILQ_LAST(&sp->tiq, _texth)->lno -
466
			    TAILQ_FIRST(&sp->tiq)->lno;
467
		return (0);
468
	}
469
470
	key.data = &lno;
471
	key.size = sizeof(lno);
472
473
	switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
474
        case -1:
475
		msgq(sp, M_SYSERR, "unable to get last line");
476
		*lnop = 0;
477
		return (1);
478
        case 1:
479
		*lnop = 0;
480
		return (0);
481
	default:
482
		break;
483
	}
484
485
	/* Fill the cache. */
486
	memcpy(&lno, key.data, sizeof(lno));
487
	ep->c_nlines = ep->c_lno = lno;
488
	ep->c_len = data.size;
489
	ep->c_lp = data.data;
490
491
	/* Return the value. */
492
	*lnop = (F_ISSET(sp, SC_TINPUT) &&
493
	    TAILQ_LAST(&sp->tiq, _texth)->lno > lno ?
494
	    TAILQ_LAST(&sp->tiq, _texth)->lno : lno);
495
	return (0);
496
}
497
498
/*
499
 * db_err --
500
 *	Report a line error.
501
 *
502
 * PUBLIC: void db_err(SCR *, recno_t);
503
 */
504
void
505
db_err(SCR *sp, recno_t lno)
506
{
507
	msgq(sp, M_ERR,
508
	    "Error: unable to retrieve line %lu", (u_long)lno);
509
}
510
511
/*
512
 * scr_update --
513
 *	Update all of the screens that are backed by the file that
514
 *	just changed.
515
 */
516
static int
517
scr_update(SCR *sp, recno_t lno, lnop_t op, int current)
518
{
519
	EXF *ep;
520
	SCR *tsp;
521
522
	if (F_ISSET(sp, SC_EX))
523
		return (0);
524
525
	ep = sp->ep;
526
	if (ep->refcnt != 1)
527
		TAILQ_FOREACH(tsp, &sp->gp->dq, q)
528
			if (sp != tsp && tsp->ep == ep)
529
				if (vs_change(tsp, lno, op))
530
					return (1);
531
	return (current ? vs_change(sp, lno, op) : 0);
532
}