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