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