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