xref: /netbsd-src/external/bsd/nvi/dist/common/log4.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: log4.c,v 1.2 2013/11/22 15:52:05 christos Exp $	*/
2 /*-
3  * Copyright (c) 1992, 1993, 1994
4  *	The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
6  *	Keith Bostic.  All rights reserved.
7  *
8  * See the LICENSE file for redistribution information.
9  */
10 
11 #include "config.h"
12 
13 #ifndef lint
14 static const char sccsid[] = "Id: log4.c,v 10.3 2002/06/08 21:00:33 skimo Exp ";
15 #endif /* not lint */
16 
17 #include <sys/types.h>
18 #include <sys/queue.h>
19 #include <sys/stat.h>
20 
21 #include <bitstring.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "common.h"
30 
31 /*
32  * The log consists of records, each containing a type byte and a variable
33  * length byte string, as follows:
34  *
35  *	LOG_CURSOR_INIT		MARK
36  *	LOG_CURSOR_END		MARK
37  *	LOG_LINE_APPEND_F 	db_recno_t		char *
38  *	LOG_LINE_APPEND_B 	db_recno_t		char *
39  *	LOG_LINE_DELETE_F	db_recno_t		char *
40  *	LOG_LINE_DELETE_B	db_recno_t		char *
41  *	LOG_LINE_RESET_F	db_recno_t		char *
42  *	LOG_LINE_RESET_B	db_recno_t		char *
43  *	LOG_MARK		LMARK
44  *
45  * We do before image physical logging.  This means that the editor layer
46  * MAY NOT modify records in place, even if simply deleting or overwriting
47  * characters.  Since the smallest unit of logging is a line, we're using
48  * up lots of space.  This may eventually have to be reduced, probably by
49  * doing logical logging, which is a much cooler database phrase.
50  *
51  * The implementation of the historic vi 'u' command, using roll-forward and
52  * roll-back, is simple.  Each set of changes has a LOG_CURSOR_INIT record,
53  * followed by a number of other records, followed by a LOG_CURSOR_END record.
54  * LOG_LINE_RESET records come in pairs.  The first is a LOG_LINE_RESET_B
55  * record, and is the line before the change.  The second is LOG_LINE_RESET_F,
56  * and is the line after the change.  Roll-back is done by backing up to the
57  * first LOG_CURSOR_INIT record before a change.  Roll-forward is done in a
58  * similar fashion.
59  *
60  * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
61  * record for a line different from the current one.  It should be noted that
62  * this means that a subsequent 'u' command will make a change based on the
63  * new position of the log's cursor.  This is okay, and, in fact, historic vi
64  * behaved that way.
65  */
66 
67 static int	log_cursor1 __P((SCR *, int));
68 
69 /*
70  * log_init --
71  *	Initialize the logging subsystem.
72  *
73  * PUBLIC: int log_init __P((SCR *, EXF *));
74  */
75 int
76 log_init(SCR *sp, EXF *ep)
77 {
78 	DB_LOGC *logc;
79 	DBT data;
80 	size_t nlen;
81 
82 	/*
83 	 * !!!
84 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
85 	 *
86 	 * Initialize the buffer.  The logging subsystem has its own
87 	 * buffers because the global ones are almost by definition
88 	 * going to be in use when the log runs.
89 	 */
90 	sp->wp->l_lp = NULL;
91 	sp->wp->l_len = 0;
92 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
93 	ep->l_cursor.cno = 0;
94 	ep->l_high = ep->l_cur = 1;
95 
96 	if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0))
97 		    != 0) {
98 		msgq(sp, M_DBERR, "env->log_cursor");
99 		F_SET(ep, F_NOLOG);
100 		return (1);
101 	}
102 	nlen = 1024;
103 retry:
104 	BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen);
105 	memset(&data, 0, sizeof(data));
106 	data.data = sp->wp->l_lp;
107 	data.ulen = sp->wp->l_len;
108 	data.flags = DB_DBT_USERMEM;
109 	switch ((sp->db_error =
110 	    logc->get(logc, &ep->lsn_first, &data, DB_LAST))) {
111 	case ENOMEM:
112 		nlen = data.size;
113 		goto retry;
114 	default:
115 alloc_err:
116 		msgq(sp, M_DBERR, "logc->get");
117 		F_SET(ep, F_NOLOG);
118 		return (1);
119 	case 0:
120 		;
121 	}
122 	MEMCPY(&ep->lsn_cur, &ep->lsn_first, 1);
123 	MEMCPY(&ep->lsn_high, &ep->lsn_first, 1);
124 	logc->close(logc, 0);
125 
126 	ep->l_win = NULL;
127 	/*LOCK_INIT(sp->wp, ep);*/
128 
129 	return (0);
130 }
131 
132 /*
133  * log_end --
134  *	Close the logging subsystem.
135  *
136  * PUBLIC: int log_end __P((SCR *, EXF *));
137  */
138 int
139 log_end(SCR *sp, EXF *ep)
140 {
141 	/*
142 	 * !!!
143 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
144 	 */
145 	/*LOCK_END(sp->wp, ep);*/
146 	if (sp->wp->l_lp != NULL) {
147 		free(sp->wp->l_lp);
148 		sp->wp->l_lp = NULL;
149 	}
150 	sp->wp->l_len = 0;
151 	ep->l_cursor.lno = 1;		/* XXX Any valid recno. */
152 	ep->l_cursor.cno = 0;
153 	ep->l_high = ep->l_cur = 1;
154 	return (0);
155 }
156 
157 /*
158  * log_cursor --
159  *	Log the current cursor position, starting an event.
160  *
161  * PUBLIC: int log_cursor __P((SCR *));
162  */
163 int
164 log_cursor(SCR *sp)
165 {
166 	EXF *ep;
167 
168 	ep = sp->ep;
169 	if (F_ISSET(ep, F_NOLOG))
170 		return (0);
171 
172 	/*
173 	 * If any changes were made since the last cursor init,
174 	 * put out the ending cursor record.
175 	 */
176 	if (ep->l_cursor.lno == OOBLNO) {
177 		if (ep->l_win && ep->l_win != sp->wp)
178 			return 0;
179 		ep->l_cursor.lno = sp->lno;
180 		ep->l_cursor.cno = sp->cno;
181 		ep->l_win = NULL;
182 		return (log_cursor1(sp, LOG_CURSOR_END));
183 	}
184 	ep->l_cursor.lno = sp->lno;
185 	ep->l_cursor.cno = sp->cno;
186 	return (0);
187 }
188 
189 /*
190  * log_cursor1 --
191  *	Actually push a cursor record out.
192  */
193 static int
194 log_cursor1(SCR *sp, int type)
195 {
196 	DBT data, key;
197 	EXF *ep;
198 
199 	ep = sp->ep;
200 
201 	/*
202 	if (type == LOG_CURSOR_INIT &&
203 	    LOCK_TRY(sp->wp, ep))
204 		return 1;
205 	*/
206 
207 	if (type == LOG_CURSOR_INIT &&
208 	    (sp->db_error = __vi_log_truncate(ep)) != 0) {
209 		msgq(sp, M_DBERR, "truncate");
210 		return 1;
211 	}
212 	if ((sp->db_error =
213 		__vi_cursor_log(ep->env, NULL, &ep->lsn_cur, 0, type,
214 			    ep->l_cursor.lno, ep->l_cursor.cno)) != 0) {
215 		msgq(sp, M_DBERR, "cursor_log");
216 		return 1;
217 	}
218 	if (type == LOG_CURSOR_END) {
219 		MEMCPY(&ep->lsn_high, &ep->lsn_cur, 1);
220 		/* XXXX should not be needed */
221 		ep->env->log_flush(ep->env, NULL);
222 	}
223 
224 #if defined(DEBUG) && 0
225 	vtrace(sp, "%lu: %s: %u/%u\n", ep->l_cur,
226 	    type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
227 	    sp->lno, sp->cno);
228 #endif
229 	/* Reset high water mark. */
230 	ep->l_high = ++ep->l_cur;
231 
232 	/*
233 	if (type == LOG_CURSOR_END)
234 		LOCK_UNLOCK(sp->wp, ep);
235 	*/
236 	return (0);
237 }
238 
239 /*
240  * log_line --
241  *	Log a line change.
242  *
243  * PUBLIC: int log_line __P((SCR *, db_recno_t, u_int));
244  */
245 int
246 log_line(SCR *sp, db_recno_t lno, u_int action)
247 {
248 	DBT data, key;
249 	EXF *ep;
250 	size_t len;
251 	CHAR_T *lp;
252 	db_recno_t lcur;
253 
254 	ep = sp->ep;
255 	if (F_ISSET(ep, F_NOLOG))
256 		return (0);
257 
258 	/*
259 	 * XXX
260 	 *
261 	 * Kluge for vi.  Clear the EXF undo flag so that the
262 	 * next 'u' command does a roll-back, regardless.
263 	 */
264 	F_CLR(ep, F_UNDO);
265 
266 	/* Put out one initial cursor record per set of changes. */
267 	if (ep->l_cursor.lno != OOBLNO) {
268 		if (log_cursor1(sp, LOG_CURSOR_INIT))
269 			return (1);
270 		ep->l_cursor.lno = OOBLNO;
271 		ep->l_win = sp->wp;
272 	} /*else if (ep->l_win != sp->wp) {
273 		printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp);
274 		return 1;
275 	}*/
276 
277 	if ((sp->db_error =
278 		__vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action,
279 			    lno)) != 0) {
280 		msgq(sp, M_DBERR, "change_log");
281 		return 1;
282 	}
283 
284 #if defined(DEBUG) && 0
285 	switch (action) {
286 	case LOG_LINE_APPEND_F:
287 		vtrace(sp, "%u: log_line: append_f: %lu {%u}\n",
288 		    ep->l_cur, lno, len);
289 		break;
290 	case LOG_LINE_APPEND_B:
291 		vtrace(sp, "%u: log_line: append_b: %lu {%u}\n",
292 		    ep->l_cur, lno, len);
293 		break;
294 	case LOG_LINE_DELETE_F:
295 		vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n",
296 		    ep->l_cur, lno, len);
297 		break;
298 	case LOG_LINE_DELETE_B:
299 		vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n",
300 		    ep->l_cur, lno, len);
301 		break;
302 	case LOG_LINE_RESET_F:
303 		vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n",
304 		    ep->l_cur, lno, len);
305 		break;
306 	case LOG_LINE_RESET_B:
307 		vtrace(sp, "%lu: log_line: reset_b: %lu {%u}\n",
308 		    ep->l_cur, lno, len);
309 		break;
310 	}
311 #endif
312 	/* Reset high water mark. */
313 	ep->l_high = ++ep->l_cur;
314 
315 	return (0);
316 }
317 
318 /*
319  * log_mark --
320  *	Log a mark position.  For the log to work, we assume that there
321  *	aren't any operations that just put out a log record -- this
322  *	would mean that undo operations would only reset marks, and not
323  *	cause any other change.
324  *
325  * PUBLIC: int log_mark __P((SCR *, LMARK *));
326  */
327 int
328 log_mark(SCR *sp, LMARK *lmp)
329 {
330 	DBT data, key;
331 	EXF *ep;
332 
333 	ep = sp->ep;
334 	if (F_ISSET(ep, F_NOLOG))
335 		return (0);
336 
337 	/* Put out one initial cursor record per set of changes. */
338 	if (ep->l_cursor.lno != OOBLNO) {
339 		if (log_cursor1(sp, LOG_CURSOR_INIT))
340 			return (1);
341 		ep->l_cursor.lno = OOBLNO;
342 		ep->l_win = sp->wp;
343 	}
344 
345 	if ((sp->db_error =
346 		__vi_mark_log(ep->env, NULL, &ep->lsn_cur, 0,
347 			    lmp)) != 0) {
348 		msgq(sp, M_DBERR, "cursor_log");
349 		return 1;
350 	}
351 
352 #if defined(DEBUG) && 0
353 	vtrace(sp, "%lu: mark %c: %lu/%u\n",
354 	    ep->l_cur, lmp->name, lmp->lno, lmp->cno);
355 #endif
356 	/* Reset high water mark. */
357 	ep->l_high = ++ep->l_cur;
358 	return (0);
359 }
360 
361 /*
362  * Log_backward --
363  *	Roll the log backward one operation.
364  *
365  * PUBLIC: int log_backward __P((SCR *, MARK *));
366  */
367 int
368 log_backward(SCR *sp, MARK *rp)
369 {
370 	EXF *ep;
371 	LMARK lm;
372 	MARK m;
373 	db_recno_t lno;
374 	int didop;
375 	u_char *p;
376 	size_t size;
377 
378 	ep = sp->ep;
379 	if (F_ISSET(ep, F_NOLOG)) {
380 		msgq(sp, M_ERR,
381 		    "010|Logging not being performed, undo not possible");
382 		return (1);
383 	}
384 
385 	if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
386 		msgq(sp, M_BERR, "011|No changes to undo");
387 		return (1);
388 	}
389 	return __vi_log_traverse(sp, UNDO_BACKWARD, rp);
390 }
391 
392 /*
393  * Log_setline --
394  *	Reset the line to its original appearance.
395  *
396  * XXX
397  * There's a bug in this code due to our not logging cursor movements
398  * unless a change was made.  If you do a change, move off the line,
399  * then move back on and do a 'U', the line will be restored to the way
400  * it was before the original change.
401  *
402  * PUBLIC: int log_setline __P((SCR *));
403  */
404 int
405 log_setline(SCR *sp)
406 {
407 	EXF *ep;
408 	LMARK lm;
409 	MARK m;
410 	db_recno_t lno;
411 	u_char *p;
412 	size_t size;
413 
414 	ep = sp->ep;
415 	if (F_ISSET(ep, F_NOLOG)) {
416 		msgq(sp, M_ERR,
417 		    "012|Logging not being performed, undo not possible");
418 		return (1);
419 	}
420 
421 	if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) {
422 		msgq(sp, M_BERR, "011|No changes to undo");
423 		return (1);
424 	}
425 	return __vi_log_traverse(sp, UNDO_SETLINE, &m);
426 }
427 
428 /*
429  * Log_forward --
430  *	Roll the log forward one operation.
431  *
432  * PUBLIC: int log_forward __P((SCR *, MARK *));
433  */
434 int
435 log_forward(SCR *sp, MARK *rp)
436 {
437 	EXF *ep;
438 	LMARK lm;
439 	MARK m;
440 	db_recno_t lno;
441 	int didop;
442 	u_char *p;
443 	size_t size;
444 
445 	ep = sp->ep;
446 	if (F_ISSET(ep, F_NOLOG)) {
447 		msgq(sp, M_ERR,
448 	    "013|Logging not being performed, roll-forward not possible");
449 		return (1);
450 	}
451 
452 	if (log_compare(&ep->lsn_cur, &ep->lsn_high) >= 0) {
453 		msgq(sp, M_BERR, "014|No changes to re-do");
454 		return (1);
455 	}
456 	return __vi_log_traverse(sp, UNDO_FORWARD, rp);
457 }
458