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