xref: /netbsd-src/external/bsd/nvi/dist/common/log4.c (revision 2f698edb5c1cb2dcd9e762b0abb50c41dde8b6b7)
1*2f698edbSchristos /*	$NetBSD: log4.c,v 1.3 2014/01/26 21:43:45 christos Exp $	*/
2dbd550edSchristos /*-
3dbd550edSchristos  * Copyright (c) 1992, 1993, 1994
4dbd550edSchristos  *	The Regents of the University of California.  All rights reserved.
5dbd550edSchristos  * Copyright (c) 1992, 1993, 1994, 1995, 1996
6dbd550edSchristos  *	Keith Bostic.  All rights reserved.
7dbd550edSchristos  *
8dbd550edSchristos  * See the LICENSE file for redistribution information.
9dbd550edSchristos  */
10dbd550edSchristos 
11dbd550edSchristos #include "config.h"
12dbd550edSchristos 
13*2f698edbSchristos #include <sys/cdefs.h>
14*2f698edbSchristos #if 0
15dbd550edSchristos #ifndef lint
16dbd550edSchristos static const char sccsid[] = "Id: log4.c,v 10.3 2002/06/08 21:00:33 skimo Exp ";
17dbd550edSchristos #endif /* not lint */
18*2f698edbSchristos #else
19*2f698edbSchristos __RCSID("$NetBSD: log4.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
20*2f698edbSchristos #endif
21dbd550edSchristos 
22dbd550edSchristos #include <sys/types.h>
23dbd550edSchristos #include <sys/queue.h>
24dbd550edSchristos #include <sys/stat.h>
25dbd550edSchristos 
26dbd550edSchristos #include <bitstring.h>
27dbd550edSchristos #include <errno.h>
28dbd550edSchristos #include <fcntl.h>
29dbd550edSchristos #include <limits.h>
30dbd550edSchristos #include <stdio.h>
31dbd550edSchristos #include <stdlib.h>
32dbd550edSchristos #include <string.h>
33dbd550edSchristos 
34dbd550edSchristos #include "common.h"
35dbd550edSchristos 
36dbd550edSchristos /*
37dbd550edSchristos  * The log consists of records, each containing a type byte and a variable
38dbd550edSchristos  * length byte string, as follows:
39dbd550edSchristos  *
40dbd550edSchristos  *	LOG_CURSOR_INIT		MARK
41dbd550edSchristos  *	LOG_CURSOR_END		MARK
42dbd550edSchristos  *	LOG_LINE_APPEND_F 	db_recno_t		char *
43dbd550edSchristos  *	LOG_LINE_APPEND_B 	db_recno_t		char *
44dbd550edSchristos  *	LOG_LINE_DELETE_F	db_recno_t		char *
45dbd550edSchristos  *	LOG_LINE_DELETE_B	db_recno_t		char *
46dbd550edSchristos  *	LOG_LINE_RESET_F	db_recno_t		char *
47dbd550edSchristos  *	LOG_LINE_RESET_B	db_recno_t		char *
48dbd550edSchristos  *	LOG_MARK		LMARK
49dbd550edSchristos  *
50dbd550edSchristos  * We do before image physical logging.  This means that the editor layer
51dbd550edSchristos  * MAY NOT modify records in place, even if simply deleting or overwriting
52dbd550edSchristos  * characters.  Since the smallest unit of logging is a line, we're using
53dbd550edSchristos  * up lots of space.  This may eventually have to be reduced, probably by
54dbd550edSchristos  * doing logical logging, which is a much cooler database phrase.
55dbd550edSchristos  *
56dbd550edSchristos  * The implementation of the historic vi 'u' command, using roll-forward and
57dbd550edSchristos  * roll-back, is simple.  Each set of changes has a LOG_CURSOR_INIT record,
58dbd550edSchristos  * followed by a number of other records, followed by a LOG_CURSOR_END record.
59dbd550edSchristos  * LOG_LINE_RESET records come in pairs.  The first is a LOG_LINE_RESET_B
60dbd550edSchristos  * record, and is the line before the change.  The second is LOG_LINE_RESET_F,
61dbd550edSchristos  * and is the line after the change.  Roll-back is done by backing up to the
62dbd550edSchristos  * first LOG_CURSOR_INIT record before a change.  Roll-forward is done in a
63dbd550edSchristos  * similar fashion.
64dbd550edSchristos  *
65dbd550edSchristos  * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
66dbd550edSchristos  * record for a line different from the current one.  It should be noted that
67dbd550edSchristos  * this means that a subsequent 'u' command will make a change based on the
68dbd550edSchristos  * new position of the log's cursor.  This is okay, and, in fact, historic vi
69dbd550edSchristos  * behaved that way.
70dbd550edSchristos  */
71dbd550edSchristos 
72dbd550edSchristos static int	log_cursor1 __P((SCR *, int));
73dbd550edSchristos 
74dbd550edSchristos /*
75dbd550edSchristos  * log_init --
76dbd550edSchristos  *	Initialize the logging subsystem.
77dbd550edSchristos  *
78dbd550edSchristos  * PUBLIC: int log_init __P((SCR *, EXF *));
79dbd550edSchristos  */
80dbd550edSchristos int
log_init(SCR * sp,EXF * ep)81dbd550edSchristos log_init(SCR *sp, EXF *ep)
82dbd550edSchristos {
83dbd550edSchristos 	DB_LOGC *logc;
84dbd550edSchristos 	DBT data;
85dbd550edSchristos 	size_t nlen;
86dbd550edSchristos 
87dbd550edSchristos 	/*
88dbd550edSchristos 	 * !!!
89dbd550edSchristos 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
90dbd550edSchristos 	 *
91dbd550edSchristos 	 * Initialize the buffer.  The logging subsystem has its own
92dbd550edSchristos 	 * buffers because the global ones are almost by definition
93dbd550edSchristos 	 * going to be in use when the log runs.
94dbd550edSchristos 	 */
95dbd550edSchristos 	sp->wp->l_lp = NULL;
96dbd550edSchristos 	sp->wp->l_len = 0;
97dbd550edSchristos 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
98dbd550edSchristos 	ep->l_cursor.cno = 0;
99dbd550edSchristos 	ep->l_high = ep->l_cur = 1;
100dbd550edSchristos 
101dbd550edSchristos 	if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0))
102dbd550edSchristos 		    != 0) {
103dbd550edSchristos 		msgq(sp, M_DBERR, "env->log_cursor");
104dbd550edSchristos 		F_SET(ep, F_NOLOG);
105dbd550edSchristos 		return (1);
106dbd550edSchristos 	}
107dbd550edSchristos 	nlen = 1024;
108dbd550edSchristos retry:
109dbd550edSchristos 	BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen);
110dbd550edSchristos 	memset(&data, 0, sizeof(data));
111dbd550edSchristos 	data.data = sp->wp->l_lp;
112dbd550edSchristos 	data.ulen = sp->wp->l_len;
113dbd550edSchristos 	data.flags = DB_DBT_USERMEM;
114dbd550edSchristos 	switch ((sp->db_error =
115dbd550edSchristos 	    logc->get(logc, &ep->lsn_first, &data, DB_LAST))) {
116dbd550edSchristos 	case ENOMEM:
117dbd550edSchristos 		nlen = data.size;
118dbd550edSchristos 		goto retry;
119dbd550edSchristos 	default:
120dbd550edSchristos alloc_err:
121dbd550edSchristos 		msgq(sp, M_DBERR, "logc->get");
122dbd550edSchristos 		F_SET(ep, F_NOLOG);
123dbd550edSchristos 		return (1);
124dbd550edSchristos 	case 0:
125dbd550edSchristos 		;
126dbd550edSchristos 	}
127dbd550edSchristos 	MEMCPY(&ep->lsn_cur, &ep->lsn_first, 1);
128dbd550edSchristos 	MEMCPY(&ep->lsn_high, &ep->lsn_first, 1);
129dbd550edSchristos 	logc->close(logc, 0);
130dbd550edSchristos 
131dbd550edSchristos 	ep->l_win = NULL;
132dbd550edSchristos 	/*LOCK_INIT(sp->wp, ep);*/
133dbd550edSchristos 
134dbd550edSchristos 	return (0);
135dbd550edSchristos }
136dbd550edSchristos 
137dbd550edSchristos /*
138dbd550edSchristos  * log_end --
139dbd550edSchristos  *	Close the logging subsystem.
140dbd550edSchristos  *
141dbd550edSchristos  * PUBLIC: int log_end __P((SCR *, EXF *));
142dbd550edSchristos  */
143dbd550edSchristos int
log_end(SCR * sp,EXF * ep)144dbd550edSchristos log_end(SCR *sp, EXF *ep)
145dbd550edSchristos {
146dbd550edSchristos 	/*
147dbd550edSchristos 	 * !!!
148dbd550edSchristos 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
149dbd550edSchristos 	 */
150dbd550edSchristos 	/*LOCK_END(sp->wp, ep);*/
151dbd550edSchristos 	if (sp->wp->l_lp != NULL) {
152dbd550edSchristos 		free(sp->wp->l_lp);
153dbd550edSchristos 		sp->wp->l_lp = NULL;
154dbd550edSchristos 	}
155dbd550edSchristos 	sp->wp->l_len = 0;
156dbd550edSchristos 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
157dbd550edSchristos 	ep->l_cursor.cno = 0;
158dbd550edSchristos 	ep->l_high = ep->l_cur = 1;
159dbd550edSchristos 	return (0);
160dbd550edSchristos }
161dbd550edSchristos 
162dbd550edSchristos /*
163dbd550edSchristos  * log_cursor --
164dbd550edSchristos  *	Log the current cursor position, starting an event.
165dbd550edSchristos  *
166dbd550edSchristos  * PUBLIC: int log_cursor __P((SCR *));
167dbd550edSchristos  */
168dbd550edSchristos int
log_cursor(SCR * sp)169dbd550edSchristos log_cursor(SCR *sp)
170dbd550edSchristos {
171dbd550edSchristos 	EXF *ep;
172dbd550edSchristos 
173dbd550edSchristos 	ep = sp->ep;
174dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG))
175dbd550edSchristos 		return (0);
176dbd550edSchristos 
177dbd550edSchristos 	/*
178dbd550edSchristos 	 * If any changes were made since the last cursor init,
179dbd550edSchristos 	 * put out the ending cursor record.
180dbd550edSchristos 	 */
181dbd550edSchristos 	if (ep->l_cursor.lno == OOBLNO) {
182dbd550edSchristos 		if (ep->l_win && ep->l_win != sp->wp)
183dbd550edSchristos 			return 0;
184dbd550edSchristos 		ep->l_cursor.lno = sp->lno;
185dbd550edSchristos 		ep->l_cursor.cno = sp->cno;
186dbd550edSchristos 		ep->l_win = NULL;
187dbd550edSchristos 		return (log_cursor1(sp, LOG_CURSOR_END));
188dbd550edSchristos 	}
189dbd550edSchristos 	ep->l_cursor.lno = sp->lno;
190dbd550edSchristos 	ep->l_cursor.cno = sp->cno;
191dbd550edSchristos 	return (0);
192dbd550edSchristos }
193dbd550edSchristos 
194dbd550edSchristos /*
195dbd550edSchristos  * log_cursor1 --
196dbd550edSchristos  *	Actually push a cursor record out.
197dbd550edSchristos  */
198dbd550edSchristos static int
log_cursor1(SCR * sp,int type)199dbd550edSchristos log_cursor1(SCR *sp, int type)
200dbd550edSchristos {
201dbd550edSchristos 	DBT data, key;
202dbd550edSchristos 	EXF *ep;
203dbd550edSchristos 
204dbd550edSchristos 	ep = sp->ep;
205dbd550edSchristos 
206dbd550edSchristos 	/*
207dbd550edSchristos 	if (type == LOG_CURSOR_INIT &&
208dbd550edSchristos 	    LOCK_TRY(sp->wp, ep))
209dbd550edSchristos 		return 1;
210dbd550edSchristos 	*/
211dbd550edSchristos 
212dbd550edSchristos 	if (type == LOG_CURSOR_INIT &&
213dbd550edSchristos 	    (sp->db_error = __vi_log_truncate(ep)) != 0) {
214dbd550edSchristos 		msgq(sp, M_DBERR, "truncate");
215dbd550edSchristos 		return 1;
216dbd550edSchristos 	}
217dbd550edSchristos 	if ((sp->db_error =
218dbd550edSchristos 		__vi_cursor_log(ep->env, NULL, &ep->lsn_cur, 0, type,
219dbd550edSchristos 			    ep->l_cursor.lno, ep->l_cursor.cno)) != 0) {
220dbd550edSchristos 		msgq(sp, M_DBERR, "cursor_log");
221dbd550edSchristos 		return 1;
222dbd550edSchristos 	}
223dbd550edSchristos 	if (type == LOG_CURSOR_END) {
224dbd550edSchristos 		MEMCPY(&ep->lsn_high, &ep->lsn_cur, 1);
225dbd550edSchristos 		/* XXXX should not be needed */
226dbd550edSchristos 		ep->env->log_flush(ep->env, NULL);
227dbd550edSchristos 	}
228dbd550edSchristos 
229dbd550edSchristos #if defined(DEBUG) && 0
230dbd550edSchristos 	vtrace(sp, "%lu: %s: %u/%u\n", ep->l_cur,
231dbd550edSchristos 	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
232dbd550edSchristos 	    sp->lno, sp->cno);
233dbd550edSchristos #endif
234dbd550edSchristos 	/* Reset high water mark. */
235dbd550edSchristos 	ep->l_high = ++ep->l_cur;
236dbd550edSchristos 
237dbd550edSchristos 	/*
238dbd550edSchristos 	if (type == LOG_CURSOR_END)
239dbd550edSchristos 		LOCK_UNLOCK(sp->wp, ep);
240dbd550edSchristos 	*/
241dbd550edSchristos 	return (0);
242dbd550edSchristos }
243dbd550edSchristos 
244dbd550edSchristos /*
245dbd550edSchristos  * log_line --
246dbd550edSchristos  *	Log a line change.
247dbd550edSchristos  *
248dbd550edSchristos  * PUBLIC: int log_line __P((SCR *, db_recno_t, u_int));
249dbd550edSchristos  */
250dbd550edSchristos int
log_line(SCR * sp,db_recno_t lno,u_int action)251dbd550edSchristos log_line(SCR *sp, db_recno_t lno, u_int action)
252dbd550edSchristos {
253dbd550edSchristos 	DBT data, key;
254dbd550edSchristos 	EXF *ep;
255dbd550edSchristos 	size_t len;
256dbd550edSchristos 	CHAR_T *lp;
257dbd550edSchristos 	db_recno_t lcur;
258dbd550edSchristos 
259dbd550edSchristos 	ep = sp->ep;
260dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG))
261dbd550edSchristos 		return (0);
262dbd550edSchristos 
263dbd550edSchristos 	/*
264dbd550edSchristos 	 * XXX
265dbd550edSchristos 	 *
266dbd550edSchristos 	 * Kluge for vi.  Clear the EXF undo flag so that the
267dbd550edSchristos 	 * next 'u' command does a roll-back, regardless.
268dbd550edSchristos 	 */
269dbd550edSchristos 	F_CLR(ep, F_UNDO);
270dbd550edSchristos 
271dbd550edSchristos 	/* Put out one initial cursor record per set of changes. */
272dbd550edSchristos 	if (ep->l_cursor.lno != OOBLNO) {
273dbd550edSchristos 		if (log_cursor1(sp, LOG_CURSOR_INIT))
274dbd550edSchristos 			return (1);
275dbd550edSchristos 		ep->l_cursor.lno = OOBLNO;
276dbd550edSchristos 		ep->l_win = sp->wp;
277dbd550edSchristos 	} /*else if (ep->l_win != sp->wp) {
278dbd550edSchristos 		printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp);
279dbd550edSchristos 		return 1;
280dbd550edSchristos 	}*/
281dbd550edSchristos 
282dbd550edSchristos 	if ((sp->db_error =
283dbd550edSchristos 		__vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action,
284dbd550edSchristos 			    lno)) != 0) {
285dbd550edSchristos 		msgq(sp, M_DBERR, "change_log");
286dbd550edSchristos 		return 1;
287dbd550edSchristos 	}
288dbd550edSchristos 
289dbd550edSchristos #if defined(DEBUG) && 0
290dbd550edSchristos 	switch (action) {
291dbd550edSchristos 	case LOG_LINE_APPEND_F:
292dbd550edSchristos 		vtrace(sp, "%u: log_line: append_f: %lu {%u}\n",
293dbd550edSchristos 		    ep->l_cur, lno, len);
294dbd550edSchristos 		break;
295dbd550edSchristos 	case LOG_LINE_APPEND_B:
296dbd550edSchristos 		vtrace(sp, "%u: log_line: append_b: %lu {%u}\n",
297dbd550edSchristos 		    ep->l_cur, lno, len);
298dbd550edSchristos 		break;
299dbd550edSchristos 	case LOG_LINE_DELETE_F:
300dbd550edSchristos 		vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n",
301dbd550edSchristos 		    ep->l_cur, lno, len);
302dbd550edSchristos 		break;
303dbd550edSchristos 	case LOG_LINE_DELETE_B:
304dbd550edSchristos 		vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n",
305dbd550edSchristos 		    ep->l_cur, lno, len);
306dbd550edSchristos 		break;
307dbd550edSchristos 	case LOG_LINE_RESET_F:
308dbd550edSchristos 		vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n",
309dbd550edSchristos 		    ep->l_cur, lno, len);
310dbd550edSchristos 		break;
311dbd550edSchristos 	case LOG_LINE_RESET_B:
312dbd550edSchristos 		vtrace(sp, "%lu: log_line: reset_b: %lu {%u}\n",
313dbd550edSchristos 		    ep->l_cur, lno, len);
314dbd550edSchristos 		break;
315dbd550edSchristos 	}
316dbd550edSchristos #endif
317dbd550edSchristos 	/* Reset high water mark. */
318dbd550edSchristos 	ep->l_high = ++ep->l_cur;
319dbd550edSchristos 
320dbd550edSchristos 	return (0);
321dbd550edSchristos }
322dbd550edSchristos 
323dbd550edSchristos /*
324dbd550edSchristos  * log_mark --
325dbd550edSchristos  *	Log a mark position.  For the log to work, we assume that there
326dbd550edSchristos  *	aren't any operations that just put out a log record -- this
327dbd550edSchristos  *	would mean that undo operations would only reset marks, and not
328dbd550edSchristos  *	cause any other change.
329dbd550edSchristos  *
330dbd550edSchristos  * PUBLIC: int log_mark __P((SCR *, LMARK *));
331dbd550edSchristos  */
332dbd550edSchristos int
log_mark(SCR * sp,LMARK * lmp)333dbd550edSchristos log_mark(SCR *sp, LMARK *lmp)
334dbd550edSchristos {
335dbd550edSchristos 	DBT data, key;
336dbd550edSchristos 	EXF *ep;
337dbd550edSchristos 
338dbd550edSchristos 	ep = sp->ep;
339dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG))
340dbd550edSchristos 		return (0);
341dbd550edSchristos 
342dbd550edSchristos 	/* Put out one initial cursor record per set of changes. */
343dbd550edSchristos 	if (ep->l_cursor.lno != OOBLNO) {
344dbd550edSchristos 		if (log_cursor1(sp, LOG_CURSOR_INIT))
345dbd550edSchristos 			return (1);
346dbd550edSchristos 		ep->l_cursor.lno = OOBLNO;
347dbd550edSchristos 		ep->l_win = sp->wp;
348dbd550edSchristos 	}
349dbd550edSchristos 
350dbd550edSchristos 	if ((sp->db_error =
351dbd550edSchristos 		__vi_mark_log(ep->env, NULL, &ep->lsn_cur, 0,
352dbd550edSchristos 			    lmp)) != 0) {
353dbd550edSchristos 		msgq(sp, M_DBERR, "cursor_log");
354dbd550edSchristos 		return 1;
355dbd550edSchristos 	}
356dbd550edSchristos 
357dbd550edSchristos #if defined(DEBUG) && 0
358dbd550edSchristos 	vtrace(sp, "%lu: mark %c: %lu/%u\n",
359dbd550edSchristos 	    ep->l_cur, lmp->name, lmp->lno, lmp->cno);
360dbd550edSchristos #endif
361dbd550edSchristos 	/* Reset high water mark. */
362dbd550edSchristos 	ep->l_high = ++ep->l_cur;
363dbd550edSchristos 	return (0);
364dbd550edSchristos }
365dbd550edSchristos 
366dbd550edSchristos /*
367dbd550edSchristos  * Log_backward --
368dbd550edSchristos  *	Roll the log backward one operation.
369dbd550edSchristos  *
370dbd550edSchristos  * PUBLIC: int log_backward __P((SCR *, MARK *));
371dbd550edSchristos  */
372dbd550edSchristos int
log_backward(SCR * sp,MARK * rp)373dbd550edSchristos log_backward(SCR *sp, MARK *rp)
374dbd550edSchristos {
375dbd550edSchristos 	EXF *ep;
376dbd550edSchristos 	LMARK lm;
377dbd550edSchristos 	MARK m;
378dbd550edSchristos 	db_recno_t lno;
379dbd550edSchristos 	int didop;
380dbd550edSchristos 	u_char *p;
381dbd550edSchristos 	size_t size;
382dbd550edSchristos 
383dbd550edSchristos 	ep = sp->ep;
384dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG)) {
385dbd550edSchristos 		msgq(sp, M_ERR,
386dbd550edSchristos 		    "010|Logging not being performed, undo not possible");
387dbd550edSchristos 		return (1);
388dbd550edSchristos 	}
389dbd550edSchristos 
390dbd550edSchristos 	if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
391dbd550edSchristos 		msgq(sp, M_BERR, "011|No changes to undo");
392dbd550edSchristos 		return (1);
393dbd550edSchristos 	}
394dbd550edSchristos 	return __vi_log_traverse(sp, UNDO_BACKWARD, rp);
395dbd550edSchristos }
396dbd550edSchristos 
397dbd550edSchristos /*
398dbd550edSchristos  * Log_setline --
399dbd550edSchristos  *	Reset the line to its original appearance.
400dbd550edSchristos  *
401dbd550edSchristos  * XXX
402dbd550edSchristos  * There's a bug in this code due to our not logging cursor movements
403dbd550edSchristos  * unless a change was made.  If you do a change, move off the line,
404dbd550edSchristos  * then move back on and do a 'U', the line will be restored to the way
405dbd550edSchristos  * it was before the original change.
406dbd550edSchristos  *
407dbd550edSchristos  * PUBLIC: int log_setline __P((SCR *));
408dbd550edSchristos  */
409dbd550edSchristos int
log_setline(SCR * sp)410dbd550edSchristos log_setline(SCR *sp)
411dbd550edSchristos {
412dbd550edSchristos 	EXF *ep;
413dbd550edSchristos 	LMARK lm;
414dbd550edSchristos 	MARK m;
415dbd550edSchristos 	db_recno_t lno;
416dbd550edSchristos 	u_char *p;
417dbd550edSchristos 	size_t size;
418dbd550edSchristos 
419dbd550edSchristos 	ep = sp->ep;
420dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG)) {
421dbd550edSchristos 		msgq(sp, M_ERR,
422dbd550edSchristos 		    "012|Logging not being performed, undo not possible");
423dbd550edSchristos 		return (1);
424dbd550edSchristos 	}
425dbd550edSchristos 
426dbd550edSchristos 	if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
427dbd550edSchristos 		msgq(sp, M_BERR, "011|No changes to undo");
428dbd550edSchristos 		return (1);
429dbd550edSchristos 	}
430dbd550edSchristos 	return __vi_log_traverse(sp, UNDO_SETLINE, &m);
431dbd550edSchristos }
432dbd550edSchristos 
433dbd550edSchristos /*
434dbd550edSchristos  * Log_forward --
435dbd550edSchristos  *	Roll the log forward one operation.
436dbd550edSchristos  *
437dbd550edSchristos  * PUBLIC: int log_forward __P((SCR *, MARK *));
438dbd550edSchristos  */
439dbd550edSchristos int
log_forward(SCR * sp,MARK * rp)440dbd550edSchristos log_forward(SCR *sp, MARK *rp)
441dbd550edSchristos {
442dbd550edSchristos 	EXF *ep;
443dbd550edSchristos 	LMARK lm;
444dbd550edSchristos 	MARK m;
445dbd550edSchristos 	db_recno_t lno;
446dbd550edSchristos 	int didop;
447dbd550edSchristos 	u_char *p;
448dbd550edSchristos 	size_t size;
449dbd550edSchristos 
450dbd550edSchristos 	ep = sp->ep;
451dbd550edSchristos 	if (F_ISSET(ep, F_NOLOG)) {
452dbd550edSchristos 		msgq(sp, M_ERR,
453dbd550edSchristos 	    "013|Logging not being performed, roll-forward not possible");
454dbd550edSchristos 		return (1);
455dbd550edSchristos 	}
456dbd550edSchristos 
457dbd550edSchristos 	if (log_compare(&ep->lsn_cur, &ep->lsn_high) >= 0) {
458dbd550edSchristos 		msgq(sp, M_BERR, "014|No changes to re-do");
459dbd550edSchristos 		return (1);
460dbd550edSchristos 	}
461dbd550edSchristos 	return __vi_log_traverse(sp, UNDO_FORWARD, rp);
462dbd550edSchristos }
463