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