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