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