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