Merge from vendor branch GDB:
[dragonfly.git] / contrib / nvi / common / log.c
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