xref: /openbsd-src/usr.bin/vi/common/log.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: log.c,v 1.5 2009/10/27 23:59:47 deraadt 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 <limits.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 
69 /* Try and restart the log on failure, i.e. if we run out of memory. */
70 #define	LOG_ERR {							\
71 	log_err(sp, __FILE__, __LINE__);				\
72 	return (1);							\
73 }
74 
75 /*
76  * log_init --
77  *	Initialize the logging subsystem.
78  *
79  * PUBLIC: int log_init(SCR *, EXF *);
80  */
81 int
82 log_init(sp, ep)
83 	SCR *sp;
84 	EXF *ep;
85 {
86 	/*
87 	 * !!!
88 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
89 	 *
90 	 * Initialize the buffer.  The logging subsystem has its own
91 	 * buffers because the global ones are almost by definition
92 	 * going to be in use when the log runs.
93 	 */
94 	ep->l_lp = NULL;
95 	ep->l_len = 0;
96 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
97 	ep->l_cursor.cno = 0;
98 	ep->l_high = ep->l_cur = 1;
99 
100 	ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
101 	    S_IRUSR | S_IWUSR, DB_RECNO, NULL);
102 	if (ep->log == NULL) {
103 		msgq(sp, M_SYSERR, "009|Log file");
104 		F_SET(ep, F_NOLOG);
105 		return (1);
106 	}
107 
108 	return (0);
109 }
110 
111 /*
112  * log_end --
113  *	Close the logging subsystem.
114  *
115  * PUBLIC: int log_end(SCR *, EXF *);
116  */
117 int
118 log_end(sp, ep)
119 	SCR *sp;
120 	EXF *ep;
121 {
122 	/*
123 	 * !!!
124 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
125 	 */
126 	if (ep->log != NULL) {
127 		(void)(ep->log->close)(ep->log);
128 		ep->log = NULL;
129 	}
130 	if (ep->l_lp != NULL) {
131 		free(ep->l_lp);
132 		ep->l_lp = NULL;
133 	}
134 	ep->l_len = 0;
135 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
136 	ep->l_cursor.cno = 0;
137 	ep->l_high = ep->l_cur = 1;
138 	return (0);
139 }
140 
141 /*
142  * log_cursor --
143  *	Log the current cursor position, starting an event.
144  *
145  * PUBLIC: int log_cursor(SCR *);
146  */
147 int
148 log_cursor(sp)
149 	SCR *sp;
150 {
151 	EXF *ep;
152 
153 	ep = sp->ep;
154 	if (F_ISSET(ep, F_NOLOG))
155 		return (0);
156 
157 	/*
158 	 * If any changes were made since the last cursor init,
159 	 * put out the ending cursor record.
160 	 */
161 	if (ep->l_cursor.lno == OOBLNO) {
162 		ep->l_cursor.lno = sp->lno;
163 		ep->l_cursor.cno = sp->cno;
164 		return (log_cursor1(sp, LOG_CURSOR_END));
165 	}
166 	ep->l_cursor.lno = sp->lno;
167 	ep->l_cursor.cno = sp->cno;
168 	return (0);
169 }
170 
171 /*
172  * log_cursor1 --
173  *	Actually push a cursor record out.
174  */
175 static int
176 log_cursor1(sp, type)
177 	SCR *sp;
178 	int type;
179 {
180 	DBT data, key;
181 	EXF *ep;
182 
183 	ep = sp->ep;
184 	BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
185 	ep->l_lp[0] = type;
186 	memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
187 
188 	key.data = &ep->l_cur;
189 	key.size = sizeof(recno_t);
190 	data.data = ep->l_lp;
191 	data.size = sizeof(u_char) + sizeof(MARK);
192 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
193 		LOG_ERR;
194 
195 #if defined(DEBUG) && 0
196 	TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
197 	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
198 	    sp->lno, sp->cno);
199 #endif
200 	/* Reset high water mark. */
201 	ep->l_high = ++ep->l_cur;
202 
203 	return (0);
204 }
205 
206 /*
207  * log_line --
208  *	Log a line change.
209  *
210  * PUBLIC: int log_line(SCR *, recno_t, u_int);
211  */
212 int
213 log_line(sp, lno, action)
214 	SCR *sp;
215 	recno_t lno;
216 	u_int action;
217 {
218 	DBT data, key;
219 	EXF *ep;
220 	size_t len;
221 	char *lp;
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 = "";
256 		}
257 	} else
258 		if (db_get(sp, lno, DBG_FATAL, &lp, &len))
259 			return (1);
260 	BINC_RET(sp,
261 	    ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
262 	ep->l_lp[0] = action;
263 	memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
264 	memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
265 
266 	key.data = &ep->l_cur;
267 	key.size = sizeof(recno_t);
268 	data.data = ep->l_lp;
269 	data.size = len + sizeof(u_char) + sizeof(recno_t);
270 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
271 		LOG_ERR;
272 
273 #if defined(DEBUG) && 0
274 	switch (action) {
275 	case LOG_LINE_APPEND:
276 		TRACE(sp, "%u: log_line: append: %lu {%u}\n",
277 		    ep->l_cur, lno, len);
278 		break;
279 	case LOG_LINE_DELETE:
280 		TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
281 		    ep->l_cur, lno, len);
282 		break;
283 	case LOG_LINE_INSERT:
284 		TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
285 		    ep->l_cur, lno, len);
286 		break;
287 	case LOG_LINE_RESET_F:
288 		TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
289 		    ep->l_cur, lno, len);
290 		break;
291 	case LOG_LINE_RESET_B:
292 		TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
293 		    ep->l_cur, lno, len);
294 		break;
295 	}
296 #endif
297 	/* Reset high water mark. */
298 	ep->l_high = ++ep->l_cur;
299 
300 	return (0);
301 }
302 
303 /*
304  * log_mark --
305  *	Log a mark position.  For the log to work, we assume that there
306  *	aren't any operations that just put out a log record -- this
307  *	would mean that undo operations would only reset marks, and not
308  *	cause any other change.
309  *
310  * PUBLIC: int log_mark(SCR *, LMARK *);
311  */
312 int
313 log_mark(sp, lmp)
314 	SCR *sp;
315 	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_RET(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(sp, rp)
360 	SCR *sp;
361 	MARK *rp;
362 {
363 	DBT key, data;
364 	EXF *ep;
365 	LMARK lm;
366 	MARK m;
367 	recno_t lno;
368 	int didop;
369 	u_char *p;
370 
371 	ep = sp->ep;
372 	if (F_ISSET(ep, F_NOLOG)) {
373 		msgq(sp, M_ERR,
374 		    "010|Logging not being performed, undo not possible");
375 		return (1);
376 	}
377 
378 	if (ep->l_cur == 1) {
379 		msgq(sp, M_BERR, "011|No changes to undo");
380 		return (1);
381 	}
382 
383 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
384 
385 	key.data = &ep->l_cur;		/* Initialize db request. */
386 	key.size = sizeof(recno_t);
387 	for (didop = 0;;) {
388 		--ep->l_cur;
389 		if (ep->log->get(ep->log, &key, &data, 0))
390 			LOG_ERR;
391 #if defined(DEBUG) && 0
392 		log_trace(sp, "log_backward", ep->l_cur, data.data);
393 #endif
394 		switch (*(p = (u_char *)data.data)) {
395 		case LOG_CURSOR_INIT:
396 			if (didop) {
397 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
398 				F_CLR(ep, F_NOLOG);
399 				return (0);
400 			}
401 			break;
402 		case LOG_CURSOR_END:
403 			break;
404 		case LOG_LINE_APPEND:
405 		case LOG_LINE_INSERT:
406 			didop = 1;
407 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
408 			if (db_delete(sp, lno))
409 				goto err;
410 			++sp->rptlines[L_DELETED];
411 			break;
412 		case LOG_LINE_DELETE:
413 			didop = 1;
414 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
415 			if (db_insert(sp, lno, p + sizeof(u_char) +
416 			    sizeof(recno_t), data.size - sizeof(u_char) -
417 			    sizeof(recno_t)))
418 				goto err;
419 			++sp->rptlines[L_ADDED];
420 			break;
421 		case LOG_LINE_RESET_F:
422 			break;
423 		case LOG_LINE_RESET_B:
424 			didop = 1;
425 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
426 			if (db_set(sp, lno, p + sizeof(u_char) +
427 			    sizeof(recno_t), data.size - sizeof(u_char) -
428 			    sizeof(recno_t)))
429 				goto err;
430 			if (sp->rptlchange != lno) {
431 				sp->rptlchange = lno;
432 				++sp->rptlines[L_CHANGED];
433 			}
434 			break;
435 		case LOG_MARK:
436 			didop = 1;
437 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
438 			m.lno = lm.lno;
439 			m.cno = lm.cno;
440 			if (mark_set(sp, lm.name, &m, 0))
441 				goto err;
442 			break;
443 		default:
444 			abort();
445 		}
446 	}
447 
448 err:	F_CLR(ep, F_NOLOG);
449 	return (1);
450 }
451 
452 /*
453  * Log_setline --
454  *	Reset the line to its original appearance.
455  *
456  * XXX
457  * There's a bug in this code due to our not logging cursor movements
458  * unless a change was made.  If you do a change, move off the line,
459  * then move back on and do a 'U', the line will be restored to the way
460  * it was before the original change.
461  *
462  * PUBLIC: int log_setline(SCR *);
463  */
464 int
465 log_setline(sp)
466 	SCR *sp;
467 {
468 	DBT key, data;
469 	EXF *ep;
470 	LMARK lm;
471 	MARK m;
472 	recno_t lno;
473 	u_char *p;
474 
475 	ep = sp->ep;
476 	if (F_ISSET(ep, F_NOLOG)) {
477 		msgq(sp, M_ERR,
478 		    "012|Logging not being performed, undo not possible");
479 		return (1);
480 	}
481 
482 	if (ep->l_cur == 1)
483 		return (1);
484 
485 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
486 
487 	key.data = &ep->l_cur;		/* Initialize db request. */
488 	key.size = sizeof(recno_t);
489 
490 	for (;;) {
491 		--ep->l_cur;
492 		if (ep->log->get(ep->log, &key, &data, 0))
493 			LOG_ERR;
494 #if defined(DEBUG) && 0
495 		log_trace(sp, "log_setline", ep->l_cur, data.data);
496 #endif
497 		switch (*(p = (u_char *)data.data)) {
498 		case LOG_CURSOR_INIT:
499 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
500 			if (m.lno != sp->lno || ep->l_cur == 1) {
501 				F_CLR(ep, F_NOLOG);
502 				return (0);
503 			}
504 			break;
505 		case LOG_CURSOR_END:
506 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
507 			if (m.lno != sp->lno) {
508 				++ep->l_cur;
509 				F_CLR(ep, F_NOLOG);
510 				return (0);
511 			}
512 			break;
513 		case LOG_LINE_APPEND:
514 		case LOG_LINE_INSERT:
515 		case LOG_LINE_DELETE:
516 		case LOG_LINE_RESET_F:
517 			break;
518 		case LOG_LINE_RESET_B:
519 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
520 			if (lno == sp->lno &&
521 			    db_set(sp, lno, p + sizeof(u_char) +
522 			    sizeof(recno_t), data.size - sizeof(u_char) -
523 			    sizeof(recno_t)))
524 				goto err;
525 			if (sp->rptlchange != lno) {
526 				sp->rptlchange = lno;
527 				++sp->rptlines[L_CHANGED];
528 			}
529 		case LOG_MARK:
530 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
531 			m.lno = lm.lno;
532 			m.cno = lm.cno;
533 			if (mark_set(sp, lm.name, &m, 0))
534 				goto err;
535 			break;
536 		default:
537 			abort();
538 		}
539 	}
540 
541 err:	F_CLR(ep, F_NOLOG);
542 	return (1);
543 }
544 
545 /*
546  * Log_forward --
547  *	Roll the log forward one operation.
548  *
549  * PUBLIC: int log_forward(SCR *, MARK *);
550  */
551 int
552 log_forward(sp, rp)
553 	SCR *sp;
554 	MARK *rp;
555 {
556 	DBT key, data;
557 	EXF *ep;
558 	LMARK lm;
559 	MARK m;
560 	recno_t lno;
561 	int didop;
562 	u_char *p;
563 
564 	ep = sp->ep;
565 	if (F_ISSET(ep, F_NOLOG)) {
566 		msgq(sp, M_ERR,
567 	    "013|Logging not being performed, roll-forward not possible");
568 		return (1);
569 	}
570 
571 	if (ep->l_cur == ep->l_high) {
572 		msgq(sp, M_BERR, "014|No changes to re-do");
573 		return (1);
574 	}
575 
576 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
577 
578 	key.data = &ep->l_cur;		/* Initialize db request. */
579 	key.size = sizeof(recno_t);
580 	for (didop = 0;;) {
581 		++ep->l_cur;
582 		if (ep->log->get(ep->log, &key, &data, 0))
583 			LOG_ERR;
584 #if defined(DEBUG) && 0
585 		log_trace(sp, "log_forward", ep->l_cur, data.data);
586 #endif
587 		switch (*(p = (u_char *)data.data)) {
588 		case LOG_CURSOR_END:
589 			if (didop) {
590 				++ep->l_cur;
591 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
592 				F_CLR(ep, F_NOLOG);
593 				return (0);
594 			}
595 			break;
596 		case LOG_CURSOR_INIT:
597 			break;
598 		case LOG_LINE_APPEND:
599 		case LOG_LINE_INSERT:
600 			didop = 1;
601 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
602 			if (db_insert(sp, lno, p + sizeof(u_char) +
603 			    sizeof(recno_t), data.size - sizeof(u_char) -
604 			    sizeof(recno_t)))
605 				goto err;
606 			++sp->rptlines[L_ADDED];
607 			break;
608 		case LOG_LINE_DELETE:
609 			didop = 1;
610 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
611 			if (db_delete(sp, lno))
612 				goto err;
613 			++sp->rptlines[L_DELETED];
614 			break;
615 		case LOG_LINE_RESET_B:
616 			break;
617 		case LOG_LINE_RESET_F:
618 			didop = 1;
619 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
620 			if (db_set(sp, lno, p + sizeof(u_char) +
621 			    sizeof(recno_t), data.size - sizeof(u_char) -
622 			    sizeof(recno_t)))
623 				goto err;
624 			if (sp->rptlchange != lno) {
625 				sp->rptlchange = lno;
626 				++sp->rptlines[L_CHANGED];
627 			}
628 			break;
629 		case LOG_MARK:
630 			didop = 1;
631 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
632 			m.lno = lm.lno;
633 			m.cno = lm.cno;
634 			if (mark_set(sp, lm.name, &m, 0))
635 				goto err;
636 			break;
637 		default:
638 			abort();
639 		}
640 	}
641 
642 err:	F_CLR(ep, F_NOLOG);
643 	return (1);
644 }
645 
646 /*
647  * log_err --
648  *	Try and restart the log on failure, i.e. if we run out of memory.
649  */
650 static void
651 log_err(sp, file, line)
652 	SCR *sp;
653 	char *file;
654 	int line;
655 {
656 	EXF *ep;
657 
658 	msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
659 	ep = sp->ep;
660 	(void)ep->log->close(ep->log);
661 	if (!log_init(sp, ep))
662 		msgq(sp, M_ERR, "267|Log restarted");
663 }
664 
665 #if defined(DEBUG) && 0
666 static void
667 log_trace(sp, msg, rno, p)
668 	SCR *sp;
669 	char *msg;
670 	recno_t rno;
671 	u_char *p;
672 {
673 	LMARK lm;
674 	MARK m;
675 	recno_t lno;
676 
677 	switch (*p) {
678 	case LOG_CURSOR_INIT:
679 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
680 		TRACE(sp, "%lu: %s:  C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
681 		break;
682 	case LOG_CURSOR_END:
683 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
684 		TRACE(sp, "%lu: %s:   C_END: %u/%u\n", rno, msg, m.lno, m.cno);
685 		break;
686 	case LOG_LINE_APPEND:
687 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
688 		TRACE(sp, "%lu: %s:  APPEND: %lu\n", rno, msg, lno);
689 		break;
690 	case LOG_LINE_INSERT:
691 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
692 		TRACE(sp, "%lu: %s:  INSERT: %lu\n", rno, msg, lno);
693 		break;
694 	case LOG_LINE_DELETE:
695 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
696 		TRACE(sp, "%lu: %s:  DELETE: %lu\n", rno, msg, lno);
697 		break;
698 	case LOG_LINE_RESET_F:
699 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
700 		TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
701 		break;
702 	case LOG_LINE_RESET_B:
703 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
704 		TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
705 		break;
706 	case LOG_MARK:
707 		memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
708 		TRACE(sp,
709 		    "%lu: %s:    MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
710 		break;
711 	default:
712 		abort();
713 	}
714 }
715 #endif
716