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[] = "@(#)log.c 10.8 (Berkeley) 3/6/96"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/stat.h> 19 20 #include <bitstring.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "common.h" 29 30 /* 31 * The log consists of records, each containing a type byte and a variable 32 * length byte string, as follows: 33 * 34 * LOG_CURSOR_INIT MARK 35 * LOG_CURSOR_END MARK 36 * LOG_LINE_APPEND recno_t char * 37 * LOG_LINE_DELETE recno_t char * 38 * LOG_LINE_INSERT recno_t char * 39 * LOG_LINE_RESET_F recno_t char * 40 * LOG_LINE_RESET_B recno_t char * 41 * LOG_MARK LMARK 42 * 43 * We do before image physical logging. This means that the editor layer 44 * MAY NOT modify records in place, even if simply deleting or overwriting 45 * characters. Since the smallest unit of logging is a line, we're using 46 * up lots of space. This may eventually have to be reduced, probably by 47 * doing logical logging, which is a much cooler database phrase. 48 * 49 * The implementation of the historic vi 'u' command, using roll-forward and 50 * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, 51 * followed by a number of other records, followed by a LOG_CURSOR_END record. 52 * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B 53 * record, and is the line before the change. The second is LOG_LINE_RESET_F, 54 * and is the line after the change. Roll-back is done by backing up to the 55 * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a 56 * similar fashion. 57 * 58 * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END 59 * record for a line different from the current one. It should be noted that 60 * this means that a subsequent 'u' command will make a change based on the 61 * new position of the log's cursor. This is okay, and, in fact, historic vi 62 * behaved that way. 63 */ 64 65 static int log_cursor1 __P((SCR *, int)); 66 static void log_err __P((SCR *, char *, int)); 67 #if defined(DEBUG) && 0 68 static void log_trace __P((SCR *, char *, recno_t, u_char *)); 69 #endif 70 71 /* Try and restart the log on failure, i.e. if we run out of memory. */ 72 #define LOG_ERR { \ 73 log_err(sp, __FILE__, __LINE__); \ 74 return (1); \ 75 } 76 77 /* 78 * log_init -- 79 * Initialize the logging subsystem. 80 * 81 * PUBLIC: int log_init __P((SCR *, EXF *)); 82 */ 83 int 84 log_init(sp, ep) 85 SCR *sp; 86 EXF *ep; 87 { 88 /* 89 * !!! 90 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 91 * 92 * Initialize the buffer. The logging subsystem has its own 93 * buffers because the global ones are almost by definition 94 * going to be in use when the log runs. 95 */ 96 ep->l_lp = NULL; 97 ep->l_len = 0; 98 ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 99 ep->l_cursor.cno = 0; 100 ep->l_high = ep->l_cur = 1; 101 102 ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR, 103 S_IRUSR | S_IWUSR, DB_RECNO, NULL); 104 if (ep->log == NULL) { 105 msgq(sp, M_SYSERR, "009|Log file"); 106 F_SET(ep, F_NOLOG); 107 return (1); 108 } 109 110 return (0); 111 } 112 113 /* 114 * log_end -- 115 * Close the logging subsystem. 116 * 117 * PUBLIC: int log_end __P((SCR *, EXF *)); 118 */ 119 int 120 log_end(sp, ep) 121 SCR *sp; 122 EXF *ep; 123 { 124 /* 125 * !!! 126 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 127 */ 128 if (ep->log != NULL) { 129 (void)(ep->log->close)(ep->log); 130 ep->log = NULL; 131 } 132 if (ep->l_lp != NULL) { 133 free(ep->l_lp); 134 ep->l_lp = NULL; 135 } 136 ep->l_len = 0; 137 ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 138 ep->l_cursor.cno = 0; 139 ep->l_high = ep->l_cur = 1; 140 return (0); 141 } 142 143 /* 144 * log_cursor -- 145 * Log the current cursor position, starting an event. 146 * 147 * PUBLIC: int log_cursor __P((SCR *)); 148 */ 149 int 150 log_cursor(sp) 151 SCR *sp; 152 { 153 EXF *ep; 154 155 ep = sp->ep; 156 if (F_ISSET(ep, F_NOLOG)) 157 return (0); 158 159 /* 160 * If any changes were made since the last cursor init, 161 * put out the ending cursor record. 162 */ 163 if (ep->l_cursor.lno == OOBLNO) { 164 ep->l_cursor.lno = sp->lno; 165 ep->l_cursor.cno = sp->cno; 166 return (log_cursor1(sp, LOG_CURSOR_END)); 167 } 168 ep->l_cursor.lno = sp->lno; 169 ep->l_cursor.cno = sp->cno; 170 return (0); 171 } 172 173 /* 174 * log_cursor1 -- 175 * Actually push a cursor record out. 176 */ 177 static int 178 log_cursor1(sp, type) 179 SCR *sp; 180 int type; 181 { 182 DBT data, key; 183 EXF *ep; 184 185 ep = sp->ep; 186 BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); 187 ep->l_lp[0] = type; 188 memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); 189 190 key.data = &ep->l_cur; 191 key.size = sizeof(recno_t); 192 data.data = ep->l_lp; 193 data.size = sizeof(u_char) + sizeof(MARK); 194 if (ep->log->put(ep->log, &key, &data, 0) == -1) 195 LOG_ERR; 196 197 #if defined(DEBUG) && 0 198 TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, 199 type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", 200 sp->lno, sp->cno); 201 #endif 202 /* Reset high water mark. */ 203 ep->l_high = ++ep->l_cur; 204 205 return (0); 206 } 207 208 /* 209 * log_line -- 210 * Log a line change. 211 * 212 * PUBLIC: int log_line __P((SCR *, recno_t, u_int)); 213 */ 214 int 215 log_line(sp, lno, action) 216 SCR *sp; 217 recno_t lno; 218 u_int action; 219 { 220 DBT data, key; 221 EXF *ep; 222 size_t len; 223 char *lp; 224 225 ep = sp->ep; 226 if (F_ISSET(ep, F_NOLOG)) 227 return (0); 228 229 /* 230 * XXX 231 * 232 * Kluge for vi. Clear the EXF undo flag so that the 233 * next 'u' command does a roll-back, regardless. 234 */ 235 F_CLR(ep, F_UNDO); 236 237 /* Put out one initial cursor record per set of changes. */ 238 if (ep->l_cursor.lno != OOBLNO) { 239 if (log_cursor1(sp, LOG_CURSOR_INIT)) 240 return (1); 241 ep->l_cursor.lno = OOBLNO; 242 } 243 244 /* 245 * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a 246 * special case, avoid the caches. Also, if it fails and it's 247 * line 1, it just means that the user started with an empty file, 248 * so fake an empty length line. 249 */ 250 if (action == LOG_LINE_RESET_B) { 251 if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) { 252 if (lno != 1) { 253 db_err(sp, lno); 254 return (1); 255 } 256 len = 0; 257 lp = ""; 258 } 259 } else 260 if (db_get(sp, lno, DBG_FATAL, &lp, &len)) 261 return (1); 262 BINC_RET(sp, 263 ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t)); 264 ep->l_lp[0] = action; 265 memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); 266 memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len); 267 268 key.data = &ep->l_cur; 269 key.size = sizeof(recno_t); 270 data.data = ep->l_lp; 271 data.size = len + sizeof(u_char) + sizeof(recno_t); 272 if (ep->log->put(ep->log, &key, &data, 0) == -1) 273 LOG_ERR; 274 275 #if defined(DEBUG) && 0 276 switch (action) { 277 case LOG_LINE_APPEND: 278 TRACE(sp, "%u: log_line: append: %lu {%u}\n", 279 ep->l_cur, lno, len); 280 break; 281 case LOG_LINE_DELETE: 282 TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", 283 ep->l_cur, lno, len); 284 break; 285 case LOG_LINE_INSERT: 286 TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", 287 ep->l_cur, lno, len); 288 break; 289 case LOG_LINE_RESET_F: 290 TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", 291 ep->l_cur, lno, len); 292 break; 293 case LOG_LINE_RESET_B: 294 TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", 295 ep->l_cur, lno, len); 296 break; 297 } 298 #endif 299 /* Reset high water mark. */ 300 ep->l_high = ++ep->l_cur; 301 302 return (0); 303 } 304 305 /* 306 * log_mark -- 307 * Log a mark position. For the log to work, we assume that there 308 * aren't any operations that just put out a log record -- this 309 * would mean that undo operations would only reset marks, and not 310 * cause any other change. 311 * 312 * PUBLIC: int log_mark __P((SCR *, LMARK *)); 313 */ 314 int 315 log_mark(sp, lmp) 316 SCR *sp; 317 LMARK *lmp; 318 { 319 DBT data, key; 320 EXF *ep; 321 322 ep = sp->ep; 323 if (F_ISSET(ep, F_NOLOG)) 324 return (0); 325 326 /* Put out one initial cursor record per set of changes. */ 327 if (ep->l_cursor.lno != OOBLNO) { 328 if (log_cursor1(sp, LOG_CURSOR_INIT)) 329 return (1); 330 ep->l_cursor.lno = OOBLNO; 331 } 332 333 BINC_RET(sp, ep->l_lp, 334 ep->l_len, sizeof(u_char) + sizeof(LMARK)); 335 ep->l_lp[0] = LOG_MARK; 336 memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK)); 337 338 key.data = &ep->l_cur; 339 key.size = sizeof(recno_t); 340 data.data = ep->l_lp; 341 data.size = sizeof(u_char) + sizeof(LMARK); 342 if (ep->log->put(ep->log, &key, &data, 0) == -1) 343 LOG_ERR; 344 345 #if defined(DEBUG) && 0 346 TRACE(sp, "%lu: mark %c: %lu/%u\n", 347 ep->l_cur, lmp->name, lmp->lno, lmp->cno); 348 #endif 349 /* Reset high water mark. */ 350 ep->l_high = ++ep->l_cur; 351 return (0); 352 } 353 354 /* 355 * Log_backward -- 356 * Roll the log backward one operation. 357 * 358 * PUBLIC: int log_backward __P((SCR *, MARK *)); 359 */ 360 int 361 log_backward(sp, rp) 362 SCR *sp; 363 MARK *rp; 364 { 365 DBT key, data; 366 EXF *ep; 367 LMARK lm; 368 MARK m; 369 recno_t lno; 370 int didop; 371 u_char *p; 372 373 ep = sp->ep; 374 if (F_ISSET(ep, F_NOLOG)) { 375 msgq(sp, M_ERR, 376 "010|Logging not being performed, undo not possible"); 377 return (1); 378 } 379 380 if (ep->l_cur == 1) { 381 msgq(sp, M_BERR, "011|No changes to undo"); 382 return (1); 383 } 384 385 F_SET(ep, F_NOLOG); /* Turn off logging. */ 386 387 key.data = &ep->l_cur; /* Initialize db request. */ 388 key.size = sizeof(recno_t); 389 for (didop = 0;;) { 390 --ep->l_cur; 391 if (ep->log->get(ep->log, &key, &data, 0)) 392 LOG_ERR; 393 #if defined(DEBUG) && 0 394 log_trace(sp, "log_backward", ep->l_cur, data.data); 395 #endif 396 switch (*(p = (u_char *)data.data)) { 397 case LOG_CURSOR_INIT: 398 if (didop) { 399 memmove(rp, p + sizeof(u_char), sizeof(MARK)); 400 F_CLR(ep, F_NOLOG); 401 return (0); 402 } 403 break; 404 case LOG_CURSOR_END: 405 break; 406 case LOG_LINE_APPEND: 407 case LOG_LINE_INSERT: 408 didop = 1; 409 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 410 if (db_delete(sp, lno)) 411 goto err; 412 ++sp->rptlines[L_DELETED]; 413 break; 414 case LOG_LINE_DELETE: 415 didop = 1; 416 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 417 if (db_insert(sp, lno, p + sizeof(u_char) + 418 sizeof(recno_t), data.size - sizeof(u_char) - 419 sizeof(recno_t))) 420 goto err; 421 ++sp->rptlines[L_ADDED]; 422 break; 423 case LOG_LINE_RESET_F: 424 break; 425 case LOG_LINE_RESET_B: 426 didop = 1; 427 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 428 if (db_set(sp, lno, p + sizeof(u_char) + 429 sizeof(recno_t), data.size - sizeof(u_char) - 430 sizeof(recno_t))) 431 goto err; 432 if (sp->rptlchange != lno) { 433 sp->rptlchange = lno; 434 ++sp->rptlines[L_CHANGED]; 435 } 436 break; 437 case LOG_MARK: 438 didop = 1; 439 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 440 m.lno = lm.lno; 441 m.cno = lm.cno; 442 if (mark_set(sp, lm.name, &m, 0)) 443 goto err; 444 break; 445 default: 446 abort(); 447 } 448 } 449 450 err: F_CLR(ep, F_NOLOG); 451 return (1); 452 } 453 454 /* 455 * Log_setline -- 456 * Reset the line to its original appearance. 457 * 458 * XXX 459 * There's a bug in this code due to our not logging cursor movements 460 * unless a change was made. If you do a change, move off the line, 461 * then move back on and do a 'U', the line will be restored to the way 462 * it was before the original change. 463 * 464 * PUBLIC: int log_setline __P((SCR *)); 465 */ 466 int 467 log_setline(sp) 468 SCR *sp; 469 { 470 DBT key, data; 471 EXF *ep; 472 LMARK lm; 473 MARK m; 474 recno_t lno; 475 u_char *p; 476 477 ep = sp->ep; 478 if (F_ISSET(ep, F_NOLOG)) { 479 msgq(sp, M_ERR, 480 "012|Logging not being performed, undo not possible"); 481 return (1); 482 } 483 484 if (ep->l_cur == 1) 485 return (1); 486 487 F_SET(ep, F_NOLOG); /* Turn off logging. */ 488 489 key.data = &ep->l_cur; /* Initialize db request. */ 490 key.size = sizeof(recno_t); 491 492 for (;;) { 493 --ep->l_cur; 494 if (ep->log->get(ep->log, &key, &data, 0)) 495 LOG_ERR; 496 #if defined(DEBUG) && 0 497 log_trace(sp, "log_setline", ep->l_cur, data.data); 498 #endif 499 switch (*(p = (u_char *)data.data)) { 500 case LOG_CURSOR_INIT: 501 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 502 if (m.lno != sp->lno || ep->l_cur == 1) { 503 F_CLR(ep, F_NOLOG); 504 return (0); 505 } 506 break; 507 case LOG_CURSOR_END: 508 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 509 if (m.lno != sp->lno) { 510 ++ep->l_cur; 511 F_CLR(ep, F_NOLOG); 512 return (0); 513 } 514 break; 515 case LOG_LINE_APPEND: 516 case LOG_LINE_INSERT: 517 case LOG_LINE_DELETE: 518 case LOG_LINE_RESET_F: 519 break; 520 case LOG_LINE_RESET_B: 521 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 522 if (lno == sp->lno && 523 db_set(sp, lno, p + sizeof(u_char) + 524 sizeof(recno_t), data.size - sizeof(u_char) - 525 sizeof(recno_t))) 526 goto err; 527 if (sp->rptlchange != lno) { 528 sp->rptlchange = lno; 529 ++sp->rptlines[L_CHANGED]; 530 } 531 case LOG_MARK: 532 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 533 m.lno = lm.lno; 534 m.cno = lm.cno; 535 if (mark_set(sp, lm.name, &m, 0)) 536 goto err; 537 break; 538 default: 539 abort(); 540 } 541 } 542 543 err: F_CLR(ep, F_NOLOG); 544 return (1); 545 } 546 547 /* 548 * Log_forward -- 549 * Roll the log forward one operation. 550 * 551 * PUBLIC: int log_forward __P((SCR *, MARK *)); 552 */ 553 int 554 log_forward(sp, rp) 555 SCR *sp; 556 MARK *rp; 557 { 558 DBT key, data; 559 EXF *ep; 560 LMARK lm; 561 MARK m; 562 recno_t lno; 563 int didop; 564 u_char *p; 565 566 ep = sp->ep; 567 if (F_ISSET(ep, F_NOLOG)) { 568 msgq(sp, M_ERR, 569 "013|Logging not being performed, roll-forward not possible"); 570 return (1); 571 } 572 573 if (ep->l_cur == ep->l_high) { 574 msgq(sp, M_BERR, "014|No changes to re-do"); 575 return (1); 576 } 577 578 F_SET(ep, F_NOLOG); /* Turn off logging. */ 579 580 key.data = &ep->l_cur; /* Initialize db request. */ 581 key.size = sizeof(recno_t); 582 for (didop = 0;;) { 583 ++ep->l_cur; 584 if (ep->log->get(ep->log, &key, &data, 0)) 585 LOG_ERR; 586 #if defined(DEBUG) && 0 587 log_trace(sp, "log_forward", ep->l_cur, data.data); 588 #endif 589 switch (*(p = (u_char *)data.data)) { 590 case LOG_CURSOR_END: 591 if (didop) { 592 ++ep->l_cur; 593 memmove(rp, p + sizeof(u_char), sizeof(MARK)); 594 F_CLR(ep, F_NOLOG); 595 return (0); 596 } 597 break; 598 case LOG_CURSOR_INIT: 599 break; 600 case LOG_LINE_APPEND: 601 case LOG_LINE_INSERT: 602 didop = 1; 603 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 604 if (db_insert(sp, lno, p + sizeof(u_char) + 605 sizeof(recno_t), data.size - sizeof(u_char) - 606 sizeof(recno_t))) 607 goto err; 608 ++sp->rptlines[L_ADDED]; 609 break; 610 case LOG_LINE_DELETE: 611 didop = 1; 612 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 613 if (db_delete(sp, lno)) 614 goto err; 615 ++sp->rptlines[L_DELETED]; 616 break; 617 case LOG_LINE_RESET_B: 618 break; 619 case LOG_LINE_RESET_F: 620 didop = 1; 621 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 622 if (db_set(sp, lno, p + sizeof(u_char) + 623 sizeof(recno_t), data.size - sizeof(u_char) - 624 sizeof(recno_t))) 625 goto err; 626 if (sp->rptlchange != lno) { 627 sp->rptlchange = lno; 628 ++sp->rptlines[L_CHANGED]; 629 } 630 break; 631 case LOG_MARK: 632 didop = 1; 633 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 634 m.lno = lm.lno; 635 m.cno = lm.cno; 636 if (mark_set(sp, lm.name, &m, 0)) 637 goto err; 638 break; 639 default: 640 abort(); 641 } 642 } 643 644 err: F_CLR(ep, F_NOLOG); 645 return (1); 646 } 647 648 /* 649 * log_err -- 650 * Try and restart the log on failure, i.e. if we run out of memory. 651 */ 652 static void 653 log_err(sp, file, line) 654 SCR *sp; 655 char *file; 656 int line; 657 { 658 EXF *ep; 659 660 msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line); 661 ep = sp->ep; 662 (void)ep->log->close(ep->log); 663 if (!log_init(sp, ep)) 664 msgq(sp, M_ERR, "267|Log restarted"); 665 } 666 667 #if defined(DEBUG) && 0 668 static void 669 log_trace(sp, msg, rno, p) 670 SCR *sp; 671 char *msg; 672 recno_t rno; 673 u_char *p; 674 { 675 LMARK lm; 676 MARK m; 677 recno_t lno; 678 679 switch (*p) { 680 case LOG_CURSOR_INIT: 681 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 682 TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); 683 break; 684 case LOG_CURSOR_END: 685 memmove(&m, p + sizeof(u_char), sizeof(MARK)); 686 TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); 687 break; 688 case LOG_LINE_APPEND: 689 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 690 TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); 691 break; 692 case LOG_LINE_INSERT: 693 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 694 TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); 695 break; 696 case LOG_LINE_DELETE: 697 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 698 TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); 699 break; 700 case LOG_LINE_RESET_F: 701 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 702 TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); 703 break; 704 case LOG_LINE_RESET_B: 705 memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 706 TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); 707 break; 708 case LOG_MARK: 709 memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 710 TRACE(sp, 711 "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno); 712 break; 713 default: 714 abort(); 715 } 716 } 717 #endif 718