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