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