Home | History | Annotate | Line # | Download | only in common
log4.c revision 1.3.4.2
      1 /*	$NetBSD: log4.c,v 1.3.4.2 2014/05/22 15:50:33 yamt 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.4.2 2014/05/22 15:50:33 yamt 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
     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
    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
    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
    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
    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
    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
    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
    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
    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