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