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