Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / common / db.c
blobd2a04fc898e192c630f49d0820850b5cd031f923
1 /* $NetBSD: db.c,v 1.2 2008/12/05 22:51:42 christos Exp $ */
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.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 static const char sccsid[] = "Id: db.c,v 10.48 2002/06/08 19:32:52 skimo Exp (Berkeley) Date: 2002/06/08 19:32:52";
16 #endif /* not lint */
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 #include <sys/time.h>
22 #include <bitstring.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "common.h"
30 #include "../vi/vi.h"
32 static int append __P((SCR*, db_recno_t, const CHAR_T*, size_t, lnop_t, int));
35 * db_eget --
36 * Front-end to db_get, special case handling for empty files.
38 * PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
40 int
41 db_eget(SCR *sp, db_recno_t lno, CHAR_T **pp, size_t *lenp, int *isemptyp)
43 /* Line number. */
44 /* Pointer store. */
45 /* Length store. */
48 db_recno_t l1;
50 if (isemptyp != NULL)
51 *isemptyp = 0;
53 /* If the line exists, simply return it. */
54 if (!db_get(sp, lno, 0, pp, lenp))
55 return (0);
58 * If the user asked for line 0 or line 1, i.e. the only possible
59 * line in an empty file, find the last line of the file; db_last
60 * fails loudly.
62 if ((lno == 0 || lno == 1) && db_last(sp, &l1))
63 return (1);
65 /* If the file isn't empty, fail loudly. */
66 if ((lno != 0 && lno != 1) || l1 != 0) {
67 db_err(sp, lno);
68 return (1);
71 if (isemptyp != NULL)
72 *isemptyp = 1;
74 return (1);
78 * db_get --
79 * Look in the text buffers for a line, followed by the cache, followed
80 * by the database.
82 * PUBLIC: int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *));
84 int
85 db_get(SCR *sp, db_recno_t lno, u_int32_t flags, CHAR_T **pp, size_t *lenp)
86 /* Line number. */ /* Pointer store. */ /* Length store. */
88 DBT data, key;
89 EXF *ep;
90 TEXT *tp;
91 db_recno_t l1, l2;
92 const CHAR_T *wp;
93 size_t wlen;
94 size_t nlen;
97 * The underlying recno stuff handles zero by returning NULL, but
98 * have to have an OOB condition for the look-aside into the input
99 * buffer anyway.
101 if (lno == 0)
102 goto err1;
104 /* Check for no underlying file. */
105 if ((ep = sp->ep) == NULL) {
106 ex_emsg(sp, NULL, EXM_NOFILEYET);
107 goto err3;
110 if (LF_ISSET(DBG_NOCACHE))
111 goto nocache;
114 * Look-aside into the TEXT buffers and see if the line we want
115 * is there.
117 if (F_ISSET(sp, SC_TINPUT)) {
118 l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
119 l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
120 if (l1 <= lno && l2 >= lno) {
121 #if defined(DEBUG) && 0
122 vtrace(sp,
123 "retrieve TEXT buffer line %lu\n", (u_long)lno);
124 #endif
125 for (tp = sp->tiq.cqh_first;
126 tp->lno != lno; tp = tp->q.cqe_next);
127 if (lenp != NULL)
128 *lenp = tp->len;
129 if (pp != NULL)
130 *pp = tp->lb;
131 return (0);
134 * Adjust the line number for the number of lines used
135 * by the text input buffers.
137 if (lno > l2)
138 lno -= l2 - l1;
141 /* Look-aside into the cache, and see if the line we want is there. */
142 if (lno == sp->c_lno) {
143 #if defined(DEBUG) && 0
144 vtrace(sp, "retrieve cached line %lu\n", (u_long)lno);
145 #endif
146 if (lenp != NULL)
147 *lenp = sp->c_len;
148 if (pp != NULL)
149 *pp = sp->c_lp;
150 return (0);
152 sp->c_lno = OOBLNO;
154 nocache:
155 nlen = 1024;
156 retry:
157 /* data.size contains length in bytes */
158 BINC_GOTO(sp, CHAR_T, sp->c_lp, sp->c_blen, nlen);
160 /* Get the line from the underlying database. */
161 memset(&key, 0, sizeof(key));
162 key.data = &lno;
163 key.size = sizeof(lno);
164 memset(&data, 0, sizeof(data));
165 data.data = sp->c_lp;
166 data.ulen = sp->c_blen;
167 data.flags = DB_DBT_USERMEM;
168 switch (ep->db->get(ep->db, NULL, &key, &data, 0)) {
169 case DB_BUFFER_SMALL:
170 nlen = data.size;
171 goto retry;
172 default:
173 goto err2;
174 case DB_NOTFOUND:
175 err1: if (LF_ISSET(DBG_FATAL))
176 err2: db_err(sp, lno);
177 alloc_err:
178 err3: if (lenp != NULL)
179 *lenp = 0;
180 if (pp != NULL)
181 *pp = NULL;
182 return (1);
183 case 0:
187 if (FILE2INT(sp, data.data, data.size, wp, wlen)) {
188 if (!F_ISSET(sp, SC_CONV_ERROR)) {
189 F_SET(sp, SC_CONV_ERROR);
190 msgq(sp, M_ERR, "324|Conversion error on line %d", lno);
192 goto err3;
195 /* Reset the cache. */
196 if (wp != data.data) {
197 BINC_GOTOW(sp, sp->c_lp, sp->c_blen, wlen);
198 MEMCPYW(sp->c_lp, wp, wlen);
200 sp->c_lno = lno;
201 sp->c_len = wlen;
203 #if defined(DEBUG) && 0
204 vtrace(sp, "retrieve DB line %lu\n", (u_long)lno);
205 #endif
206 if (lenp != NULL)
207 *lenp = wlen;
208 if (pp != NULL)
209 *pp = sp->c_lp;
210 return (0);
214 * db_delete --
215 * Delete a line from the file.
217 * PUBLIC: int db_delete __P((SCR *, db_recno_t));
220 db_delete(SCR *sp, db_recno_t lno)
222 DBT key;
223 EXF *ep;
225 #if defined(DEBUG) && 0
226 vtrace(sp, "delete line %lu\n", (u_long)lno);
227 #endif
228 /* Check for no underlying file. */
229 if ((ep = sp->ep) == NULL) {
230 ex_emsg(sp, NULL, EXM_NOFILEYET);
231 return (1);
233 if (ep->l_win && ep->l_win != sp->wp) {
234 ex_emsg(sp, NULL, EXM_LOCKED);
235 return 1;
238 /* Update marks, @ and global commands. */
239 if (line_insdel(sp, LINE_DELETE, lno))
240 return 1;
242 /* Log before change. */
243 log_line(sp, lno, LOG_LINE_DELETE_B);
245 /* Update file. */
246 memset(&key, 0, sizeof(key));
247 key.data = &lno;
248 key.size = sizeof(lno);
249 if ((sp->db_error = ep->db->del(ep->db, NULL, &key, 0)) != 0) {
250 msgq(sp, M_DBERR, "003|unable to delete line %lu",
251 (u_long)lno);
252 return (1);
255 /* Flush the cache, update line count, before screen update. */
256 update_cache(sp, LINE_DELETE, lno);
258 /* File now modified. */
259 if (F_ISSET(ep, F_FIRSTMODIFY))
260 (void)rcv_init(sp);
261 F_SET(ep, F_MODIFIED);
263 /* Log after change. */
264 log_line(sp, lno, LOG_LINE_DELETE_F);
266 /* Update screen. */
267 return (scr_update(sp, lno, LINE_DELETE, 1));
270 /* maybe this could be simpler
272 * DB3 behaves differently from DB1
274 * if lno != 0 just go to lno and put the new line after it
275 * if lno == 0 then if there are any record, put in front of the first
276 * otherwise just append to the end thus creating the first
277 * line
279 static int
280 append(SCR *sp, db_recno_t lno, const CHAR_T *p, size_t len, lnop_t op, int update)
282 DBT data, key;
283 DBC *dbcp_put;
284 EXF *ep;
285 const char *fp;
286 size_t flen;
287 int rval;
289 /* Check for no underlying file. */
290 if ((ep = sp->ep) == NULL) {
291 ex_emsg(sp, NULL, EXM_NOFILEYET);
292 return (1);
294 if (ep->l_win && ep->l_win != sp->wp) {
295 ex_emsg(sp, NULL, EXM_LOCKED);
296 return 1;
299 /* Log before change. */
300 log_line(sp, lno + 1, LOG_LINE_APPEND_B);
302 /* Update file. */
303 memset(&key, 0, sizeof(key));
304 key.data = &lno;
305 key.size = sizeof(lno);
306 memset(&data, 0, sizeof(data));
308 if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp_put, 0)) != 0)
309 return 1;
311 INT2FILE(sp, p, len, fp, flen);
313 if (lno != 0) {
314 if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_SET)) != 0)
315 goto err2;
317 data.data = __UNCONST(fp);
318 data.size = flen;
319 if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_AFTER)) != 0) {
320 err2:
321 (void)dbcp_put->c_close(dbcp_put);
322 msgq(sp, M_DBERR,
323 (op == LINE_APPEND)
324 ? "004|unable to append to line %lu"
325 : "005|unable to insert at line %lu",
326 (u_long)lno);
327 return (1);
329 } else {
330 if ((sp->db_error = dbcp_put->c_get(dbcp_put, &key, &data, DB_FIRST)) != 0) {
331 if (sp->db_error != DB_NOTFOUND)
332 goto err2;
334 data.data = __UNCONST(fp);
335 data.size = flen;
336 if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, DB_APPEND)) != 0) {
337 goto err2;
339 } else {
340 key.data = &lno;
341 key.size = sizeof(lno);
342 data.data = __UNCONST(fp);
343 data.size = flen;
344 if ((sp->db_error = dbcp_put->c_put(dbcp_put, &key, &data, DB_BEFORE)) != 0) {
345 goto err2;
350 (void)dbcp_put->c_close(dbcp_put);
352 /* Flush the cache, update line count, before screen update. */
353 update_cache(sp, LINE_INSERT, lno);
355 /* File now dirty. */
356 if (F_ISSET(ep, F_FIRSTMODIFY))
357 (void)rcv_init(sp);
358 F_SET(ep, F_MODIFIED);
360 /* Log after change. */
361 log_line(sp, lno + 1, LOG_LINE_APPEND_F);
363 /* Update marks, @ and global commands. */
364 rval = line_insdel(sp, LINE_INSERT, lno + 1);
367 * Update screen.
369 * comment copied from db_append
370 * XXX
371 * Nasty hack. If multiple lines are input by the user, they aren't
372 * committed until an <ESC> is entered. The problem is the screen was
373 * updated/scrolled as each line was entered. So, when this routine
374 * is called to copy the new lines from the cut buffer into the file,
375 * it has to know not to update the screen again.
377 return (scr_update(sp, lno + 1, LINE_INSERT, update) || rval);
381 * db_append --
382 * Append a line into the file.
384 * PUBLIC: int db_append __P((SCR *, int, db_recno_t, CHAR_T *, size_t));
387 db_append(SCR *sp, int update, db_recno_t lno, const CHAR_T *p, size_t len)
389 #if defined(DEBUG) && 0
390 vtrace(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
391 #endif
393 /* Update file. */
394 return append(sp, lno, p, len, LINE_APPEND, update);
398 * db_insert --
399 * Insert a line into the file.
401 * PUBLIC: int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t));
404 db_insert(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len)
406 #if defined(DEBUG) && 0
407 vtrace(sp, "insert before %lu: len %lu {%.*s}\n",
408 (u_long)lno, (u_long)len, MIN(len, 20), p);
409 #endif
410 return append(sp, lno - 1, p, len, LINE_INSERT, 1);
414 * db_set --
415 * Store a line in the file.
417 * PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t));
420 db_set(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len)
422 DBT data, key;
423 EXF *ep;
424 const char *fp;
425 size_t flen;
427 #if defined(DEBUG) && 0
428 vtrace(sp, "replace line %lu: len %lu {%.*s}\n",
429 (u_long)lno, (u_long)len, MIN(len, 20), p);
430 #endif
431 /* Check for no underlying file. */
432 if ((ep = sp->ep) == NULL) {
433 ex_emsg(sp, NULL, EXM_NOFILEYET);
434 return (1);
436 if (ep->l_win && ep->l_win != sp->wp) {
437 ex_emsg(sp, NULL, EXM_LOCKED);
438 return 1;
441 /* Log before change. */
442 log_line(sp, lno, LOG_LINE_RESET_B);
444 INT2FILE(sp, p, len, fp, flen);
446 /* Update file. */
447 memset(&key, 0, sizeof(key));
448 key.data = &lno;
449 key.size = sizeof(lno);
450 memset(&data, 0, sizeof(data));
451 data.data = __UNCONST(fp);
452 data.size = flen;
453 if ((sp->db_error = ep->db->put(ep->db, NULL, &key, &data, 0)) != 0) {
454 msgq(sp, M_DBERR, "006|unable to store line %lu", (u_long)lno);
455 return (1);
458 /* Flush the cache, update line count, before screen update. */
459 update_cache(sp, LINE_RESET, lno);
461 /* File now dirty. */
462 if (F_ISSET(ep, F_FIRSTMODIFY))
463 (void)rcv_init(sp);
464 F_SET(ep, F_MODIFIED);
466 /* Log after change. */
467 log_line(sp, lno, LOG_LINE_RESET_F);
469 /* Update screen. */
470 return (scr_update(sp, lno, LINE_RESET, 1));
474 * db_exist --
475 * Return if a line exists.
477 * PUBLIC: int db_exist __P((SCR *, db_recno_t));
480 db_exist(SCR *sp, db_recno_t lno)
482 EXF *ep;
484 /* Check for no underlying file. */
485 if ((ep = sp->ep) == NULL) {
486 ex_emsg(sp, NULL, EXM_NOFILEYET);
487 return (1);
490 if (lno == OOBLNO)
491 return (0);
494 * Check the last-line number cache. Adjust the cached line
495 * number for the lines used by the text input buffers.
497 if (ep->c_nlines != OOBLNO)
498 return (lno <= (F_ISSET(sp, SC_TINPUT) ?
499 ep->c_nlines + (((TEXT *)sp->tiq.cqh_last)->lno -
500 ((TEXT *)sp->tiq.cqh_first)->lno) : ep->c_nlines));
502 /* Go get the line. */
503 return (!db_get(sp, lno, 0, NULL, NULL));
507 * db_last --
508 * Return the number of lines in the file.
510 * PUBLIC: int db_last __P((SCR *, db_recno_t *));
513 db_last(SCR *sp, db_recno_t *lnop)
515 DBT data, key;
516 DBC *dbcp;
517 EXF *ep;
518 db_recno_t lno;
519 const CHAR_T *wp;
520 size_t wlen;
522 /* Check for no underlying file. */
523 if ((ep = sp->ep) == NULL) {
524 ex_emsg(sp, NULL, EXM_NOFILEYET);
525 return (1);
529 * Check the last-line number cache. Adjust the cached line
530 * number for the lines used by the text input buffers.
532 if (ep->c_nlines != OOBLNO) {
533 *lnop = ep->c_nlines;
534 if (F_ISSET(sp, SC_TINPUT))
535 *lnop += ((TEXT *)sp->tiq.cqh_last)->lno -
536 ((TEXT *)sp->tiq.cqh_first)->lno;
537 return (0);
540 memset(&key, 0, sizeof(key));
541 key.data = &lno;
542 key.size = sizeof(lno);
543 memset(&data, 0, sizeof(data));
545 if ((sp->db_error = ep->db->cursor(ep->db, NULL, &dbcp, 0)) != 0)
546 goto err1;
547 switch (sp->db_error = dbcp->c_get(dbcp, &key, &data, DB_LAST)) {
548 case DB_NOTFOUND:
549 *lnop = 0;
550 return (0);
551 default:
552 (void)dbcp->c_close(dbcp);
553 alloc_err:
554 err1:
555 msgq(sp, M_DBERR, "007|unable to get last line");
556 *lnop = 0;
557 return (1);
558 case 0:
562 memcpy(&lno, key.data, sizeof(lno));
564 if (lno != sp->c_lno) {
565 FILE2INT(sp, data.data, data.size, wp, wlen);
567 /* Fill the cache. */
568 BINC_GOTOW(sp, sp->c_lp, sp->c_blen, wlen);
569 MEMCPYW(sp->c_lp, wp, wlen);
570 sp->c_lno = lno;
571 sp->c_len = wlen;
573 ep->c_nlines = lno;
575 (void)dbcp->c_close(dbcp);
577 /* Return the value. */
578 *lnop = (F_ISSET(sp, SC_TINPUT) &&
579 ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
580 ((TEXT *)sp->tiq.cqh_last)->lno : lno);
581 return (0);
585 * db_err --
586 * Report a line error.
588 * PUBLIC: void db_err __P((SCR *, db_recno_t));
590 void
591 db_err(SCR *sp, db_recno_t lno)
593 msgq(sp, M_ERR,
594 "008|Error: unable to retrieve line %lu", (u_long)lno);
598 * scr_update --
599 * Update all of the screens that are backed by the file that
600 * just changed.
602 * PUBLIC: int scr_update __P((SCR *sp, db_recno_t lno,
603 * PUBLIC: lnop_t op, int current));
606 scr_update(SCR *sp, db_recno_t lno, lnop_t op, int current)
608 EXF *ep;
609 SCR *tsp;
610 WIN *wp;
612 if (F_ISSET(sp, SC_EX))
613 return (0);
615 /* XXXX goes outside of window */
616 ep = sp->ep;
617 if (ep->refcnt != 1)
618 for (wp = sp->gp->dq.cqh_first; wp != (void *)&sp->gp->dq;
619 wp = wp->q.cqe_next)
620 for (tsp = wp->scrq.cqh_first;
621 tsp != (void *)&wp->scrq; tsp = tsp->q.cqe_next)
622 if (sp != tsp && tsp->ep == ep)
623 if (vs_change(tsp, lno, op))
624 return (1);
625 return (current ? vs_change(sp, lno, op) : 0);
629 * PUBLIC: void update_cache __P((SCR *sp, lnop_t op, db_recno_t lno));
631 void
632 update_cache(SCR *sp, lnop_t op, db_recno_t lno)
634 SCR* scrp;
635 EXF *ep;
637 ep = sp->ep;
639 /* Flush the cache, update line count, before screen update. */
640 /* The flushing is probably not needed, since it was incorrect
641 * for db_insert. It might be better to adjust it, like
642 * marks, @ and global
644 for (scrp = ep->scrq.cqh_first; scrp != (void *)&ep->scrq;
645 scrp = scrp->eq.cqe_next)
646 switch (op) {
647 case LINE_INSERT:
648 case LINE_DELETE:
649 if (lno <= scrp->c_lno)
650 scrp->c_lno = OOBLNO;
651 break;
652 case LINE_RESET:
653 if (lno == scrp->c_lno)
654 scrp->c_lno = OOBLNO;
655 /*FALLTHROUGH*/
656 case LINE_APPEND:
657 break;
660 if (ep->c_nlines != OOBLNO)
661 switch (op) {
662 case LINE_INSERT:
663 ++ep->c_nlines;
664 break;
665 case LINE_DELETE:
666 --ep->c_nlines;
667 /*FALLTHROUGH*/
668 case LINE_APPEND:
669 case LINE_RESET:
670 break;
675 * PUBLIC: int line_insdel __P((SCR *sp, lnop_t op, db_recno_t lno));
678 line_insdel(SCR *sp, lnop_t op, db_recno_t lno)
680 int rval;
682 /* Update marks, @ and global commands. */
683 rval = 0;
684 if (mark_insdel(sp, op, lno))
685 rval = 1;
686 if (ex_g_insdel(sp, op, lno))
687 rval = 1;
689 return rval;