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 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 "; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.h> 19 #include <sys/stat.h> 20 21 #include <bitstring.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <limits.h> 25 #include <stdio.h> 26 #include <string.h> 27 28 #include "common.h" 29 #include "../vi/vi.h" 30 #include "dbinternal.h" 31 32 /* 33 * db_eget -- 34 * Front-end to db_get, special case handling for empty files. 35 * 36 * PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *)); 37 */ 38 int 39 db_eget(SCR *sp, db_recno_t lno, CHAR_T **pp, size_t *lenp, int *isemptyp) 40 41 /* Line number. */ 42 /* Pointer store. */ 43 /* Length store. */ 44 45 { 46 db_recno_t l1; 47 48 if (isemptyp != NULL) 49 *isemptyp = 0; 50 51 /* If the line exists, simply return it. */ 52 if (!db_get(sp, lno, 0, pp, lenp)) 53 return (0); 54 55 /* 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. 59 */ 60 if ((lno == 0 || lno == 1) && db_last(sp, &l1)) 61 return (1); 62 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); 67 } 68 69 if (isemptyp != NULL) 70 *isemptyp = 1; 71 72 return (1); 73 } 74 75 /* 76 * db_get -- 77 * Look in the text buffers for a line, followed by the cache, followed 78 * by the database. 79 * 80 * PUBLIC: int db_get __P((SCR *, db_recno_t, u_int32_t, CHAR_T **, size_t *)); 81 */ 82 int 83 db_get(SCR *sp, db_recno_t lno, u_int32_t flags, CHAR_T **pp, size_t *lenp) 84 85 /* Line number. */ 86 87 /* Pointer store. */ 88 /* Length store. */ 89 { 90 DBT data, key; 91 EXF *ep; 92 TEXT *tp; 93 db_recno_t l1, l2; 94 const CHAR_T *wp; 95 size_t wlen; 96 size_t nlen; 97 98 /* 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. 102 */ 103 if (lno == 0) 104 goto err1; 105 106 /* Check for no underlying file. */ 107 if ((ep = sp->ep) == NULL) { 108 ex_emsg(sp, NULL, EXM_NOFILEYET); 109 goto err3; 110 } 111 112 if (LF_ISSET(DBG_NOCACHE)) 113 goto nocache; 114 115 /* 116 * Look-aside into the TEXT buffers and see if the line we want 117 * is there. 118 */ 119 if (F_ISSET(sp, SC_TINPUT)) { 120 l1 = TAILQ_FIRST(&sp->tiq)->lno; 121 l2 = TAILQ_LAST(&sp->tiq, _texth)->lno; 122 if (l1 <= lno && l2 >= lno) { 123 #if defined(DBDEBUG) && defined(TRACE) 124 vtrace( 125 "retrieve TEXT buffer line %lu\n", (u_long)lno); 126 #endif 127 for (tp = TAILQ_FIRST(&sp->tiq); 128 tp->lno != lno; tp = TAILQ_NEXT(tp, q)); 129 if (lenp != NULL) 130 *lenp = tp->len; 131 if (pp != NULL) 132 *pp = tp->lb; 133 return (0); 134 } 135 /* 136 * Adjust the line number for the number of lines used 137 * by the text input buffers. 138 */ 139 if (lno > l2) 140 lno -= l2 - l1; 141 } 142 143 /* Look-aside into the cache, and see if the line we want is there. */ 144 if (lno == sp->c_lno) { 145 #if defined(DBDEBUG) && defined(TRACE) 146 vtrace("retrieve cached line %lu\n", (u_long)lno); 147 #endif 148 if (lenp != NULL) 149 *lenp = sp->c_len; 150 if (pp != NULL) 151 *pp = sp->c_lp; 152 return (0); 153 } 154 sp->c_lno = OOBLNO; 155 156 nocache: 157 nlen = 1024; 158 retry: 159 /* data.size contains length in bytes */ 160 BINC_GOTO(sp, CHAR_T, sp->c_lp, sp->c_blen, nlen); 161 162 /* Get the line from the underlying database. */ 163 key.data = &lno; 164 key.size = sizeof(lno); 165 switch (ep->db->get(ep->db, &key, &data, 0)) { 166 case -1: 167 goto err2; 168 case 1: 169 err1: if (LF_ISSET(DBG_FATAL)) 170 err2: db_err(sp, lno); 171 alloc_err: 172 err3: if (lenp != NULL) 173 *lenp = 0; 174 if (pp != NULL) 175 *pp = NULL; 176 return (1); 177 case 0: 178 if (data.size > nlen) { 179 nlen = data.size; 180 goto retry; 181 } else 182 memcpy(sp->c_lp, data.data, nlen); 183 } 184 185 if (FILE2INT(sp, data.data, data.size, wp, wlen)) { 186 if (!F_ISSET(sp, SC_CONV_ERROR)) { 187 F_SET(sp, SC_CONV_ERROR); 188 msgq(sp, M_ERR, "324|Conversion error on line %d", lno); 189 } 190 goto err3; 191 } 192 193 /* Reset the cache. */ 194 if (wp != data.data) { 195 BINC_GOTOW(sp, sp->c_lp, sp->c_blen, wlen); 196 MEMCPYW(sp->c_lp, wp, wlen); 197 } 198 sp->c_lno = lno; 199 sp->c_len = wlen; 200 201 #if defined(DBDEBUG) && defined(TRACE) 202 vtrace("retrieve DB line %lu\n", (u_long)lno); 203 #endif 204 if (lenp != NULL) 205 *lenp = wlen; 206 if (pp != NULL) 207 *pp = sp->c_lp; 208 return (0); 209 } 210 211 /* 212 * db_delete -- 213 * Delete a line from the file. 214 * 215 * PUBLIC: int db_delete __P((SCR *, db_recno_t)); 216 */ 217 int 218 db_delete(SCR *sp, db_recno_t lno) 219 { 220 DBT key; 221 EXF *ep; 222 223 #if defined(DBDEBUG) && defined(TRACE) 224 vtrace("delete line %lu\n", (u_long)lno); 225 #endif 226 /* Check for no underlying file. */ 227 if ((ep = sp->ep) == NULL) { 228 ex_emsg(sp, NULL, EXM_NOFILEYET); 229 return (1); 230 } 231 if (ep->l_win && ep->l_win != sp->wp) { 232 ex_emsg(sp, NULL, EXM_LOCKED); 233 return 1; 234 } 235 236 /* Update marks, @ and global commands. */ 237 if (mark_insdel(sp, LINE_DELETE, lno)) 238 return (1); 239 if (ex_g_insdel(sp, LINE_DELETE, lno)) 240 return (1); 241 242 /* Log change. */ 243 log_line(sp, lno, LOG_LINE_DELETE_B); 244 245 /* Update file. */ 246 key.data = &lno; 247 key.size = sizeof(lno); 248 sp->db_error = ep->db->del(ep->db, &key, 0); 249 if (sp->db_error != 0) { 250 if (sp->db_error == -1) 251 sp->db_error = errno; 252 253 msgq(sp, M_DBERR, "003|unable to delete line %lu", 254 (u_long)lno); 255 return (1); 256 } 257 258 /* Flush the cache, update line count, before screen update. */ 259 update_cache(sp, LINE_DELETE, lno); 260 261 /* File now modified. */ 262 if (F_ISSET(ep, F_FIRSTMODIFY)) 263 (void)rcv_init(sp); 264 F_SET(ep, F_MODIFIED); 265 266 /* Log after change. */ 267 log_line(sp, lno, LOG_LINE_DELETE_F); 268 269 /* Update screen. */ 270 return (scr_update(sp, lno, LINE_DELETE, 1)); 271 } 272 273 /* 274 * db_append -- 275 * Append a line into the file. 276 * 277 * PUBLIC: int db_append __P((SCR *, int, db_recno_t, const CHAR_T *, size_t)); 278 */ 279 int 280 db_append(SCR *sp, int update, db_recno_t lno, const CHAR_T *p, size_t len) 281 { 282 DBT data, key; 283 EXF *ep; 284 const char *fp; 285 size_t flen; 286 int rval; 287 288 #if defined(DBDEBUG) && defined(TRACE) 289 vtrace("append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); 290 #endif 291 /* Check for no underlying file. */ 292 if ((ep = sp->ep) == NULL) { 293 ex_emsg(sp, NULL, EXM_NOFILEYET); 294 return (1); 295 } 296 if (ep->l_win && ep->l_win != sp->wp) { 297 ex_emsg(sp, NULL, EXM_LOCKED); 298 return 1; 299 } 300 301 /* Log before change. */ 302 log_line(sp, lno + 1, LOG_LINE_APPEND_B); 303 304 INT2FILE(sp, p, len, fp, flen); 305 306 /* Update file. */ 307 key.data = &lno; 308 key.size = sizeof(lno); 309 data.data = __UNCONST(fp); 310 data.size = flen; 311 if (ep->db->put(ep->db, &key, &data, R_IAFTER)) { 312 msgq(sp, M_DBERR, "004|unable to append to line %lu", 313 (u_long)lno); 314 return (1); 315 } 316 317 /* Flush the cache, update line count, before screen update. */ 318 update_cache(sp, LINE_INSERT, lno); 319 320 /* File now dirty. */ 321 if (F_ISSET(ep, F_FIRSTMODIFY)) 322 (void)rcv_init(sp); 323 F_SET(ep, F_MODIFIED); 324 325 /* Log after change. */ 326 log_line(sp, lno + 1, LOG_LINE_APPEND_F); 327 328 /* Update marks, @ and global commands. */ 329 rval = 0; 330 if (mark_insdel(sp, LINE_INSERT, lno + 1)) 331 rval = 1; 332 if (ex_g_insdel(sp, LINE_INSERT, lno + 1)) 333 rval = 1; 334 335 /* 336 * Update screen. 337 * 338 * XXX 339 * Nasty hack. If multiple lines are input by the user, they aren't 340 * committed until an <ESC> is entered. The problem is the screen was 341 * updated/scrolled as each line was entered. So, when this routine 342 * is called to copy the new lines from the cut buffer into the file, 343 * it has to know not to update the screen again. 344 */ 345 return (scr_update(sp, lno, LINE_APPEND, update) || rval); 346 } 347 348 /* 349 * db_insert -- 350 * Insert a line into the file. 351 * 352 * PUBLIC: int db_insert __P((SCR *, db_recno_t, CHAR_T *, size_t)); 353 */ 354 int 355 db_insert(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len) 356 { 357 DBT data, key; 358 EXF *ep; 359 const char *fp; 360 size_t flen; 361 int rval; 362 363 #if defined(DBDEBUG) && defined(TRACE) 364 vtrace("insert before %lu: len %lu {%.*s}\n", 365 (u_long)lno, (u_long)len, MIN(len, 20), p); 366 #endif 367 /* Check for no underlying file. */ 368 if ((ep = sp->ep) == NULL) { 369 ex_emsg(sp, NULL, EXM_NOFILEYET); 370 return (1); 371 } 372 if (ep->l_win && ep->l_win != sp->wp) { 373 ex_emsg(sp, NULL, EXM_LOCKED); 374 return 1; 375 } 376 377 /* Log before change. */ 378 log_line(sp, lno, LOG_LINE_APPEND_B); 379 380 INT2FILE(sp, p, len, fp, flen); 381 382 /* Update file. */ 383 key.data = &lno; 384 key.size = sizeof(lno); 385 data.data = __UNCONST(fp); 386 data.size = flen; 387 if (ep->db->put(ep->db, &key, &data, R_IBEFORE)) { 388 msgq(sp, M_SYSERR, 389 "005|unable to insert at line %lu", (u_long)lno); 390 return (1); 391 } 392 393 /* Flush the cache, update line count, before screen update. */ 394 update_cache(sp, LINE_INSERT, lno); 395 396 /* File now dirty. */ 397 if (F_ISSET(ep, F_FIRSTMODIFY)) 398 (void)rcv_init(sp); 399 F_SET(ep, F_MODIFIED); 400 401 /* Log after change. */ 402 log_line(sp, lno, LOG_LINE_APPEND_F); 403 404 /* Update marks, @ and global commands. */ 405 rval = 0; 406 if (mark_insdel(sp, LINE_INSERT, lno)) 407 rval = 1; 408 if (ex_g_insdel(sp, LINE_INSERT, lno)) 409 rval = 1; 410 411 /* Update screen. */ 412 return (scr_update(sp, lno, LINE_INSERT, 1) || rval); 413 } 414 415 /* 416 * db_set -- 417 * Store a line in the file. 418 * 419 * PUBLIC: int db_set __P((SCR *, db_recno_t, CHAR_T *, size_t)); 420 */ 421 int 422 db_set(SCR *sp, db_recno_t lno, CHAR_T *p, size_t len) 423 { 424 DBT data, key; 425 EXF *ep; 426 const char *fp; 427 size_t flen; 428 429 #if defined(DBDEBUG) && defined(TRACE) 430 vtrace("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 if (ep->l_win && ep->l_win != sp->wp) { 439 ex_emsg(sp, NULL, EXM_LOCKED); 440 return 1; 441 } 442 443 /* Log before change. */ 444 log_line(sp, lno, LOG_LINE_RESET_B); 445 446 INT2FILE(sp, p, len, fp, flen); 447 448 /* Update file. */ 449 key.data = &lno; 450 key.size = sizeof(lno); 451 data.data = __UNCONST(fp); 452 data.size = flen; 453 sp->db_error = 454 ep->db->put(ep->db, &key, &data, 0); 455 if (sp->db_error != 0) { 456 if (sp->db_error == -1) 457 sp->db_error = errno; 458 459 msgq(sp, M_DBERR, "006|unable to store line %lu", (u_long)lno); 460 return (1); 461 } 462 463 /* Flush the cache, before logging or screen update. */ 464 update_cache(sp, LINE_RESET, lno); 465 466 /* File now dirty. */ 467 if (F_ISSET(ep, F_FIRSTMODIFY)) 468 (void)rcv_init(sp); 469 F_SET(ep, F_MODIFIED); 470 471 /* Log after change. */ 472 log_line(sp, lno, LOG_LINE_RESET_F); 473 474 /* Update screen. */ 475 return (scr_update(sp, lno, LINE_RESET, 1)); 476 } 477 478 /* 479 * db_exist -- 480 * Return if a line exists. 481 * 482 * PUBLIC: int db_exist __P((SCR *, db_recno_t)); 483 */ 484 int 485 db_exist(SCR *sp, db_recno_t lno) 486 { 487 EXF *ep; 488 489 /* Check for no underlying file. */ 490 if ((ep = sp->ep) == NULL) { 491 ex_emsg(sp, NULL, EXM_NOFILEYET); 492 return (1); 493 } 494 495 if (lno == OOBLNO) 496 return (0); 497 498 /* 499 * Check the last-line number cache. Adjust the cached line 500 * number for the lines used by the text input buffers. 501 */ 502 if (ep->c_nlines != OOBLNO) 503 return (lno <= (F_ISSET(sp, SC_TINPUT) ? 504 ep->c_nlines + (TAILQ_LAST(&sp->tiq, _texth)->lno - 505 TAILQ_FIRST(&sp->tiq)->lno) : ep->c_nlines)); 506 507 /* Go get the line. */ 508 return (!db_get(sp, lno, 0, NULL, NULL)); 509 } 510 511 /* 512 * db_last -- 513 * Return the number of lines in the file. 514 * 515 * PUBLIC: int db_last __P((SCR *, db_recno_t *)); 516 */ 517 int 518 db_last(SCR *sp, db_recno_t *lnop) 519 { 520 DBT data, key; 521 EXF *ep; 522 db_recno_t lno; 523 const CHAR_T *wp; 524 size_t wlen; 525 526 /* Check for no underlying file. */ 527 if ((ep = sp->ep) == NULL) { 528 ex_emsg(sp, NULL, EXM_NOFILEYET); 529 return (1); 530 } 531 532 /* 533 * Check the last-line number cache. Adjust the cached line 534 * number for the lines used by the text input buffers. 535 */ 536 if (ep->c_nlines != OOBLNO) { 537 *lnop = ep->c_nlines; 538 if (F_ISSET(sp, SC_TINPUT)) 539 *lnop += TAILQ_LAST(&sp->tiq, _texth)->lno - 540 TAILQ_FIRST(&sp->tiq)->lno; 541 return (0); 542 } 543 544 key.data = &lno; 545 key.size = sizeof(lno); 546 547 sp->db_error = ep->db->seq(ep->db, &key, &data, R_LAST); 548 switch (sp->db_error) { 549 case 1: 550 *lnop = 0; 551 return (0); 552 case -1: 553 sp->db_error = errno; 554 alloc_err: 555 msgq(sp, M_DBERR, "007|unable to get last line"); 556 *lnop = 0; 557 return (1); 558 case 0: 559 ; 560 } 561 562 memcpy(&lno, key.data, sizeof(lno)); 563 564 if (lno != sp->c_lno) { 565 FILE2INT(sp, data.data, data.size, wp, wlen); 566 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; 572 } 573 ep->c_nlines = lno; 574 575 /* Return the value. */ 576 *lnop = (F_ISSET(sp, SC_TINPUT) && 577 TAILQ_LAST(&sp->tiq, _texth)->lno > lno ? 578 TAILQ_LAST(&sp->tiq, _texth)->lno : lno); 579 return (0); 580 } 581 582 /* 583 * db_err -- 584 * Report a line error. 585 * 586 * PUBLIC: void db_err __P((SCR *, db_recno_t)); 587 */ 588 void 589 db_err(SCR *sp, db_recno_t lno) 590 { 591 msgq(sp, M_ERR, 592 "008|Error: unable to retrieve line %lu", (u_long)lno); 593 } 594 595 /* 596 * scr_update -- 597 * Update all of the screens that are backed by the file that 598 * just changed. 599 * PUBLIC: int scr_update __P((SCR *sp, db_recno_t lno, 600 * PUBLIC: lnop_t op, int current)); 601 */ 602 int 603 scr_update(SCR *sp, db_recno_t lno, lnop_t op, int current) 604 { 605 EXF *ep; 606 SCR *tsp; 607 WIN *wp; 608 609 if (F_ISSET(sp, SC_EX)) 610 return (0); 611 612 /* XXXX goes outside of window */ 613 ep = sp->ep; 614 if (ep->refcnt != 1) 615 TAILQ_FOREACH(wp, &sp->gp->dq, q) 616 TAILQ_FOREACH(tsp, &wp->scrq, q) 617 if (sp != tsp && tsp->ep == ep) 618 if (vs_change(tsp, lno, op)) 619 return (1); 620 return (current ? vs_change(sp, lno, op) : 0); 621 } 622 623 /* 624 * PUBLIC: void update_cache __P((SCR *sp, lnop_t op, db_recno_t lno)); 625 */ 626 void 627 update_cache(SCR *sp, lnop_t op, db_recno_t lno) 628 { 629 SCR* scrp; 630 EXF *ep; 631 632 ep = sp->ep; 633 634 /* Flush the cache, update line count, before screen update. */ 635 /* The flushing is probably not needed, since it was incorrect 636 * for db_insert. It might be better to adjust it, like 637 * marks, @ and global 638 */ 639 TAILQ_FOREACH(scrp, &ep->scrq, eq) 640 switch (op) { 641 case LINE_INSERT: 642 case LINE_DELETE: 643 if (lno <= scrp->c_lno) 644 scrp->c_lno = OOBLNO; 645 break; 646 case LINE_RESET: 647 if (lno == scrp->c_lno) 648 scrp->c_lno = OOBLNO; 649 break; 650 case LINE_APPEND: 651 break; 652 } 653 654 if (ep->c_nlines != OOBLNO) 655 switch (op) { 656 case LINE_INSERT: 657 ++ep->c_nlines; 658 break; 659 case LINE_DELETE: 660 --ep->c_nlines; 661 break; 662 case LINE_APPEND: 663 case LINE_RESET: 664 break; 665 } 666 } 667 668 /* 669 * PUBLIC: int db_msg_open __P((SCR *, const char *, DB **)); 670 */ 671 int db_msg_open(SCR *sp, const char *file, DB **dbp) 672 { 673 *dbp = dbopen(file, O_NONBLOCK | O_RDONLY, 0, DB_RECNO, NULL); 674 675 return *dbp == NULL; 676 } 677 678 /* 679 * PUBLIC: int db_init __P((SCR *, EXF *, char *, char *, size_t, int *)); 680 */ 681 int 682 db_init(SCR *sp, EXF *ep, char *rcv_name, char *oname, size_t psize, int *open_err) 683 { 684 RECNOINFO oinfo; 685 686 memset(&oinfo, 0, sizeof(RECNOINFO)); 687 oinfo.bval = '\n'; /* Always set. */ 688 /* 689 * If we are not recovering, set the pagesize and arrange to 690 * first get a snapshot of the file. 691 */ 692 if (rcv_name == NULL) { 693 oinfo.psize = psize; 694 oinfo.flags = R_SNAPSHOT; 695 } 696 /* 697 * Always set the btree name, otherwise we are going to be using 698 * an in-memory database for the btree. 699 */ 700 oinfo.bfname = ep->rcv_path; 701 702 #define _DB_OPEN_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH 703 704 ep->db = dbopen(rcv_name == NULL ? oname : NULL, 705 O_NONBLOCK | O_RDONLY, _DB_OPEN_MODE, DB_RECNO, &oinfo); 706 707 if (!ep->db) { 708 msgq_str(sp, 709 M_DBERR, rcv_name == NULL ? oname : rcv_name, "%s"); 710 /* 711 * !!! 712 * Historically, vi permitted users to edit files that couldn't 713 * be read. This isn't useful for single files from a command 714 * line, but it's quite useful for "vi *.c", since you can skip 715 * past files that you can't read. 716 */ 717 ep->db = NULL; /* Don't close it; it wasn't opened */ 718 719 *open_err = 1; 720 return 1; 721 } else { 722 /* 723 * We always sync the underlying btree so that the header 724 * is written first 725 */ 726 ep->db->sync(ep->db, R_RECNOSYNC); 727 } 728 729 return 0; 730 } 731 732 const char * 733 db_strerror(int error) 734 { 735 return error > 0 ? strerror(error) : "record not found"; 736 } 737