xref: /minix/external/bsd/nvi/dist/common/log.c (revision 0a6a1f1d)
1 /*	$NetBSD: log.c,v 1.3 2014/01/26 21:43:45 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 #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.3 2014/01/26 21:43:45 christos 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 ((char *)(((log_t*)0)->str) - (char *)0)
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 		case LOG_MARK:
624 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
625 			m.lno = lm.lno;
626 			m.cno = lm.cno;
627 			if (mark_set(sp, lm.name, &m, 0))
628 				goto err;
629 			break;
630 		default:
631 			abort();
632 		}
633 	}
634 
635 err:	F_CLR(ep, F_NOLOG);
636 	ep->l_win = NULL;
637 	return (1);
638 }
639 
640 /*
641  * Log_forward --
642  *	Roll the log forward one operation.
643  *
644  * PUBLIC: int log_forward __P((SCR *, MARK *));
645  */
646 int
log_forward(SCR * sp,MARK * rp)647 log_forward(SCR *sp, MARK *rp)
648 {
649 	EXF *ep;
650 	LMARK lm;
651 	MARK m;
652 	db_recno_t lno;
653 	int didop;
654 	u_char *p;
655 	size_t size;
656 
657 	ep = sp->ep;
658 	if (F_ISSET(ep, F_NOLOG)) {
659 		msgq(sp, M_ERR,
660 	    "013|Logging not being performed, roll-forward not possible");
661 		return (1);
662 	}
663 
664 	if (ep->l_cur == ep->l_high) {
665 		msgq(sp, M_BERR, "014|No changes to re-do");
666 		return (1);
667 	}
668 
669 	if (ep->l_win && ep->l_win != sp->wp) {
670 		ex_emsg(sp, NULL, EXM_LOCKED);
671 		return 1;
672 	}
673 	ep->l_win = sp->wp;
674 
675 	F_SET(ep, F_NOLOG);		/* Turn off logging. */
676 
677 	for (didop = 0;;) {
678 		++ep->l_cur;
679 		if (vi_log_get(sp, &ep->l_cur, &size))
680 			LOG_ERR;
681 #if defined(DEBUG) && 0
682 		log_trace(sp, "log_forward", ep->l_cur, data.data);
683 #endif
684 		switch (*(p = (u_char *)sp->wp->l_lp)) {
685 		case LOG_CURSOR_END:
686 			if (didop) {
687 				++ep->l_cur;
688 				memmove(rp, p + sizeof(u_char), sizeof(MARK));
689 				F_CLR(ep, F_NOLOG);
690 				ep->l_win = NULL;
691 				return (0);
692 			}
693 			break;
694 		case LOG_CURSOR_INIT:
695 			break;
696 		case LOG_LINE_APPEND_F:
697 			didop = 1;
698 			memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
699 			if (db_insert(sp, lno,
700 			    (CHAR_T *)(p + CHAR_T_OFFSET),
701 			    (size - CHAR_T_OFFSET) / sizeof(CHAR_T)))
702 				goto err;
703 			++sp->rptlines[L_ADDED];
704 			break;
705 		case LOG_LINE_DELETE_B:
706 			didop = 1;
707 			memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
708 			if (db_delete(sp, lno))
709 				goto err;
710 			++sp->rptlines[L_DELETED];
711 			break;
712 		case LOG_LINE_RESET_B:
713 			break;
714 		case LOG_LINE_RESET_F:
715 			didop = 1;
716 			memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
717 			if (db_set(sp, lno,
718 			    (CHAR_T *)(p + CHAR_T_OFFSET),
719 			    (size - CHAR_T_OFFSET) / sizeof(CHAR_T)))
720 				goto err;
721 			if (sp->rptlchange != lno) {
722 				sp->rptlchange = lno;
723 				++sp->rptlines[L_CHANGED];
724 			}
725 			break;
726 		case LOG_MARK:
727 			didop = 1;
728 			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
729 			m.lno = lm.lno;
730 			m.cno = lm.cno;
731 			if (mark_set(sp, lm.name, &m, 0))
732 				goto err;
733 			break;
734 		default:
735 			abort();
736 		}
737 	}
738 
739 err:	F_CLR(ep, F_NOLOG);
740 	ep->l_win = NULL;
741 	return (1);
742 }
743 
744 /*
745  * log_err --
746  *	Try and restart the log on failure, i.e. if we run out of memory.
747  */
748 static void
log_err(SCR * sp,const char * file,int line)749 log_err(SCR *sp, const char *file, int line)
750 {
751 	EXF *ep;
752 
753 	msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
754 	ep = sp->ep;
755 	(void)ep->log->close(ep->log, DB_NOSYNC);
756 	if (!log_init(sp, ep))
757 		msgq(sp, M_ERR, "267|Log restarted");
758 }
759 
760 #if defined(DEBUG) && 0
761 static void
log_trace(SCR * sp,const char * msg,db_recno_t rno,u_char * p)762 log_trace(SCR *sp, const char *msg, db_recno_t rno, u_char *p)
763 {
764 	LMARK lm;
765 	MARK m;
766 	db_recno_t lno;
767 
768 	switch (*p) {
769 	case LOG_CURSOR_INIT:
770 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
771 		vtrace(sp, "%lu: %s:  C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
772 		break;
773 	case LOG_CURSOR_END:
774 		memmove(&m, p + sizeof(u_char), sizeof(MARK));
775 		vtrace(sp, "%lu: %s:   C_END: %u/%u\n", rno, msg, m.lno, m.cno);
776 		break;
777 	case LOG_LINE_APPEND_F:
778 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
779 		vtrace(sp, "%lu: %s:  APPEND_F: %lu\n", rno, msg, lno);
780 		break;
781 	case LOG_LINE_APPEND_B:
782 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
783 		vtrace(sp, "%lu: %s:  APPEND_B: %lu\n", rno, msg, lno);
784 		break;
785 	case LOG_LINE_DELETE_F:
786 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
787 		vtrace(sp, "%lu: %s:  DELETE_F: %lu\n", rno, msg, lno);
788 		break;
789 	case LOG_LINE_DELETE_B:
790 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
791 		vtrace(sp, "%lu: %s:  DELETE_B: %lu\n", rno, msg, lno);
792 		break;
793 	case LOG_LINE_RESET_F:
794 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
795 		vtrace(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
796 		break;
797 	case LOG_LINE_RESET_B:
798 		memmove(&lno, p + sizeof(u_char), sizeof(db_recno_t));
799 		vtrace(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
800 		break;
801 	case LOG_MARK:
802 		memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
803 		vtrace(sp,
804 		    "%lu: %s:    MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
805 		break;
806 	default:
807 		abort();
808 	}
809 }
810 #endif
811