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