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