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