119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: log.c,v 10.27 2011/07/13 06:25:50 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/stat.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <errno.h> 2219304Speter#include <fcntl.h> 2319304Speter#include <limits.h> 24254225Speter#include <stdint.h> 2519304Speter#include <stdio.h> 2619304Speter#include <stdlib.h> 2719304Speter#include <string.h> 2819304Speter 2919304Speter#include "common.h" 3019304Speter 3119304Speter/* 3219304Speter * The log consists of records, each containing a type byte and a variable 3319304Speter * length byte string, as follows: 3419304Speter * 3519304Speter * LOG_CURSOR_INIT MARK 3619304Speter * LOG_CURSOR_END MARK 3719304Speter * LOG_LINE_APPEND recno_t char * 3819304Speter * LOG_LINE_DELETE recno_t char * 3919304Speter * LOG_LINE_INSERT recno_t char * 4019304Speter * LOG_LINE_RESET_F recno_t char * 4119304Speter * LOG_LINE_RESET_B recno_t char * 4219304Speter * LOG_MARK LMARK 4319304Speter * 4419304Speter * We do before image physical logging. This means that the editor layer 4519304Speter * MAY NOT modify records in place, even if simply deleting or overwriting 4619304Speter * characters. Since the smallest unit of logging is a line, we're using 4719304Speter * up lots of space. This may eventually have to be reduced, probably by 4819304Speter * doing logical logging, which is a much cooler database phrase. 4919304Speter * 5019304Speter * The implementation of the historic vi 'u' command, using roll-forward and 5119304Speter * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, 5219304Speter * followed by a number of other records, followed by a LOG_CURSOR_END record. 5319304Speter * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B 5419304Speter * record, and is the line before the change. The second is LOG_LINE_RESET_F, 5519304Speter * and is the line after the change. Roll-back is done by backing up to the 5619304Speter * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a 5719304Speter * similar fashion. 5819304Speter * 5919304Speter * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END 6019304Speter * record for a line different from the current one. It should be noted that 6119304Speter * this means that a subsequent 'u' command will make a change based on the 6219304Speter * new position of the log's cursor. This is okay, and, in fact, historic vi 6319304Speter * behaved that way. 6419304Speter */ 6519304Speter 6619304Speterstatic int log_cursor1 __P((SCR *, int)); 6719304Speterstatic void log_err __P((SCR *, char *, int)); 6819304Speter#if defined(DEBUG) && 0 6919304Speterstatic void log_trace __P((SCR *, char *, recno_t, u_char *)); 7019304Speter#endif 71254225Speterstatic int apply_with __P((int (*)(SCR *, recno_t, CHAR_T *, size_t), 72254225Speter SCR *, recno_t, u_char *, size_t)); 7319304Speter 7419304Speter/* Try and restart the log on failure, i.e. if we run out of memory. */ 7519304Speter#define LOG_ERR { \ 7619304Speter log_err(sp, __FILE__, __LINE__); \ 7719304Speter return (1); \ 7819304Speter} 7919304Speter 80254225Speter/* offset of CHAR_T string in log needs to be aligned on some systems 81254225Speter * because it is passed to db_set as a string 82254225Speter */ 83254225Spetertypedef struct { 84254225Speter char data[sizeof(u_char) /* type */ + sizeof(recno_t)]; 85254225Speter CHAR_T str[1]; 86254225Speter} log_t; 87254225Speter#define CHAR_T_OFFSET ((char *)(((log_t*)0)->str) - (char *)0) 88254225Speter 8919304Speter/* 9019304Speter * log_init -- 9119304Speter * Initialize the logging subsystem. 9219304Speter * 9319304Speter * PUBLIC: int log_init __P((SCR *, EXF *)); 9419304Speter */ 9519304Speterint 96254225Speterlog_init( 97254225Speter SCR *sp, 98254225Speter EXF *ep) 9919304Speter{ 10019304Speter /* 10119304Speter * !!! 10219304Speter * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 10319304Speter * 10419304Speter * Initialize the buffer. The logging subsystem has its own 10519304Speter * buffers because the global ones are almost by definition 10619304Speter * going to be in use when the log runs. 10719304Speter */ 10819304Speter ep->l_lp = NULL; 10919304Speter ep->l_len = 0; 11019304Speter ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 11119304Speter ep->l_cursor.cno = 0; 11219304Speter ep->l_high = ep->l_cur = 1; 11319304Speter 11419304Speter ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR, 11519304Speter S_IRUSR | S_IWUSR, DB_RECNO, NULL); 11619304Speter if (ep->log == NULL) { 11719304Speter msgq(sp, M_SYSERR, "009|Log file"); 11819304Speter F_SET(ep, F_NOLOG); 11919304Speter return (1); 12019304Speter } 12119304Speter 12219304Speter return (0); 12319304Speter} 12419304Speter 12519304Speter/* 12619304Speter * log_end -- 12719304Speter * Close the logging subsystem. 12819304Speter * 12919304Speter * PUBLIC: int log_end __P((SCR *, EXF *)); 13019304Speter */ 13119304Speterint 132254225Speterlog_end( 133254225Speter SCR *sp, 134254225Speter EXF *ep) 13519304Speter{ 13619304Speter /* 13719304Speter * !!! 13819304Speter * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 13919304Speter */ 14019304Speter if (ep->log != NULL) { 14119304Speter (void)(ep->log->close)(ep->log); 14219304Speter ep->log = NULL; 14319304Speter } 14419304Speter if (ep->l_lp != NULL) { 14519304Speter free(ep->l_lp); 14619304Speter ep->l_lp = NULL; 14719304Speter } 14819304Speter ep->l_len = 0; 14919304Speter ep->l_cursor.lno = 1; /* XXX Any valid recno. */ 15019304Speter ep->l_cursor.cno = 0; 15119304Speter ep->l_high = ep->l_cur = 1; 15219304Speter return (0); 15319304Speter} 15419304Speter 15519304Speter/* 15619304Speter * log_cursor -- 15719304Speter * Log the current cursor position, starting an event. 15819304Speter * 15919304Speter * PUBLIC: int log_cursor __P((SCR *)); 16019304Speter */ 16119304Speterint 162254225Speterlog_cursor(SCR *sp) 16319304Speter{ 16419304Speter EXF *ep; 16519304Speter 16619304Speter ep = sp->ep; 16719304Speter if (F_ISSET(ep, F_NOLOG)) 16819304Speter return (0); 16919304Speter 17019304Speter /* 17119304Speter * If any changes were made since the last cursor init, 17219304Speter * put out the ending cursor record. 17319304Speter */ 17419304Speter if (ep->l_cursor.lno == OOBLNO) { 17519304Speter ep->l_cursor.lno = sp->lno; 17619304Speter ep->l_cursor.cno = sp->cno; 17719304Speter return (log_cursor1(sp, LOG_CURSOR_END)); 17819304Speter } 17919304Speter ep->l_cursor.lno = sp->lno; 18019304Speter ep->l_cursor.cno = sp->cno; 18119304Speter return (0); 18219304Speter} 18319304Speter 18419304Speter/* 18519304Speter * log_cursor1 -- 18619304Speter * Actually push a cursor record out. 18719304Speter */ 18819304Speterstatic int 189254225Speterlog_cursor1( 190254225Speter SCR *sp, 191254225Speter int type) 19219304Speter{ 19319304Speter DBT data, key; 19419304Speter EXF *ep; 19519304Speter 19619304Speter ep = sp->ep; 197254225Speter 198254225Speter BINC_RETC(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); 19919304Speter ep->l_lp[0] = type; 20019304Speter memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); 20119304Speter 20219304Speter key.data = &ep->l_cur; 20319304Speter key.size = sizeof(recno_t); 20419304Speter data.data = ep->l_lp; 20519304Speter data.size = sizeof(u_char) + sizeof(MARK); 20619304Speter if (ep->log->put(ep->log, &key, &data, 0) == -1) 20719304Speter LOG_ERR; 20819304Speter 20919304Speter#if defined(DEBUG) && 0 21019304Speter TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, 21119304Speter type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", 21219304Speter sp->lno, sp->cno); 21319304Speter#endif 21419304Speter /* Reset high water mark. */ 21519304Speter ep->l_high = ++ep->l_cur; 21619304Speter 21719304Speter return (0); 21819304Speter} 21919304Speter 22019304Speter/* 22119304Speter * log_line -- 22219304Speter * Log a line change. 22319304Speter * 22419304Speter * PUBLIC: int log_line __P((SCR *, recno_t, u_int)); 22519304Speter */ 22619304Speterint 227254225Speterlog_line( 228254225Speter SCR *sp, 229254225Speter recno_t lno, 230254225Speter u_int action) 23119304Speter{ 23219304Speter DBT data, key; 23319304Speter EXF *ep; 23419304Speter size_t len; 235254225Speter CHAR_T *lp; 236254225Speter recno_t lcur; 23719304Speter 23819304Speter ep = sp->ep; 23919304Speter if (F_ISSET(ep, F_NOLOG)) 24019304Speter return (0); 24119304Speter 24219304Speter /* 24319304Speter * XXX 24419304Speter * 24519304Speter * Kluge for vi. Clear the EXF undo flag so that the 24619304Speter * next 'u' command does a roll-back, regardless. 24719304Speter */ 24819304Speter F_CLR(ep, F_UNDO); 24919304Speter 25019304Speter /* Put out one initial cursor record per set of changes. */ 25119304Speter if (ep->l_cursor.lno != OOBLNO) { 25219304Speter if (log_cursor1(sp, LOG_CURSOR_INIT)) 25319304Speter return (1); 25419304Speter ep->l_cursor.lno = OOBLNO; 25519304Speter } 25619304Speter 25719304Speter /* 25819304Speter * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a 25919304Speter * special case, avoid the caches. Also, if it fails and it's 26019304Speter * line 1, it just means that the user started with an empty file, 26119304Speter * so fake an empty length line. 26219304Speter */ 26319304Speter if (action == LOG_LINE_RESET_B) { 26419304Speter if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) { 26519304Speter if (lno != 1) { 26619304Speter db_err(sp, lno); 26719304Speter return (1); 26819304Speter } 26919304Speter len = 0; 270254225Speter lp = L(""); 27119304Speter } 27219304Speter } else 27319304Speter if (db_get(sp, lno, DBG_FATAL, &lp, &len)) 27419304Speter return (1); 275254225Speter BINC_RETC(sp, 276254225Speter ep->l_lp, ep->l_len, 277254225Speter len * sizeof(CHAR_T) + CHAR_T_OFFSET); 27819304Speter ep->l_lp[0] = action; 27919304Speter memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); 280254225Speter memmove(ep->l_lp + CHAR_T_OFFSET, lp, len * sizeof(CHAR_T)); 28119304Speter 282254225Speter lcur = ep->l_cur; 283254225Speter key.data = &lcur; 28419304Speter key.size = sizeof(recno_t); 28519304Speter data.data = ep->l_lp; 286254225Speter data.size = len * sizeof(CHAR_T) + CHAR_T_OFFSET; 28719304Speter if (ep->log->put(ep->log, &key, &data, 0) == -1) 28819304Speter LOG_ERR; 28919304Speter 29019304Speter#if defined(DEBUG) && 0 29119304Speter switch (action) { 29219304Speter case LOG_LINE_APPEND: 293254225Speter TRACE(sp, "%lu: log_line: append: %lu {%u}\n", 29419304Speter ep->l_cur, lno, len); 29519304Speter break; 29619304Speter case LOG_LINE_DELETE: 29719304Speter TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", 29819304Speter ep->l_cur, lno, len); 29919304Speter break; 30019304Speter case LOG_LINE_INSERT: 30119304Speter TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", 30219304Speter ep->l_cur, lno, len); 30319304Speter break; 30419304Speter case LOG_LINE_RESET_F: 30519304Speter TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", 30619304Speter ep->l_cur, lno, len); 30719304Speter break; 30819304Speter case LOG_LINE_RESET_B: 30919304Speter TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", 31019304Speter ep->l_cur, lno, len); 31119304Speter break; 31219304Speter } 31319304Speter#endif 31419304Speter /* Reset high water mark. */ 31519304Speter ep->l_high = ++ep->l_cur; 31619304Speter 31719304Speter return (0); 31819304Speter} 31919304Speter 32019304Speter/* 32119304Speter * log_mark -- 32219304Speter * Log a mark position. For the log to work, we assume that there 32319304Speter * aren't any operations that just put out a log record -- this 32419304Speter * would mean that undo operations would only reset marks, and not 32519304Speter * cause any other change. 32619304Speter * 32719304Speter * PUBLIC: int log_mark __P((SCR *, LMARK *)); 32819304Speter */ 32919304Speterint 330254225Speterlog_mark( 331254225Speter SCR *sp, 332254225Speter LMARK *lmp) 33319304Speter{ 33419304Speter DBT data, key; 33519304Speter EXF *ep; 33619304Speter 33719304Speter ep = sp->ep; 33819304Speter if (F_ISSET(ep, F_NOLOG)) 33919304Speter return (0); 34019304Speter 34119304Speter /* Put out one initial cursor record per set of changes. */ 34219304Speter if (ep->l_cursor.lno != OOBLNO) { 34319304Speter if (log_cursor1(sp, LOG_CURSOR_INIT)) 34419304Speter return (1); 34519304Speter ep->l_cursor.lno = OOBLNO; 34619304Speter } 34719304Speter 348254225Speter BINC_RETC(sp, ep->l_lp, 34919304Speter ep->l_len, sizeof(u_char) + sizeof(LMARK)); 35019304Speter ep->l_lp[0] = LOG_MARK; 35119304Speter memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK)); 35219304Speter 35319304Speter key.data = &ep->l_cur; 35419304Speter key.size = sizeof(recno_t); 35519304Speter data.data = ep->l_lp; 35619304Speter data.size = sizeof(u_char) + sizeof(LMARK); 35719304Speter if (ep->log->put(ep->log, &key, &data, 0) == -1) 35819304Speter LOG_ERR; 35919304Speter 36019304Speter#if defined(DEBUG) && 0 36119304Speter TRACE(sp, "%lu: mark %c: %lu/%u\n", 36219304Speter ep->l_cur, lmp->name, lmp->lno, lmp->cno); 36319304Speter#endif 36419304Speter /* Reset high water mark. */ 36519304Speter ep->l_high = ++ep->l_cur; 36619304Speter return (0); 36719304Speter} 36819304Speter 36919304Speter/* 37019304Speter * Log_backward -- 37119304Speter * Roll the log backward one operation. 37219304Speter * 37319304Speter * PUBLIC: int log_backward __P((SCR *, MARK *)); 37419304Speter */ 37519304Speterint 376254225Speterlog_backward( 377254225Speter SCR *sp, 378254225Speter MARK *rp) 37919304Speter{ 38019304Speter DBT key, data; 38119304Speter EXF *ep; 38219304Speter LMARK lm; 38319304Speter MARK m; 38419304Speter recno_t lno; 38519304Speter int didop; 38619304Speter u_char *p; 38719304Speter 38819304Speter ep = sp->ep; 38919304Speter if (F_ISSET(ep, F_NOLOG)) { 39019304Speter msgq(sp, M_ERR, 39119304Speter "010|Logging not being performed, undo not possible"); 39219304Speter return (1); 39319304Speter } 39419304Speter 39519304Speter if (ep->l_cur == 1) { 39619304Speter msgq(sp, M_BERR, "011|No changes to undo"); 39719304Speter return (1); 39819304Speter } 39919304Speter 40019304Speter F_SET(ep, F_NOLOG); /* Turn off logging. */ 40119304Speter 40219304Speter key.data = &ep->l_cur; /* Initialize db request. */ 40319304Speter key.size = sizeof(recno_t); 40419304Speter for (didop = 0;;) { 40519304Speter --ep->l_cur; 40619304Speter if (ep->log->get(ep->log, &key, &data, 0)) 40719304Speter LOG_ERR; 40819304Speter#if defined(DEBUG) && 0 40919304Speter log_trace(sp, "log_backward", ep->l_cur, data.data); 41019304Speter#endif 41119304Speter switch (*(p = (u_char *)data.data)) { 41219304Speter case LOG_CURSOR_INIT: 41319304Speter if (didop) { 41419304Speter memmove(rp, p + sizeof(u_char), sizeof(MARK)); 41519304Speter F_CLR(ep, F_NOLOG); 41619304Speter return (0); 41719304Speter } 41819304Speter break; 41919304Speter case LOG_CURSOR_END: 42019304Speter break; 42119304Speter case LOG_LINE_APPEND: 42219304Speter case LOG_LINE_INSERT: 42319304Speter didop = 1; 42419304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 42519304Speter if (db_delete(sp, lno)) 42619304Speter goto err; 42719304Speter ++sp->rptlines[L_DELETED]; 42819304Speter break; 42919304Speter case LOG_LINE_DELETE: 43019304Speter didop = 1; 43119304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 432254225Speter if (apply_with(db_insert, sp, lno, 433254225Speter p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET)) 43419304Speter goto err; 43519304Speter ++sp->rptlines[L_ADDED]; 43619304Speter break; 43719304Speter case LOG_LINE_RESET_F: 43819304Speter break; 43919304Speter case LOG_LINE_RESET_B: 44019304Speter didop = 1; 44119304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 442254225Speter if (apply_with(db_set, sp, lno, 443254225Speter p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET)) 44419304Speter goto err; 44519304Speter if (sp->rptlchange != lno) { 44619304Speter sp->rptlchange = lno; 44719304Speter ++sp->rptlines[L_CHANGED]; 44819304Speter } 44919304Speter break; 45019304Speter case LOG_MARK: 45119304Speter didop = 1; 45219304Speter memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 45319304Speter m.lno = lm.lno; 45419304Speter m.cno = lm.cno; 45519304Speter if (mark_set(sp, lm.name, &m, 0)) 45619304Speter goto err; 45719304Speter break; 45819304Speter default: 45919304Speter abort(); 46019304Speter } 46119304Speter } 46219304Speter 46319304Spetererr: F_CLR(ep, F_NOLOG); 46419304Speter return (1); 46519304Speter} 46619304Speter 46719304Speter/* 46819304Speter * Log_setline -- 46919304Speter * Reset the line to its original appearance. 47019304Speter * 47119304Speter * XXX 47219304Speter * There's a bug in this code due to our not logging cursor movements 47319304Speter * unless a change was made. If you do a change, move off the line, 47419304Speter * then move back on and do a 'U', the line will be restored to the way 47519304Speter * it was before the original change. 47619304Speter * 47719304Speter * PUBLIC: int log_setline __P((SCR *)); 47819304Speter */ 47919304Speterint 480254225Speterlog_setline(SCR *sp) 48119304Speter{ 48219304Speter DBT key, data; 48319304Speter EXF *ep; 48419304Speter LMARK lm; 48519304Speter MARK m; 48619304Speter recno_t lno; 48719304Speter u_char *p; 48819304Speter 48919304Speter ep = sp->ep; 49019304Speter if (F_ISSET(ep, F_NOLOG)) { 49119304Speter msgq(sp, M_ERR, 49219304Speter "012|Logging not being performed, undo not possible"); 49319304Speter return (1); 49419304Speter } 49519304Speter 49619304Speter if (ep->l_cur == 1) 49719304Speter return (1); 49819304Speter 49919304Speter F_SET(ep, F_NOLOG); /* Turn off logging. */ 50019304Speter 50119304Speter key.data = &ep->l_cur; /* Initialize db request. */ 50219304Speter key.size = sizeof(recno_t); 50319304Speter for (;;) { 50419304Speter --ep->l_cur; 50519304Speter if (ep->log->get(ep->log, &key, &data, 0)) 50619304Speter LOG_ERR; 50719304Speter#if defined(DEBUG) && 0 50819304Speter log_trace(sp, "log_setline", ep->l_cur, data.data); 50919304Speter#endif 51019304Speter switch (*(p = (u_char *)data.data)) { 51119304Speter case LOG_CURSOR_INIT: 51219304Speter memmove(&m, p + sizeof(u_char), sizeof(MARK)); 51319304Speter if (m.lno != sp->lno || ep->l_cur == 1) { 51419304Speter F_CLR(ep, F_NOLOG); 51519304Speter return (0); 51619304Speter } 51719304Speter break; 51819304Speter case LOG_CURSOR_END: 51919304Speter memmove(&m, p + sizeof(u_char), sizeof(MARK)); 52019304Speter if (m.lno != sp->lno) { 52119304Speter ++ep->l_cur; 52219304Speter F_CLR(ep, F_NOLOG); 52319304Speter return (0); 52419304Speter } 52519304Speter break; 52619304Speter case LOG_LINE_APPEND: 52719304Speter case LOG_LINE_INSERT: 52819304Speter case LOG_LINE_DELETE: 52919304Speter case LOG_LINE_RESET_F: 53019304Speter break; 53119304Speter case LOG_LINE_RESET_B: 53219304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 53319304Speter if (lno == sp->lno && 534254225Speter apply_with(db_set, sp, lno, 535254225Speter p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET)) 53619304Speter goto err; 53719304Speter if (sp->rptlchange != lno) { 53819304Speter sp->rptlchange = lno; 53919304Speter ++sp->rptlines[L_CHANGED]; 54019304Speter } 54119304Speter case LOG_MARK: 54219304Speter memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 54319304Speter m.lno = lm.lno; 54419304Speter m.cno = lm.cno; 54519304Speter if (mark_set(sp, lm.name, &m, 0)) 54619304Speter goto err; 54719304Speter break; 54819304Speter default: 54919304Speter abort(); 55019304Speter } 55119304Speter } 55219304Speter 55319304Spetererr: F_CLR(ep, F_NOLOG); 55419304Speter return (1); 55519304Speter} 55619304Speter 55719304Speter/* 55819304Speter * Log_forward -- 55919304Speter * Roll the log forward one operation. 56019304Speter * 56119304Speter * PUBLIC: int log_forward __P((SCR *, MARK *)); 56219304Speter */ 56319304Speterint 564254225Speterlog_forward( 565254225Speter SCR *sp, 566254225Speter MARK *rp) 56719304Speter{ 56819304Speter DBT key, data; 56919304Speter EXF *ep; 57019304Speter LMARK lm; 57119304Speter MARK m; 57219304Speter recno_t lno; 57319304Speter int didop; 57419304Speter u_char *p; 57519304Speter 57619304Speter ep = sp->ep; 57719304Speter if (F_ISSET(ep, F_NOLOG)) { 57819304Speter msgq(sp, M_ERR, 57919304Speter "013|Logging not being performed, roll-forward not possible"); 58019304Speter return (1); 58119304Speter } 58219304Speter 58319304Speter if (ep->l_cur == ep->l_high) { 58419304Speter msgq(sp, M_BERR, "014|No changes to re-do"); 58519304Speter return (1); 58619304Speter } 58719304Speter 58819304Speter F_SET(ep, F_NOLOG); /* Turn off logging. */ 58919304Speter 59019304Speter key.data = &ep->l_cur; /* Initialize db request. */ 59119304Speter key.size = sizeof(recno_t); 59219304Speter for (didop = 0;;) { 59319304Speter ++ep->l_cur; 59419304Speter if (ep->log->get(ep->log, &key, &data, 0)) 59519304Speter LOG_ERR; 59619304Speter#if defined(DEBUG) && 0 59719304Speter log_trace(sp, "log_forward", ep->l_cur, data.data); 59819304Speter#endif 59919304Speter switch (*(p = (u_char *)data.data)) { 60019304Speter case LOG_CURSOR_END: 60119304Speter if (didop) { 60219304Speter ++ep->l_cur; 60319304Speter memmove(rp, p + sizeof(u_char), sizeof(MARK)); 60419304Speter F_CLR(ep, F_NOLOG); 60519304Speter return (0); 60619304Speter } 60719304Speter break; 60819304Speter case LOG_CURSOR_INIT: 60919304Speter break; 61019304Speter case LOG_LINE_APPEND: 61119304Speter case LOG_LINE_INSERT: 61219304Speter didop = 1; 61319304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 614254225Speter if (apply_with(db_insert, sp, lno, 615254225Speter p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET)) 61619304Speter goto err; 61719304Speter ++sp->rptlines[L_ADDED]; 61819304Speter break; 61919304Speter case LOG_LINE_DELETE: 62019304Speter didop = 1; 62119304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 62219304Speter if (db_delete(sp, lno)) 62319304Speter goto err; 62419304Speter ++sp->rptlines[L_DELETED]; 62519304Speter break; 62619304Speter case LOG_LINE_RESET_B: 62719304Speter break; 62819304Speter case LOG_LINE_RESET_F: 62919304Speter didop = 1; 63019304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 631254225Speter if (apply_with(db_set, sp, lno, 632254225Speter p + CHAR_T_OFFSET, data.size - CHAR_T_OFFSET)) 63319304Speter goto err; 63419304Speter if (sp->rptlchange != lno) { 63519304Speter sp->rptlchange = lno; 63619304Speter ++sp->rptlines[L_CHANGED]; 63719304Speter } 63819304Speter break; 63919304Speter case LOG_MARK: 64019304Speter didop = 1; 64119304Speter memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 64219304Speter m.lno = lm.lno; 64319304Speter m.cno = lm.cno; 64419304Speter if (mark_set(sp, lm.name, &m, 0)) 64519304Speter goto err; 64619304Speter break; 64719304Speter default: 64819304Speter abort(); 64919304Speter } 65019304Speter } 65119304Speter 65219304Spetererr: F_CLR(ep, F_NOLOG); 65319304Speter return (1); 65419304Speter} 65519304Speter 65619304Speter/* 65719304Speter * log_err -- 65819304Speter * Try and restart the log on failure, i.e. if we run out of memory. 65919304Speter */ 66019304Speterstatic void 661254225Speterlog_err( 662254225Speter SCR *sp, 663254225Speter char *file, 664254225Speter int line) 66519304Speter{ 66619304Speter EXF *ep; 66719304Speter 66819304Speter msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line); 66919304Speter ep = sp->ep; 67019304Speter (void)ep->log->close(ep->log); 67119304Speter if (!log_init(sp, ep)) 67219304Speter msgq(sp, M_ERR, "267|Log restarted"); 67319304Speter} 67419304Speter 67519304Speter#if defined(DEBUG) && 0 67619304Speterstatic void 677254225Speterlog_trace( 678254225Speter SCR *sp, 679254225Speter char *msg, 680254225Speter recno_t rno, 681254225Speter u_char *p) 68219304Speter{ 68319304Speter LMARK lm; 68419304Speter MARK m; 68519304Speter recno_t lno; 68619304Speter 68719304Speter switch (*p) { 68819304Speter case LOG_CURSOR_INIT: 68919304Speter memmove(&m, p + sizeof(u_char), sizeof(MARK)); 69019304Speter TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); 69119304Speter break; 69219304Speter case LOG_CURSOR_END: 69319304Speter memmove(&m, p + sizeof(u_char), sizeof(MARK)); 69419304Speter TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); 69519304Speter break; 69619304Speter case LOG_LINE_APPEND: 69719304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 69819304Speter TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); 69919304Speter break; 70019304Speter case LOG_LINE_INSERT: 70119304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 70219304Speter TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); 70319304Speter break; 70419304Speter case LOG_LINE_DELETE: 70519304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 70619304Speter TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); 70719304Speter break; 70819304Speter case LOG_LINE_RESET_F: 70919304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 71019304Speter TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); 71119304Speter break; 71219304Speter case LOG_LINE_RESET_B: 71319304Speter memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); 71419304Speter TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); 71519304Speter break; 71619304Speter case LOG_MARK: 71719304Speter memmove(&lm, p + sizeof(u_char), sizeof(LMARK)); 71819304Speter TRACE(sp, 71919304Speter "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno); 72019304Speter break; 72119304Speter default: 72219304Speter abort(); 72319304Speter } 72419304Speter} 72519304Speter#endif 726254225Speter 727254225Speter/* 728254225Speter * apply_with -- 729254225Speter * Apply a realigned line from the log db to the file db. 730254225Speter */ 731254225Speterstatic int 732254225Speterapply_with( 733254225Speter int (*db_func)(SCR *, recno_t, CHAR_T *, size_t), 734254225Speter SCR *sp, 735254225Speter recno_t lno, 736254225Speter u_char *p, 737254225Speter size_t len) 738254225Speter{ 739254225Speter#ifdef USE_WIDECHAR 740254225Speter typedef unsigned long nword; 741254225Speter 742254225Speter static size_t blen; 743254225Speter static nword *bp; 744254225Speter nword *lp = (nword *)((uintptr_t)p / sizeof(nword) * sizeof(nword)); 745254225Speter 746254225Speter if (lp != (nword *)p) { 747254225Speter int offl = ((uintptr_t)p - (uintptr_t)lp) << 3; 748254225Speter int offr = (sizeof(nword) << 3) - offl; 749254225Speter size_t i, cnt = (len + sizeof(nword) / 2) / sizeof(nword); 750254225Speter 751254225Speter if (len > blen) { 752254225Speter blen = p2roundup(MAX(len, 512)); 753254225Speter REALLOC(sp, bp, nword *, blen); 754254225Speter if (bp == NULL) 755254225Speter return (1); 756254225Speter } 757254225Speter for (i = 0; i < cnt; ++i) 758254225Speter#if BYTE_ORDER == BIG_ENDIAN 759254225Speter bp[i] = (lp[i] << offl) ^ (lp[i+1] >> offr); 760254225Speter#else 761254225Speter bp[i] = (lp[i] >> offl) ^ (lp[i+1] << offr); 762254225Speter#endif 763254225Speter p = (u_char *)bp; 764254225Speter } 765254225Speter#endif 766254225Speter return db_func(sp, lno, (CHAR_T *)p, len / sizeof(CHAR_T)); 767254225Speter} 768