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 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