119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: line.c,v 10.26 2011/08/12 12:36:41 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <errno.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
2419304Speter#include <string.h>
2519304Speter
2619304Speter#include "common.h"
2719304Speter#include "../vi/vi.h"
2819304Speter
2919304Speterstatic int scr_update __P((SCR *, recno_t, lnop_t, int));
3019304Speter
3119304Speter/*
3219304Speter * db_eget --
3319304Speter *	Front-end to db_get, special case handling for empty files.
3419304Speter *
35254225Speter * PUBLIC: int db_eget __P((SCR *, recno_t, CHAR_T **, size_t *, int *));
3619304Speter */
3719304Speterint
38254225Speterdb_eget(
39254225Speter	SCR *sp,
40254225Speter	recno_t lno,				/* Line number. */
41254225Speter	CHAR_T **pp,				/* Pointer store. */
42254225Speter	size_t *lenp,				/* Length store. */
43254225Speter	int *isemptyp)
4419304Speter{
4519304Speter	recno_t l1;
4619304Speter
4719304Speter	if (isemptyp != NULL)
4819304Speter		*isemptyp = 0;
4919304Speter
5019304Speter	/* If the line exists, simply return it. */
5119304Speter	if (!db_get(sp, lno, 0, pp, lenp))
5219304Speter		return (0);
5319304Speter
5419304Speter	/*
5519304Speter	 * If the user asked for line 0 or line 1, i.e. the only possible
5619304Speter	 * line in an empty file, find the last line of the file; db_last
5719304Speter	 * fails loudly.
5819304Speter	 */
5919304Speter	if ((lno == 0 || lno == 1) && db_last(sp, &l1))
6019304Speter		return (1);
6119304Speter
6219304Speter	/* If the file isn't empty, fail loudly. */
63254225Speter	if ((lno != 0 && lno != 1) || l1 != 0) {
6419304Speter		db_err(sp, lno);
6519304Speter		return (1);
6619304Speter	}
6719304Speter
6819304Speter	if (isemptyp != NULL)
6919304Speter		*isemptyp = 1;
7019304Speter
7119304Speter	return (1);
7219304Speter}
7319304Speter
7419304Speter/*
7519304Speter * db_get --
7619304Speter *	Look in the text buffers for a line, followed by the cache, followed
7719304Speter *	by the database.
7819304Speter *
79254225Speter * PUBLIC: int db_get __P((SCR *, recno_t, u_int32_t, CHAR_T **, size_t *));
8019304Speter */
8119304Speterint
82254225Speterdb_get(
83254225Speter	SCR *sp,
84254225Speter	recno_t lno,				/* Line number. */
85254225Speter	u_int32_t flags,
86254225Speter	CHAR_T **pp,				/* Pointer store. */
87254225Speter	size_t *lenp)				/* Length store. */
8819304Speter{
8919304Speter	DBT data, key;
9019304Speter	EXF *ep;
9119304Speter	TEXT *tp;
9219304Speter	recno_t l1, l2;
93254225Speter	CHAR_T *wp;
94254225Speter	size_t wlen;
95254225Speter	size_t nlen;
9619304Speter
9719304Speter	/*
9819304Speter	 * The underlying recno stuff handles zero by returning NULL, but
9919304Speter	 * have to have an OOB condition for the look-aside into the input
10019304Speter	 * buffer anyway.
10119304Speter	 */
10219304Speter	if (lno == 0)
10319304Speter		goto err1;
10419304Speter
10519304Speter	/* Check for no underlying file. */
10619304Speter	if ((ep = sp->ep) == NULL) {
10719304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
10819304Speter		goto err3;
10919304Speter	}
11019304Speter
11119304Speter	if (LF_ISSET(DBG_NOCACHE))
11219304Speter		goto nocache;
11319304Speter
11419304Speter	/*
11519304Speter	 * Look-aside into the TEXT buffers and see if the line we want
11619304Speter	 * is there.
11719304Speter	 */
11819304Speter	if (F_ISSET(sp, SC_TINPUT)) {
119254225Speter		l1 = ((TEXT *)TAILQ_FIRST(sp->tiq))->lno;
120254225Speter		l2 = ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno;
12119304Speter		if (l1 <= lno && l2 >= lno) {
12219304Speter#if defined(DEBUG) && 0
12319304Speter	TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno);
12419304Speter#endif
125254225Speter			for (tp = TAILQ_FIRST(sp->tiq);
126254225Speter			    tp->lno != lno; tp = TAILQ_NEXT(tp, q));
12719304Speter			if (lenp != NULL)
12819304Speter				*lenp = tp->len;
12919304Speter			if (pp != NULL)
13019304Speter				*pp = tp->lb;
13119304Speter			return (0);
13219304Speter		}
13319304Speter		/*
13419304Speter		 * Adjust the line number for the number of lines used
13519304Speter		 * by the text input buffers.
13619304Speter		 */
13719304Speter		if (lno > l2)
13819304Speter			lno -= l2 - l1;
13919304Speter	}
14019304Speter
14119304Speter	/* Look-aside into the cache, and see if the line we want is there. */
14219304Speter	if (lno == ep->c_lno) {
14319304Speter#if defined(DEBUG) && 0
14419304Speter	TRACE(sp, "retrieve cached line %lu\n", (u_long)lno);
14519304Speter#endif
14619304Speter		if (lenp != NULL)
14719304Speter			*lenp = ep->c_len;
14819304Speter		if (pp != NULL)
14919304Speter			*pp = ep->c_lp;
15019304Speter		return (0);
15119304Speter	}
15219304Speter	ep->c_lno = OOBLNO;
15319304Speter
15419304Speternocache:
155254225Speter	nlen = 1024;
156254225Speterretry:
15719304Speter	/* Get the line from the underlying database. */
15819304Speter	key.data = &lno;
15919304Speter	key.size = sizeof(lno);
16019304Speter	switch (ep->db->get(ep->db, &key, &data, 0)) {
161254225Speter	case -1:
16219304Speter		goto err2;
16319304Speter	case 1:
16419304Spetererr1:		if (LF_ISSET(DBG_FATAL))
16519304Spetererr2:			db_err(sp, lno);
166254225Speteralloc_err:
16719304Spetererr3:		if (lenp != NULL)
16819304Speter			*lenp = 0;
16919304Speter		if (pp != NULL)
17019304Speter			*pp = NULL;
17119304Speter		return (1);
172254225Speter	case 0:
173254225Speter		if (data.size > nlen) {
174254225Speter			nlen = data.size;
175254225Speter			goto retry;
176254225Speter		}
17719304Speter	}
17819304Speter
179254225Speter	if (FILE2INT(sp, data.data, data.size, wp, wlen)) {
180254225Speter		if (!F_ISSET(sp, SC_CONV_ERROR)) {
181254225Speter			F_SET(sp, SC_CONV_ERROR);
182254225Speter			msgq(sp, M_ERR, "324|Conversion error on line %d", lno);
183254225Speter		}
184254225Speter		goto err3;
185254225Speter	}
186254225Speter
18719304Speter	/* Reset the cache. */
188254225Speter	if (wp != data.data) {
189254225Speter		BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
190254225Speter		MEMCPY(ep->c_lp, wp, wlen);
191254225Speter	} else
192254225Speter		ep->c_lp = data.data;
19319304Speter	ep->c_lno = lno;
194254225Speter	ep->c_len = wlen;
19519304Speter
19619304Speter#if defined(DEBUG) && 0
19719304Speter	TRACE(sp, "retrieve DB line %lu\n", (u_long)lno);
19819304Speter#endif
19919304Speter	if (lenp != NULL)
200254225Speter		*lenp = wlen;
20119304Speter	if (pp != NULL)
20219304Speter		*pp = ep->c_lp;
20319304Speter	return (0);
20419304Speter}
20519304Speter
20619304Speter/*
20719304Speter * db_delete --
20819304Speter *	Delete a line from the file.
20919304Speter *
21019304Speter * PUBLIC: int db_delete __P((SCR *, recno_t));
21119304Speter */
21219304Speterint
213254225Speterdb_delete(
214254225Speter	SCR *sp,
215254225Speter	recno_t lno)
21619304Speter{
21719304Speter	DBT key;
21819304Speter	EXF *ep;
21919304Speter
22019304Speter#if defined(DEBUG) && 0
22119304Speter	TRACE(sp, "delete line %lu\n", (u_long)lno);
22219304Speter#endif
22319304Speter	/* Check for no underlying file. */
22419304Speter	if ((ep = sp->ep) == NULL) {
22519304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
22619304Speter		return (1);
22719304Speter	}
22819304Speter
22919304Speter	/* Update marks, @ and global commands. */
23019304Speter	if (mark_insdel(sp, LINE_DELETE, lno))
23119304Speter		return (1);
23219304Speter	if (ex_g_insdel(sp, LINE_DELETE, lno))
23319304Speter		return (1);
23419304Speter
23519304Speter	/* Log change. */
23619304Speter	log_line(sp, lno, LOG_LINE_DELETE);
23719304Speter
23819304Speter	/* Update file. */
23919304Speter	key.data = &lno;
24019304Speter	key.size = sizeof(lno);
24119304Speter	SIGBLOCK;
24219304Speter	if (ep->db->del(ep->db, &key, 0) == 1) {
24319304Speter		msgq(sp, M_SYSERR,
24419304Speter		    "003|unable to delete line %lu", (u_long)lno);
24519304Speter		return (1);
24619304Speter	}
24719304Speter	SIGUNBLOCK;
24819304Speter
24919304Speter	/* Flush the cache, update line count, before screen update. */
25019304Speter	if (lno <= ep->c_lno)
25119304Speter		ep->c_lno = OOBLNO;
25219304Speter	if (ep->c_nlines != OOBLNO)
25319304Speter		--ep->c_nlines;
25419304Speter
25519304Speter	/* File now modified. */
25619304Speter	if (F_ISSET(ep, F_FIRSTMODIFY))
25719304Speter		(void)rcv_init(sp);
25819304Speter	F_SET(ep, F_MODIFIED);
25919304Speter
26019304Speter	/* Update screen. */
26119304Speter	return (scr_update(sp, lno, LINE_DELETE, 1));
26219304Speter}
26319304Speter
26419304Speter/*
26519304Speter * db_append --
26619304Speter *	Append a line into the file.
26719304Speter *
268254225Speter * PUBLIC: int db_append __P((SCR *, int, recno_t, CHAR_T *, size_t));
26919304Speter */
27019304Speterint
271254225Speterdb_append(
272254225Speter	SCR *sp,
273254225Speter	int update,
274254225Speter	recno_t lno,
275254225Speter	CHAR_T *p,
276254225Speter	size_t len)
27719304Speter{
27819304Speter	DBT data, key;
27919304Speter	EXF *ep;
280254225Speter	char *fp;
281254225Speter	size_t flen;
28219304Speter	int rval;
28319304Speter
28419304Speter#if defined(DEBUG) && 0
28519304Speter	TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
28619304Speter#endif
28719304Speter	/* Check for no underlying file. */
28819304Speter	if ((ep = sp->ep) == NULL) {
28919304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
29019304Speter		return (1);
29119304Speter	}
29219304Speter
293254225Speter	INT2FILE(sp, p, len, fp, flen);
294254225Speter
29519304Speter	/* Update file. */
29619304Speter	key.data = &lno;
29719304Speter	key.size = sizeof(lno);
298254225Speter	data.data = fp;
299254225Speter	data.size = flen;
30019304Speter	SIGBLOCK;
30119304Speter	if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
30219304Speter		msgq(sp, M_SYSERR,
30319304Speter		    "004|unable to append to line %lu", (u_long)lno);
30419304Speter		return (1);
30519304Speter	}
30619304Speter	SIGUNBLOCK;
30719304Speter
30819304Speter	/* Flush the cache, update line count, before screen update. */
30919304Speter	if (lno < ep->c_lno)
31019304Speter		ep->c_lno = OOBLNO;
31119304Speter	if (ep->c_nlines != OOBLNO)
31219304Speter		++ep->c_nlines;
31319304Speter
31419304Speter	/* File now dirty. */
31519304Speter	if (F_ISSET(ep, F_FIRSTMODIFY))
31619304Speter		(void)rcv_init(sp);
31719304Speter	F_SET(ep, F_MODIFIED);
31819304Speter
31919304Speter	/* Log change. */
32019304Speter	log_line(sp, lno + 1, LOG_LINE_APPEND);
32119304Speter
32219304Speter	/* Update marks, @ and global commands. */
32319304Speter	rval = 0;
32419304Speter	if (mark_insdel(sp, LINE_INSERT, lno + 1))
32519304Speter		rval = 1;
32619304Speter	if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
32719304Speter		rval = 1;
32819304Speter
32919304Speter	/*
33019304Speter	 * Update screen.
33119304Speter	 *
33219304Speter	 * XXX
33319304Speter	 * Nasty hack.  If multiple lines are input by the user, they aren't
33419304Speter	 * committed until an <ESC> is entered.  The problem is the screen was
33519304Speter	 * updated/scrolled as each line was entered.  So, when this routine
33619304Speter	 * is called to copy the new lines from the cut buffer into the file,
33719304Speter	 * it has to know not to update the screen again.
33819304Speter	 */
33919304Speter	return (scr_update(sp, lno, LINE_APPEND, update) || rval);
34019304Speter}
34119304Speter
34219304Speter/*
34319304Speter * db_insert --
34419304Speter *	Insert a line into the file.
34519304Speter *
346254225Speter * PUBLIC: int db_insert __P((SCR *, recno_t, CHAR_T *, size_t));
34719304Speter */
34819304Speterint
349254225Speterdb_insert(
350254225Speter	SCR *sp,
351254225Speter	recno_t lno,
352254225Speter	CHAR_T *p,
353254225Speter	size_t len)
35419304Speter{
35519304Speter	DBT data, key;
35619304Speter	EXF *ep;
357254225Speter	char *fp;
358254225Speter	size_t flen;
35919304Speter	int rval;
36019304Speter
36119304Speter#if defined(DEBUG) && 0
36219304Speter	TRACE(sp, "insert before %lu: len %lu {%.*s}\n",
36319304Speter	    (u_long)lno, (u_long)len, MIN(len, 20), p);
36419304Speter#endif
36519304Speter	/* Check for no underlying file. */
36619304Speter	if ((ep = sp->ep) == NULL) {
36719304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
36819304Speter		return (1);
36919304Speter	}
37019304Speter
371254225Speter	INT2FILE(sp, p, len, fp, flen);
372254225Speter
37319304Speter	/* Update file. */
37419304Speter	key.data = &lno;
37519304Speter	key.size = sizeof(lno);
376254225Speter	data.data = fp;
377254225Speter	data.size = flen;
37819304Speter	SIGBLOCK;
37919304Speter	if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
38019304Speter		msgq(sp, M_SYSERR,
38119304Speter		    "005|unable to insert at line %lu", (u_long)lno);
38219304Speter		return (1);
38319304Speter	}
38419304Speter	SIGUNBLOCK;
38519304Speter
38619304Speter	/* Flush the cache, update line count, before screen update. */
38719304Speter	if (lno >= ep->c_lno)
38819304Speter		ep->c_lno = OOBLNO;
38919304Speter	if (ep->c_nlines != OOBLNO)
39019304Speter		++ep->c_nlines;
39119304Speter
39219304Speter	/* File now dirty. */
39319304Speter	if (F_ISSET(ep, F_FIRSTMODIFY))
39419304Speter		(void)rcv_init(sp);
39519304Speter	F_SET(ep, F_MODIFIED);
39619304Speter
39719304Speter	/* Log change. */
39819304Speter	log_line(sp, lno, LOG_LINE_INSERT);
39919304Speter
40019304Speter	/* Update marks, @ and global commands. */
40119304Speter	rval = 0;
40219304Speter	if (mark_insdel(sp, LINE_INSERT, lno))
40319304Speter		rval = 1;
40419304Speter	if (ex_g_insdel(sp, LINE_INSERT, lno))
40519304Speter		rval = 1;
40619304Speter
40719304Speter	/* Update screen. */
40819304Speter	return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
40919304Speter}
41019304Speter
41119304Speter/*
41219304Speter * db_set --
41319304Speter *	Store a line in the file.
41419304Speter *
415254225Speter * PUBLIC: int db_set __P((SCR *, recno_t, CHAR_T *, size_t));
41619304Speter */
41719304Speterint
418254225Speterdb_set(
419254225Speter	SCR *sp,
420254225Speter	recno_t lno,
421254225Speter	CHAR_T *p,
422254225Speter	size_t len)
42319304Speter{
42419304Speter	DBT data, key;
42519304Speter	EXF *ep;
426254225Speter	char *fp;
427254225Speter	size_t flen;
42819304Speter
42919304Speter#if defined(DEBUG) && 0
43019304Speter	TRACE(sp, "replace line %lu: len %lu {%.*s}\n",
43119304Speter	    (u_long)lno, (u_long)len, MIN(len, 20), p);
43219304Speter#endif
43319304Speter	/* Check for no underlying file. */
43419304Speter	if ((ep = sp->ep) == NULL) {
43519304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
43619304Speter		return (1);
43719304Speter	}
43819304Speter
43919304Speter	/* Log before change. */
44019304Speter	log_line(sp, lno, LOG_LINE_RESET_B);
44119304Speter
442254225Speter	INT2FILE(sp, p, len, fp, flen);
443254225Speter
44419304Speter	/* Update file. */
44519304Speter	key.data = &lno;
44619304Speter	key.size = sizeof(lno);
447254225Speter	data.data = fp;
448254225Speter	data.size = flen;
44919304Speter	SIGBLOCK;
45019304Speter	if (ep->db->put(ep->db, &key, &data, 0) == -1) {
45119304Speter		msgq(sp, M_SYSERR,
45219304Speter		    "006|unable to store line %lu", (u_long)lno);
45319304Speter		return (1);
45419304Speter	}
45519304Speter	SIGUNBLOCK;
45619304Speter
45719304Speter	/* Flush the cache, before logging or screen update. */
45819304Speter	if (lno == ep->c_lno)
45919304Speter		ep->c_lno = OOBLNO;
46019304Speter
46119304Speter	/* File now dirty. */
46219304Speter	if (F_ISSET(ep, F_FIRSTMODIFY))
46319304Speter		(void)rcv_init(sp);
46419304Speter	F_SET(ep, F_MODIFIED);
46519304Speter
46619304Speter	/* Log after change. */
46719304Speter	log_line(sp, lno, LOG_LINE_RESET_F);
46819304Speter
46919304Speter	/* Update screen. */
47019304Speter	return (scr_update(sp, lno, LINE_RESET, 1));
47119304Speter}
47219304Speter
47319304Speter/*
47419304Speter * db_exist --
47519304Speter *	Return if a line exists.
47619304Speter *
47719304Speter * PUBLIC: int db_exist __P((SCR *, recno_t));
47819304Speter */
47919304Speterint
480254225Speterdb_exist(
481254225Speter	SCR *sp,
482254225Speter	recno_t lno)
48319304Speter{
48419304Speter	EXF *ep;
48519304Speter
48619304Speter	/* Check for no underlying file. */
48719304Speter	if ((ep = sp->ep) == NULL) {
48819304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
48919304Speter		return (1);
49019304Speter	}
49119304Speter
49219304Speter	if (lno == OOBLNO)
49319304Speter		return (0);
49419304Speter
49519304Speter	/*
49619304Speter	 * Check the last-line number cache.  Adjust the cached line
49719304Speter	 * number for the lines used by the text input buffers.
49819304Speter	 */
49919304Speter	if (ep->c_nlines != OOBLNO)
50019304Speter		return (lno <= (F_ISSET(sp, SC_TINPUT) ?
501254225Speter		    ep->c_nlines + (((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno -
502254225Speter		    ((TEXT *)TAILQ_FIRST(sp->tiq))->lno) : ep->c_nlines));
50319304Speter
50419304Speter	/* Go get the line. */
50519304Speter	return (!db_get(sp, lno, 0, NULL, NULL));
50619304Speter}
50719304Speter
50819304Speter/*
50919304Speter * db_last --
51019304Speter *	Return the number of lines in the file.
51119304Speter *
51219304Speter * PUBLIC: int db_last __P((SCR *, recno_t *));
51319304Speter */
51419304Speterint
515254225Speterdb_last(
516254225Speter	SCR *sp,
517254225Speter	recno_t *lnop)
51819304Speter{
51919304Speter	DBT data, key;
52019304Speter	EXF *ep;
52119304Speter	recno_t lno;
522254225Speter	CHAR_T *wp;
523254225Speter	size_t wlen;
52419304Speter
52519304Speter	/* Check for no underlying file. */
52619304Speter	if ((ep = sp->ep) == NULL) {
52719304Speter		ex_emsg(sp, NULL, EXM_NOFILEYET);
52819304Speter		return (1);
52919304Speter	}
53019304Speter
53119304Speter	/*
53219304Speter	 * Check the last-line number cache.  Adjust the cached line
53319304Speter	 * number for the lines used by the text input buffers.
53419304Speter	 */
53519304Speter	if (ep->c_nlines != OOBLNO) {
53619304Speter		*lnop = ep->c_nlines;
53719304Speter		if (F_ISSET(sp, SC_TINPUT))
538254225Speter			*lnop += ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno -
539254225Speter			    ((TEXT *)TAILQ_FIRST(sp->tiq))->lno;
54019304Speter		return (0);
54119304Speter	}
54219304Speter
54319304Speter	key.data = &lno;
54419304Speter	key.size = sizeof(lno);
54519304Speter
54619304Speter	switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
547254225Speter	case -1:
548254225Speteralloc_err:
54919304Speter		msgq(sp, M_SYSERR, "007|unable to get last line");
55019304Speter		*lnop = 0;
55119304Speter		return (1);
552254225Speter	case 1:
55319304Speter		*lnop = 0;
55419304Speter		return (0);
555254225Speter	case 0:
556254225Speter		;
55719304Speter	}
55819304Speter
55919304Speter	memcpy(&lno, key.data, sizeof(lno));
56019304Speter
561254225Speter	if (lno != ep->c_lno) {
562254225Speter		FILE2INT(sp, data.data, data.size, wp, wlen);
563254225Speter
564254225Speter		/* Fill the cache. */
565254225Speter		if (wp != data.data) {
566254225Speter			BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
567254225Speter			MEMCPY(ep->c_lp, wp, wlen);
568254225Speter		} else
569254225Speter			ep->c_lp = data.data;
570254225Speter		ep->c_lno = lno;
571254225Speter		ep->c_len = wlen;
572254225Speter	}
573254225Speter	ep->c_nlines = lno;
574254225Speter
57519304Speter	/* Return the value. */
57619304Speter	*lnop = (F_ISSET(sp, SC_TINPUT) &&
577254225Speter	    ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno > lno ?
578254225Speter	    ((TEXT *)TAILQ_LAST(sp->tiq, _texth))->lno : lno);
57919304Speter	return (0);
58019304Speter}
58119304Speter
58219304Speter/*
583254225Speter * db_rget --
584254225Speter *	Retrieve a raw line from database. No cache, no conversion.
585254225Speter *
586254225Speter * PUBLIC: int db_rget __P((SCR *, recno_t, char **, size_t *));
587254225Speter */
588254225Speterint
589254225Speterdb_rget(
590254225Speter	SCR *sp,
591254225Speter	recno_t lno,				/* Line number. */
592254225Speter	char **pp,				/* Pointer store. */
593254225Speter	size_t *lenp)				/* Length store. */
594254225Speter{
595254225Speter	DBT data, key;
596254225Speter	EXF *ep;
597254225Speter
598254225Speter	/* Check for no underlying file. */
599254225Speter	if ((ep = sp->ep) == NULL)
600254225Speter		return (1);
601254225Speter
602254225Speter	/* Get the line from the underlying database. */
603254225Speter	key.data = &lno;
604254225Speter	key.size = sizeof(lno);
605254225Speter	if (ep->db->get(ep->db, &key, &data, 0))
606254225Speter	/* We do not report error, and do not ensure the size! */
607254225Speter		return (1);
608254225Speter
609254225Speter	if (lenp != NULL)
610254225Speter		*lenp = data.size;
611254225Speter	if (pp != NULL)
612254225Speter		*pp = data.data;
613254225Speter	return (0);
614254225Speter}
615254225Speter
616254225Speter/*
617254225Speter * db_rset --
618254225Speter *	Store a line in the file. No log, no conversion.
619254225Speter *
620254225Speter * PUBLIC: int db_rset __P((SCR *, recno_t, char *, size_t));
621254225Speter */
622254225Speterint
623254225Speterdb_rset(
624254225Speter	SCR *sp,
625254225Speter	recno_t lno,
626254225Speter	char *p,
627254225Speter	size_t len)
628254225Speter{
629254225Speter	DBT data, key;
630254225Speter	EXF *ep;
631254225Speter
632254225Speter	/* Check for no underlying file. */
633254225Speter	if ((ep = sp->ep) == NULL)
634254225Speter		return (1);
635254225Speter
636254225Speter	/* Update file. */
637254225Speter	key.data = &lno;
638254225Speter	key.size = sizeof(lno);
639254225Speter	data.data = p;
640254225Speter	data.size = len;
641254225Speter	if (ep->db->put(ep->db, &key, &data, 0) == -1)
642254225Speter	/* We do not report error, and do not ensure the size! */
643254225Speter		return (1);
644254225Speter
645254225Speter	return (0);
646254225Speter}
647254225Speter
648254225Speter/*
64919304Speter * db_err --
65019304Speter *	Report a line error.
65119304Speter *
65219304Speter * PUBLIC: void db_err __P((SCR *, recno_t));
65319304Speter */
65419304Spetervoid
655254225Speterdb_err(
656254225Speter	SCR *sp,
657254225Speter	recno_t lno)
65819304Speter{
65919304Speter	msgq(sp, M_ERR,
66019304Speter	    "008|Error: unable to retrieve line %lu", (u_long)lno);
66119304Speter}
66219304Speter
66319304Speter/*
66419304Speter * scr_update --
66519304Speter *	Update all of the screens that are backed by the file that
66619304Speter *	just changed.
66719304Speter */
66819304Speterstatic int
669254225Speterscr_update(
670254225Speter	SCR *sp,
671254225Speter	recno_t lno,
672254225Speter	lnop_t op,
673254225Speter	int current)
67419304Speter{
67519304Speter	EXF *ep;
67619304Speter	SCR *tsp;
67719304Speter
67819304Speter	if (F_ISSET(sp, SC_EX))
67919304Speter		return (0);
68019304Speter
68119304Speter	ep = sp->ep;
68219304Speter	if (ep->refcnt != 1)
683254225Speter		TAILQ_FOREACH(tsp, sp->gp->dq, q)
68419304Speter			if (sp != tsp && tsp->ep == ep)
68519304Speter				if (vs_change(tsp, lno, op))
68619304Speter					return (1);
68719304Speter	return (current ? vs_change(sp, lno, op) : 0);
68819304Speter}
689