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