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