xref: /freebsd/contrib/nvi/common/log.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
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