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