119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: log.c,v 10.27 2011/07/13 06:25:50 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/stat.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <errno.h>
2219304Speter#include <fcntl.h>
2319304Speter#include <limits.h>
24254225Speter#include <stdint.h>
2519304Speter#include <stdio.h>
2619304Speter#include <stdlib.h>
2719304Speter#include <string.h>
2819304Speter
2919304Speter#include "common.h"
3019304Speter
3119304Speter/*
3219304Speter * The log consists of records, each containing a type byte and a variable
3319304Speter * length byte string, as follows:
3419304Speter *
3519304Speter *	LOG_CURSOR_INIT		MARK
3619304Speter *	LOG_CURSOR_END		MARK
3719304Speter *	LOG_LINE_APPEND 	recno_t		char *
3819304Speter *	LOG_LINE_DELETE		recno_t		char *
3919304Speter *	LOG_LINE_INSERT		recno_t		char *
4019304Speter *	LOG_LINE_RESET_F	recno_t		char *
4119304Speter *	LOG_LINE_RESET_B	recno_t		char *
4219304Speter *	LOG_MARK		LMARK
4319304Speter *
4419304Speter * We do before image physical logging.  This means that the editor layer
4519304Speter * MAY NOT modify records in place, even if simply deleting or overwriting
4619304Speter * characters.  Since the smallest unit of logging is a line, we're using
4719304Speter * up lots of space.  This may eventually have to be reduced, probably by
4819304Speter * doing logical logging, which is a much cooler database phrase.
4919304Speter *
5019304Speter * The implementation of the historic vi 'u' command, using roll-forward and
5119304Speter * roll-back, is simple.  Each set of changes has a LOG_CURSOR_INIT record,
5219304Speter * followed by a number of other records, followed by a LOG_CURSOR_END record.
5319304Speter * LOG_LINE_RESET records come in pairs.  The first is a LOG_LINE_RESET_B
5419304Speter * record, and is the line before the change.  The second is LOG_LINE_RESET_F,
5519304Speter * and is the line after the change.  Roll-back is done by backing up to the
5619304Speter * first LOG_CURSOR_INIT record before a change.  Roll-forward is done in a
5719304Speter * similar fashion.
5819304Speter *
5919304Speter * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
6019304Speter * record for a line different from the current one.  It should be noted that
6119304Speter * this means that a subsequent 'u' command will make a change based on the
6219304Speter * new position of the log's cursor.  This is okay, and, in fact, historic vi
6319304Speter * behaved that way.
6419304Speter */
6519304Speter
6619304Speterstatic int	log_cursor1 __P((SCR *, int));
6719304Speterstatic void	log_err __P((SCR *, char *, int));
6819304Speter#if defined(DEBUG) && 0
6919304Speterstatic void	log_trace __P((SCR *, char *, recno_t, u_char *));
7019304Speter#endif
71254225Speterstatic int	apply_with __P((int (*)(SCR *, recno_t, CHAR_T *, size_t),
72254225Speter					SCR *, recno_t, u_char *, size_t));
7319304Speter
7419304Speter/* Try and restart the log on failure, i.e. if we run out of memory. */
7519304Speter#define	LOG_ERR {							\
7619304Speter	log_err(sp, __FILE__, __LINE__);				\
7719304Speter	return (1);							\
7819304Speter}
7919304Speter
80254225Speter/* offset of CHAR_T string in log needs to be aligned on some systems
81254225Speter * because it is passed to db_set as a string
82254225Speter */
83254225Spetertypedef struct {
84254225Speter    char    data[sizeof(u_char) /* type */ + sizeof(recno_t)];
85254225Speter    CHAR_T  str[1];
86254225Speter} log_t;
87254225Speter#define CHAR_T_OFFSET ((char *)(((log_t*)0)->str) - (char *)0)
88254225Speter
8919304Speter/*
9019304Speter * log_init --
9119304Speter *	Initialize the logging subsystem.
9219304Speter *
9319304Speter * PUBLIC: int log_init __P((SCR *, EXF *));
9419304Speter */
9519304Speterint
96254225Speterlog_init(
97254225Speter	SCR *sp,
98254225Speter	EXF *ep)
9919304Speter{
10019304Speter	/*
10119304Speter	 * !!!
10219304Speter	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
10319304Speter	 *
10419304Speter	 * Initialize the buffer.  The logging subsystem has its own
10519304Speter	 * buffers because the global ones are almost by definition
10619304Speter	 * going to be in use when the log runs.
10719304Speter	 */
10819304Speter	ep->l_lp = NULL;
10919304Speter	ep->l_len = 0;
11019304Speter	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
11119304Speter	ep->l_cursor.cno = 0;
11219304Speter	ep->l_high = ep->l_cur = 1;
11319304Speter
11419304Speter	ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
11519304Speter	    S_IRUSR | S_IWUSR, DB_RECNO, NULL);
11619304Speter	if (ep->log == NULL) {
11719304Speter		msgq(sp, M_SYSERR, "009|Log file");
11819304Speter		F_SET(ep, F_NOLOG);
11919304Speter		return (1);
12019304Speter	}
12119304Speter
12219304Speter	return (0);
12319304Speter}
12419304Speter
12519304Speter/*
12619304Speter * log_end --
12719304Speter *	Close the logging subsystem.
12819304Speter *
12919304Speter * PUBLIC: int log_end __P((SCR *, EXF *));
13019304Speter */
13119304Speterint
132254225Speterlog_end(
133254225Speter	SCR *sp,
134254225Speter	EXF *ep)
13519304Speter{
13619304Speter	/*
13719304Speter	 * !!!
13819304Speter	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
13919304Speter	 */
14019304Speter	if (ep->log != NULL) {
14119304Speter		(void)(ep->log->close)(ep->log);
14219304Speter		ep->log = NULL;
14319304Speter	}
14419304Speter	if (ep->l_lp != NULL) {
14519304Speter		free(ep->l_lp);
14619304Speter		ep->l_lp = NULL;
14719304Speter	}
14819304Speter	ep->l_len = 0;
14919304Speter	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
15019304Speter	ep->l_cursor.cno = 0;
15119304Speter	ep->l_high = ep->l_cur = 1;
15219304Speter	return (0);
15319304Speter}
15419304Speter
15519304Speter/*
15619304Speter * log_cursor --
15719304Speter *	Log the current cursor position, starting an event.
15819304Speter *
15919304Speter * PUBLIC: int log_cursor __P((SCR *));
16019304Speter */
16119304Speterint
162254225Speterlog_cursor(SCR *sp)
16319304Speter{
16419304Speter	EXF *ep;
16519304Speter
16619304Speter	ep = sp->ep;
16719304Speter	if (F_ISSET(ep, F_NOLOG))
16819304Speter		return (0);
16919304Speter
17019304Speter	/*
17119304Speter	 * If any changes were made since the last cursor init,
17219304Speter	 * put out the ending cursor record.
17319304Speter	 */
17419304Speter	if (ep->l_cursor.lno == OOBLNO) {
17519304Speter		ep->l_cursor.lno = sp->lno;
17619304Speter		ep->l_cursor.cno = sp->cno;
17719304Speter		return (log_cursor1(sp, LOG_CURSOR_END));
17819304Speter	}
17919304Speter	ep->l_cursor.lno = sp->lno;
18019304Speter	ep->l_cursor.cno = sp->cno;
18119304Speter	return (0);
18219304Speter}
18319304Speter
18419304Speter/*
18519304Speter * log_cursor1 --
18619304Speter *	Actually push a cursor record out.
18719304Speter */
18819304Speterstatic int
189254225Speterlog_cursor1(
190254225Speter	SCR *sp,
191254225Speter	int type)
19219304Speter{
19319304Speter	DBT data, key;
19419304Speter	EXF *ep;
19519304Speter
19619304Speter	ep = sp->ep;
197254225Speter
198254225Speter	BINC_RETC(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
19919304Speter	ep->l_lp[0] = type;
20019304Speter	memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
20119304Speter
20219304Speter	key.data = &ep->l_cur;
20319304Speter	key.size = sizeof(recno_t);
20419304Speter	data.data = ep->l_lp;
20519304Speter	data.size = sizeof(u_char) + sizeof(MARK);
20619304Speter	if (ep->log->put(ep->log, &key, &data, 0) == -1)
20719304Speter		LOG_ERR;
20819304Speter
20919304Speter#if defined(DEBUG) && 0
21019304Speter	TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
21119304Speter	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
21219304Speter	    sp->lno, sp->cno);
21319304Speter#endif
21419304Speter	/* Reset high water mark. */
21519304Speter	ep->l_high = ++ep->l_cur;
21619304Speter
21719304Speter	return (0);
21819304Speter}
21919304Speter
22019304Speter/*
22119304Speter * log_line --
22219304Speter *	Log a line change.
22319304Speter *
22419304Speter * PUBLIC: int log_line __P((SCR *, recno_t, u_int));
22519304Speter */
22619304Speterint
227254225Speterlog_line(
228254225Speter	SCR *sp,
229254225Speter	recno_t lno,
230254225Speter	u_int action)
23119304Speter{
23219304Speter	DBT data, key;
23319304Speter	EXF *ep;
23419304Speter	size_t len;
235254225Speter	CHAR_T *lp;
236254225Speter	recno_t lcur;
23719304Speter
23819304Speter	ep = sp->ep;
23919304Speter	if (F_ISSET(ep, F_NOLOG))
24019304Speter		return (0);
24119304Speter
24219304Speter	/*
24319304Speter	 * XXX
24419304Speter	 *
24519304Speter	 * Kluge for vi.  Clear the EXF undo flag so that the
24619304Speter	 * next 'u' command does a roll-back, regardless.
24719304Speter	 */
24819304Speter	F_CLR(ep, F_UNDO);
24919304Speter
25019304Speter	/* Put out one initial cursor record per set of changes. */
25119304Speter	if (ep->l_cursor.lno != OOBLNO) {
25219304Speter		if (log_cursor1(sp, LOG_CURSOR_INIT))
25319304Speter			return (1);
25419304Speter		ep->l_cursor.lno = OOBLNO;
25519304Speter	}
25619304Speter
25719304Speter	/*
25819304Speter	 * Put out the changes.  If it's a LOG_LINE_RESET_B call, it's a
25919304Speter	 * special case, avoid the caches.  Also, if it fails and it's
26019304Speter	 * line 1, it just means that the user started with an empty file,
26119304Speter	 * so fake an empty length line.
26219304Speter	 */
26319304Speter	if (action == LOG_LINE_RESET_B) {
26419304Speter		if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
26519304Speter			if (lno != 1) {
26619304Speter				db_err(sp, lno);
26719304Speter				return (1);
26819304Speter			}
26919304Speter			len = 0;
270254225Speter			lp = L("");
27119304Speter		}
27219304Speter	} else
27319304Speter		if (db_get(sp, lno, DBG_FATAL, &lp, &len))
27419304Speter			return (1);
275254225Speter	BINC_RETC(sp,
276254225Speter	    ep->l_lp, ep->l_len,
277254225Speter	    len * sizeof(CHAR_T) + CHAR_T_OFFSET);
27819304Speter	ep->l_lp[0] = action;
27919304Speter	memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
280254225Speter	memmove(ep->l_lp + CHAR_T_OFFSET, lp, len * sizeof(CHAR_T));
28119304Speter
282254225Speter	lcur = ep->l_cur;
283254225Speter	key.data = &lcur;
28419304Speter	key.size = sizeof(recno_t);
28519304Speter	data.data = ep->l_lp;
286254225Speter	data.size = len * sizeof(CHAR_T) + CHAR_T_OFFSET;
28719304Speter	if (ep->log->put(ep->log, &key, &data, 0) == -1)
28819304Speter		LOG_ERR;
28919304Speter
29019304Speter#if defined(DEBUG) && 0
29119304Speter	switch (action) {
29219304Speter	case LOG_LINE_APPEND:
293254225Speter		TRACE(sp, "%lu: log_line: append: %lu {%u}\n",
29419304Speter		    ep->l_cur, lno, len);
29519304Speter		break;
29619304Speter	case LOG_LINE_DELETE:
29719304Speter		TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
29819304Speter		    ep->l_cur, lno, len);
29919304Speter		break;
30019304Speter	case LOG_LINE_INSERT:
30119304Speter		TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
30219304Speter		    ep->l_cur, lno, len);
30319304Speter		break;
30419304Speter	case LOG_LINE_RESET_F:
30519304Speter		TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
30619304Speter		    ep->l_cur, lno, len);
30719304Speter		break;
30819304Speter	case LOG_LINE_RESET_B:
30919304Speter		TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
31019304Speter		    ep->l_cur, lno, len);
31119304Speter		break;
31219304Speter	}
31319304Speter#endif
31419304Speter	/* Reset high water mark. */
31519304Speter	ep->l_high = ++ep->l_cur;
31619304Speter
31719304Speter	return (0);
31819304Speter}
31919304Speter
32019304Speter/*
32119304Speter * log_mark --
32219304Speter *	Log a mark position.  For the log to work, we assume that there
32319304Speter *	aren't any operations that just put out a log record -- this
32419304Speter *	would mean that undo operations would only reset marks, and not
32519304Speter *	cause any other change.
32619304Speter *
32719304Speter * PUBLIC: int log_mark __P((SCR *, LMARK *));
32819304Speter */
32919304Speterint
330254225Speterlog_mark(
331254225Speter	SCR *sp,
332254225Speter	LMARK *lmp)
33319304Speter{
33419304Speter	DBT data, key;
33519304Speter	EXF *ep;
33619304Speter
33719304Speter	ep = sp->ep;
33819304Speter	if (F_ISSET(ep, F_NOLOG))
33919304Speter		return (0);
34019304Speter
34119304Speter	/* Put out one initial cursor record per set of changes. */
34219304Speter	if (ep->l_cursor.lno != OOBLNO) {
34319304Speter		if (log_cursor1(sp, LOG_CURSOR_INIT))
34419304Speter			return (1);
34519304Speter		ep->l_cursor.lno = OOBLNO;
34619304Speter	}
34719304Speter
348254225Speter	BINC_RETC(sp, ep->l_lp,
34919304Speter	    ep->l_len, sizeof(u_char) + sizeof(LMARK));
35019304Speter	ep->l_lp[0] = LOG_MARK;
35119304Speter	memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
35219304Speter
35319304Speter	key.data = &ep->l_cur;
35419304Speter	key.size = sizeof(recno_t);
35519304Speter	data.data = ep->l_lp;
35619304Speter	data.size = sizeof(u_char) + sizeof(LMARK);
35719304Speter	if (ep->log->put(ep->log, &key, &data, 0) == -1)
35819304Speter		LOG_ERR;
35919304Speter
36019304Speter#if defined(DEBUG) && 0
36119304Speter	TRACE(sp, "%lu: mark %c: %lu/%u\n",
36219304Speter	    ep->l_cur, lmp->name, lmp->lno, lmp->cno);
36319304Speter#endif
36419304Speter	/* Reset high water mark. */
36519304Speter	ep->l_high = ++ep->l_cur;
36619304Speter	return (0);
36719304Speter}
36819304Speter
36919304Speter/*
37019304Speter * Log_backward --
37119304Speter *	Roll the log backward one operation.
37219304Speter *
37319304Speter * PUBLIC: int log_backward __P((SCR *, MARK *));
37419304Speter */
37519304Speterint
376254225Speterlog_backward(
377254225Speter	SCR *sp,
378254225Speter	MARK *rp)
37919304Speter{
38019304Speter	DBT key, data;
38119304Speter	EXF *ep;
38219304Speter	LMARK lm;
38319304Speter	MARK m;
38419304Speter	recno_t lno;
38519304Speter	int didop;
38619304Speter	u_char *p;
38719304Speter
38819304Speter	ep = sp->ep;
38919304Speter	if (F_ISSET(ep, F_NOLOG)) {
39019304Speter		msgq(sp, M_ERR,
39119304Speter		    "010|Logging not being performed, undo not possible");
39219304Speter		return (1);
39319304Speter	}
39419304Speter
39519304Speter	if (ep->l_cur == 1) {
39619304Speter		msgq(sp, M_BERR, "011|No changes to undo");
39719304Speter		return (1);
39819304Speter	}
39919304Speter
40019304Speter	F_SET(ep, F_NOLOG);		/* Turn off logging. */
40119304Speter
40219304Speter	key.data = &ep->l_cur;		/* Initialize db request. */
40319304Speter	key.size = sizeof(recno_t);
40419304Speter	for (didop = 0;;) {
40519304Speter		--ep->l_cur;
40619304Speter		if (ep->log->get(ep->log, &key, &data, 0))
40719304Speter			LOG_ERR;
40819304Speter#if defined(DEBUG) && 0
40919304Speter		log_trace(sp, "log_backward", ep->l_cur, data.data);
41019304Speter#endif
41119304Speter		switch (*(p = (u_char *)data.data)) {
41219304Speter		case LOG_CURSOR_INIT:
41319304Speter			if (didop) {
41419304Speter				memmove(rp, p + sizeof(u_char), sizeof(MARK));
41519304Speter				F_CLR(ep, F_NOLOG);
41619304Speter				return (0);
41719304Speter			}
41819304Speter			break;
41919304Speter		case LOG_CURSOR_END:
42019304Speter			break;
42119304Speter		case LOG_LINE_APPEND:
42219304Speter		case LOG_LINE_INSERT:
42319304Speter			didop = 1;
42419304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
42519304Speter			if (db_delete(sp, lno))
42619304Speter				goto err;
42719304Speter			++sp->rptlines[L_DELETED];
42819304Speter			break;
42919304Speter		case LOG_LINE_DELETE:
43019304Speter			didop = 1;
43119304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
432254225Speter			if (apply_with(db_insert, sp, lno,
433254225Speter				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
43419304Speter				goto err;
43519304Speter			++sp->rptlines[L_ADDED];
43619304Speter			break;
43719304Speter		case LOG_LINE_RESET_F:
43819304Speter			break;
43919304Speter		case LOG_LINE_RESET_B:
44019304Speter			didop = 1;
44119304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
442254225Speter			if (apply_with(db_set, sp, lno,
443254225Speter				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
44419304Speter				goto err;
44519304Speter			if (sp->rptlchange != lno) {
44619304Speter				sp->rptlchange = lno;
44719304Speter				++sp->rptlines[L_CHANGED];
44819304Speter			}
44919304Speter			break;
45019304Speter		case LOG_MARK:
45119304Speter			didop = 1;
45219304Speter			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
45319304Speter			m.lno = lm.lno;
45419304Speter			m.cno = lm.cno;
45519304Speter			if (mark_set(sp, lm.name, &m, 0))
45619304Speter				goto err;
45719304Speter			break;
45819304Speter		default:
45919304Speter			abort();
46019304Speter		}
46119304Speter	}
46219304Speter
46319304Spetererr:	F_CLR(ep, F_NOLOG);
46419304Speter	return (1);
46519304Speter}
46619304Speter
46719304Speter/*
46819304Speter * Log_setline --
46919304Speter *	Reset the line to its original appearance.
47019304Speter *
47119304Speter * XXX
47219304Speter * There's a bug in this code due to our not logging cursor movements
47319304Speter * unless a change was made.  If you do a change, move off the line,
47419304Speter * then move back on and do a 'U', the line will be restored to the way
47519304Speter * it was before the original change.
47619304Speter *
47719304Speter * PUBLIC: int log_setline __P((SCR *));
47819304Speter */
47919304Speterint
480254225Speterlog_setline(SCR *sp)
48119304Speter{
48219304Speter	DBT key, data;
48319304Speter	EXF *ep;
48419304Speter	LMARK lm;
48519304Speter	MARK m;
48619304Speter	recno_t lno;
48719304Speter	u_char *p;
48819304Speter
48919304Speter	ep = sp->ep;
49019304Speter	if (F_ISSET(ep, F_NOLOG)) {
49119304Speter		msgq(sp, M_ERR,
49219304Speter		    "012|Logging not being performed, undo not possible");
49319304Speter		return (1);
49419304Speter	}
49519304Speter
49619304Speter	if (ep->l_cur == 1)
49719304Speter		return (1);
49819304Speter
49919304Speter	F_SET(ep, F_NOLOG);		/* Turn off logging. */
50019304Speter
50119304Speter	key.data = &ep->l_cur;		/* Initialize db request. */
50219304Speter	key.size = sizeof(recno_t);
50319304Speter	for (;;) {
50419304Speter		--ep->l_cur;
50519304Speter		if (ep->log->get(ep->log, &key, &data, 0))
50619304Speter			LOG_ERR;
50719304Speter#if defined(DEBUG) && 0
50819304Speter		log_trace(sp, "log_setline", ep->l_cur, data.data);
50919304Speter#endif
51019304Speter		switch (*(p = (u_char *)data.data)) {
51119304Speter		case LOG_CURSOR_INIT:
51219304Speter			memmove(&m, p + sizeof(u_char), sizeof(MARK));
51319304Speter			if (m.lno != sp->lno || ep->l_cur == 1) {
51419304Speter				F_CLR(ep, F_NOLOG);
51519304Speter				return (0);
51619304Speter			}
51719304Speter			break;
51819304Speter		case LOG_CURSOR_END:
51919304Speter			memmove(&m, p + sizeof(u_char), sizeof(MARK));
52019304Speter			if (m.lno != sp->lno) {
52119304Speter				++ep->l_cur;
52219304Speter				F_CLR(ep, F_NOLOG);
52319304Speter				return (0);
52419304Speter			}
52519304Speter			break;
52619304Speter		case LOG_LINE_APPEND:
52719304Speter		case LOG_LINE_INSERT:
52819304Speter		case LOG_LINE_DELETE:
52919304Speter		case LOG_LINE_RESET_F:
53019304Speter			break;
53119304Speter		case LOG_LINE_RESET_B:
53219304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
53319304Speter			if (lno == sp->lno &&
534254225Speter				apply_with(db_set, sp, lno,
535254225Speter				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
53619304Speter				goto err;
53719304Speter			if (sp->rptlchange != lno) {
53819304Speter				sp->rptlchange = lno;
53919304Speter				++sp->rptlines[L_CHANGED];
54019304Speter			}
54119304Speter		case LOG_MARK:
54219304Speter			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
54319304Speter			m.lno = lm.lno;
54419304Speter			m.cno = lm.cno;
54519304Speter			if (mark_set(sp, lm.name, &m, 0))
54619304Speter				goto err;
54719304Speter			break;
54819304Speter		default:
54919304Speter			abort();
55019304Speter		}
55119304Speter	}
55219304Speter
55319304Spetererr:	F_CLR(ep, F_NOLOG);
55419304Speter	return (1);
55519304Speter}
55619304Speter
55719304Speter/*
55819304Speter * Log_forward --
55919304Speter *	Roll the log forward one operation.
56019304Speter *
56119304Speter * PUBLIC: int log_forward __P((SCR *, MARK *));
56219304Speter */
56319304Speterint
564254225Speterlog_forward(
565254225Speter	SCR *sp,
566254225Speter	MARK *rp)
56719304Speter{
56819304Speter	DBT key, data;
56919304Speter	EXF *ep;
57019304Speter	LMARK lm;
57119304Speter	MARK m;
57219304Speter	recno_t lno;
57319304Speter	int didop;
57419304Speter	u_char *p;
57519304Speter
57619304Speter	ep = sp->ep;
57719304Speter	if (F_ISSET(ep, F_NOLOG)) {
57819304Speter		msgq(sp, M_ERR,
57919304Speter	    "013|Logging not being performed, roll-forward not possible");
58019304Speter		return (1);
58119304Speter	}
58219304Speter
58319304Speter	if (ep->l_cur == ep->l_high) {
58419304Speter		msgq(sp, M_BERR, "014|No changes to re-do");
58519304Speter		return (1);
58619304Speter	}
58719304Speter
58819304Speter	F_SET(ep, F_NOLOG);		/* Turn off logging. */
58919304Speter
59019304Speter	key.data = &ep->l_cur;		/* Initialize db request. */
59119304Speter	key.size = sizeof(recno_t);
59219304Speter	for (didop = 0;;) {
59319304Speter		++ep->l_cur;
59419304Speter		if (ep->log->get(ep->log, &key, &data, 0))
59519304Speter			LOG_ERR;
59619304Speter#if defined(DEBUG) && 0
59719304Speter		log_trace(sp, "log_forward", ep->l_cur, data.data);
59819304Speter#endif
59919304Speter		switch (*(p = (u_char *)data.data)) {
60019304Speter		case LOG_CURSOR_END:
60119304Speter			if (didop) {
60219304Speter				++ep->l_cur;
60319304Speter				memmove(rp, p + sizeof(u_char), sizeof(MARK));
60419304Speter				F_CLR(ep, F_NOLOG);
60519304Speter				return (0);
60619304Speter			}
60719304Speter			break;
60819304Speter		case LOG_CURSOR_INIT:
60919304Speter			break;
61019304Speter		case LOG_LINE_APPEND:
61119304Speter		case LOG_LINE_INSERT:
61219304Speter			didop = 1;
61319304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
614254225Speter			if (apply_with(db_insert, sp, lno,
615254225Speter				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
61619304Speter				goto err;
61719304Speter			++sp->rptlines[L_ADDED];
61819304Speter			break;
61919304Speter		case LOG_LINE_DELETE:
62019304Speter			didop = 1;
62119304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
62219304Speter			if (db_delete(sp, lno))
62319304Speter				goto err;
62419304Speter			++sp->rptlines[L_DELETED];
62519304Speter			break;
62619304Speter		case LOG_LINE_RESET_B:
62719304Speter			break;
62819304Speter		case LOG_LINE_RESET_F:
62919304Speter			didop = 1;
63019304Speter			memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
631254225Speter			if (apply_with(db_set, sp, lno,
632254225Speter				p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET))
63319304Speter				goto err;
63419304Speter			if (sp->rptlchange != lno) {
63519304Speter				sp->rptlchange = lno;
63619304Speter				++sp->rptlines[L_CHANGED];
63719304Speter			}
63819304Speter			break;
63919304Speter		case LOG_MARK:
64019304Speter			didop = 1;
64119304Speter			memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
64219304Speter			m.lno = lm.lno;
64319304Speter			m.cno = lm.cno;
64419304Speter			if (mark_set(sp, lm.name, &m, 0))
64519304Speter				goto err;
64619304Speter			break;
64719304Speter		default:
64819304Speter			abort();
64919304Speter		}
65019304Speter	}
65119304Speter
65219304Spetererr:	F_CLR(ep, F_NOLOG);
65319304Speter	return (1);
65419304Speter}
65519304Speter
65619304Speter/*
65719304Speter * log_err --
65819304Speter *	Try and restart the log on failure, i.e. if we run out of memory.
65919304Speter */
66019304Speterstatic void
661254225Speterlog_err(
662254225Speter	SCR *sp,
663254225Speter	char *file,
664254225Speter	int line)
66519304Speter{
66619304Speter	EXF *ep;
66719304Speter
66819304Speter	msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
66919304Speter	ep = sp->ep;
67019304Speter	(void)ep->log->close(ep->log);
67119304Speter	if (!log_init(sp, ep))
67219304Speter		msgq(sp, M_ERR, "267|Log restarted");
67319304Speter}
67419304Speter
67519304Speter#if defined(DEBUG) && 0
67619304Speterstatic void
677254225Speterlog_trace(
678254225Speter	SCR *sp,
679254225Speter	char *msg,
680254225Speter	recno_t rno,
681254225Speter	u_char *p)
68219304Speter{
68319304Speter	LMARK lm;
68419304Speter	MARK m;
68519304Speter	recno_t lno;
68619304Speter
68719304Speter	switch (*p) {
68819304Speter	case LOG_CURSOR_INIT:
68919304Speter		memmove(&m, p + sizeof(u_char), sizeof(MARK));
69019304Speter		TRACE(sp, "%lu: %s:  C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
69119304Speter		break;
69219304Speter	case LOG_CURSOR_END:
69319304Speter		memmove(&m, p + sizeof(u_char), sizeof(MARK));
69419304Speter		TRACE(sp, "%lu: %s:   C_END: %u/%u\n", rno, msg, m.lno, m.cno);
69519304Speter		break;
69619304Speter	case LOG_LINE_APPEND:
69719304Speter		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
69819304Speter		TRACE(sp, "%lu: %s:  APPEND: %lu\n", rno, msg, lno);
69919304Speter		break;
70019304Speter	case LOG_LINE_INSERT:
70119304Speter		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
70219304Speter		TRACE(sp, "%lu: %s:  INSERT: %lu\n", rno, msg, lno);
70319304Speter		break;
70419304Speter	case LOG_LINE_DELETE:
70519304Speter		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
70619304Speter		TRACE(sp, "%lu: %s:  DELETE: %lu\n", rno, msg, lno);
70719304Speter		break;
70819304Speter	case LOG_LINE_RESET_F:
70919304Speter		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
71019304Speter		TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
71119304Speter		break;
71219304Speter	case LOG_LINE_RESET_B:
71319304Speter		memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
71419304Speter		TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
71519304Speter		break;
71619304Speter	case LOG_MARK:
71719304Speter		memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
71819304Speter		TRACE(sp,
71919304Speter		    "%lu: %s:    MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
72019304Speter		break;
72119304Speter	default:
72219304Speter		abort();
72319304Speter	}
72419304Speter}
72519304Speter#endif
726254225Speter
727254225Speter/*
728254225Speter * apply_with --
729254225Speter *	Apply a realigned line from the log db to the file db.
730254225Speter */
731254225Speterstatic int
732254225Speterapply_with(
733254225Speter	int (*db_func)(SCR *, recno_t, CHAR_T *, size_t),
734254225Speter	SCR *sp,
735254225Speter	recno_t lno,
736254225Speter	u_char *p,
737254225Speter	size_t len)
738254225Speter{
739254225Speter#ifdef USE_WIDECHAR
740254225Speter	typedef unsigned long nword;
741254225Speter
742254225Speter	static size_t blen;
743254225Speter	static nword *bp;
744254225Speter	nword *lp = (nword *)((uintptr_t)p / sizeof(nword) * sizeof(nword));
745254225Speter
746254225Speter	if (lp != (nword *)p) {
747254225Speter		int offl = ((uintptr_t)p - (uintptr_t)lp) << 3;
748254225Speter		int offr = (sizeof(nword) << 3) - offl;
749254225Speter		size_t i, cnt = (len + sizeof(nword) / 2) / sizeof(nword);
750254225Speter
751254225Speter		if (len > blen) {
752254225Speter			blen = p2roundup(MAX(len, 512));
753254225Speter			REALLOC(sp, bp, nword *, blen);
754254225Speter			if (bp == NULL)
755254225Speter				return (1);
756254225Speter		}
757254225Speter		for (i = 0; i < cnt; ++i)
758254225Speter#if BYTE_ORDER == BIG_ENDIAN
759254225Speter			bp[i] = (lp[i] << offl) ^ (lp[i+1] >> offr);
760254225Speter#else
761254225Speter			bp[i] = (lp[i] >> offl) ^ (lp[i+1] << offr);
762254225Speter#endif
763254225Speter		p = (u_char *)bp;
764254225Speter	}
765254225Speter#endif
766254225Speter	return db_func(sp, lno, (CHAR_T *)p, len / sizeof(CHAR_T));
767254225Speter}
768