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