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