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