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.
13 static const char sccsid
[] = "$Id: db1.c,v 10.1 2002/03/09 12:53:57 skimo Exp $ (Berkeley) $Date: 2002/03/09 12:53:57 $";
16 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
29 static int scr_update
__P((SCR
*, db_recno_t
, lnop_t
, int));
33 * Front-end to db_get, special case handling for empty files.
35 * PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
38 db_eget(SCR
*sp
, db_recno_t lno
, CHAR_T
**pp
, size_t *lenp
, int *isemptyp
)
50 /* If the line exists, simply return it. */
51 if (!db_get(sp
, lno
, 0, pp
, lenp
))
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
59 if ((lno
== 0 || lno
== 1) && db_last(sp
, &l1
))
62 /* If the file isn't empty, fail loudly. */
63 if ((lno
!= 0 && lno
!= 1) || l1
!= 0) {
76 * Look in the text buffers for a line, followed by the cache, followed
79 * PUBLIC: int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *));
82 db_get(SCR
*sp
, db_recno_t lno
, u_int32_t flags
, CHAR_T
**pp
, size_t *lenp
)
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
105 /* Check for no underlying file. */
106 if ((ep
= sp
->ep
) == NULL
) {
107 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
111 if (LF_ISSET(DBG_NOCACHE
))
115 * Look-aside into the TEXT buffers and see if the line we want
118 if (F_ISSET(sp
, SC_TINPUT
)) {
119 l1
= ((TEXT
*)sp
->tiq
.cqh_first
)->lno
;
120 l2
= ((TEXT
*)sp
->tiq
.cqh_last
)->lno
;
121 if (l1
<= lno
&& l2
>= lno
) {
122 #if defined(DEBUG) && 0
124 "retrieve TEXT buffer line %lu\n", (u_long
)lno
);
126 for (tp
= sp
->tiq
.cqh_first
;
127 tp
->lno
!= lno
; tp
= tp
->q
.cqe_next
);
135 * Adjust the line number for the number of lines used
136 * by the text input buffers.
142 /* Look-aside into the cache, and see if the line we want is there. */
144 * Line cache will not work if different screens view the same
145 * file with different encodings.
146 * Multiple threads accessing the same cache can be a problem as
148 * So, line cache is (temporarily) disabled.
150 if (0 && lno
== ep
->c_lno
) {
151 #if defined(DEBUG) && 0
152 vtrace(sp
, "retrieve cached line %lu\n", (u_long
)lno
);
165 /* data.size contains length in bytes */
166 BINC_GOTO(sp
, (char *)ep
->c_lp
, ep
->c_blen
, nlen
);
168 /* Get the line from the underlying database. */
170 key
.size
= sizeof(lno
);
171 switch (ep
->db
->actual_db
->get(ep
->db
->actual_db
, &key
, &data
, 0)) {
175 err1
: if (LF_ISSET(DBG_FATAL
))
176 err2
: db_err(sp
, lno
);
178 err3
: if (lenp
!= NULL
)
184 if (data
.size
> nlen
) {
188 memcpy(ep
->c_lp
, data
.data
, nlen
);
191 if (FILE2INT(sp
, data
.data
, data
.size
, wp
, wlen
)) {
192 if (!F_ISSET(sp
, SC_CONV_ERROR
)) {
193 F_SET(sp
, SC_CONV_ERROR
);
194 msgq(sp
, M_ERR
, "324|Conversion error on line %d", lno
);
199 /* Reset the cache. */
200 if (wp
!= data
.data
) {
201 BINC_GOTOW(sp
, ep
->c_lp
, ep
->c_blen
, wlen
);
202 MEMCPYW(ep
->c_lp
, wp
, wlen
);
207 #if defined(DEBUG) && 0
208 vtrace(sp
, "retrieve DB line %lu\n", (u_long
)lno
);
219 * Delete a line from the file.
221 * PUBLIC: int db_delete __P((SCR *, db_recno_t));
224 db_delete(SCR
*sp
, db_recno_t lno
)
229 #if defined(DEBUG) && 0
230 vtrace(sp
, "delete line %lu\n", (u_long
)lno
);
232 /* Check for no underlying file. */
233 if ((ep
= sp
->ep
) == NULL
) {
234 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
237 if (ep
->l_win
&& ep
->l_win
!= sp
->wp
) {
238 ex_emsg(sp
, NULL
, EXM_LOCKED
);
242 /* Update marks, @ and global commands. */
243 if (mark_insdel(sp
, LINE_DELETE
, lno
))
245 if (ex_g_insdel(sp
, LINE_DELETE
, lno
))
249 log_line(sp
, lno
, LOG_LINE_DELETE
);
253 key
.size
= sizeof(lno
);
254 sp
->db_error
= ep
->db
->actual_db
->del(ep
->db
->actual_db
, &key
, 0);
255 if (sp
->db_error
!= 0) {
256 if (sp
->db_error
== -1)
257 sp
->db_error
= errno
;
259 msgq(sp
, M_DBERR
, "003|unable to delete line %lu",
264 /* Flush the cache, update line count, before screen update. */
265 if (lno
<= ep
->c_lno
)
267 if (ep
->c_nlines
!= OOBLNO
)
270 /* File now modified. */
271 if (F_ISSET(ep
, F_FIRSTMODIFY
))
273 F_SET(ep
, F_MODIFIED
);
276 return (scr_update(sp
, lno
, LINE_DELETE
, 1));
281 * Append a line into the file.
283 * PUBLIC: int db_append __P((SCR *, int, db_recno_t, CHAR_T *, size_t));
286 db_append(SCR
*sp
, int update
, db_recno_t lno
, CHAR_T
*p
, size_t len
)
292 #if defined(DEBUG) && 0
293 vtrace(sp
, "append to %lu: len %u {%.*s}\n", lno
, len
, MIN(len
, 20), p
);
295 /* Check for no underlying file. */
296 if ((ep
= sp
->ep
) == NULL
) {
297 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
300 if (ep
->l_win
&& ep
->l_win
!= sp
->wp
) {
301 ex_emsg(sp
, NULL
, EXM_LOCKED
);
307 key
.size
= sizeof(lno
);
310 if (ep
->db
->actual_db
->put(ep
->db
->actual_db
, &key
, &data
, R_IAFTER
)) {
311 msgq(sp
, M_DBERR
, "004|unable to append to line %lu",
316 /* Flush the cache, update line count, before screen update. */
319 if (ep
->c_nlines
!= OOBLNO
)
322 /* File now dirty. */
323 if (F_ISSET(ep
, F_FIRSTMODIFY
))
325 F_SET(ep
, F_MODIFIED
);
328 log_line(sp
, lno
+ 1, LOG_LINE_APPEND
);
330 /* Update marks, @ and global commands. */
332 if (mark_insdel(sp
, LINE_INSERT
, lno
+ 1))
334 if (ex_g_insdel(sp
, LINE_INSERT
, lno
+ 1))
341 * Nasty hack. If multiple lines are input by the user, they aren't
342 * committed until an <ESC> is entered. The problem is the screen was
343 * updated/scrolled as each line was entered. So, when this routine
344 * is called to copy the new lines from the cut buffer into the file,
345 * it has to know not to update the screen again.
347 return (scr_update(sp
, lno
, LINE_APPEND
, update
) || rval
);
352 * Insert a line into the file.
354 * PUBLIC: int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t));
357 db_insert(SCR
*sp
, db_recno_t lno
, CHAR_T
*p
, size_t len
)
363 #if defined(DEBUG) && 0
364 vtrace(sp
, "insert before %lu: len %lu {%.*s}\n",
365 (u_long
)lno
, (u_long
)len
, MIN(len
, 20), p
);
367 /* Check for no underlying file. */
368 if ((ep
= sp
->ep
) == NULL
) {
369 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
372 if (ep
->l_win
&& ep
->l_win
!= sp
->wp
) {
373 ex_emsg(sp
, NULL
, EXM_LOCKED
);
379 key
.size
= sizeof(lno
);
382 if (ep
->db
->actual_db
->put(ep
->db
->actual_db
, &key
, &data
, R_IBEFORE
)) {
384 "005|unable to insert at line %lu", (u_long
)lno
);
388 /* Flush the cache, update line count, before screen update. */
389 if (lno
>= ep
->c_lno
)
391 if (ep
->c_nlines
!= OOBLNO
)
394 /* File now dirty. */
395 if (F_ISSET(ep
, F_FIRSTMODIFY
))
397 F_SET(ep
, F_MODIFIED
);
400 log_line(sp
, lno
, LOG_LINE_INSERT
);
402 /* Update marks, @ and global commands. */
404 if (mark_insdel(sp
, LINE_INSERT
, lno
))
406 if (ex_g_insdel(sp
, LINE_INSERT
, lno
))
410 return (scr_update(sp
, lno
, LINE_INSERT
, 1) || rval
);
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
)
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
);
431 /* Check for no underlying file. */
432 if ((ep
= sp
->ep
) == NULL
) {
433 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
436 if (ep
->l_win
&& ep
->l_win
!= sp
->wp
) {
437 ex_emsg(sp
, NULL
, EXM_LOCKED
);
441 /* Log before change. */
442 log_line(sp
, lno
, LOG_LINE_RESET_B
);
444 INT2FILE(sp
, p
, len
, fp
, flen
);
448 key
.size
= sizeof(lno
);
452 ep
->db
->actual_db
->put(ep
->db
->actual_db
, &key
, &data
, 0);
453 if (sp
->db_error
!= 0) {
454 if (sp
->db_error
== -1)
455 sp
->db_error
= errno
;
457 msgq(sp
, M_DBERR
, "006|unable to store line %lu", (u_long
)lno
);
461 /* Flush the cache, before logging or screen update. */
462 if (lno
== ep
->c_lno
)
465 /* File now dirty. */
466 if (F_ISSET(ep
, F_FIRSTMODIFY
))
468 F_SET(ep
, F_MODIFIED
);
470 /* Log after change. */
471 log_line(sp
, lno
, LOG_LINE_RESET_F
);
474 return (scr_update(sp
, lno
, LINE_RESET
, 1));
479 * Return if a line exists.
481 * PUBLIC: int db_exist __P((SCR *, db_recno_t));
484 db_exist(SCR
*sp
, db_recno_t lno
)
488 /* Check for no underlying file. */
489 if ((ep
= sp
->ep
) == NULL
) {
490 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
498 * Check the last-line number cache. Adjust the cached line
499 * number for the lines used by the text input buffers.
501 if (ep
->c_nlines
!= OOBLNO
)
502 return (lno
<= (F_ISSET(sp
, SC_TINPUT
) ?
503 ep
->c_nlines
+ (((TEXT
*)sp
->tiq
.cqh_last
)->lno
-
504 ((TEXT
*)sp
->tiq
.cqh_first
)->lno
) : ep
->c_nlines
));
506 /* Go get the line. */
507 return (!db_get(sp
, lno
, 0, NULL
, NULL
));
512 * Return the number of lines in the file.
514 * PUBLIC: int db_last __P((SCR *, db_recno_t *));
517 db_last(SCR
*sp
, db_recno_t
*lnop
)
525 /* Check for no underlying file. */
526 if ((ep
= sp
->ep
) == NULL
) {
527 ex_emsg(sp
, NULL
, EXM_NOFILEYET
);
532 * Check the last-line number cache. Adjust the cached line
533 * number for the lines used by the text input buffers.
535 if (ep
->c_nlines
!= OOBLNO
) {
536 *lnop
= ep
->c_nlines
;
537 if (F_ISSET(sp
, SC_TINPUT
))
538 *lnop
+= ((TEXT
*)sp
->tiq
.cqh_last
)->lno
-
539 ((TEXT
*)sp
->tiq
.cqh_first
)->lno
;
544 key
.size
= sizeof(lno
);
546 sp
->db_error
= ep
->db
->actual_db
->seq(ep
->db
->actual_db
, &key
, &data
,
548 switch (sp
->db_error
) {
553 sp
->db_error
= errno
;
555 msgq(sp
, M_DBERR
, "007|unable to get last line");
562 memcpy(&lno
, key
.data
, sizeof(lno
));
564 if (lno
!= ep
->c_lno
) {
565 FILE2INT(sp
, data
.data
, data
.size
, wp
, wlen
);
567 /* Fill the cache. */
568 if (wp
!= data
.data
) {
569 BINC_GOTOW(sp
, ep
->c_lp
, ep
->c_blen
, wlen
);
570 MEMCPYW(ep
->c_lp
, wp
, wlen
);
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
);
586 * Report a line error.
588 * PUBLIC: void db_err __P((SCR *, db_recno_t));
591 db_err(SCR
*sp
, db_recno_t lno
)
594 "008|Error: unable to retrieve line %lu", (u_long
)lno
);
599 * Update all of the screens that are backed by the file that
603 scr_update(SCR
*sp
, db_recno_t lno
, lnop_t op
, int current
)
609 if (F_ISSET(sp
, SC_EX
))
612 /* XXXX goes outside of window */
615 for (wp
= sp
->gp
->dq
.cqh_first
; wp
!= (void *)&sp
->gp
->dq
;
617 for (tsp
= wp
->scrq
.cqh_first
;
618 tsp
!= (void *)&wp
->scrq
; tsp
= tsp
->q
.cqe_next
)
619 if (sp
!= tsp
&& tsp
->ep
== ep
)
620 if (vs_change(tsp
, lno
, op
))
622 return (current
? vs_change(sp
, lno
, op
) : 0);
626 * DB1->3 compatibility layer
636 static int db1_close(DB
*, u_int32_t
);
637 static int db1_open(DB
*, const char *, const char *, DBTYPE
, u_int32_t
, int);
638 static int db1_sync(DB
*, u_int32_t
);
639 static int db1_get(DB
*, DB_TXN
*, DBT
*, DBT
*, u_int32_t
);
640 static int db1_put(DB
*, DB_TXN
*, DBT
*, DBT
*, u_int32_t
);
641 static int db1_set_flags(DB
*, u_int32_t
);
642 static int db1_set_pagesize(DB
*, u_int32_t
);
643 static int db1_set_re_delim(DB
*, int);
644 static int db1_set_re_source(DB
*, const char *);
647 db_create(DB
**dbp
, DB_ENV
*dbenv
, u_int32_t flags
) {
648 assert(dbenv
== NULL
&& flags
== 0);
650 *dbp
= malloc(sizeof **dbp
);
654 (*dbp
)->type
= DB_UNKNOWN
;
655 (*dbp
)->actual_db
= NULL
;
656 (*dbp
)->_pagesize
= 0;
658 memset(&(*dbp
)->_recno_info
, 0, sizeof (RECNOINFO
));
660 (*dbp
)->close
= db1_close
;
661 (*dbp
)->open
= db1_open
;
662 (*dbp
)->sync
= db1_sync
;
663 (*dbp
)->get
= db1_get
;
664 (*dbp
)->put
= db1_put
;
665 (*dbp
)->set_flags
= db1_set_flags
;
666 (*dbp
)->set_pagesize
= db1_set_pagesize
;
667 (*dbp
)->set_re_delim
= db1_set_re_delim
;
668 (*dbp
)->set_re_source
= db1_set_re_source
;
674 db_strerror(int error
) {
675 return error
> 0? strerror(error
) : "record not found";
679 db1_close(DB
*db
, u_int32_t flags
) {
680 if (flags
& DB_NOSYNC
) {
683 db
->actual_db
->close(db
->actual_db
);
685 db
->type
= DB_UNKNOWN
;
686 db
->actual_db
= NULL
;
689 memset(&db
->_recno_info
, 0, sizeof (RECNOINFO
));
695 db1_open(DB
*db
, const char *file
, const char *database
, DBTYPE type
,
696 u_int32_t flags
, int mode
) {
699 assert(database
== NULL
&& !(flags
& ~(DB_CREATE
| DB_TRUNCATE
)));
703 if (flags
& DB_CREATE
)
705 if (flags
& DB_TRUNCATE
)
708 if (type
== DB_RECNO
) {
709 char *tmp
= (char *) file
;
711 /* The interface is reversed in DB3 */
712 file
= db
->_recno_info
.bfname
;
713 db
->_recno_info
.bfname
= tmp
;
715 /* ... and so, we should avoid to truncate the main file! */
716 oldflags
&= ~O_TRUNC
;
718 db
->_recno_info
.flags
=
719 db
->_flags
& DB_SNAPSHOT
? R_SNAPSHOT
: 0;
720 db
->_recno_info
.psize
= db
->_pagesize
;
723 db
->actual_db
= dbopen(file
, oldflags
, mode
, type
,
724 type
== DB_RECNO
? &db
->_recno_info
: NULL
);
726 return db
->actual_db
== NULL
? errno
: 0;
730 db1_sync(DB
*db
, u_int32_t flags
) {
733 return db
->actual_db
->sync(db
->actual_db
, db
->type
== DB_UNKNOWN
?
734 R_RECNOSYNC
: 0) == 0? 0 : errno
;
738 db1_get(DB
*db
, DB_TXN
*txnid
, DBT
*key
, DBT
*data
, u_int32_t flags
) {
741 assert(flags
== 0 && txnid
== NULL
);
743 err
= db
->actual_db
->get(db
->actual_db
, key
, data
, flags
);
745 return err
== -1? errno
: err
;
749 db1_put(DB
*db
, DB_TXN
*txnid
, DBT
*key
, DBT
*data
, u_int32_t flags
) {
752 assert(flags
== 0 && txnid
== NULL
);
754 err
= db
->actual_db
->put(db
->actual_db
, key
, data
, flags
);
756 return err
== -1? errno
: err
;
760 db1_set_flags(DB
*db
, u_int32_t flags
) {
761 assert((flags
& ~(DB_RENUMBER
| DB_SNAPSHOT
)) == 0);
763 /* Can't prevent renumbering from happening with DB1 */
764 assert((flags
| db
->_flags
) & DB_RENUMBER
);
773 db1_set_pagesize(DB
*db
, u_int32_t pagesize
) {
774 db
->_pagesize
= pagesize
;
780 db1_set_re_delim(DB
*db
, int re_delim
) {
781 db
->_recno_info
.bval
= re_delim
;
787 db1_set_re_source(DB
*db
, const char *re_source
) {
788 db
->_recno_info
.bfname
= (char *) re_source
;