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