Use usermem for getting from db.
[nvi.git] / common / db.c
blobab3b6463ed19f561eb13451a134a6feb436d3c77
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.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: db.c,v 10.30 2000/07/21 18:36:31 skimo Exp $ (Berkeley) $Date: 2000/07/21 18:36:31 $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
20 #include <bitstring.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include "common.h"
27 #include "../vi/vi.h"
29 static int scr_update __P((SCR *, db_recno_t, lnop_t, int));
30 static int append __P((SCR*, db_recno_t, CHAR_T*, size_t));
33 * db_eget --
34 * Front-end to db_get, special case handling for empty files.
36 * PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
38 int
39 db_eget(sp, lno, pp, lenp, isemptyp)
40 SCR *sp;
41 db_recno_t lno; /* Line number. */
42 CHAR_T **pp; /* Pointer store. */
43 size_t *lenp; /* Length store. */
44 int *isemptyp;
46 db_recno_t l1;
48 if (isemptyp != NULL)
49 *isemptyp = 0;
51 /* If the line exists, simply return it. */
52 if (!db_get(sp, lno, 0, pp, lenp))
53 return (0);
56 * If the user asked for line 0 or line 1, i.e. the only possible
57 * line in an empty file, find the last line of the file; db_last
58 * fails loudly.
60 if ((lno == 0 || lno == 1) && db_last(sp, &l1))
61 return (1);
63 /* If the file isn't empty, fail loudly. */
64 if ((lno != 0 && lno != 1) || l1 != 0) {
65 db_err(sp, lno);
66 return (1);
69 if (isemptyp != NULL)
70 *isemptyp = 1;
72 return (1);
76 * db_get --
77 * Look in the text buffers for a line, followed by the cache, followed
78 * by the database.
80 * PUBLIC: int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *));
82 int
83 db_get(sp, lno, flags, pp, lenp)
84 SCR *sp;
85 db_recno_t lno; /* Line number. */
86 u_int32_t flags;
87 CHAR_T **pp; /* Pointer store. */
88 size_t *lenp; /* Length store. */
90 DBT data, key;
91 EXF *ep;
92 TEXT *tp;
93 db_recno_t l1, l2;
94 CHAR_T *wp;
95 size_t wlen;
96 size_t nlen;
99 * The underlying recno stuff handles zero by returning NULL, but
100 * have to have an OOB condition for the look-aside into the input
101 * buffer anyway.
103 if (lno == 0)
104 goto err1;
106 /* Check for no underlying file. */
107 if ((ep = sp->ep) == NULL) {
108 ex_emsg(sp, NULL, EXM_NOFILEYET);
109 goto err3;
112 if (LF_ISSET(DBG_NOCACHE))
113 goto nocache;
116 * Look-aside into the TEXT buffers and see if the line we want
117 * is there.
119 if (F_ISSET(sp, SC_TINPUT)) {
120 l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
121 l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
122 if (l1 <= lno && l2 >= lno) {
123 #if defined(DEBUG) && 0
124 vtrace(sp,
125 "retrieve TEXT buffer line %lu\n", (u_long)lno);
126 #endif
127 for (tp = sp->tiq.cqh_first;
128 tp->lno != lno; tp = tp->q.cqe_next);
129 if (lenp != NULL)
130 *lenp = tp->len;
131 if (pp != NULL)
132 *pp = tp->lb;
133 return (0);
136 * Adjust the line number for the number of lines used
137 * by the text input buffers.
139 if (lno > l2)
140 lno -= l2 - l1;
143 /* Look-aside into the cache, and see if the line we want is there. */
144 /* Disable cache for now */
145 if (0 && lno == ep->c_lno) {
146 #if defined(DEBUG) && 0
147 vtrace(sp, "retrieve cached line %lu\n", (u_long)lno);
148 #endif
149 if (lenp != NULL)
150 *lenp = ep->c_len;
151 if (pp != NULL)
152 *pp = ep->c_lp;
153 return (0);
155 ep->c_lno = OOBLNO;
157 nocache:
158 nlen = 1024;
159 retry:
160 BINC_GOTO(sp, ep->c_lp, ep->c_blen, nlen);
162 /* Get the line from the underlying database. */
163 memset(&key, 0, sizeof(key));
164 key.data = &lno;
165 key.size = sizeof(lno);
166 memset(&data, 0, sizeof(data));
167 data.data = ep->c_lp;
168 data.ulen = nlen;
169 data.flags = DB_DBT_USERMEM;
170 switch (ep->db->get(ep->db, NULL, &key, &data, 0)) {
171 case ENOMEM:
172 nlen = data.size;
173 goto retry;
174 default:
175 goto err2;
176 case DB_NOTFOUND:
177 err1: if (LF_ISSET(DBG_FATAL))
178 err2: db_err(sp, lno);
179 alloc_err:
180 err3: if (lenp != NULL)
181 *lenp = 0;
182 if (pp != NULL)
183 *pp = NULL;
184 return (1);
185 case 0:
189 FILE2INT(sp, data.data, data.size, wp, wlen);
191 /* Reset the cache. */
192 BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
193 MEMCPYW(ep->c_lp, wp, wlen);
194 ep->c_lno = lno;
195 ep->c_len = wlen;
197 #if defined(DEBUG) && 0
198 vtrace(sp, "retrieve DB line %lu\n", (u_long)lno);
199 #endif
200 if (lenp != NULL)
201 *lenp = wlen;
202 if (pp != NULL)
203 *pp = ep->c_lp;
204 return (0);
208 * db_delete --
209 * Delete a line from the file.
211 * PUBLIC: int db_delete __P((SCR *, db_recno_t));
214 db_delete(sp, lno)
215 SCR *sp;
216 db_recno_t lno;
218 DBT key;
219 EXF *ep;
221 #if defined(DEBUG) && 0
222 vtrace(sp, "delete line %lu\n", (u_long)lno);
223 #endif
224 /* Check for no underlying file. */
225 if ((ep = sp->ep) == NULL) {
226 ex_emsg(sp, NULL, EXM_NOFILEYET);
227 return (1);
230 /* Update marks, @ and global commands. */
231 if (mark_insdel(sp, LINE_DELETE, lno))
232 return (1);
233 if (ex_g_insdel(sp, LINE_DELETE, lno))
234 return (1);
236 /* Log change. */
237 log_line(sp, lno, LOG_LINE_DELETE);
239 /* Update file. */
240 memset(&key, 0, sizeof(key));
241 key.data = &lno;
242 key.size = sizeof(lno);
243 if ((sp->db_error = ep->db->del(ep->db, NULL, &key, 0)) != 0) {
244 msgq(sp, M_DBERR, "003|unable to delete line %lu",
245 (u_long)lno);
246 return (1);
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;
255 /* File now modified. */
256 if (F_ISSET(ep, F_FIRSTMODIFY))
257 (void)rcv_init(sp);
258 F_SET(ep, F_MODIFIED);
260 /* Update screen. */
261 return (scr_update(sp, lno, LINE_DELETE, 1));
264 /* maybe this could be simpler
266 * DB3 behaves differently from DB1
268 * if lno != 0 just go to lno and put the new line after it
269 * if lno == 0 then if there are any record, put in front of the first
270 * otherwise just append to the end thus creating the first
271 * line
273 static int
274 append(sp, lno, p, len)
275 SCR *sp;
276 db_recno_t lno;
277 CHAR_T *p;
278 size_t len;
280 DBT data, key;
281 DBC *dbcp_put;
282 EXF *ep;
283 char *fp;
284 size_t flen;
286 ep = sp->ep;
288 memset(&key, 0, sizeof(key));
289 key.data = &lno;
290 key.size = sizeof(lno);
291 memset(&data, 0, sizeof(data));
293 if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp_put, 0)) != 0)
294 return 1;
296 INT2FILE(sp, p, len, fp, flen);
298 if (lno != 0) {
299 if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_SET)) != 0)
300 goto err2;
302 data.data = fp;
303 data.size = flen;
304 if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_AFTER)) != 0) {
305 err2:
306 (void)dbcp_put->c_close(dbcp_put);
307 return (1);
309 } else {
310 if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_FIRST)) != 0) {
311 if (sp->db_error != DB_NOTFOUND)
312 goto err2;
314 data.data = fp;
315 data.size = flen;
316 if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, DB_APPEND)) != 0) {
317 goto err2;
319 } else {
320 key.data = &lno;
321 key.size = sizeof(lno);
322 data.data = fp;
323 data.size = flen;
324 if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_BEFORE)) != 0) {
325 goto err2;
330 (void)dbcp_put->c_close(dbcp_put);
332 return 0;
336 * db_append --
337 * Append a line into the file.
339 * PUBLIC: int db_append __P((SCR *, int, db_recno_t, CHAR_T *, size_t));
342 db_append(sp, update, lno, p, len)
343 SCR *sp;
344 int update;
345 db_recno_t lno;
346 CHAR_T *p;
347 size_t len;
349 EXF *ep;
350 int rval;
352 #if defined(DEBUG) && 0
353 vtrace(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
354 #endif
355 /* Check for no underlying file. */
356 if ((ep = sp->ep) == NULL) {
357 ex_emsg(sp, NULL, EXM_NOFILEYET);
358 return (1);
361 /* Update file. */
362 if (append(sp, lno, p, len) != 0) {
363 msgq(sp, M_DBERR, "004|unable to append to line %lu",
364 (u_long)lno);
365 return (1);
368 /* Flush the cache, update line count, before screen update. */
369 if (lno < ep->c_lno)
370 ep->c_lno = OOBLNO;
371 if (ep->c_nlines != OOBLNO)
372 ++ep->c_nlines;
374 /* File now dirty. */
375 if (F_ISSET(ep, F_FIRSTMODIFY))
376 (void)rcv_init(sp);
377 F_SET(ep, F_MODIFIED);
379 /* Log change. */
380 log_line(sp, lno + 1, LOG_LINE_APPEND);
382 /* Update marks, @ and global commands. */
383 rval = 0;
384 if (mark_insdel(sp, LINE_INSERT, lno + 1))
385 rval = 1;
386 if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
387 rval = 1;
390 * Update screen.
392 * XXX
393 * Nasty hack. If multiple lines are input by the user, they aren't
394 * committed until an <ESC> is entered. The problem is the screen was
395 * updated/scrolled as each line was entered. So, when this routine
396 * is called to copy the new lines from the cut buffer into the file,
397 * it has to know not to update the screen again.
399 return (scr_update(sp, lno, LINE_APPEND, update) || rval);
403 * db_insert --
404 * Insert a line into the file.
406 * PUBLIC: int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t));
409 db_insert(sp, lno, p, len)
410 SCR *sp;
411 db_recno_t lno;
412 CHAR_T *p;
413 size_t len;
415 DBT data, key;
416 DBC *dbcp_put;
417 EXF *ep;
418 int rval;
420 #if defined(DEBUG) && 0
421 vtrace(sp, "insert before %lu: len %lu {%.*s}\n",
422 (u_long)lno, (u_long)len, MIN(len, 20), p);
423 #endif
424 /* Check for no underlying file. */
425 if ((ep = sp->ep) == NULL) {
426 ex_emsg(sp, NULL, EXM_NOFILEYET);
427 return (1);
430 /* Update file. */
431 if (append(sp, lno - 1, p, len) != 0) {
432 msgq(sp, M_DBERR, "005|unable to insert at line %lu",
433 (u_long)lno);
434 return (1);
437 /* Flush the cache, update line count, before screen update. */
438 if (lno >= ep->c_lno)
439 ep->c_lno = OOBLNO;
440 if (ep->c_nlines != OOBLNO)
441 ++ep->c_nlines;
443 /* File now dirty. */
444 if (F_ISSET(ep, F_FIRSTMODIFY))
445 (void)rcv_init(sp);
446 F_SET(ep, F_MODIFIED);
448 /* Log change. */
449 log_line(sp, lno, LOG_LINE_INSERT);
451 /* Update marks, @ and global commands. */
452 rval = 0;
453 if (mark_insdel(sp, LINE_INSERT, lno))
454 rval = 1;
455 if (ex_g_insdel(sp, LINE_INSERT, lno))
456 rval = 1;
458 /* Update screen. */
459 return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
463 * db_set --
464 * Store a line in the file.
466 * PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t));
469 db_set(sp, lno, p, len)
470 SCR *sp;
471 db_recno_t lno;
472 CHAR_T *p;
473 size_t len;
475 DBT data, key;
476 EXF *ep;
477 char *fp;
478 size_t flen;
480 #if defined(DEBUG) && 0
481 vtrace(sp, "replace line %lu: len %lu {%.*s}\n",
482 (u_long)lno, (u_long)len, MIN(len, 20), p);
483 #endif
484 /* Check for no underlying file. */
485 if ((ep = sp->ep) == NULL) {
486 ex_emsg(sp, NULL, EXM_NOFILEYET);
487 return (1);
490 /* Log before change. */
491 log_line(sp, lno, LOG_LINE_RESET_B);
493 INT2FILE(sp, p, len, fp, flen);
495 /* Update file. */
496 memset(&key, 0, sizeof(key));
497 key.data = &lno;
498 key.size = sizeof(lno);
499 memset(&data, 0, sizeof(data));
500 data.data = fp;
501 data.size = flen;
502 if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, 0)) != 0) {
503 msgq(sp, M_DBERR, "006|unable to store line %lu", (u_long)lno);
504 return (1);
507 /* Flush the cache, before logging or screen update. */
508 if (lno == ep->c_lno)
509 ep->c_lno = OOBLNO;
511 /* File now dirty. */
512 if (F_ISSET(ep, F_FIRSTMODIFY))
513 (void)rcv_init(sp);
514 F_SET(ep, F_MODIFIED);
516 /* Log after change. */
517 log_line(sp, lno, LOG_LINE_RESET_F);
519 /* Update screen. */
520 return (scr_update(sp, lno, LINE_RESET, 1));
524 * db_exist --
525 * Return if a line exists.
527 * PUBLIC: int db_exist __P((SCR *, db_recno_t));
530 db_exist(sp, lno)
531 SCR *sp;
532 db_recno_t lno;
534 EXF *ep;
536 /* Check for no underlying file. */
537 if ((ep = sp->ep) == NULL) {
538 ex_emsg(sp, NULL, EXM_NOFILEYET);
539 return (1);
542 if (lno == OOBLNO)
543 return (0);
546 * Check the last-line number cache. Adjust the cached line
547 * number for the lines used by the text input buffers.
549 if (ep->c_nlines != OOBLNO)
550 return (lno <= (F_ISSET(sp, SC_TINPUT) ?
551 ep->c_nlines + (((TEXT *)sp->tiq.cqh_last)->lno -
552 ((TEXT *)sp->tiq.cqh_first)->lno) : ep->c_nlines));
554 /* Go get the line. */
555 return (!db_get(sp, lno, 0, NULL, NULL));
559 * db_last --
560 * Return the number of lines in the file.
562 * PUBLIC: int db_last __P((SCR *, db_recno_t *));
565 db_last(sp, lnop)
566 SCR *sp;
567 db_recno_t *lnop;
569 DBT data, key;
570 DBC *dbcp;
571 EXF *ep;
572 db_recno_t lno;
573 CHAR_T *wp;
574 size_t wlen;
576 /* Check for no underlying file. */
577 if ((ep = sp->ep) == NULL) {
578 ex_emsg(sp, NULL, EXM_NOFILEYET);
579 return (1);
583 * Check the last-line number cache. Adjust the cached line
584 * number for the lines used by the text input buffers.
586 if (ep->c_nlines != OOBLNO) {
587 *lnop = ep->c_nlines;
588 if (F_ISSET(sp, SC_TINPUT))
589 *lnop += ((TEXT *)sp->tiq.cqh_last)->lno -
590 ((TEXT *)sp->tiq.cqh_first)->lno;
591 return (0);
594 memset(&key, 0, sizeof(key));
595 key.data = &lno;
596 key.size = sizeof(lno);
597 memset(&data, 0, sizeof(data));
599 if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp, 0)) != 0)
600 goto err1;
601 switch (sp->db_error = dbcp->c_get(dbcp, &key, &data, DB_LAST)) {
602 case DB_NOTFOUND:
603 *lnop = 0;
604 return (0);
605 default:
606 (void)dbcp->c_close(dbcp);
607 alloc_err:
608 err1:
609 msgq(sp, M_DBERR, "007|unable to get last line");
610 *lnop = 0;
611 return (1);
612 case 0:
615 (void)dbcp->c_close(dbcp);
617 FILE2INT(sp, data.data, data.size, wp, wlen);
619 /* Fill the cache. */
620 BINC_GOTOW(sp, ep->c_lp, ep->c_blen, wlen);
621 MEMCPYW(ep->c_lp, wp, wlen);
622 memcpy(&lno, key.data, sizeof(lno));
623 ep->c_nlines = ep->c_lno = lno;
624 ep->c_len = wlen;
626 /* Return the value. */
627 *lnop = (F_ISSET(sp, SC_TINPUT) &&
628 ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
629 ((TEXT *)sp->tiq.cqh_last)->lno : lno);
630 return (0);
634 * db_err --
635 * Report a line error.
637 * PUBLIC: void db_err __P((SCR *, db_recno_t));
639 void
640 db_err(sp, lno)
641 SCR *sp;
642 db_recno_t lno;
644 msgq(sp, M_ERR,
645 "008|Error: unable to retrieve line %lu", (u_long)lno);
649 * scr_update --
650 * Update all of the screens that are backed by the file that
651 * just changed.
653 static int
654 scr_update(sp, lno, op, current)
655 SCR *sp;
656 db_recno_t lno;
657 lnop_t op;
658 int current;
660 EXF *ep;
661 SCR *tsp;
662 WIN *wp;
664 if (F_ISSET(sp, SC_EX))
665 return (0);
667 /* XXXX goes outside of window */
668 ep = sp->ep;
669 if (ep->refcnt != 1)
670 for (wp = sp->gp->dq.cqh_first; wp != (void *)&sp->gp->dq;
671 wp = wp->q.cqe_next)
672 for (tsp = wp->scrq.cqh_first;
673 tsp != (void *)&wp->scrq; tsp = tsp->q.cqe_next)
674 if (sp != tsp && tsp->ep == ep)
675 if (vs_change(tsp, lno, op))
676 return (1);
677 return (current ? vs_change(sp, lno, op) : 0);