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