#include "config.h"
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <bitstring.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
static int log_cursor1(SCR *, int);
static void log_err(SCR *, char *, int);
#if defined(DEBUG) && 0
static void log_trace(SCR *, char *, recno_t, u_char *);
#endif
#define LOG_ERR { \
log_err(sp, __FILE__, __LINE__); \
return (1); \
}
int
log_init(SCR *sp, EXF *ep)
{
ep->l_lp = NULL;
ep->l_len = 0;
ep->l_cursor.lno = 1;
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
S_IRUSR | S_IWUSR, DB_RECNO, NULL);
if (ep->log == NULL) {
msgq(sp, M_SYSERR, "Log file");
F_SET(ep, F_NOLOG);
return (1);
}
return (0);
}
int
log_end(SCR *sp, EXF *ep)
{
if (ep->log != NULL) {
(void)(ep->log->close)(ep->log);
ep->log = NULL;
}
free(ep->l_lp);
ep->l_lp = NULL;
ep->l_len = 0;
ep->l_cursor.lno = 1;
ep->l_cursor.cno = 0;
ep->l_high = ep->l_cur = 1;
return (0);
}
int
log_cursor(SCR *sp)
{
EXF *ep;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG))
return (0);
if (ep->l_cursor.lno == OOBLNO) {
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
return (log_cursor1(sp, LOG_CURSOR_END));
}
ep->l_cursor.lno = sp->lno;
ep->l_cursor.cno = sp->cno;
return (0);
}
static int
log_cursor1(SCR *sp, int type)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
ep->l_lp[0] = type;
memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = sizeof(u_char) + sizeof(MARK);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
sp->lno, sp->cno);
#endif
ep->l_high = ++ep->l_cur;
return (0);
}
int
log_line(SCR *sp, recno_t lno, u_int action)
{
DBT data, key;
EXF *ep;
size_t len;
char *lp;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG))
return (0);
F_CLR(ep, F_UNDO);
if (ep->l_cursor.lno != OOBLNO) {
if (log_cursor1(sp, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
}
if (action == LOG_LINE_RESET_B) {
if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
if (lno != 1) {
db_err(sp, lno);
return (1);
}
len = 0;
lp = "";
}
} else
if (db_get(sp, lno, DBG_FATAL, &lp, &len))
return (1);
BINC_RET(sp,
ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
ep->l_lp[0] = action;
memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = len + sizeof(u_char) + sizeof(recno_t);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
switch (action) {
case LOG_LINE_APPEND:
TRACE(sp, "%u: log_line: append: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_DELETE:
TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_INSERT:
TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_F:
TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
ep->l_cur, lno, len);
break;
case LOG_LINE_RESET_B:
TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
ep->l_cur, lno, len);
break;
}
#endif
ep->l_high = ++ep->l_cur;
return (0);
}
int
log_mark(SCR *sp, LMARK *lmp)
{
DBT data, key;
EXF *ep;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG))
return (0);
if (ep->l_cursor.lno != OOBLNO) {
if (log_cursor1(sp, LOG_CURSOR_INIT))
return (1);
ep->l_cursor.lno = OOBLNO;
}
BINC_RET(sp, ep->l_lp,
ep->l_len, sizeof(u_char) + sizeof(LMARK));
ep->l_lp[0] = LOG_MARK;
memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
data.data = ep->l_lp;
data.size = sizeof(u_char) + sizeof(LMARK);
if (ep->log->put(ep->log, &key, &data, 0) == -1)
LOG_ERR;
#if defined(DEBUG) && 0
TRACE(sp, "%lu: mark %c: %lu/%u\n",
ep->l_cur, lmp->name, lmp->lno, lmp->cno);
#endif
ep->l_high = ++ep->l_cur;
return (0);
}
int
log_backward(SCR *sp, MARK *rp)
{
DBT key, data;
EXF *ep;
LMARK lm;
MARK m;
recno_t lno;
int didop;
u_char *p;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1) {
msgq(sp, M_BERR, "No changes to undo");
return (1);
}
F_SET(ep, F_NOLOG);
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
for (didop = 0;;) {
--ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_backward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_INIT:
if (didop) {
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_END:
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_delete(sp, lno))
goto err;
++sp->rptlines[L_DELETED];
break;
case LOG_LINE_DELETE:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_insert(sp, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
++sp->rptlines[L_ADDED];
break;
case LOG_LINE_RESET_F:
break;
case LOG_LINE_RESET_B:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_set(sp, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
break;
case LOG_MARK:
didop = 1;
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
int
log_setline(SCR *sp)
{
DBT key, data;
EXF *ep;
LMARK lm;
MARK m;
recno_t lno;
u_char *p;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, undo not possible");
return (1);
}
if (ep->l_cur == 1)
return (1);
F_SET(ep, F_NOLOG);
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
for (;;) {
--ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_setline", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_INIT:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
if (m.lno != sp->lno || ep->l_cur == 1) {
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_END:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
if (m.lno != sp->lno) {
++ep->l_cur;
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
case LOG_LINE_DELETE:
case LOG_LINE_RESET_F:
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (lno == sp->lno &&
db_set(sp, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
case LOG_MARK:
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
int
log_forward(SCR *sp, MARK *rp)
{
DBT key, data;
EXF *ep;
LMARK lm;
MARK m;
recno_t lno;
int didop;
u_char *p;
ep = sp->ep;
if (F_ISSET(ep, F_NOLOG)) {
msgq(sp, M_ERR,
"Logging not being performed, roll-forward not possible");
return (1);
}
if (ep->l_cur == ep->l_high) {
msgq(sp, M_BERR, "No changes to re-do");
return (1);
}
F_SET(ep, F_NOLOG);
key.data = &ep->l_cur;
key.size = sizeof(recno_t);
for (didop = 0;;) {
++ep->l_cur;
if (ep->log->get(ep->log, &key, &data, 0))
LOG_ERR;
#if defined(DEBUG) && 0
log_trace(sp, "log_forward", ep->l_cur, data.data);
#endif
switch (*(p = (u_char *)data.data)) {
case LOG_CURSOR_END:
if (didop) {
++ep->l_cur;
memmove(rp, p + sizeof(u_char), sizeof(MARK));
F_CLR(ep, F_NOLOG);
return (0);
}
break;
case LOG_CURSOR_INIT:
break;
case LOG_LINE_APPEND:
case LOG_LINE_INSERT:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_insert(sp, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
++sp->rptlines[L_ADDED];
break;
case LOG_LINE_DELETE:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_delete(sp, lno))
goto err;
++sp->rptlines[L_DELETED];
break;
case LOG_LINE_RESET_B:
break;
case LOG_LINE_RESET_F:
didop = 1;
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
if (db_set(sp, lno, p + sizeof(u_char) +
sizeof(recno_t), data.size - sizeof(u_char) -
sizeof(recno_t)))
goto err;
if (sp->rptlchange != lno) {
sp->rptlchange = lno;
++sp->rptlines[L_CHANGED];
}
break;
case LOG_MARK:
didop = 1;
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
m.lno = lm.lno;
m.cno = lm.cno;
if (mark_set(sp, lm.name, &m, 0))
goto err;
break;
default:
abort();
}
}
err: F_CLR(ep, F_NOLOG);
return (1);
}
static void
log_err(SCR *sp, char *file, int line)
{
EXF *ep;
msgq(sp, M_SYSERR, "%s/%d: log put error", basename(file), line);
ep = sp->ep;
(void)ep->log->close(ep->log);
if (!log_init(sp, ep))
msgq(sp, M_ERR, "Log restarted");
}
#if defined(DEBUG) && 0
static void
log_trace(SCR *sp, char *msg, recno_t rno, u_char *p)
{
LMARK lm;
MARK m;
recno_t lno;
switch (*p) {
case LOG_CURSOR_INIT:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
break;
case LOG_CURSOR_END:
memmove(&m, p + sizeof(u_char), sizeof(MARK));
TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
break;
case LOG_LINE_APPEND:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno);
break;
case LOG_LINE_INSERT:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno);
break;
case LOG_LINE_DELETE:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_F:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
break;
case LOG_LINE_RESET_B:
memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
break;
case LOG_MARK:
memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
TRACE(sp,
"%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
break;
default:
abort();
}
}
#endif