]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
*** empty log message ***
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.57 2008/12/10 00:52:46 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <sys/param.h>
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <err.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "private.h"
32 #include "roff.h"
33
34 /* FIXME: First letters of quoted-text interpreted in rofffindtok. */
35 /* FIXME: `No' not implemented. */
36 /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
37 /* TODO: warn about empty lists. */
38 /* TODO: (warn) some sections need specific elements. */
39 /* TODO: (warn) NAME section has particular order. */
40 /* TODO: macros with a set number of arguments? */
41 /* TODO: validate Dt macro arguments. */
42 /* FIXME: Bl -diag supposed to ignore callable children. */
43
44 struct roffnode {
45 int tok; /* Token id. */
46 struct roffnode *parent; /* Parent (or NULL). */
47 };
48
49 enum rofferr {
50 ERR_ARGEQ1, /* Macro requires arg == 1. */
51 ERR_ARGEQ0, /* Macro requires arg == 0. */
52 ERR_ARGGE1, /* Macro requires arg >= 1. */
53 ERR_ARGGE2, /* Macro requires arg >= 2. */
54 ERR_ARGLEN, /* Macro argument too long. */
55 ERR_BADARG, /* Macro has bad arg. */
56 ERR_ARGMNY, /* Too many macro arguments. */
57 ERR_NOTSUP, /* Macro not supported. */
58 ERR_DEPREC, /* Macro deprecated. */
59 ERR_PR_OOO, /* Prelude macro bad order. */
60 ERR_PR_REP, /* Prelude macro repeated. */
61 ERR_NOT_PR, /* Not allowed in prelude. */
62 WRN_SECORD, /* Sections out-of-order. */
63 };
64
65 struct rofftree {
66 struct roffnode *last; /* Last parsed node. */
67 char *cur; /* Line start. */
68 struct tm tm; /* `Dd' results. */
69 char name[64]; /* `Nm' results. */
70 char os[64]; /* `Os' results. */
71 char title[64]; /* `Dt' results. */
72 enum roffmsec section;
73 char volume[64]; /* `Dt' results. */
74 int state;
75 #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */ /* FIXME: put into asec. */
76 #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
77 #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
78 #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
79 #define ROFF_BODY (1 << 5) /* In roff body. */
80 struct roffcb cb; /* Callbacks. */
81 void *arg; /* Callbacks' arg. */
82 int csec; /* Current section. */
83 int asec; /* Thus-far sections. */
84 };
85
86 static struct roffnode *roffnode_new(int, struct rofftree *);
87 static void roffnode_free(struct rofftree *);
88 static int roff_warn(const struct rofftree *,
89 const char *, char *, ...);
90 static int roff_warnp(const struct rofftree *,
91 const char *, int, enum rofferr);
92 static int roff_err(const struct rofftree *,
93 const char *, char *, ...);
94 static int roff_errp(const struct rofftree *,
95 const char *, int, enum rofferr);
96 static int roffpurgepunct(struct rofftree *, char **);
97 static int roffscan(int, const int *);
98 static int rofffindtok(const char *);
99 static int rofffindarg(const char *);
100 static int rofffindcallable(const char *);
101 static int roffispunct(const char *);
102 static int roffchecksec(struct rofftree *,
103 const char *, int);
104 static int roffargs(const struct rofftree *,
105 int, char *, char **);
106 static int roffargok(int, int);
107 static int roffnextopt(const struct rofftree *,
108 int, char ***, char **);
109 static int roffparseopts(struct rofftree *, int,
110 char ***, int *, char **);
111 static int roffcall(struct rofftree *, int, char **);
112 static int roffexit(struct rofftree *, int);
113 static int roffparse(struct rofftree *, char *);
114 static int textparse(struct rofftree *, char *);
115 static int roffdata(struct rofftree *, int, char *);
116 static int roffspecial(struct rofftree *, int,
117 const char *, const int *,
118 const char **, size_t, char **);
119 static int roffsetname(struct rofftree *, char **);
120
121 #ifdef __linux__
122 extern size_t strlcat(char *, const char *, size_t);
123 extern size_t strlcpy(char *, const char *, size_t);
124 extern int vsnprintf(char *, size_t,
125 const char *, va_list);
126 extern char *strptime(const char *, const char *,
127 struct tm *);
128 #endif
129
130 int
131 roff_free(struct rofftree *tree, int flush)
132 {
133 int error, t;
134 struct roffnode *n;
135
136 error = 0;
137
138 if ( ! flush)
139 goto end;
140
141 error = 1;
142
143 if (ROFF_PRELUDE & tree->state) {
144 (void)roff_err(tree, NULL, "prelude never finished");
145 goto end;
146 } else if ( ! (ROFFSec_NAME & tree->asec)) {
147 (void)roff_err(tree, NULL, "missing `NAME' section");
148 goto end;
149 } else if ( ! (ROFFSec_NMASK & tree->asec))
150 (void)roff_warn(tree, NULL, "missing suggested `NAME', "
151 "`SYNOPSIS', `DESCRIPTION' sections");
152
153 for (n = tree->last; n; n = n->parent) {
154 if (0 != tokens[n->tok].ctx)
155 continue;
156 (void)roff_err(tree, NULL, "closing explicit scope "
157 "`%s'", toknames[n->tok]);
158 goto end;
159 }
160
161 while (tree->last) {
162 t = tree->last->tok;
163 if ( ! roffexit(tree, t))
164 goto end;
165 }
166
167 if ( ! (*tree->cb.rofftail)(tree->arg))
168 goto end;
169
170 error = 0;
171
172 end:
173
174 while (tree->last)
175 roffnode_free(tree);
176
177 free(tree);
178
179 return(error ? 0 : 1);
180 }
181
182
183 struct rofftree *
184 roff_alloc(const struct roffcb *cb, void *args)
185 {
186 struct rofftree *tree;
187
188 assert(args);
189 assert(cb);
190
191 if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
192 err(1, "calloc");
193
194 tree->state = ROFF_PRELUDE;
195 tree->arg = args;
196 tree->section = ROFF_MSEC_MAX;
197
198 (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
199
200 return(tree);
201 }
202
203
204 int
205 roff_engine(struct rofftree *tree, char *buf)
206 {
207
208 tree->cur = buf;
209 assert(buf);
210
211 if (0 == *buf)
212 return(roff_err(tree, buf, "blank line"));
213 else if ('.' != *buf)
214 return(textparse(tree, buf));
215
216 return(roffparse(tree, buf));
217 }
218
219
220 static int
221 textparse(struct rofftree *tree, char *buf)
222 {
223 char *bufp;
224
225 /* TODO: literal parsing. */
226
227 if ( ! (ROFF_BODY & tree->state))
228 return(roff_err(tree, buf, "data not in body"));
229
230 /* LINTED */
231 while (*buf) {
232 while (*buf && isspace(*buf))
233 buf++;
234
235 if (0 == *buf)
236 break;
237
238 bufp = buf++;
239
240 while (*buf && ! isspace(*buf))
241 buf++;
242
243 if (0 != *buf) {
244 *buf++ = 0;
245 if ( ! roffdata(tree, 1, bufp))
246 return(0);
247 continue;
248 }
249
250 if ( ! roffdata(tree, 1, bufp))
251 return(0);
252 break;
253 }
254
255 return(1);
256 }
257
258
259 static int
260 roffargs(const struct rofftree *tree,
261 int tok, char *buf, char **argv)
262 {
263 int i;
264 char *p;
265
266 assert(tok >= 0 && tok < ROFF_MAX);
267 assert('.' == *buf);
268
269 p = buf;
270
271 /*
272 * This is an ugly little loop. It parses a line into
273 * space-delimited tokens. If a quote mark is encountered, a
274 * token is alloted the entire quoted text. If whitespace is
275 * escaped, it's included in the prior alloted token.
276 */
277
278 /* LINTED */
279 for (i = 0; *buf && i < ROFF_MAXLINEARG; i++) {
280 if ('\"' == *buf) {
281 argv[i] = ++buf;
282 while (*buf && '\"' != *buf)
283 buf++;
284 if (0 == *buf)
285 return(roff_err(tree, argv[i],
286 "unclosed quote in arg list"));
287 } else {
288 argv[i] = buf++;
289 while (*buf) {
290 if ( ! isspace(*buf)) {
291 buf++;
292 continue;
293 }
294 if (*(buf - 1) == '\\') {
295 buf++;
296 continue;
297 }
298 break;
299 }
300 if (0 == *buf)
301 continue;
302 }
303 *buf++ = 0;
304 while (*buf && isspace(*buf))
305 buf++;
306 }
307
308 assert(i > 0);
309 if (ROFF_MAXLINEARG == i && *buf)
310 return(roff_err(tree, p, "too many args"));
311
312 argv[i] = NULL;
313 return(1);
314 }
315
316
317 static int
318 roffscan(int tok, const int *tokv)
319 {
320
321 if (NULL == tokv)
322 return(1);
323
324 for ( ; ROFF_MAX != *tokv; tokv++)
325 if (tok == *tokv)
326 return(1);
327
328 return(0);
329 }
330
331
332 static int
333 roffparse(struct rofftree *tree, char *buf)
334 {
335 int tok, t;
336 struct roffnode *n;
337 char *argv[ROFF_MAXLINEARG];
338 char **argvp;
339
340 if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))
341 if (0 == strncmp(buf, ".\\\"", 3))
342 return(1);
343
344 if (ROFF_MAX == (tok = rofffindtok(buf + 1)))
345 return(roff_err(tree, buf, "bogus line macro"));
346 else if ( ! roffargs(tree, tok, buf, argv))
347 return(0);
348
349 argvp = (char **)argv;
350
351 /*
352 * Prelude macros break some assumptions, so branch now.
353 */
354
355 if (ROFF_PRELUDE & tree->state) {
356 assert(NULL == tree->last);
357 return(roffcall(tree, tok, argvp));
358 }
359
360 assert(ROFF_BODY & tree->state);
361
362 /*
363 * First check that our possible parents and parent's possible
364 * children are satisfied.
365 */
366
367 if (tree->last && ! roffscan
368 (tree->last->tok, tokens[tok].parents))
369 return(roff_err(tree, *argvp, "`%s' has invalid "
370 "parent `%s'", toknames[tok],
371 toknames[tree->last->tok]));
372
373 if (tree->last && ! roffscan
374 (tok, tokens[tree->last->tok].children))
375 return(roff_err(tree, *argvp, "`%s' has invalid "
376 "child `%s'", toknames[tok],
377 toknames[tree->last->tok]));
378
379 /*
380 * Branch if we're not a layout token.
381 */
382
383 if (ROFF_LAYOUT != tokens[tok].type)
384 return(roffcall(tree, tok, argvp));
385 if (0 == tokens[tok].ctx)
386 return(roffcall(tree, tok, argvp));
387
388 /*
389 * First consider implicit-end tags, like as follows:
390 * .Sh SECTION 1
391 * .Sh SECTION 2
392 * In this, we want to close the scope of the NAME section. If
393 * there's an intermediary implicit-end tag, such as
394 * .Sh SECTION 1
395 * .Ss Subsection 1
396 * .Sh SECTION 2
397 * then it must be closed as well.
398 */
399
400 if (tok == tokens[tok].ctx) {
401 /*
402 * First search up to the point where we must close.
403 * If one doesn't exist, then we can open a new scope.
404 */
405
406 for (n = tree->last; n; n = n->parent) {
407 assert(0 == tokens[n->tok].ctx ||
408 n->tok == tokens[n->tok].ctx);
409 if (n->tok == tok)
410 break;
411 if (ROFF_SHALLOW & tokens[tok].flags) {
412 n = NULL;
413 break;
414 }
415 if (tokens[n->tok].ctx == n->tok)
416 continue;
417 return(roff_err(tree, *argv, "`%s' breaks "
418 "scope of prior`%s'",
419 toknames[tok],
420 toknames[n->tok]));
421 }
422
423 /*
424 * Create a new scope, as no previous one exists to
425 * close out.
426 */
427
428 if (NULL == n)
429 return(roffcall(tree, tok, argvp));
430
431 /*
432 * Close out all intermediary scoped blocks, then hang
433 * the current scope from our predecessor's parent.
434 */
435
436 do {
437 t = tree->last->tok;
438 if ( ! roffexit(tree, t))
439 return(0);
440 } while (t != tok);
441
442 return(roffcall(tree, tok, argvp));
443 }
444
445 /*
446 * Now consider explicit-end tags, where we want to close back
447 * to a specific tag. Example:
448 * .Bl
449 * .It Item.
450 * .El
451 * In this, the `El' tag closes out the scope of `Bl'.
452 */
453
454 assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
455
456 /* LINTED */
457 for (n = tree->last; n; n = n->parent)
458 if (n->tok != tokens[tok].ctx) {
459 if (n->tok == tokens[n->tok].ctx)
460 continue;
461 return(roff_err(tree, *argv, "`%s' breaks "
462 "scope of prior `%s'",
463 toknames[tok],
464 toknames[n->tok]));
465 } else
466 break;
467
468 if (NULL == n)
469 return(roff_err(tree, *argv, "`%s' has no starting "
470 "tag `%s'", toknames[tok],
471 toknames[tokens[tok].ctx]));
472
473 /* LINTED */
474 do {
475 t = tree->last->tok;
476 if ( ! roffexit(tree, t))
477 return(0);
478 } while (t != tokens[tok].ctx);
479
480 return(1);
481 }
482
483
484 static int
485 rofffindarg(const char *name)
486 {
487 size_t i;
488
489 /* FIXME: use a table, this is slow but ok for now. */
490
491 /* LINTED */
492 for (i = 0; i < ROFF_ARGMAX; i++)
493 /* LINTED */
494 if (0 == strcmp(name, tokargnames[i]))
495 return((int)i);
496
497 return(ROFF_ARGMAX);
498 }
499
500
501 static int
502 rofffindtok(const char *buf)
503 {
504 char token[4];
505 int i;
506
507 for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
508 token[i] = *buf;
509
510 if (i == 3)
511 return(ROFF_MAX);
512
513 token[i] = 0;
514
515 /* FIXME: use a table, this is slow but ok for now. */
516
517 /* LINTED */
518 for (i = 0; i < ROFF_MAX; i++)
519 /* LINTED */
520 if (0 == strcmp(toknames[i], token))
521 return((int)i);
522
523 return(ROFF_MAX);
524 }
525
526
527 static int
528 roffchecksec(struct rofftree *tree, const char *start, int sec)
529 {
530 int prior;
531
532 switch (sec) {
533 case(ROFFSec_SYNOP):
534 if ((prior = ROFFSec_NAME) & tree->asec)
535 return(1);
536 break;
537 case(ROFFSec_DESC):
538 if ((prior = ROFFSec_SYNOP) & tree->asec)
539 return(1);
540 break;
541 case(ROFFSec_RETVAL):
542 if ((prior = ROFFSec_DESC) & tree->asec)
543 return(1);
544 break;
545 case(ROFFSec_ENV):
546 if ((prior = ROFFSec_RETVAL) & tree->asec)
547 return(1);
548 break;
549 case(ROFFSec_FILES):
550 if ((prior = ROFFSec_ENV) & tree->asec)
551 return(1);
552 break;
553 case(ROFFSec_EX):
554 if ((prior = ROFFSec_FILES) & tree->asec)
555 return(1);
556 break;
557 case(ROFFSec_DIAG):
558 if ((prior = ROFFSec_EX) & tree->asec)
559 return(1);
560 break;
561 case(ROFFSec_ERRS):
562 if ((prior = ROFFSec_DIAG) & tree->asec)
563 return(1);
564 break;
565 case(ROFFSec_SEEALSO):
566 if ((prior = ROFFSec_ERRS) & tree->asec)
567 return(1);
568 break;
569 case(ROFFSec_STAND):
570 if ((prior = ROFFSec_SEEALSO) & tree->asec)
571 return(1);
572 break;
573 case(ROFFSec_HIST):
574 if ((prior = ROFFSec_STAND) & tree->asec)
575 return(1);
576 break;
577 case(ROFFSec_AUTH):
578 if ((prior = ROFFSec_HIST) & tree->asec)
579 return(1);
580 break;
581 case(ROFFSec_CAVEATS):
582 if ((prior = ROFFSec_AUTH) & tree->asec)
583 return(1);
584 break;
585 case(ROFFSec_BUGS):
586 if ((prior = ROFFSec_CAVEATS) & tree->asec)
587 return(1);
588 break;
589 default:
590 return(1);
591 }
592
593 return(roff_warnp(tree, start, ROFF_Sh, WRN_SECORD));
594 }
595
596
597 /* FIXME: move this into literals.c (or similar). */
598 static int
599 roffispunct(const char *p)
600 {
601
602 if (0 == *p)
603 return(0);
604 if (0 != *(p + 1))
605 return(0);
606
607 switch (*p) {
608 case('{'):
609 /* FALLTHROUGH */
610 case('.'):
611 /* FALLTHROUGH */
612 case(','):
613 /* FALLTHROUGH */
614 case(';'):
615 /* FALLTHROUGH */
616 case(':'):
617 /* FALLTHROUGH */
618 case('?'):
619 /* FALLTHROUGH */
620 case('!'):
621 /* FALLTHROUGH */
622 case('('):
623 /* FALLTHROUGH */
624 case(')'):
625 /* FALLTHROUGH */
626 case('['):
627 /* FALLTHROUGH */
628 case(']'):
629 /* FALLTHROUGH */
630 case('}'):
631 return(1);
632 default:
633 break;
634 }
635
636 return(0);
637 }
638
639
640 static int
641 rofffindcallable(const char *name)
642 {
643 int c;
644
645 if (ROFF_MAX == (c = rofffindtok(name)))
646 return(ROFF_MAX);
647 assert(c >= 0 && c < ROFF_MAX);
648 return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
649 }
650
651
652 static struct roffnode *
653 roffnode_new(int tokid, struct rofftree *tree)
654 {
655 struct roffnode *p;
656
657 if (NULL == (p = malloc(sizeof(struct roffnode))))
658 err(1, "malloc");
659
660 p->tok = tokid;
661 p->parent = tree->last;
662 tree->last = p;
663
664 return(p);
665 }
666
667
668 static int
669 roffargok(int tokid, int argid)
670 {
671 const int *c;
672
673 if (NULL == (c = tokens[tokid].args))
674 return(0);
675
676 for ( ; ROFF_ARGMAX != *c; c++)
677 if (argid == *c)
678 return(1);
679
680 return(0);
681 }
682
683
684 static void
685 roffnode_free(struct rofftree *tree)
686 {
687 struct roffnode *p;
688
689 assert(tree->last);
690
691 p = tree->last;
692 tree->last = tree->last->parent;
693 free(p);
694 }
695
696
697 static int
698 roffspecial(struct rofftree *tree, int tok, const char *start,
699 const int *argc, const char **argv,
700 size_t sz, char **ordp)
701 {
702
703 switch (tok) {
704 case (ROFF_At):
705 if (0 == sz)
706 break;
707 if (ROFF_ATT_MAX != roff_att(*ordp))
708 break;
709 return(roff_errp(tree, *ordp, tok, ERR_BADARG));
710
711 case (ROFF_Xr):
712 if (2 == sz) {
713 assert(ordp[1]);
714 if (ROFF_MSEC_MAX != roff_msec(ordp[1]))
715 break;
716 if ( ! roff_warn(tree, start, "invalid `%s' manual "
717 "section", toknames[tok]))
718 return(0);
719 }
720 /* FALLTHROUGH */
721
722 case (ROFF_Sx):
723 /* FALLTHROUGH*/
724 case (ROFF_Fn):
725 if (0 != sz)
726 break;
727 return(roff_errp(tree, start, tok, ERR_ARGGE1));
728
729 case (ROFF_Nm):
730 if (0 == sz) {
731 if (0 != tree->name[0]) {
732 ordp[0] = tree->name;
733 ordp[1] = NULL;
734 break;
735 }
736 return(roff_err(tree, start, "`Nm' not set"));
737 } else if ( ! roffsetname(tree, ordp))
738 return(0);
739 break;
740
741 case (ROFF_Rv):
742 /* FALLTHROUGH*/
743 case (ROFF_Ex):
744 if (1 == sz)
745 break;
746 return(roff_errp(tree, start, tok, ERR_ARGEQ1));
747
748 case (ROFF_Sm):
749 if (1 != sz)
750 return(roff_errp(tree, start, tok, ERR_ARGEQ1));
751 else if (0 == strcmp(ordp[0], "on") ||
752 0 == strcmp(ordp[0], "off"))
753 break;
754 return(roff_errp(tree, *ordp, tok, ERR_BADARG));
755
756 case (ROFF_Ud):
757 /* FALLTHROUGH */
758 case (ROFF_Ux):
759 /* FALLTHROUGH */
760 case (ROFF_Bt):
761 if (0 == sz)
762 break;
763 return(roff_errp(tree, start, tok, ERR_ARGEQ0));
764 default:
765 break;
766 }
767
768 return((*tree->cb.roffspecial)(tree->arg, tok, tree->cur,
769 argc, argv, (const char **)ordp));
770 }
771
772
773 static int
774 roffexit(struct rofftree *tree, int tok)
775 {
776
777 assert(tokens[tok].cb);
778 return((*tokens[tok].cb)(tok, tree, NULL, ROFF_EXIT));
779 }
780
781
782 static int
783 roffcall(struct rofftree *tree, int tok, char **argv)
784 {
785 int i;
786 enum roffmsec c;
787
788 if (NULL == tokens[tok].cb)
789 return(roff_errp(tree, *argv, tok, ERR_NOTSUP));
790
791 if (tokens[tok].sections && ROFF_MSEC_MAX != tree->section) {
792 i = 0;
793 while (ROFF_MSEC_MAX !=
794 (c = tokens[tok].sections[i++]))
795 if (c == tree->section)
796 break;
797 if (ROFF_MSEC_MAX == c) {
798 if ( ! roff_warn(tree, *argv, "`%s' is not a valid "
799 "macro in this manual section",
800 toknames[tok]))
801 return(0);
802 }
803 }
804
805 return((*tokens[tok].cb)(tok, tree, argv, ROFF_ENTER));
806 }
807
808
809 static int
810 roffnextopt(const struct rofftree *tree, int tok,
811 char ***in, char **val)
812 {
813 char *arg, **argv;
814 int v;
815
816 *val = NULL;
817 argv = *in;
818 assert(argv);
819
820 if (NULL == (arg = *argv))
821 return(-1);
822 if ('-' != *arg)
823 return(-1);
824
825 if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
826 if ( ! roff_warn(tree, arg, "argument-like parameter `%s' to "
827 "`%s'", arg, toknames[tok]))
828 return(ROFF_ARGMAX);
829 return(-1);
830 }
831
832 if ( ! roffargok(tok, v)) {
833 if ( ! roff_warn(tree, arg, "invalid argument parameter `%s' to "
834 "`%s'", tokargnames[v], toknames[tok]))
835 return(ROFF_ARGMAX);
836 return(-1);
837 }
838
839 if ( ! (ROFF_VALUE & tokenargs[v]))
840 return(v);
841
842 *in = ++argv;
843
844 if (NULL == *argv) {
845 (void)roff_err(tree, arg, "empty value of `%s' for `%s'",
846 tokargnames[v], toknames[tok]);
847 return(ROFF_ARGMAX);
848 }
849
850 return(v);
851 }
852
853
854 static int
855 roffpurgepunct(struct rofftree *tree, char **argv)
856 {
857 int i;
858
859 i = 0;
860 while (argv[i])
861 i++;
862 assert(i > 0);
863 if ( ! roffispunct(argv[--i]))
864 return(1);
865 while (i >= 0 && roffispunct(argv[i]))
866 i--;
867 i++;
868
869 /* LINTED */
870 while (argv[i])
871 if ( ! roffdata(tree, 0, argv[i++]))
872 return(0);
873 return(1);
874 }
875
876
877 static int
878 roffparseopts(struct rofftree *tree, int tok,
879 char ***args, int *argc, char **argv)
880 {
881 int i, c;
882 char *v;
883
884 i = 0;
885
886 while (-1 != (c = roffnextopt(tree, tok, args, &v))) {
887 if (ROFF_ARGMAX == c)
888 return(0);
889
890 argc[i] = c;
891 argv[i] = v;
892 i++;
893 *args = *args + 1;
894 }
895
896 argc[i] = ROFF_ARGMAX;
897 argv[i] = NULL;
898 return(1);
899 }
900
901
902 static int
903 roffdata(struct rofftree *tree, int space, char *buf)
904 {
905
906 if (0 == *buf)
907 return(1);
908 return((*tree->cb.roffdata)(tree->arg,
909 space != 0, tree->cur, buf));
910 }
911
912
913 /* ARGSUSED */
914 static int
915 roff_Dd(ROFFCALL_ARGS)
916 {
917 time_t t;
918 char *p, buf[32];
919 size_t sz;
920
921 if (ROFF_BODY & tree->state) {
922 assert( ! (ROFF_PRELUDE & tree->state));
923 assert(ROFF_PRELUDE_Dd & tree->state);
924 return(roff_text(tok, tree, argv, type));
925 }
926
927 assert(ROFF_PRELUDE & tree->state);
928 assert( ! (ROFF_BODY & tree->state));
929
930 if (ROFF_PRELUDE_Dd & tree->state)
931 return(roff_errp(tree, *argv, tok, ERR_PR_REP));
932 if (ROFF_PRELUDE_Dt & tree->state)
933 return(roff_errp(tree, *argv, tok, ERR_PR_OOO));
934
935 assert(NULL == tree->last);
936
937 argv++;
938
939 /*
940 * This is a bit complex because there are many forms the date
941 * can be in: it can be simply $Mdocdate: December 10 2008 $, $Mdocdate <date>$,
942 * or a raw date. Process accordingly.
943 */
944
945 if (0 == strcmp(*argv, "$Mdocdate: December 10 2008 $")) {
946 t = time(NULL);
947 if (NULL == localtime_r(&t, &tree->tm))
948 err(1, "localtime_r");
949 tree->state |= ROFF_PRELUDE_Dd;
950 return(1);
951 }
952
953 buf[0] = 0;
954 p = *argv;
955 sz = sizeof(buf);
956
957 if (0 != strcmp(*argv, "$Mdocdate:")) {
958 while (*argv) {
959 if (strlcat(buf, *argv++, sz) < sz)
960 continue;
961 return(roff_errp(tree, p, tok, ERR_BADARG));
962 }
963 if (strptime(buf, "%b%d,%Y", &tree->tm)) {
964 tree->state |= ROFF_PRELUDE_Dd;
965 return(1);
966 }
967 return(roff_errp(tree, p, tok, ERR_BADARG));
968 }
969
970 argv++;
971
972 while (*argv && **argv != '$') {
973 if (strlcat(buf, *argv++, sz) >= sz)
974 return(roff_errp(tree, p, tok, ERR_BADARG));
975 if (strlcat(buf, " ", sz) >= sz)
976 return(roff_errp(tree, p, tok, ERR_BADARG));
977 }
978
979 if (NULL == *argv)
980 return(roff_errp(tree, p, tok, ERR_BADARG));
981 if (NULL == strptime(buf, "%b %d %Y", &tree->tm))
982 return(roff_errp(tree, p, tok, ERR_BADARG));
983
984 tree->state |= ROFF_PRELUDE_Dd;
985 return(1);
986 }
987
988
989 /* ARGSUSED */
990 static int
991 roff_Dt(ROFFCALL_ARGS)
992 {
993 size_t sz;
994
995 if (ROFF_BODY & tree->state) {
996 assert( ! (ROFF_PRELUDE & tree->state));
997 assert(ROFF_PRELUDE_Dt & tree->state);
998 return(roff_text(tok, tree, argv, type));
999 }
1000
1001 assert(ROFF_PRELUDE & tree->state);
1002 assert( ! (ROFF_BODY & tree->state));
1003
1004 if ( ! (ROFF_PRELUDE_Dd & tree->state))
1005 return(roff_errp(tree, *argv, tok, ERR_PR_OOO));
1006 if (ROFF_PRELUDE_Dt & tree->state)
1007 return(roff_errp(tree, *argv, tok, ERR_PR_REP));
1008
1009 argv++;
1010 sz = sizeof(tree->title);
1011
1012 if (NULL == *argv)
1013 return(roff_errp(tree, *argv, tok, ERR_ARGGE2));
1014 if (strlcpy(tree->title, *argv, sz) >= sz)
1015 return(roff_errp(tree, *argv, tok, ERR_ARGLEN));
1016
1017 argv++;
1018 if (NULL == *argv)
1019 return(roff_errp(tree, *argv, tok, ERR_ARGGE2));
1020
1021 if (ROFF_MSEC_MAX == (tree->section = roff_msec(*argv)))
1022 return(roff_errp(tree, *argv, tok, ERR_BADARG));
1023
1024 argv++;
1025 sz = sizeof(tree->volume);
1026
1027 if (NULL == *argv) {
1028 tree->volume[0] = 0;
1029 } else if (strlcpy(tree->volume, *argv, sz) >= sz)
1030 return(roff_errp(tree, *argv, tok, ERR_ARGLEN));
1031
1032 assert(NULL == tree->last);
1033 tree->state |= ROFF_PRELUDE_Dt;
1034
1035 return(1);
1036 }
1037
1038
1039 static int
1040 roffsetname(struct rofftree *tree, char **ordp)
1041 {
1042 size_t sz;
1043
1044 assert(*ordp);
1045
1046 /* FIXME: not all sections can set this. */
1047
1048 if (NULL != *(ordp + 1))
1049 return(roff_errp(tree, *ordp, ROFF_Nm, ERR_ARGMNY));
1050
1051 sz = sizeof(tree->name);
1052 if (strlcpy(tree->name, *ordp, sz) >= sz)
1053 return(roff_errp(tree, *ordp, ROFF_Nm, ERR_ARGLEN));
1054
1055 return(1);
1056 }
1057
1058
1059 /* ARGSUSED */
1060 static int
1061 roff_Ns(ROFFCALL_ARGS)
1062 {
1063 int j, c, first;
1064 char *morep[1];
1065
1066 first = (*argv++ == tree->cur);
1067 morep[0] = NULL;
1068
1069 if ( ! roffspecial(tree, tok, *argv, NULL, NULL, 0, morep))
1070 return(0);
1071
1072 while (*argv) {
1073 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1074 if ( ! roffcall(tree, c, argv))
1075 return(0);
1076 break;
1077 }
1078
1079 if ( ! roffispunct(*argv)) {
1080 if ( ! roffdata(tree, 1, *argv++))
1081 return(0);
1082 continue;
1083 }
1084
1085 for (j = 0; argv[j]; j++)
1086 if ( ! roffispunct(argv[j]))
1087 break;
1088
1089 if (argv[j]) {
1090 if ( ! roffdata(tree, 0, *argv++))
1091 return(0);
1092 continue;
1093 }
1094
1095 break;
1096 }
1097
1098 if ( ! first)
1099 return(1);
1100
1101 return(roffpurgepunct(tree, argv));
1102 }
1103
1104
1105 /* ARGSUSED */
1106 static int
1107 roff_Os(ROFFCALL_ARGS)
1108 {
1109 char *p;
1110 size_t sz;
1111
1112 if (ROFF_BODY & tree->state) {
1113 assert( ! (ROFF_PRELUDE & tree->state));
1114 assert(ROFF_PRELUDE_Os & tree->state);
1115 return(roff_text(tok, tree, argv, type));
1116 }
1117
1118 assert(ROFF_PRELUDE & tree->state);
1119 if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
1120 ! (ROFF_PRELUDE_Dd & tree->state))
1121 return(roff_errp(tree, *argv, tok, ERR_PR_OOO));
1122
1123 tree->os[0] = 0;
1124
1125 p = *++argv;
1126 sz = sizeof(tree->os);
1127
1128 while (*argv)
1129 if (strlcat(tree->os, *argv++, sz) >= sz)
1130 return(roff_errp(tree, p, tok, ERR_ARGLEN));
1131
1132 if (0 == tree->os[0])
1133 if (strlcpy(tree->os, "LOCAL", sz) >= sz)
1134 return(roff_errp(tree, p, tok, ERR_ARGLEN));
1135
1136 tree->state |= ROFF_PRELUDE_Os;
1137 tree->state &= ~ROFF_PRELUDE;
1138 tree->state |= ROFF_BODY;
1139
1140 assert(ROFF_MSEC_MAX != tree->section);
1141 assert(0 != tree->title[0]);
1142 assert(0 != tree->os[0]);
1143
1144 assert(NULL == tree->last);
1145
1146 return((*tree->cb.roffhead)(tree->arg, &tree->tm,
1147 tree->os, tree->title,
1148 tree->section, tree->volume));
1149 }
1150
1151
1152 /* ARGSUSED */
1153 static int
1154 roff_layout(ROFFCALL_ARGS)
1155 {
1156 int i, c, argcp[ROFF_MAXLINEARG];
1157 char *argvp[ROFF_MAXLINEARG];
1158
1159 /*
1160 * The roff_layout function is for multi-line macros. A layout
1161 * has a start and end point, which is either declared
1162 * explicitly or implicitly. An explicit start and end is
1163 * embodied by `.Bl' and `.El', with the former being the start
1164 * and the latter being an end. The `.Sh' and `.Ss' tags, on
1165 * the other hand, are implicit. The scope of a layout is the
1166 * space between start and end. Explicit layouts may not close
1167 * out implicit ones and vice versa; implicit layouts may close
1168 * out other implicit layouts.
1169 */
1170
1171 assert( ! (ROFF_CALLABLE & tokens[tok].flags));
1172
1173 if (ROFF_PRELUDE & tree->state)
1174 return(roff_errp(tree, *argv, tok, ERR_NOT_PR));
1175
1176 if (ROFF_EXIT == type) {
1177 roffnode_free(tree);
1178 if ( ! (*tree->cb.roffblkbodyout)(tree->arg, tok))
1179 return(0);
1180 return((*tree->cb.roffblkout)(tree->arg, tok));
1181 }
1182
1183 argv++;
1184 assert( ! (ROFF_CALLABLE & tokens[tok].flags));
1185
1186 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1187 return(0);
1188 if (NULL == roffnode_new(tok, tree))
1189 return(0);
1190
1191 /*
1192 * Layouts have two parts: the layout body and header. The
1193 * layout header is the trailing text of the line macro, while
1194 * the layout body is everything following until termination.
1195 * Example:
1196 *
1197 * .It Fl f ) ;
1198 * Bar.
1199 *
1200 * ...Produces...
1201 *
1202 * <block>
1203 * <head>
1204 * <!Fl f!> ;
1205 * </head>
1206 *
1207 * <body>
1208 * Bar.
1209 * </body>
1210 * </block>
1211 */
1212
1213 if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp,
1214 (const char **)argvp))
1215 return(0);
1216
1217 /* +++ Begin run macro-specific hooks over argv. */
1218
1219 switch (tok) {
1220 case (ROFF_Sh):
1221 if (NULL == *argv) {
1222 argv--;
1223 return(roff_errp(tree, *argv, tok, ERR_ARGGE1));
1224 }
1225
1226 tree->csec = roff_sec((const char **)argv);
1227
1228 if ( ! (ROFFSec_OTHER & tree->csec) &&
1229 tree->asec & tree->csec)
1230 if ( ! roff_warn(tree, *argv, "section repeated"))
1231 return(0);
1232
1233 if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec))
1234 return(roff_err(tree, *argv, "`NAME' section "
1235 "must be first"));
1236 if ( ! roffchecksec(tree, *argv, tree->csec))
1237 return(0);
1238
1239 tree->asec |= tree->csec;
1240 break;
1241 default:
1242 break;
1243 }
1244
1245 /* --- End run macro-specific hooks over argv. */
1246
1247 if (NULL == *argv)
1248 return((*tree->cb.roffblkbodyin)
1249 (tree->arg, tok, argcp,
1250 (const char **)argvp));
1251
1252 if ( ! (*tree->cb.roffblkheadin)(tree->arg, tok, argcp,
1253 (const char **)argvp))
1254 return(0);
1255
1256 /*
1257 * If there are no parsable parts, then write remaining tokens
1258 * into the layout header and exit.
1259 */
1260
1261 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1262 i = 0;
1263 while (*argv)
1264 if ( ! roffdata(tree, i++, *argv++))
1265 return(0);
1266
1267 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1268 return(0);
1269 return((*tree->cb.roffblkbodyin)(tree->arg, tok, argcp,
1270 (const char **)argvp));
1271 }
1272
1273 /*
1274 * Parsable elements may be in the header (or be the header, for
1275 * that matter). Follow the regular parsing rules for these.
1276 */
1277
1278 i = 0;
1279 while (*argv) {
1280 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1281 assert(tree->arg);
1282 if ( ! roffdata(tree, i++, *argv++))
1283 return(0);
1284 continue;
1285 }
1286 if ( ! roffcall(tree, c, argv))
1287 return(0);
1288 break;
1289 }
1290
1291 /*
1292 * If there's trailing punctuation in the header, then write it
1293 * out now. Here we mimic the behaviour of a line-dominant text
1294 * macro.
1295 */
1296
1297 if (NULL == *argv) {
1298 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1299 return(0);
1300 return((*tree->cb.roffblkbodyin)
1301 (tree->arg, tok, argcp,
1302 (const char **)argvp));
1303 }
1304
1305 /*
1306 * Expensive. Scan to the end of line then work backwards until
1307 * a token isn't punctuation.
1308 */
1309
1310 if ( ! roffpurgepunct(tree, argv))
1311 return(0);
1312 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1313 return(0);
1314 return((*tree->cb.roffblkbodyin)(tree->arg,
1315 tok, argcp, (const char **)argvp));
1316 }
1317
1318
1319 /* ARGSUSED */
1320 static int
1321 roff_ordered(ROFFCALL_ARGS)
1322 {
1323 int i, first, c, argcp[ROFF_MAXLINEARG];
1324 char *ordp[ROFF_MAXLINEARG], *p,
1325 *argvp[ROFF_MAXLINEARG];
1326
1327 /*
1328 * Ordered macros pass their arguments directly to handlers,
1329 * instead of considering it free-form text. Thus, the
1330 * following macro looks as follows:
1331 *
1332 * .Xr foo 1 ) ,
1333 *
1334 * .Xr arg1 arg2 punctuation
1335 */
1336
1337 if (ROFF_PRELUDE & tree->state)
1338 return(roff_errp(tree, *argv, tok, ERR_NOT_PR));
1339
1340 first = (*argv == tree->cur);
1341 p = *argv++;
1342 ordp[0] = NULL;
1343
1344 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1345 return(0);
1346
1347 if (NULL == *argv)
1348 return(roffspecial(tree, tok, p, argcp,
1349 (const char **)argvp, 0, ordp));
1350
1351 i = 0;
1352 while (*argv && i < ROFF_MAXLINEARG) {
1353 c = ROFF_PARSED & tokens[tok].flags ?
1354 rofffindcallable(*argv) : ROFF_MAX;
1355
1356 if (ROFF_MAX == c && ! roffispunct(*argv)) {
1357 ordp[i++] = *argv++;
1358 continue;
1359 }
1360 ordp[i] = NULL;
1361
1362 if (ROFF_MAX == c)
1363 break;
1364
1365 if ( ! roffspecial(tree, tok, p, argcp,
1366 (const char **)argvp,
1367 (size_t)i, ordp))
1368 return(0);
1369
1370 return(roffcall(tree, c, argv));
1371 }
1372
1373 assert(i != ROFF_MAXLINEARG);
1374 ordp[i] = NULL;
1375
1376 if ( ! roffspecial(tree, tok, p, argcp,
1377 (const char**)argvp,
1378 (size_t)i, ordp))
1379 return(0);
1380
1381 /* FIXME: error if there's stuff after the punctuation. */
1382
1383 if ( ! first || NULL == *argv)
1384 return(1);
1385
1386 return(roffpurgepunct(tree, argv));
1387 }
1388
1389
1390 /* ARGSUSED */
1391 static int
1392 roff_text(ROFFCALL_ARGS)
1393 {
1394 int i, j, first, c, argcp[ROFF_MAXLINEARG];
1395 char *argvp[ROFF_MAXLINEARG];
1396
1397 /*
1398 * Text macros are similar to special tokens, except that
1399 * arguments are instead flushed as pure data: we're only
1400 * concerned with the macro and its arguments. Example:
1401 *
1402 * .Fl v W f ;
1403 *
1404 * ...Produces...
1405 *
1406 * <fl> v W f </fl> ;
1407 */
1408
1409 if (ROFF_PRELUDE & tree->state)
1410 return(roff_errp(tree, *argv, tok, ERR_NOT_PR));
1411
1412 first = (*argv == tree->cur);
1413 argv++;
1414
1415 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1416 return(0);
1417 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp,
1418 (const char **)argvp))
1419 return(0);
1420 if (NULL == *argv)
1421 return((*tree->cb.roffout)(tree->arg, tok));
1422
1423 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1424 i = 0;
1425 while (*argv)
1426 if ( ! roffdata(tree, i++, *argv++))
1427 return(0);
1428 return((*tree->cb.roffout)(tree->arg, tok));
1429 }
1430
1431 /*
1432 * Deal with punctuation. Ugly. Work ahead until we encounter
1433 * terminating punctuation. If we encounter it and all
1434 * subsequent tokens are punctuation, then stop processing (the
1435 * line-dominant macro will print these tokens after closure).
1436 * If the punctuation is followed by non-punctuation, then close
1437 * and re-open our scope, then continue.
1438 */
1439
1440 i = 0;
1441 while (*argv) {
1442 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1443 if ( ! (ROFF_LSCOPE & tokens[tok].flags))
1444 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1445 return(0);
1446
1447 if ( ! roffcall(tree, c, argv))
1448 return(0);
1449 if (ROFF_LSCOPE & tokens[tok].flags)
1450 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1451 return(0);
1452 break;
1453 }
1454
1455 if ( ! roffispunct(*argv)) {
1456 if ( ! roffdata(tree, i++, *argv++))
1457 return(0);
1458 continue;
1459 }
1460
1461 i = 1;
1462 for (j = 0; argv[j]; j++)
1463 if ( ! roffispunct(argv[j]))
1464 break;
1465
1466 if (argv[j]) {
1467 if (ROFF_LSCOPE & tokens[tok].flags) {
1468 if ( ! roffdata(tree, 0, *argv++))
1469 return(0);
1470 continue;
1471 }
1472 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1473 return(0);
1474 if ( ! roffdata(tree, 0, *argv++))
1475 return(0);
1476 if ( ! (*tree->cb.roffin)(tree->arg, tok,
1477 argcp,
1478 (const char **)argvp))
1479 return(0);
1480
1481 i = 0;
1482 continue;
1483 }
1484
1485 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1486 return(0);
1487 break;
1488 }
1489
1490 if (NULL == *argv)
1491 return((*tree->cb.roffout)(tree->arg, tok));
1492 if ( ! first)
1493 return(1);
1494
1495 return(roffpurgepunct(tree, argv));
1496 }
1497
1498
1499 /* ARGSUSED */
1500 static int
1501 roff_noop(ROFFCALL_ARGS)
1502 {
1503
1504 return(1);
1505 }
1506
1507
1508 /* ARGSUSED */
1509 static int
1510 roff_depr(ROFFCALL_ARGS)
1511 {
1512
1513 return(roff_errp(tree, *argv, tok, ERR_DEPREC));
1514 }
1515
1516
1517 static int
1518 roff_warnp(const struct rofftree *tree, const char *pos,
1519 int tok, enum rofferr type)
1520 {
1521 char *p;
1522
1523 switch (type) {
1524 case (WRN_SECORD):
1525 p = "section at `%s' out of order";
1526 break;
1527 default:
1528 abort();
1529 /* NOTREACHED */
1530 }
1531
1532 return(roff_warn(tree, pos, p, toknames[tok]));
1533 }
1534
1535
1536 static int
1537 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
1538 {
1539 va_list ap;
1540 char buf[128];
1541
1542 va_start(ap, fmt);
1543 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1544 va_end(ap);
1545
1546 return((*tree->cb.roffmsg)(tree->arg,
1547 ROFF_WARN, tree->cur, pos, buf));
1548 }
1549
1550
1551 static int
1552 roff_errp(const struct rofftree *tree, const char *pos,
1553 int tok, enum rofferr type)
1554 {
1555 char *p;
1556
1557 switch (type) {
1558 case (ERR_ARGEQ1):
1559 p = "`%s' expects exactly one argument";
1560 break;
1561 case (ERR_ARGEQ0):
1562 p = "`%s' expects exactly zero arguments";
1563 break;
1564 case (ERR_ARGGE1):
1565 p = "`%s' expects one or more arguments";
1566 break;
1567 case (ERR_ARGGE2):
1568 p = "`%s' expects two or more arguments";
1569 break;
1570 case (ERR_BADARG):
1571 p = "invalid argument for `%s'";
1572 break;
1573 case (ERR_NOTSUP):
1574 p = "macro `%s' is not supported";
1575 break;
1576 case(ERR_PR_OOO):
1577 p = "prelude macro `%s' is out of order";
1578 break;
1579 case(ERR_PR_REP):
1580 p = "prelude macro `%s' repeated";
1581 break;
1582 case(ERR_ARGLEN):
1583 p = "macro argument for `%s' is too long";
1584 break;
1585 case(ERR_DEPREC):
1586 p = "macro `%s' is deprecated";
1587 break;
1588 case(ERR_NOT_PR):
1589 p = "macro `%s' disallowed in prelude";
1590 break;
1591 case(ERR_ARGMNY):
1592 p = "too many arguments for macro `%s'";
1593 break;
1594 default:
1595 abort();
1596 /* NOTREACHED */
1597 }
1598
1599 return(roff_err(tree, pos, p, toknames[tok]));
1600 }
1601
1602
1603 static int
1604 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
1605 {
1606 va_list ap;
1607 char buf[128];
1608
1609 va_start(ap, fmt);
1610 if (-1 == vsnprintf(buf, sizeof(buf), fmt, ap))
1611 err(1, "vsnprintf");
1612 va_end(ap);
1613
1614 return((*tree->cb.roffmsg)
1615 (tree->arg, ROFF_ERROR, tree->cur, pos, buf));
1616 }
1617