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