xref: /openbsd/usr.bin/vi/common/log.c (revision a0242ada)
1 /*	$OpenBSD: log.c,v 1.12 2017/04/18 01:45:35 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 <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
log_init(SCR * sp,EXF * ep)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
log_end(SCR * sp,EXF * ep)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 	free(ep->l_lp);
128 	ep->l_lp = NULL;
129 	ep->l_len = 0;
130 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
131 	ep->l_cursor.cno = 0;
132 	ep->l_high = ep->l_cur = 1;
133 	return (0);
134 }
135 
136 /*
137  * log_cursor --
138  *	Log the current cursor position, starting an event.
139  *
140  * PUBLIC: int log_cursor(SCR *);
141  */
142 int
log_cursor(SCR * sp)143 log_cursor(SCR *sp)
144 {
145 	EXF *ep;
146 
147 	ep = sp->ep;
148 	if (F_ISSET(ep, F_NOLOG))
149 		return (0);
150 
151 	/*
152 	 * If any changes were made since the last cursor init,
153 	 * put out the ending cursor record.
154 	 */
155 	if (ep->l_cursor.lno == OOBLNO) {
156 		ep->l_cursor.lno = sp->lno;
157 		ep->l_cursor.cno = sp->cno;
158 		return (log_cursor1(sp, LOG_CURSOR_END));
159 	}
160 	ep->l_cursor.lno = sp->lno;
161 	ep->l_cursor.cno = sp->cno;
162 	return (0);
163 }
164 
165 /*
166  * log_cursor1 --
167  *	Actually push a cursor record out.
168  */
169 static int
log_cursor1(SCR * sp,int type)170 log_cursor1(SCR *sp, int type)
171 {
172 	DBT data, key;
173 	EXF *ep;
174 
175 	ep = sp->ep;
176 	BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
177 	ep->l_lp[0] = type;
178 	memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
179 
180 	key.data = &ep->l_cur;
181 	key.size = sizeof(recno_t);
182 	data.data = ep->l_lp;
183 	data.size = sizeof(u_char) + sizeof(MARK);
184 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
185 		LOG_ERR;
186 
187 #if defined(DEBUG) && 0
188 	TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
189 	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
190 	    sp->lno, sp->cno);
191 #endif
192 	/* Reset high water mark. */
193 	ep->l_high = ++ep->l_cur;
194 
195 	return (0);
196 }
197 
198 /*
199  * log_line --
200  *	Log a line change.
201  *
202  * PUBLIC: int log_line(SCR *, recno_t, u_int);
203  */
204 int
log_line(SCR * sp,recno_t lno,u_int action)205 log_line(SCR *sp, recno_t lno, u_int action)
206 {
207 	DBT data, key;
208 	EXF *ep;
209 	size_t len;
210 	char *lp;
211 
212 	ep = sp->ep;
213 	if (F_ISSET(ep, F_NOLOG))
214 		return (0);
215 
216 	/*
217 	 * XXX
218 	 *
219 	 * Kluge for vi.  Clear the EXF undo flag so that the
220 	 * next 'u' command does a roll-back, regardless.
221 	 */
222 	F_CLR(ep, F_UNDO);
223 
224 	/* Put out one initial cursor record per set of changes. */
225 	if (ep->l_cursor.lno != OOBLNO) {
226 		if (log_cursor1(sp, LOG_CURSOR_INIT))
227 			return (1);
228 		ep->l_cursor.lno = OOBLNO;
229 	}
230 
231 	/*
232 	 * Put out the changes.  If it's a LOG_LINE_RESET_B call, it's a
233 	 * special case, avoid the caches.  Also, if it fails and it's
234 	 * line 1, it just means that the user started with an empty file,
235 	 * so fake an empty length line.
236 	 */
237 	if (action == LOG_LINE_RESET_B) {
238 		if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
239 			if (lno != 1) {
240 				db_err(sp, lno);
241 				return (1);
242 			}
243 			len = 0;
244 			lp = "";
245 		}
246 	} else
247 		if (db_get(sp, lno, DBG_FATAL, &lp, &len))
248 			return (1);
249 	BINC_RET(sp,
250 	    ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
251 	ep->l_lp[0] = action;
252 	memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
253 	memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
254 
255 	key.data = &ep->l_cur;
256 	key.size = sizeof(recno_t);
257 	data.data = ep->l_lp;
258 	data.size = len + sizeof(u_char) + sizeof(recno_t);
259 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
260 		LOG_ERR;
261 
262 #if defined(DEBUG) && 0
263 	switch (action) {
264 	case LOG_LINE_APPEND:
265 		TRACE(sp, "%u: log_line: append: %lu {%u}\n",
266 		    ep->l_cur, lno, len);
267 		break;
268 	case LOG_LINE_DELETE:
269 		TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
270 		    ep->l_cur, lno, len);
271 		break;
272 	case LOG_LINE_INSERT:
273 		TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
274 		    ep->l_cur, lno, len);
275 		break;
276 	case LOG_LINE_RESET_F:
277 		TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
278 		    ep->l_cur, lno, len);
279 		break;
280 	case LOG_LINE_RESET_B:
281 		TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
282 		    ep->l_cur, lno, len);
283 		break;
284 	}
285 #endif
286 	/* Reset high water mark. */
287 	ep->l_high = ++ep->l_cur;
288 
289 	return (0);
290 }
291 
292 /*
293  * log_mark --
294  *	Log a mark position.  For the log to work, we assume that there
295  *	aren't any operations that just put out a log record -- this
296  *	would mean that undo operations would only reset marks, and not
297  *	cause any other change.
298  *
299  * PUBLIC: int log_mark(SCR *, LMARK *);
300  */
301 int
log_mark(SCR * sp,LMARK * lmp)302 log_mark(SCR *sp, LMARK *lmp)
303 {
304 	DBT data, key;
305 	EXF *ep;
306 
307 	ep = sp->ep;
308 	if (F_ISSET(ep, F_NOLOG))
309 		return (0);
310 
311 	/* Put out one initial cursor record per set of changes. */
312 	if (ep->l_cursor.lno != OOBLNO) {
313 		if (log_cursor1(sp, LOG_CURSOR_INIT))
314 			return (1);
315 		ep->l_cursor.lno = OOBLNO;
316 	}
317 
318 	BINC_RET(sp, ep->l_lp,
319 	    ep->l_len, sizeof(u_char) + sizeof(LMARK));
320 	ep->l_lp[0] = LOG_MARK;
321 	memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
322 
323 	key.data = &ep->l_cur;
324 	key.size = sizeof(recno_t);
325 	data.data = ep->l_lp;
326 	data.size = sizeof(u_char) + sizeof(LMARK);
327 	if (ep->log->put(ep->log, &key, &data, 0) == -1)
328 		LOG_ERR;
329 
330 #if defined(DEBUG) && 0
331 	TRACE(sp, "%lu: mark %c: %lu/%u\n",
332 	    ep->l_cur, lmp->name, lmp->lno, lmp->cno);
333 #endif
334 	/* Reset high water mark. */
335 	ep->l_high = ++ep->l_cur;
336 	return (0);
337 }
338 
339 /*
340  * Log_backward --
341  *	Roll the log backward one operation.
342  *
343  * PUBLIC: int log_backward(SCR *, MARK *);
344  */
345 int
log_backward(SCR * sp,MARK * rp)346 log_backward(SCR *sp, MARK *rp)
347 {
348 	DBT key, data;
349 	EXF *ep;
350 	LMARK lm;
351 	MARK m;
352 	recno_t lno;
353 	int didop;
354 	u_char *p;
355 
356 	ep = sp->ep;
357 	if (F_ISSET(ep, F_NOLOG)) {
358 		msgq(sp, M_ERR,
359 		    "Logging not being performed, undo not possible");
360 		return (1);
361 	}
362 
363 	if (ep->l_cur == 1) {
364 		msgq(sp, M_BERR, "No changes to undo");
365 		return (1);
366 	}
367 
368 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
369 
370 	key.data = &ep->l_cur;		/* Initialize db request. */
371 	key.size = sizeof(recno_t);
372 	for (didop = 0;;) {
373 		--ep->l_cur;
374 		if (ep->log->get(ep->log, &key, &data, 0))
375 			LOG_ERR;
376 #if defined(DEBUG) && 0
377 		log_trace(sp, "log_backward", ep->l_cur, data.data);
378 #endif
379 		switch (*(p = (u_char *)data.data)) {
380 		case LOG_CURSOR_INIT:
381 			if (didop) {
382 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
383 				F_CLR(ep, F_NOLOG);
384 				return (0);
385 			}
386 			break;
387 		case LOG_CURSOR_END:
388 			break;
389 		case LOG_LINE_APPEND:
390 		case LOG_LINE_INSERT:
391 			didop = 1;
392 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
393 			if (db_delete(sp, lno))
394 				goto err;
395 			++sp->rptlines[L_DELETED];
396 			break;
397 		case LOG_LINE_DELETE:
398 			didop = 1;
399 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
400 			if (db_insert(sp, lno, p + sizeof(u_char) +
401 			    sizeof(recno_t), data.size - sizeof(u_char) -
402 			    sizeof(recno_t)))
403 				goto err;
404 			++sp->rptlines[L_ADDED];
405 			break;
406 		case LOG_LINE_RESET_F:
407 			break;
408 		case LOG_LINE_RESET_B:
409 			didop = 1;
410 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
411 			if (db_set(sp, lno, p + sizeof(u_char) +
412 			    sizeof(recno_t), data.size - sizeof(u_char) -
413 			    sizeof(recno_t)))
414 				goto err;
415 			if (sp->rptlchange != lno) {
416 				sp->rptlchange = lno;
417 				++sp->rptlines[L_CHANGED];
418 			}
419 			break;
420 		case LOG_MARK:
421 			didop = 1;
422 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
423 			m.lno = lm.lno;
424 			m.cno = lm.cno;
425 			if (mark_set(sp, lm.name, &m, 0))
426 				goto err;
427 			break;
428 		default:
429 			abort();
430 		}
431 	}
432 
433 err:	F_CLR(ep, F_NOLOG);
434 	return (1);
435 }
436 
437 /*
438  * Log_setline --
439  *	Reset the line to its original appearance.
440  *
441  * XXX
442  * There's a bug in this code due to our not logging cursor movements
443  * unless a change was made.  If you do a change, move off the line,
444  * then move back on and do a 'U', the line will be restored to the way
445  * it was before the original change.
446  *
447  * PUBLIC: int log_setline(SCR *);
448  */
449 int
log_setline(SCR * sp)450 log_setline(SCR *sp)
451 {
452 	DBT key, data;
453 	EXF *ep;
454 	LMARK lm;
455 	MARK m;
456 	recno_t lno;
457 	u_char *p;
458 
459 	ep = sp->ep;
460 	if (F_ISSET(ep, F_NOLOG)) {
461 		msgq(sp, M_ERR,
462 		    "Logging not being performed, undo not possible");
463 		return (1);
464 	}
465 
466 	if (ep->l_cur == 1)
467 		return (1);
468 
469 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
470 
471 	key.data = &ep->l_cur;		/* Initialize db request. */
472 	key.size = sizeof(recno_t);
473 
474 	for (;;) {
475 		--ep->l_cur;
476 		if (ep->log->get(ep->log, &key, &data, 0))
477 			LOG_ERR;
478 #if defined(DEBUG) && 0
479 		log_trace(sp, "log_setline", ep->l_cur, data.data);
480 #endif
481 		switch (*(p = (u_char *)data.data)) {
482 		case LOG_CURSOR_INIT:
483 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
484 			if (m.lno != sp->lno || ep->l_cur == 1) {
485 				F_CLR(ep, F_NOLOG);
486 				return (0);
487 			}
488 			break;
489 		case LOG_CURSOR_END:
490 			memmove(&m, p + sizeof(u_char), sizeof(MARK));
491 			if (m.lno != sp->lno) {
492 				++ep->l_cur;
493 				F_CLR(ep, F_NOLOG);
494 				return (0);
495 			}
496 			break;
497 		case LOG_LINE_APPEND:
498 		case LOG_LINE_INSERT:
499 		case LOG_LINE_DELETE:
500 		case LOG_LINE_RESET_F:
501 			break;
502 		case LOG_LINE_RESET_B:
503 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
504 			if (lno == sp->lno &&
505 			    db_set(sp, lno, p + sizeof(u_char) +
506 			    sizeof(recno_t), data.size - sizeof(u_char) -
507 			    sizeof(recno_t)))
508 				goto err;
509 			if (sp->rptlchange != lno) {
510 				sp->rptlchange = lno;
511 				++sp->rptlines[L_CHANGED];
512 			}
513 		case LOG_MARK:
514 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
515 			m.lno = lm.lno;
516 			m.cno = lm.cno;
517 			if (mark_set(sp, lm.name, &m, 0))
518 				goto err;
519 			break;
520 		default:
521 			abort();
522 		}
523 	}
524 
525 err:	F_CLR(ep, F_NOLOG);
526 	return (1);
527 }
528 
529 /*
530  * Log_forward --
531  *	Roll the log forward one operation.
532  *
533  * PUBLIC: int log_forward(SCR *, MARK *);
534  */
535 int
log_forward(SCR * sp,MARK * rp)536 log_forward(SCR *sp, MARK *rp)
537 {
538 	DBT key, data;
539 	EXF *ep;
540 	LMARK lm;
541 	MARK m;
542 	recno_t lno;
543 	int didop;
544 	u_char *p;
545 
546 	ep = sp->ep;
547 	if (F_ISSET(ep, F_NOLOG)) {
548 		msgq(sp, M_ERR,
549 	    "Logging not being performed, roll-forward not possible");
550 		return (1);
551 	}
552 
553 	if (ep->l_cur == ep->l_high) {
554 		msgq(sp, M_BERR, "No changes to re-do");
555 		return (1);
556 	}
557 
558 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
559 
560 	key.data = &ep->l_cur;		/* Initialize db request. */
561 	key.size = sizeof(recno_t);
562 	for (didop = 0;;) {
563 		++ep->l_cur;
564 		if (ep->log->get(ep->log, &key, &data, 0))
565 			LOG_ERR;
566 #if defined(DEBUG) && 0
567 		log_trace(sp, "log_forward", ep->l_cur, data.data);
568 #endif
569 		switch (*(p = (u_char *)data.data)) {
570 		case LOG_CURSOR_END:
571 			if (didop) {
572 				++ep->l_cur;
573 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
574 				F_CLR(ep, F_NOLOG);
575 				return (0);
576 			}
577 			break;
578 		case LOG_CURSOR_INIT:
579 			break;
580 		case LOG_LINE_APPEND:
581 		case LOG_LINE_INSERT:
582 			didop = 1;
583 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
584 			if (db_insert(sp, lno, p + sizeof(u_char) +
585 			    sizeof(recno_t), data.size - sizeof(u_char) -
586 			    sizeof(recno_t)))
587 				goto err;
588 			++sp->rptlines[L_ADDED];
589 			break;
590 		case LOG_LINE_DELETE:
591 			didop = 1;
592 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
593 			if (db_delete(sp, lno))
594 				goto err;
595 			++sp->rptlines[L_DELETED];
596 			break;
597 		case LOG_LINE_RESET_B:
598 			break;
599 		case LOG_LINE_RESET_F:
600 			didop = 1;
601 			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
602 			if (db_set(sp, lno, p + sizeof(u_char) +
603 			    sizeof(recno_t), data.size - sizeof(u_char) -
604 			    sizeof(recno_t)))
605 				goto err;
606 			if (sp->rptlchange != lno) {
607 				sp->rptlchange = lno;
608 				++sp->rptlines[L_CHANGED];
609 			}
610 			break;
611 		case LOG_MARK:
612 			didop = 1;
613 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
614 			m.lno = lm.lno;
615 			m.cno = lm.cno;
616 			if (mark_set(sp, lm.name, &m, 0))
617 				goto err;
618 			break;
619 		default:
620 			abort();
621 		}
622 	}
623 
624 err:	F_CLR(ep, F_NOLOG);
625 	return (1);
626 }
627 
628 /*
629  * log_err --
630  *	Try and restart the log on failure, i.e. if we run out of memory.
631  */
632 static void
log_err(SCR * sp,char * file,int line)633 log_err(SCR *sp, char *file, int line)
634 {
635 	EXF *ep;
636 
637 	msgq(sp, M_SYSERR, "%s/%d: log put error", basename(file), line);
638 	ep = sp->ep;
639 	(void)ep->log->close(ep->log);
640 	if (!log_init(sp, ep))
641 		msgq(sp, M_ERR, "Log restarted");
642 }
643 
644 #if defined(DEBUG) && 0
645 static void
log_trace(SCR * sp,char * msg,recno_t rno,u_char * p)646 log_trace(SCR *sp, char *msg, recno_t rno, u_char *p)
647 {
648 	LMARK lm;
649 	MARK m;
650 	recno_t lno;
651 
652 	switch (*p) {
653 	case LOG_CURSOR_INIT:
654 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
655 		TRACE(sp, "%lu: %s:  C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
656 		break;
657 	case LOG_CURSOR_END:
658 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
659 		TRACE(sp, "%lu: %s:   C_END: %u/%u\n", rno, msg, m.lno, m.cno);
660 		break;
661 	case LOG_LINE_APPEND:
662 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
663 		TRACE(sp, "%lu: %s:  APPEND: %lu\n", rno, msg, lno);
664 		break;
665 	case LOG_LINE_INSERT:
666 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
667 		TRACE(sp, "%lu: %s:  INSERT: %lu\n", rno, msg, lno);
668 		break;
669 	case LOG_LINE_DELETE:
670 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
671 		TRACE(sp, "%lu: %s:  DELETE: %lu\n", rno, msg, lno);
672 		break;
673 	case LOG_LINE_RESET_F:
674 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
675 		TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
676 		break;
677 	case LOG_LINE_RESET_B:
678 		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
679 		TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
680 		break;
681 	case LOG_MARK:
682 		memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
683 		TRACE(sp,
684 		    "%lu: %s:    MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
685 		break;
686 	default:
687 		abort();
688 	}
689 }
690 #endif
691