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[] = "$Id: line.c,v 10.26 2011/08/12 12:36:41 zy Exp $";
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_T **, size_t *, int *));
36 */
37int
38db_eget(
39	SCR *sp,
40	recno_t lno,				/* Line number. */
41	CHAR_T **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_T **, size_t *));
80 */
81int
82db_get(
83	SCR *sp,
84	recno_t lno,				/* Line number. */
85	u_int32_t flags,
86	CHAR_T **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	CHAR_T *wp;
94	size_t wlen;
95	size_t nlen;
96
97	/*
98	 * The underlying recno stuff handles zero by returning NULL, but
99	 * have to have an OOB condition for the look-aside into the input
100	 * buffer anyway.
101	 */
102	if (lno == 0)
103		goto err1;
104
105	/* Check for no underlying file. */
106	if ((ep = sp->ep) == NULL) {
107		ex_emsg(sp, NULL, EXM_NOFILEYET);
108		goto err3;
109	}
110
111	if (LF_ISSET(DBG_NOCACHE))
112		goto nocache;
113
114	/*
115	 * Look-aside into the TEXT buffers and see if the line we want
116	 * is there.
117	 */
118	if (F_ISSET(sp, SC_TINPUT)) {
119		l1 = ((TEXT *)TAILQ_FIRST(sp->tiq))->lno;
120		l2 = ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno;
121		if (l1 <= lno && l2 >= lno) {
122#if defined(DEBUG) && 0
123	TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno);
124#endif
125			for (tp = TAILQ_FIRST(sp->tiq);
126			    tp->lno != lno; tp = TAILQ_NEXT(tp, q));
127			if (lenp != NULL)
128				*lenp = tp->len;
129			if (pp != NULL)
130				*pp = tp->lb;
131			return (0);
132		}
133		/*
134		 * Adjust the line number for the number of lines used
135		 * by the text input buffers.
136		 */
137		if (lno > l2)
138			lno -= l2 - l1;
139	}
140
141	/* Look-aside into the cache, and see if the line we want is there. */
142	if (lno == ep->c_lno) {
143#if defined(DEBUG) && 0
144	TRACE(sp, "retrieve cached line %lu\n", (u_long)lno);
145#endif
146		if (lenp != NULL)
147			*lenp = ep->c_len;
148		if (pp != NULL)
149			*pp = ep->c_lp;
150		return (0);
151	}
152	ep->c_lno = OOBLNO;
153
154nocache:
155	nlen = 1024;
156retry:
157	/* Get the line from the underlying database. */
158	key.data = &lno;
159	key.size = sizeof(lno);
160	switch (ep->db->get(ep->db, &key, &data, 0)) {
161	case -1:
162		goto err2;
163	case 1:
164err1:		if (LF_ISSET(DBG_FATAL))
165err2:			db_err(sp, lno);
166alloc_err:
167err3:		if (lenp != NULL)
168			*lenp = 0;
169		if (pp != NULL)
170			*pp = NULL;
171		return (1);
172	case 0:
173		if (data.size > nlen) {
174			nlen = data.size;
175			goto retry;
176		}
177	}
178
179	if (FILE2INT(sp, data.data, data.size, wp, wlen)) {
180		if (!F_ISSET(sp, SC_CONV_ERROR)) {
181			F_SET(sp, SC_CONV_ERROR);
182			msgq(sp, M_ERR, "324|Conversion error on line %d", lno);
183		}
184		goto err3;
185	}
186
187	/* Reset the cache. */
188	if (wp != data.data) {
189		BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
190		MEMCPY(ep->c_lp, wp, wlen);
191	} else
192		ep->c_lp = data.data;
193	ep->c_lno = lno;
194	ep->c_len = wlen;
195
196#if defined(DEBUG) && 0
197	TRACE(sp, "retrieve DB line %lu\n", (u_long)lno);
198#endif
199	if (lenp != NULL)
200		*lenp = wlen;
201	if (pp != NULL)
202		*pp = ep->c_lp;
203	return (0);
204}
205
206/*
207 * db_delete --
208 *	Delete a line from the file.
209 *
210 * PUBLIC: int db_delete __P((SCR *, recno_t));
211 */
212int
213db_delete(
214	SCR *sp,
215	recno_t lno)
216{
217	DBT key;
218	EXF *ep;
219
220#if defined(DEBUG) && 0
221	TRACE(sp, "delete line %lu\n", (u_long)lno);
222#endif
223	/* Check for no underlying file. */
224	if ((ep = sp->ep) == NULL) {
225		ex_emsg(sp, NULL, EXM_NOFILEYET);
226		return (1);
227	}
228
229	/* Update marks, @ and global commands. */
230	if (mark_insdel(sp, LINE_DELETE, lno))
231		return (1);
232	if (ex_g_insdel(sp, LINE_DELETE, lno))
233		return (1);
234
235	/* Log change. */
236	log_line(sp, lno, LOG_LINE_DELETE);
237
238	/* Update file. */
239	key.data = &lno;
240	key.size = sizeof(lno);
241	SIGBLOCK;
242	if (ep->db->del(ep->db, &key, 0) == 1) {
243		msgq(sp, M_SYSERR,
244		    "003|unable to delete line %lu", (u_long)lno);
245		return (1);
246	}
247	SIGUNBLOCK;
248
249	/* Flush the cache, update line count, before screen update. */
250	if (lno <= ep->c_lno)
251		ep->c_lno = OOBLNO;
252	if (ep->c_nlines != OOBLNO)
253		--ep->c_nlines;
254
255	/* File now modified. */
256	if (F_ISSET(ep, F_FIRSTMODIFY))
257		(void)rcv_init(sp);
258	F_SET(ep, F_MODIFIED);
259
260	/* Update screen. */
261	return (scr_update(sp, lno, LINE_DELETE, 1));
262}
263
264/*
265 * db_append --
266 *	Append a line into the file.
267 *
268 * PUBLIC: int db_append __P((SCR *, int, recno_t, CHAR_T *, size_t));
269 */
270int
271db_append(
272	SCR *sp,
273	int update,
274	recno_t lno,
275	CHAR_T *p,
276	size_t len)
277{
278	DBT data, key;
279	EXF *ep;
280	char *fp;
281	size_t flen;
282	int rval;
283
284#if defined(DEBUG) && 0
285	TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
286#endif
287	/* Check for no underlying file. */
288	if ((ep = sp->ep) == NULL) {
289		ex_emsg(sp, NULL, EXM_NOFILEYET);
290		return (1);
291	}
292
293	INT2FILE(sp, p, len, fp, flen);
294
295	/* Update file. */
296	key.data = &lno;
297	key.size = sizeof(lno);
298	data.data = fp;
299	data.size = flen;
300	SIGBLOCK;
301	if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
302		msgq(sp, M_SYSERR,
303		    "004|unable to append to line %lu", (u_long)lno);
304		return (1);
305	}
306	SIGUNBLOCK;
307
308	/* Flush the cache, update line count, before screen update. */
309	if (lno < ep->c_lno)
310		ep->c_lno = OOBLNO;
311	if (ep->c_nlines != OOBLNO)
312		++ep->c_nlines;
313
314	/* File now dirty. */
315	if (F_ISSET(ep, F_FIRSTMODIFY))
316		(void)rcv_init(sp);
317	F_SET(ep, F_MODIFIED);
318
319	/* Log change. */
320	log_line(sp, lno + 1, LOG_LINE_APPEND);
321
322	/* Update marks, @ and global commands. */
323	rval = 0;
324	if (mark_insdel(sp, LINE_INSERT, lno + 1))
325		rval = 1;
326	if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
327		rval = 1;
328
329	/*
330	 * Update screen.
331	 *
332	 * XXX
333	 * Nasty hack.  If multiple lines are input by the user, they aren't
334	 * committed until an <ESC> is entered.  The problem is the screen was
335	 * updated/scrolled as each line was entered.  So, when this routine
336	 * is called to copy the new lines from the cut buffer into the file,
337	 * it has to know not to update the screen again.
338	 */
339	return (scr_update(sp, lno, LINE_APPEND, update) || rval);
340}
341
342/*
343 * db_insert --
344 *	Insert a line into the file.
345 *
346 * PUBLIC: int db_insert __P((SCR *, recno_t, CHAR_T *, size_t));
347 */
348int
349db_insert(
350	SCR *sp,
351	recno_t lno,
352	CHAR_T *p,
353	size_t len)
354{
355	DBT data, key;
356	EXF *ep;
357	char *fp;
358	size_t flen;
359	int rval;
360
361#if defined(DEBUG) && 0
362	TRACE(sp, "insert before %lu: len %lu {%.*s}\n",
363	    (u_long)lno, (u_long)len, MIN(len, 20), p);
364#endif
365	/* Check for no underlying file. */
366	if ((ep = sp->ep) == NULL) {
367		ex_emsg(sp, NULL, EXM_NOFILEYET);
368		return (1);
369	}
370
371	INT2FILE(sp, p, len, fp, flen);
372
373	/* Update file. */
374	key.data = &lno;
375	key.size = sizeof(lno);
376	data.data = fp;
377	data.size = flen;
378	SIGBLOCK;
379	if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
380		msgq(sp, M_SYSERR,
381		    "005|unable to insert at line %lu", (u_long)lno);
382		return (1);
383	}
384	SIGUNBLOCK;
385
386	/* Flush the cache, update line count, before screen update. */
387	if (lno >= ep->c_lno)
388		ep->c_lno = OOBLNO;
389	if (ep->c_nlines != OOBLNO)
390		++ep->c_nlines;
391
392	/* File now dirty. */
393	if (F_ISSET(ep, F_FIRSTMODIFY))
394		(void)rcv_init(sp);
395	F_SET(ep, F_MODIFIED);
396
397	/* Log change. */
398	log_line(sp, lno, LOG_LINE_INSERT);
399
400	/* Update marks, @ and global commands. */
401	rval = 0;
402	if (mark_insdel(sp, LINE_INSERT, lno))
403		rval = 1;
404	if (ex_g_insdel(sp, LINE_INSERT, lno))
405		rval = 1;
406
407	/* Update screen. */
408	return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
409}
410
411/*
412 * db_set --
413 *	Store a line in the file.
414 *
415 * PUBLIC: int db_set __P((SCR *, recno_t, CHAR_T *, size_t));
416 */
417int
418db_set(
419	SCR *sp,
420	recno_t lno,
421	CHAR_T *p,
422	size_t len)
423{
424	DBT data, key;
425	EXF *ep;
426	char *fp;
427	size_t flen;
428
429#if defined(DEBUG) && 0
430	TRACE(sp, "replace line %lu: len %lu {%.*s}\n",
431	    (u_long)lno, (u_long)len, MIN(len, 20), p);
432#endif
433	/* Check for no underlying file. */
434	if ((ep = sp->ep) == NULL) {
435		ex_emsg(sp, NULL, EXM_NOFILEYET);
436		return (1);
437	}
438
439	/* Log before change. */
440	log_line(sp, lno, LOG_LINE_RESET_B);
441
442	INT2FILE(sp, p, len, fp, flen);
443
444	/* Update file. */
445	key.data = &lno;
446	key.size = sizeof(lno);
447	data.data = fp;
448	data.size = flen;
449	SIGBLOCK;
450	if (ep->db->put(ep->db, &key, &data, 0) == -1) {
451		msgq(sp, M_SYSERR,
452		    "006|unable to store line %lu", (u_long)lno);
453		return (1);
454	}
455	SIGUNBLOCK;
456
457	/* Flush the cache, before logging or screen update. */
458	if (lno == ep->c_lno)
459		ep->c_lno = OOBLNO;
460
461	/* File now dirty. */
462	if (F_ISSET(ep, F_FIRSTMODIFY))
463		(void)rcv_init(sp);
464	F_SET(ep, F_MODIFIED);
465
466	/* Log after change. */
467	log_line(sp, lno, LOG_LINE_RESET_F);
468
469	/* Update screen. */
470	return (scr_update(sp, lno, LINE_RESET, 1));
471}
472
473/*
474 * db_exist --
475 *	Return if a line exists.
476 *
477 * PUBLIC: int db_exist __P((SCR *, recno_t));
478 */
479int
480db_exist(
481	SCR *sp,
482	recno_t lno)
483{
484	EXF *ep;
485
486	/* Check for no underlying file. */
487	if ((ep = sp->ep) == NULL) {
488		ex_emsg(sp, NULL, EXM_NOFILEYET);
489		return (1);
490	}
491
492	if (lno == OOBLNO)
493		return (0);
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		return (lno <= (F_ISSET(sp, SC_TINPUT) ?
501		    ep->c_nlines + (((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno -
502		    ((TEXT *)TAILQ_FIRST(sp->tiq))->lno) : ep->c_nlines));
503
504	/* Go get the line. */
505	return (!db_get(sp, lno, 0, NULL, NULL));
506}
507
508/*
509 * db_last --
510 *	Return the number of lines in the file.
511 *
512 * PUBLIC: int db_last __P((SCR *, recno_t *));
513 */
514int
515db_last(
516	SCR *sp,
517	recno_t *lnop)
518{
519	DBT data, key;
520	EXF *ep;
521	recno_t lno;
522	CHAR_T *wp;
523	size_t wlen;
524
525	/* Check for no underlying file. */
526	if ((ep = sp->ep) == NULL) {
527		ex_emsg(sp, NULL, EXM_NOFILEYET);
528		return (1);
529	}
530
531	/*
532	 * Check the last-line number cache.  Adjust the cached line
533	 * number for the lines used by the text input buffers.
534	 */
535	if (ep->c_nlines != OOBLNO) {
536		*lnop = ep->c_nlines;
537		if (F_ISSET(sp, SC_TINPUT))
538			*lnop += ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno -
539			    ((TEXT *)TAILQ_FIRST(sp->tiq))->lno;
540		return (0);
541	}
542
543	key.data = &lno;
544	key.size = sizeof(lno);
545
546	switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
547	case -1:
548alloc_err:
549		msgq(sp, M_SYSERR, "007|unable to get last line");
550		*lnop = 0;
551		return (1);
552	case 1:
553		*lnop = 0;
554		return (0);
555	case 0:
556		;
557	}
558
559	memcpy(&lno, key.data, sizeof(lno));
560
561	if (lno != ep->c_lno) {
562		FILE2INT(sp, data.data, data.size, wp, wlen);
563
564		/* Fill the cache. */
565		if (wp != data.data) {
566			BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
567			MEMCPY(ep->c_lp, wp, wlen);
568		} else
569			ep->c_lp = data.data;
570		ep->c_lno = lno;
571		ep->c_len = wlen;
572	}
573	ep->c_nlines = lno;
574
575	/* Return the value. */
576	*lnop = (F_ISSET(sp, SC_TINPUT) &&
577	    ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno > lno ?
578	    ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno : lno);
579	return (0);
580}
581
582/*
583 * db_rget --
584 *	Retrieve a raw line from database. No cache, no conversion.
585 *
586 * PUBLIC: int db_rget __P((SCR *, recno_t, char **, size_t *));
587 */
588int
589db_rget(
590	SCR *sp,
591	recno_t lno,				/* Line number. */
592	char **pp,				/* Pointer store. */
593	size_t *lenp)				/* Length store. */
594{
595	DBT data, key;
596	EXF *ep;
597
598	/* Check for no underlying file. */
599	if ((ep = sp->ep) == NULL)
600		return (1);
601
602	/* Get the line from the underlying database. */
603	key.data = &lno;
604	key.size = sizeof(lno);
605	if (ep->db->get(ep->db, &key, &data, 0))
606	/* We do not report error, and do not ensure the size! */
607		return (1);
608
609	if (lenp != NULL)
610		*lenp = data.size;
611	if (pp != NULL)
612		*pp = data.data;
613	return (0);
614}
615
616/*
617 * db_rset --
618 *	Store a line in the file. No log, no conversion.
619 *
620 * PUBLIC: int db_rset __P((SCR *, recno_t, char *, size_t));
621 */
622int
623db_rset(
624	SCR *sp,
625	recno_t lno,
626	char *p,
627	size_t len)
628{
629	DBT data, key;
630	EXF *ep;
631
632	/* Check for no underlying file. */
633	if ((ep = sp->ep) == NULL)
634		return (1);
635
636	/* Update file. */
637	key.data = &lno;
638	key.size = sizeof(lno);
639	data.data = p;
640	data.size = len;
641	if (ep->db->put(ep->db, &key, &data, 0) == -1)
642	/* We do not report error, and do not ensure the size! */
643		return (1);
644
645	return (0);
646}
647
648/*
649 * db_err --
650 *	Report a line error.
651 *
652 * PUBLIC: void db_err __P((SCR *, recno_t));
653 */
654void
655db_err(
656	SCR *sp,
657	recno_t lno)
658{
659	msgq(sp, M_ERR,
660	    "008|Error: unable to retrieve line %lu", (u_long)lno);
661}
662
663/*
664 * scr_update --
665 *	Update all of the screens that are backed by the file that
666 *	just changed.
667 */
668static int
669scr_update(
670	SCR *sp,
671	recno_t lno,
672	lnop_t op,
673	int current)
674{
675	EXF *ep;
676	SCR *tsp;
677
678	if (F_ISSET(sp, SC_EX))
679		return (0);
680
681	ep = sp->ep;
682	if (ep->refcnt != 1)
683		TAILQ_FOREACH(tsp, sp->gp->dq, q)
684			if (sp != tsp && tsp->ep == ep)
685				if (vs_change(tsp, lno, op))
686					return (1);
687	return (current ? vs_change(sp, lno, op) : 0);
688}
689