2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: log.c,v 8.9 1993/12/28 16:39:21 bostic Exp $ (Berkeley) $Date: 1993/12/28 16:39:21 $";
12 #include <sys/types.h>
23 * The log consists of records, each containing a type byte and a variable
24 * length byte string, as follows:
26 * LOG_CURSOR_INIT MARK
28 * LOG_LINE_APPEND recno_t char *
29 * LOG_LINE_DELETE recno_t char *
30 * LOG_LINE_INSERT recno_t char *
31 * LOG_LINE_RESET_F recno_t char *
32 * LOG_LINE_RESET_B recno_t char *
35 * We do before image physical logging. This means that the editor layer
36 * MAY NOT modify records in place, even if simply deleting or overwriting
37 * characters. Since the smallest unit of logging is a line, we're using
38 * up lots of space. This may eventually have to be reduced, probably by
39 * doing logical logging, which is a much cooler database phrase.
41 * The implementation of the historic vi 'u' command, using roll-forward and
42 * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
43 * followed by a number of other records, followed by a LOG_CURSOR_END record.
44 * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
45 * record, and is the line before the change. The second is LOG_LINE_RESET_F,
46 * and is the line after the change. Roll-back is done by backing up to the
47 * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
50 * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
51 * record for a line different from the current one. It should be noted that
52 * this means that a subsequent 'u' command will make a change based on the
53 * new position of the log's cursor. This is okay, and, in fact, historic vi
57 static int log_cursor1
__P((SCR
*, EXF
*, int));
58 #if defined(DEBUG) && 0
59 static void log_trace
__P((SCR
*, char *, recno_t
, u_char
*));
62 /* Try and restart the log on failure, i.e. if we run out of memory. */
64 msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.", \
65 tail(__FILE__), __LINE__, strerror(errno)); \
66 (void)ep->log->close(ep->log); \
67 if (!log_init(sp, ep)) \
68 msgq(sp, M_ERR, "Log restarted."); \
74 * Initialize the logging subsystem.
82 * Initialize the buffer. The logging subsystem has its own
83 * buffers because the global ones are almost by definition
84 * going to be in use when the log runs.
88 ep
->l_cursor
.lno
= 1; /* XXX Any valid recno. */
90 ep
->l_high
= ep
->l_cur
= 1;
92 ep
->log
= dbopen(NULL
, O_CREAT
| O_NONBLOCK
| O_RDWR
,
93 S_IRUSR
| S_IWUSR
, DB_RECNO
, NULL
);
94 if (ep
->log
== NULL
) {
95 msgq(sp
, M_ERR
, "log db: %s", strerror(errno
));
105 * Close the logging subsystem.
112 if (ep
->log
!= NULL
) {
113 (void)(ep
->log
->close
)(ep
->log
);
116 if (ep
->l_lp
!= NULL
) {
121 ep
->l_cursor
.lno
= 1; /* XXX Any valid recno. */
122 ep
->l_cursor
.cno
= 0;
123 ep
->l_high
= ep
->l_cur
= 1;
129 * Log the current cursor position, starting an event.
137 * If any changes were made since the last cursor init,
138 * put out the ending cursor record.
140 if (ep
->l_cursor
.lno
== OOBLNO
) {
141 ep
->l_cursor
.lno
= sp
->lno
;
142 ep
->l_cursor
.cno
= sp
->cno
;
143 return (log_cursor1(sp
, ep
, LOG_CURSOR_END
));
145 ep
->l_cursor
.lno
= sp
->lno
;
146 ep
->l_cursor
.cno
= sp
->cno
;
152 * Actually push a cursor record out.
155 log_cursor1(sp
, ep
, type
)
162 BINC_RET(sp
, ep
->l_lp
, ep
->l_len
, sizeof(u_char
) + sizeof(MARK
));
164 memmove(ep
->l_lp
+ sizeof(u_char
), &ep
->l_cursor
, sizeof(MARK
));
166 key
.data
= &ep
->l_cur
;
167 key
.size
= sizeof(recno_t
);
168 data
.data
= ep
->l_lp
;
169 data
.size
= sizeof(u_char
) + sizeof(MARK
);
170 if (ep
->log
->put(ep
->log
, &key
, &data
, 0) == -1)
173 #if defined(DEBUG) && 0
174 TRACE(sp
, "%lu: %s: %u/%u\n", ep
->l_cur
,
175 type
== LOG_CURSOR_INIT
? "log_cursor_init" : "log_cursor_end",
178 /* Reset high water mark. */
179 ep
->l_high
= ++ep
->l_cur
;
189 log_line(sp
, ep
, lno
, action
)
199 if (F_ISSET(ep
, F_NOLOG
))
205 * Kluge for vi. Clear the EXF undo flag so that the
206 * next 'u' command does a roll-back, regardless.
210 /* Put out one initial cursor record per set of changes. */
211 if (ep
->l_cursor
.lno
!= OOBLNO
) {
212 if (log_cursor1(sp
, ep
, LOG_CURSOR_INIT
))
214 ep
->l_cursor
.lno
= OOBLNO
;
218 * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
219 * special case, avoid the caches. Also, if it fails and it's
220 * line 1, it just means that the user started with an empty file,
221 * so fake an empty length line.
223 if (action
== LOG_LINE_RESET_B
) {
224 if ((lp
= file_rline(sp
, ep
, lno
, &len
)) == NULL
) {
226 GETLINE_ERR(sp
, lno
);
233 if ((lp
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
234 GETLINE_ERR(sp
, lno
);
238 ep
->l_lp
, ep
->l_len
, len
+ sizeof(u_char
) + sizeof(recno_t
));
239 ep
->l_lp
[0] = action
;
240 memmove(ep
->l_lp
+ sizeof(u_char
), &lno
, sizeof(recno_t
));
241 memmove(ep
->l_lp
+ sizeof(u_char
) + sizeof(recno_t
), lp
, len
);
243 key
.data
= &ep
->l_cur
;
244 key
.size
= sizeof(recno_t
);
245 data
.data
= ep
->l_lp
;
246 data
.size
= len
+ sizeof(u_char
) + sizeof(recno_t
);
247 if (ep
->log
->put(ep
->log
, &key
, &data
, 0) == -1)
250 #if defined(DEBUG) && 0
252 case LOG_LINE_APPEND
:
253 TRACE(sp
, "%u: log_line: append: %lu {%u}\n",
254 ep
->l_cur
, lno
, len
);
256 case LOG_LINE_DELETE
:
257 TRACE(sp
, "%lu: log_line: delete: %lu {%u}\n",
258 ep
->l_cur
, lno
, len
);
260 case LOG_LINE_INSERT
:
261 TRACE(sp
, "%lu: log_line: insert: %lu {%u}\n",
262 ep
->l_cur
, lno
, len
);
264 case LOG_LINE_RESET_F
:
265 TRACE(sp
, "%lu: log_line: reset_f: %lu {%u}\n",
266 ep
->l_cur
, lno
, len
);
268 case LOG_LINE_RESET_B
:
269 TRACE(sp
, "%lu: log_line: reset_b: %lu {%u}\n",
270 ep
->l_cur
, lno
, len
);
274 /* Reset high water mark. */
275 ep
->l_high
= ++ep
->l_cur
;
282 * Log a mark position. For the log to work, we assume that there
283 * aren't any operations that just put out a log record -- this
284 * would mean that undo operations would only reset marks, and not
285 * cause any other change.
295 if (F_ISSET(ep
, F_NOLOG
))
298 /* Put out one initial cursor record per set of changes. */
299 if (ep
->l_cursor
.lno
!= OOBLNO
) {
300 if (log_cursor1(sp
, ep
, LOG_CURSOR_INIT
))
302 ep
->l_cursor
.lno
= OOBLNO
;
305 BINC_RET(sp
, ep
->l_lp
,
306 ep
->l_len
, sizeof(u_char
) + sizeof(MARK
));
307 ep
->l_lp
[0] = LOG_MARK
;
308 memmove(ep
->l_lp
+ sizeof(u_char
), mp
, sizeof(MARK
));
310 key
.data
= &ep
->l_cur
;
311 key
.size
= sizeof(recno_t
);
312 data
.data
= ep
->l_lp
;
313 data
.size
= sizeof(u_char
) + sizeof(MARK
);
314 if (ep
->log
->put(ep
->log
, &key
, &data
, 0) == -1)
317 /* Reset high water mark. */
318 ep
->l_high
= ++ep
->l_cur
;
324 * Roll the log backward one operation.
327 log_backward(sp
, ep
, rp
)
338 if (F_ISSET(ep
, F_NOLOG
)) {
340 "Logging not being performed, undo not possible.");
344 if (ep
->l_cur
== 1) {
345 msgq(sp
, M_BERR
, "No changes to undo.");
349 F_SET(ep
, F_NOLOG
); /* Turn off logging. */
351 key
.data
= &ep
->l_cur
; /* Initialize db request. */
352 key
.size
= sizeof(recno_t
);
355 if (ep
->log
->get(ep
->log
, &key
, &data
, 0))
357 #if defined(DEBUG) && 0
358 log_trace(sp
, "log_backward", ep
->l_cur
, data
.data
);
360 switch (*(p
= (u_char
*)data
.data
)) {
361 case LOG_CURSOR_INIT
:
363 memmove(rp
, p
+ sizeof(u_char
), sizeof(MARK
));
370 case LOG_LINE_APPEND
:
371 case LOG_LINE_INSERT
:
373 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
374 if (file_dline(sp
, ep
, lno
))
376 ++sp
->rptlines
[L_DELETED
];
378 case LOG_LINE_DELETE
:
380 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
381 if (file_iline(sp
, ep
, lno
, p
+ sizeof(u_char
) +
382 sizeof(recno_t
), data
.size
- sizeof(u_char
) -
385 ++sp
->rptlines
[L_ADDED
];
387 case LOG_LINE_RESET_F
:
389 case LOG_LINE_RESET_B
:
391 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
392 if (file_sline(sp
, ep
, lno
, p
+ sizeof(u_char
) +
393 sizeof(recno_t
), data
.size
- sizeof(u_char
) -
396 ++sp
->rptlines
[L_CHANGED
];
400 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
401 if (mark_set(sp
, ep
, m
.name
, &m
, 0))
409 err
: F_CLR(ep
, F_NOLOG
);
415 * Reset the line to its original appearance.
418 * There's a bug in this code due to our not logging cursor movements
419 * unless a change was made. If you do a change, move off the line,
420 * then move back on and do a 'U', the line will be restored to the way
421 * it was before the original change.
433 if (F_ISSET(ep
, F_NOLOG
)) {
435 "Logging not being performed, undo not possible.");
442 F_SET(ep
, F_NOLOG
); /* Turn off logging. */
444 key
.data
= &ep
->l_cur
; /* Initialize db request. */
445 key
.size
= sizeof(recno_t
);
449 if (ep
->log
->get(ep
->log
, &key
, &data
, 0))
451 #if defined(DEBUG) && 0
452 log_trace(sp
, "log_setline", ep
->l_cur
, data
.data
);
454 switch (*(p
= (u_char
*)data
.data
)) {
455 case LOG_CURSOR_INIT
:
456 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
457 if (m
.lno
!= sp
->lno
|| ep
->l_cur
== 1) {
463 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
464 if (m
.lno
!= sp
->lno
) {
470 case LOG_LINE_APPEND
:
471 case LOG_LINE_INSERT
:
472 case LOG_LINE_DELETE
:
473 case LOG_LINE_RESET_F
:
475 case LOG_LINE_RESET_B
:
476 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
477 if (lno
== sp
->lno
&&
478 file_sline(sp
, ep
, lno
, p
+ sizeof(u_char
) +
479 sizeof(recno_t
), data
.size
- sizeof(u_char
) -
482 ++sp
->rptlines
[L_CHANGED
];
484 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
485 if (mark_set(sp
, ep
, m
.name
, &m
, 0))
493 err
: F_CLR(ep
, F_NOLOG
);
499 * Roll the log forward one operation.
502 log_forward(sp
, ep
, rp
)
513 if (F_ISSET(ep
, F_NOLOG
)) {
515 "Logging not being performed, roll-forward not possible.");
519 if (ep
->l_cur
== ep
->l_high
) {
520 msgq(sp
, M_BERR
, "No changes to re-do.");
524 F_SET(ep
, F_NOLOG
); /* Turn off logging. */
526 key
.data
= &ep
->l_cur
; /* Initialize db request. */
527 key
.size
= sizeof(recno_t
);
530 if (ep
->log
->get(ep
->log
, &key
, &data
, 0))
532 #if defined(DEBUG) && 0
533 log_trace(sp
, "log_forward", ep
->l_cur
, data
.data
);
535 switch (*(p
= (u_char
*)data
.data
)) {
539 memmove(rp
, p
+ sizeof(u_char
), sizeof(MARK
));
544 case LOG_CURSOR_INIT
:
546 case LOG_LINE_APPEND
:
547 case LOG_LINE_INSERT
:
549 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
550 if (file_iline(sp
, ep
, lno
, p
+ sizeof(u_char
) +
551 sizeof(recno_t
), data
.size
- sizeof(u_char
) -
554 ++sp
->rptlines
[L_ADDED
];
556 case LOG_LINE_DELETE
:
558 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
559 if (file_dline(sp
, ep
, lno
))
561 ++sp
->rptlines
[L_DELETED
];
563 case LOG_LINE_RESET_B
:
565 case LOG_LINE_RESET_F
:
567 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
568 if (file_sline(sp
, ep
, lno
, p
+ sizeof(u_char
) +
569 sizeof(recno_t
), data
.size
- sizeof(u_char
) -
572 ++sp
->rptlines
[L_CHANGED
];
576 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
577 if (mark_set(sp
, ep
, m
.name
, &m
, 0))
585 err
: F_CLR(ep
, F_NOLOG
);
589 #if defined(DEBUG) && 0
591 log_trace(sp
, msg
, rno
, p
)
601 case LOG_CURSOR_INIT
:
602 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
603 TRACE(sp
, "%lu: %s: C_INIT: %u/%u\n", rno
, msg
, m
.lno
, m
.cno
);
606 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
607 TRACE(sp
, "%lu: %s: C_END: %u/%u\n", rno
, msg
, m
.lno
, m
.cno
);
609 case LOG_LINE_APPEND
:
610 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
611 TRACE(sp
, "%lu: %s: APPEND: %lu\n", rno
, msg
, lno
);
613 case LOG_LINE_INSERT
:
614 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
615 TRACE(sp
, "%lu: %s: INSERT: %lu\n", rno
, msg
, lno
);
617 case LOG_LINE_DELETE
:
618 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
619 TRACE(sp
, "%lu: %s: DELETE: %lu\n", rno
, msg
, lno
);
621 case LOG_LINE_RESET_F
:
622 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
623 TRACE(sp
, "%lu: %s: RESET_F: %lu\n", rno
, msg
, lno
);
625 case LOG_LINE_RESET_B
:
626 memmove(&lno
, p
+ sizeof(u_char
), sizeof(recno_t
));
627 TRACE(sp
, "%lu: %s: RESET_B: %lu\n", rno
, msg
, lno
);
630 memmove(&m
, p
+ sizeof(u_char
), sizeof(MARK
));
631 TRACE(sp
, "%lu: %s: MARK: %u/%u\n", rno
, msg
, m
.lno
, m
.cno
);