1 /* $NetBSD: log4.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: log4.c,v 10.3 2002/06/08 21:00:33 skimo Exp "; 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 69 /* 70 * log_init -- 71 * Initialize the logging subsystem. 72 * 73 * PUBLIC: int log_init __P((SCR *, EXF *)); 74 */ 75 int 76 log_init(SCR *sp, EXF *ep) 77 { 78 DB_LOGC *logc; 79 DBT data; 80 size_t nlen; 81 82 /* 83 * !!! 84 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 85 * 86 * Initialize the buffer. The logging subsystem has its own 87 * buffers because the global ones are almost by definition 88 * going to be in use when the log runs. 89 */ 90 sp->wp->l_lp = NULL; 91 sp->wp->l_len = 0; 92 ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 93 ep->l_cursor.cno = 0; 94 ep->l_high = ep->l_cur = 1; 95 96 if ((sp->db_error = ep->env->log_cursor(ep->env, &logc, 0)) 97 != 0) { 98 msgq(sp, M_DBERR, "env->log_cursor"); 99 F_SET(ep, F_NOLOG); 100 return (1); 101 } 102 nlen = 1024; 103 retry: 104 BINC_GOTO(sp, sp->wp->l_lp, sp->wp->l_len, nlen); 105 memset(&data, 0, sizeof(data)); 106 data.data = sp->wp->l_lp; 107 data.ulen = sp->wp->l_len; 108 data.flags = DB_DBT_USERMEM; 109 switch ((sp->db_error = 110 logc->get(logc, &ep->lsn_first, &data, DB_LAST))) { 111 case ENOMEM: 112 nlen = data.size; 113 goto retry; 114 default: 115 alloc_err: 116 msgq(sp, M_DBERR, "logc->get"); 117 F_SET(ep, F_NOLOG); 118 return (1); 119 case 0: 120 ; 121 } 122 MEMCPY(&ep->lsn_cur, &ep->lsn_first, 1); 123 MEMCPY(&ep->lsn_high, &ep->lsn_first, 1); 124 logc->close(logc, 0); 125 126 ep->l_win = NULL; 127 /*LOCK_INIT(sp->wp, ep);*/ 128 129 return (0); 130 } 131 132 /* 133 * log_end -- 134 * Close the logging subsystem. 135 * 136 * PUBLIC: int log_end __P((SCR *, EXF *)); 137 */ 138 int 139 log_end(SCR *sp, EXF *ep) 140 { 141 /* 142 * !!! 143 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 144 */ 145 /*LOCK_END(sp->wp, ep);*/ 146 if (sp->wp->l_lp != NULL) { 147 free(sp->wp->l_lp); 148 sp->wp->l_lp = NULL; 149 } 150 sp->wp->l_len = 0; 151 ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 152 ep->l_cursor.cno = 0; 153 ep->l_high = ep->l_cur = 1; 154 return (0); 155 } 156 157 /* 158 * log_cursor -- 159 * Log the current cursor position, starting an event. 160 * 161 * PUBLIC: int log_cursor __P((SCR *)); 162 */ 163 int 164 log_cursor(SCR *sp) 165 { 166 EXF *ep; 167 168 ep = sp->ep; 169 if (F_ISSET(ep, F_NOLOG)) 170 return (0); 171 172 /* 173 * If any changes were made since the last cursor init, 174 * put out the ending cursor record. 175 */ 176 if (ep->l_cursor.lno == OOBLNO) { 177 if (ep->l_win && ep->l_win != sp->wp) 178 return 0; 179 ep->l_cursor.lno = sp->lno; 180 ep->l_cursor.cno = sp->cno; 181 ep->l_win = NULL; 182 return (log_cursor1(sp, LOG_CURSOR_END)); 183 } 184 ep->l_cursor.lno = sp->lno; 185 ep->l_cursor.cno = sp->cno; 186 return (0); 187 } 188 189 /* 190 * log_cursor1 -- 191 * Actually push a cursor record out. 192 */ 193 static int 194 log_cursor1(SCR *sp, int type) 195 { 196 DBT data, key; 197 EXF *ep; 198 199 ep = sp->ep; 200 201 /* 202 if (type == LOG_CURSOR_INIT && 203 LOCK_TRY(sp->wp, ep)) 204 return 1; 205 */ 206 207 if (type == LOG_CURSOR_INIT && 208 (sp->db_error = __vi_log_truncate(ep)) != 0) { 209 msgq(sp, M_DBERR, "truncate"); 210 return 1; 211 } 212 if ((sp->db_error = 213 __vi_cursor_log(ep->env, NULL, &ep->lsn_cur, 0, type, 214 ep->l_cursor.lno, ep->l_cursor.cno)) != 0) { 215 msgq(sp, M_DBERR, "cursor_log"); 216 return 1; 217 } 218 if (type == LOG_CURSOR_END) { 219 MEMCPY(&ep->lsn_high, &ep->lsn_cur, 1); 220 /* XXXX should not be needed */ 221 ep->env->log_flush(ep->env, NULL); 222 } 223 224 #if defined(DEBUG) && 0 225 vtrace(sp, "%lu: %s: %u/%u\n", ep->l_cur, 226 type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", 227 sp->lno, sp->cno); 228 #endif 229 /* Reset high water mark. */ 230 ep->l_high = ++ep->l_cur; 231 232 /* 233 if (type == LOG_CURSOR_END) 234 LOCK_UNLOCK(sp->wp, ep); 235 */ 236 return (0); 237 } 238 239 /* 240 * log_line -- 241 * Log a line change. 242 * 243 * PUBLIC: int log_line __P((SCR *, db_recno_t, u_int)); 244 */ 245 int 246 log_line(SCR *sp, db_recno_t lno, u_int action) 247 { 248 DBT data, key; 249 EXF *ep; 250 size_t len; 251 CHAR_T *lp; 252 db_recno_t lcur; 253 254 ep = sp->ep; 255 if (F_ISSET(ep, F_NOLOG)) 256 return (0); 257 258 /* 259 * XXX 260 * 261 * Kluge for vi. Clear the EXF undo flag so that the 262 * next 'u' command does a roll-back, regardless. 263 */ 264 F_CLR(ep, F_UNDO); 265 266 /* Put out one initial cursor record per set of changes. */ 267 if (ep->l_cursor.lno != OOBLNO) { 268 if (log_cursor1(sp, LOG_CURSOR_INIT)) 269 return (1); 270 ep->l_cursor.lno = OOBLNO; 271 ep->l_win = sp->wp; 272 } /*else if (ep->l_win != sp->wp) { 273 printf("log_line own: %p, this: %p\n", ep->l_win, sp->wp); 274 return 1; 275 }*/ 276 277 if ((sp->db_error = 278 __vi_change_log(ep->env, NULL, &ep->lsn_cur, 0, action, 279 lno)) != 0) { 280 msgq(sp, M_DBERR, "change_log"); 281 return 1; 282 } 283 284 #if defined(DEBUG) && 0 285 switch (action) { 286 case LOG_LINE_APPEND_F: 287 vtrace(sp, "%u: log_line: append_f: %lu {%u}\n", 288 ep->l_cur, lno, len); 289 break; 290 case LOG_LINE_APPEND_B: 291 vtrace(sp, "%u: log_line: append_b: %lu {%u}\n", 292 ep->l_cur, lno, len); 293 break; 294 case LOG_LINE_DELETE_F: 295 vtrace(sp, "%lu: log_line: delete_f: %lu {%u}\n", 296 ep->l_cur, lno, len); 297 break; 298 case LOG_LINE_DELETE_B: 299 vtrace(sp, "%lu: log_line: delete_b: %lu {%u}\n", 300 ep->l_cur, lno, len); 301 break; 302 case LOG_LINE_RESET_F: 303 vtrace(sp, "%lu: log_line: reset_f: %lu {%u}\n", 304 ep->l_cur, lno, len); 305 break; 306 case LOG_LINE_RESET_B: 307 vtrace(sp, "%lu: log_line: reset_b: %lu {%u}\n", 308 ep->l_cur, lno, len); 309 break; 310 } 311 #endif 312 /* Reset high water mark. */ 313 ep->l_high = ++ep->l_cur; 314 315 return (0); 316 } 317 318 /* 319 * log_mark -- 320 * Log a mark position. For the log to work, we assume that there 321 * aren't any operations that just put out a log record -- this 322 * would mean that undo operations would only reset marks, and not 323 * cause any other change. 324 * 325 * PUBLIC: int log_mark __P((SCR *, LMARK *)); 326 */ 327 int 328 log_mark(SCR *sp, LMARK *lmp) 329 { 330 DBT data, key; 331 EXF *ep; 332 333 ep = sp->ep; 334 if (F_ISSET(ep, F_NOLOG)) 335 return (0); 336 337 /* Put out one initial cursor record per set of changes. */ 338 if (ep->l_cursor.lno != OOBLNO) { 339 if (log_cursor1(sp, LOG_CURSOR_INIT)) 340 return (1); 341 ep->l_cursor.lno = OOBLNO; 342 ep->l_win = sp->wp; 343 } 344 345 if ((sp->db_error = 346 __vi_mark_log(ep->env, NULL, &ep->lsn_cur, 0, 347 lmp)) != 0) { 348 msgq(sp, M_DBERR, "cursor_log"); 349 return 1; 350 } 351 352 #if defined(DEBUG) && 0 353 vtrace(sp, "%lu: mark %c: %lu/%u\n", 354 ep->l_cur, lmp->name, lmp->lno, lmp->cno); 355 #endif 356 /* Reset high water mark. */ 357 ep->l_high = ++ep->l_cur; 358 return (0); 359 } 360 361 /* 362 * Log_backward -- 363 * Roll the log backward one operation. 364 * 365 * PUBLIC: int log_backward __P((SCR *, MARK *)); 366 */ 367 int 368 log_backward(SCR *sp, MARK *rp) 369 { 370 EXF *ep; 371 LMARK lm; 372 MARK m; 373 db_recno_t lno; 374 int didop; 375 u_char *p; 376 size_t size; 377 378 ep = sp->ep; 379 if (F_ISSET(ep, F_NOLOG)) { 380 msgq(sp, M_ERR, 381 "010|Logging not being performed, undo not possible"); 382 return (1); 383 } 384 385 if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) { 386 msgq(sp, M_BERR, "011|No changes to undo"); 387 return (1); 388 } 389 return __vi_log_traverse(sp, UNDO_BACKWARD, rp); 390 } 391 392 /* 393 * Log_setline -- 394 * Reset the line to its original appearance. 395 * 396 * XXX 397 * There's a bug in this code due to our not logging cursor movements 398 * unless a change was made. If you do a change, move off the line, 399 * then move back on and do a 'U', the line will be restored to the way 400 * it was before the original change. 401 * 402 * PUBLIC: int log_setline __P((SCR *)); 403 */ 404 int 405 log_setline(SCR *sp) 406 { 407 EXF *ep; 408 LMARK lm; 409 MARK m; 410 db_recno_t lno; 411 u_char *p; 412 size_t size; 413 414 ep = sp->ep; 415 if (F_ISSET(ep, F_NOLOG)) { 416 msgq(sp, M_ERR, 417 "012|Logging not being performed, undo not possible"); 418 return (1); 419 } 420 421 if (log_compare(&ep->lsn_cur, &ep->lsn_first) <= 0) { 422 msgq(sp, M_BERR, "011|No changes to undo"); 423 return (1); 424 } 425 return __vi_log_traverse(sp, UNDO_SETLINE, &m); 426 } 427 428 /* 429 * Log_forward -- 430 * Roll the log forward one operation. 431 * 432 * PUBLIC: int log_forward __P((SCR *, MARK *)); 433 */ 434 int 435 log_forward(SCR *sp, MARK *rp) 436 { 437 EXF *ep; 438 LMARK lm; 439 MARK m; 440 db_recno_t lno; 441 int didop; 442 u_char *p; 443 size_t size; 444 445 ep = sp->ep; 446 if (F_ISSET(ep, F_NOLOG)) { 447 msgq(sp, M_ERR, 448 "013|Logging not being performed, roll-forward not possible"); 449 return (1); 450 } 451 452 if (log_compare(&ep->lsn_cur, &ep->lsn_high) >= 0) { 453 msgq(sp, M_BERR, "014|No changes to re-do"); 454 return (1); 455 } 456 return __vi_log_traverse(sp, UNDO_FORWARD, rp); 457 } 458