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