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
log_init(SCR * sp,EXF * ep)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
log_end(SCR * sp,EXF * ep)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
log_cursor(SCR * sp)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
log_cursor1(SCR * sp,int type)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
log_line(SCR * sp,recno_t lno,u_int action)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
log_mark(SCR * sp,LMARK * lmp)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
log_backward(SCR * sp,MARK * rp)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
log_setline(SCR * sp)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
log_forward(SCR * sp,MARK * rp)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
log_err(SCR * sp,char * file,int line)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
log_trace(SCR * sp,char * msg,recno_t rno,u_char * p)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
apply_with(int (* db_func)(SCR *,recno_t,CHAR_T *,size_t),SCR * sp,recno_t lno,u_char * p,size_t len)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