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