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