]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
cd6bca3a9ab5adec868a808bba2f17ab9edf7289
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.55 2008/12/09 17:09:12 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 9 2008 $, $Mdocdate <date>$,
942 * or a raw date. Process accordingly.
943 */
944
945 if (0 == strcmp(*argv, "$Mdocdate: December 9 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], *p;
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 assert( ! (ROFF_CALLABLE & tokens[tok].flags));
1184
1185 p = *argv++;
1186
1187 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1188 return(0);
1189 if (NULL == roffnode_new(tok, tree))
1190 return(0);
1191
1192 /*
1193 * Layouts have two parts: the layout body and header. The
1194 * layout header is the trailing text of the line macro, while
1195 * the layout body is everything following until termination.
1196 * Example:
1197 *
1198 * .It Fl f ) ;
1199 * Bar.
1200 *
1201 * ...Produces...
1202 *
1203 * <block>
1204 * <head>
1205 * <!Fl f!> ;
1206 * </head>
1207 *
1208 * <body>
1209 * Bar.
1210 * </body>
1211 * </block>
1212 */
1213
1214 if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp,
1215 (const char **)argvp))
1216 return(0);
1217
1218 /* +++ Begin run macro-specific hooks over argv. */
1219
1220 switch (tok) {
1221 case (ROFF_Sh):
1222 if (NULL == *argv) {
1223 argv--;
1224 return(roff_errp(tree, *argv, tok, ERR_ARGGE1));
1225 }
1226
1227 tree->csec = roff_sec((const char **)argv);
1228
1229 if ( ! (ROFFSec_OTHER & tree->csec) &&
1230 tree->asec & tree->csec)
1231 if ( ! roff_warn(tree, *argv, "section repeated"))
1232 return(0);
1233
1234 if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec))
1235 return(roff_err(tree, *argv, "`NAME' section "
1236 "must be first"));
1237 if ( ! roffchecksec(tree, *argv, tree->csec))
1238 return(0);
1239
1240 tree->asec |= tree->csec;
1241 break;
1242 default:
1243 break;
1244 }
1245
1246 /* --- End run macro-specific hooks over argv. */
1247
1248 if (NULL == *argv)
1249 return((*tree->cb.roffblkbodyin)
1250 (tree->arg, tok, argcp,
1251 (const char **)argvp));
1252
1253 if ( ! (*tree->cb.roffblkheadin)(tree->arg, tok, argcp,
1254 (const char **)argvp))
1255 return(0);
1256
1257 /*
1258 * If there are no parsable parts, then write remaining tokens
1259 * into the layout header and exit.
1260 */
1261
1262 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1263 i = 0;
1264 while (*argv)
1265 if ( ! roffdata(tree, i++, *argv++))
1266 return(0);
1267
1268 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1269 return(0);
1270 return((*tree->cb.roffblkbodyin)(tree->arg, tok, argcp,
1271 (const char **)argvp));
1272 }
1273
1274 /*
1275 * Parsable elements may be in the header (or be the header, for
1276 * that matter). Follow the regular parsing rules for these.
1277 */
1278
1279 i = 0;
1280 while (*argv) {
1281 if (ROFF_MAX == (c = rofffindcallable(*argv))) {
1282 assert(tree->arg);
1283 if ( ! roffdata(tree, i++, *argv++))
1284 return(0);
1285 continue;
1286 }
1287 if ( ! roffcall(tree, c, argv))
1288 return(0);
1289 break;
1290 }
1291
1292 /*
1293 * If there's trailing punctuation in the header, then write it
1294 * out now. Here we mimic the behaviour of a line-dominant text
1295 * macro.
1296 */
1297
1298 if (NULL == *argv) {
1299 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1300 return(0);
1301 return((*tree->cb.roffblkbodyin)
1302 (tree->arg, tok, argcp,
1303 (const char **)argvp));
1304 }
1305
1306 /*
1307 * Expensive. Scan to the end of line then work backwards until
1308 * a token isn't punctuation.
1309 */
1310
1311 if ( ! roffpurgepunct(tree, argv))
1312 return(0);
1313 if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
1314 return(0);
1315 return((*tree->cb.roffblkbodyin)(tree->arg,
1316 tok, argcp, (const char **)argvp));
1317 }
1318
1319
1320 /* ARGSUSED */
1321 static int
1322 roff_ordered(ROFFCALL_ARGS)
1323 {
1324 int i, first, c, argcp[ROFF_MAXLINEARG];
1325 char *ordp[ROFF_MAXLINEARG], *p,
1326 *argvp[ROFF_MAXLINEARG];
1327
1328 /*
1329 * Ordered macros pass their arguments directly to handlers,
1330 * instead of considering it free-form text. Thus, the
1331 * following macro looks as follows:
1332 *
1333 * .Xr foo 1 ) ,
1334 *
1335 * .Xr arg1 arg2 punctuation
1336 */
1337
1338 if (ROFF_PRELUDE & tree->state)
1339 return(roff_errp(tree, *argv, tok, ERR_NOT_PR));
1340
1341 first = (*argv == tree->cur);
1342 p = *argv++;
1343 ordp[0] = NULL;
1344
1345 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1346 return(0);
1347
1348 if (NULL == *argv)
1349 return(roffspecial(tree, tok, p, argcp,
1350 (const char **)argvp, 0, ordp));
1351
1352 i = 0;
1353 while (*argv && i < ROFF_MAXLINEARG) {
1354 c = ROFF_PARSED & tokens[tok].flags ?
1355 rofffindcallable(*argv) : ROFF_MAX;
1356
1357 if (ROFF_MAX == c && ! roffispunct(*argv)) {
1358 ordp[i++] = *argv++;
1359 continue;
1360 }
1361 ordp[i] = NULL;
1362
1363 if (ROFF_MAX == c)
1364 break;
1365
1366 if ( ! roffspecial(tree, tok, p, argcp,
1367 (const char **)argvp,
1368 (size_t)i, ordp))
1369 return(0);
1370
1371 return(roffcall(tree, c, argv));
1372 }
1373
1374 assert(i != ROFF_MAXLINEARG);
1375 ordp[i] = NULL;
1376
1377 if ( ! roffspecial(tree, tok, p, argcp,
1378 (const char**)argvp,
1379 (size_t)i, ordp))
1380 return(0);
1381
1382 /* FIXME: error if there's stuff after the punctuation. */
1383
1384 if ( ! first || NULL == *argv)
1385 return(1);
1386
1387 return(roffpurgepunct(tree, argv));
1388 }
1389
1390
1391 /* ARGSUSED */
1392 static int
1393 roff_text(ROFFCALL_ARGS)
1394 {
1395 int i, j, first, c, argcp[ROFF_MAXLINEARG];
1396 char *argvp[ROFF_MAXLINEARG];
1397
1398 /*
1399 * Text macros are similar to special tokens, except that
1400 * arguments are instead flushed as pure data: we're only
1401 * concerned with the macro and its arguments. Example:
1402 *
1403 * .Fl v W f ;
1404 *
1405 * ...Produces...
1406 *
1407 * <fl> v W f </fl> ;
1408 */
1409
1410 if (ROFF_PRELUDE & tree->state)
1411 return(roff_errp(tree, *argv, tok, ERR_NOT_PR));
1412
1413 first = (*argv == tree->cur);
1414 argv++;
1415
1416 if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
1417 return(0);
1418 if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp,
1419 (const char **)argvp))
1420 return(0);
1421 if (NULL == *argv)
1422 return((*tree->cb.roffout)(tree->arg, tok));
1423
1424 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1425 i = 0;
1426 while (*argv)
1427 if ( ! roffdata(tree, i++, *argv++))
1428 return(0);
1429 return((*tree->cb.roffout)(tree->arg, tok));
1430 }
1431
1432 /*
1433 * Deal with punctuation. Ugly. Work ahead until we encounter
1434 * terminating punctuation. If we encounter it and all
1435 * subsequent tokens are punctuation, then stop processing (the
1436 * line-dominant macro will print these tokens after closure).
1437 * If the punctuation is followed by non-punctuation, then close
1438 * and re-open our scope, then continue.
1439 */
1440
1441 i = 0;
1442 while (*argv) {
1443 if (ROFF_MAX != (c = rofffindcallable(*argv))) {
1444 if ( ! (ROFF_LSCOPE & tokens[tok].flags))
1445 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1446 return(0);
1447
1448 if ( ! roffcall(tree, c, argv))
1449 return(0);
1450 if (ROFF_LSCOPE & tokens[tok].flags)
1451 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1452 return(0);
1453 break;
1454 }
1455
1456 if ( ! roffispunct(*argv)) {
1457 if ( ! roffdata(tree, i++, *argv++))
1458 return(0);
1459 continue;
1460 }
1461
1462 i = 1;
1463 for (j = 0; argv[j]; j++)
1464 if ( ! roffispunct(argv[j]))
1465 break;
1466
1467 if (argv[j]) {
1468 if (ROFF_LSCOPE & tokens[tok].flags) {
1469 if ( ! roffdata(tree, 0, *argv++))
1470 return(0);
1471 continue;
1472 }
1473 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1474 return(0);
1475 if ( ! roffdata(tree, 0, *argv++))
1476 return(0);
1477 if ( ! (*tree->cb.roffin)(tree->arg, tok,
1478 argcp,
1479 (const char **)argvp))
1480 return(0);
1481
1482 i = 0;
1483 continue;
1484 }
1485
1486 if ( ! (*tree->cb.roffout)(tree->arg, tok))
1487 return(0);
1488 break;
1489 }
1490
1491 if (NULL == *argv)
1492 return((*tree->cb.roffout)(tree->arg, tok));
1493 if ( ! first)
1494 return(1);
1495
1496 return(roffpurgepunct(tree, argv));
1497 }
1498
1499
1500 /* ARGSUSED */
1501 static int
1502 roff_noop(ROFFCALL_ARGS)
1503 {
1504
1505 return(1);
1506 }
1507
1508
1509 /* ARGSUSED */
1510 static int
1511 roff_depr(ROFFCALL_ARGS)
1512 {
1513
1514 return(roff_errp(tree, *argv, tok, ERR_DEPREC));
1515 }
1516
1517
1518 static int
1519 roff_warnp(const struct rofftree *tree, const char *pos,
1520 int tok, enum rofferr type)
1521 {
1522 char *p;
1523
1524 switch (type) {
1525 case (WRN_SECORD):
1526 p = "section at `%s' out of order";
1527 break;
1528 default:
1529 abort();
1530 /* NOTREACHED */
1531 }
1532
1533 return(roff_warn(tree, pos, p, toknames[tok]));
1534 }
1535
1536
1537 static int
1538 roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
1539 {
1540 va_list ap;
1541 char buf[128];
1542
1543 va_start(ap, fmt);
1544 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1545 va_end(ap);
1546
1547 return((*tree->cb.roffmsg)(tree->arg,
1548 ROFF_WARN, tree->cur, pos, buf));
1549 }
1550
1551
1552 static int
1553 roff_errp(const struct rofftree *tree, const char *pos,
1554 int tok, enum rofferr type)
1555 {
1556 char *p;
1557
1558 switch (type) {
1559 case (ERR_ARGEQ1):
1560 p = "`%s' expects exactly one argument";
1561 break;
1562 case (ERR_ARGEQ0):
1563 p = "`%s' expects exactly zero arguments";
1564 break;
1565 case (ERR_ARGGE1):
1566 p = "`%s' expects one or more arguments";
1567 break;
1568 case (ERR_ARGGE2):
1569 p = "`%s' expects two or more arguments";
1570 break;
1571 case (ERR_BADARG):
1572 p = "invalid argument for `%s'";
1573 break;
1574 case (ERR_NOTSUP):
1575 p = "macro `%s' is not supported";
1576 break;
1577 case(ERR_PR_OOO):
1578 p = "prelude macro `%s' is out of order";
1579 break;
1580 case(ERR_PR_REP):
1581 p = "prelude macro `%s' repeated";
1582 break;
1583 case(ERR_ARGLEN):
1584 p = "macro argument for `%s' is too long";
1585 break;
1586 case(ERR_DEPREC):
1587 p = "macro `%s' is deprecated";
1588 break;
1589 case(ERR_NOT_PR):
1590 p = "macro `%s' disallowed in prelude";
1591 break;
1592 case(ERR_ARGMNY):
1593 p = "too many arguments for macro `%s'";
1594 break;
1595 default:
1596 abort();
1597 /* NOTREACHED */
1598 }
1599
1600 return(roff_err(tree, pos, p, toknames[tok]));
1601 }
1602
1603
1604 static int
1605 roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
1606 {
1607 va_list ap;
1608 char buf[128];
1609
1610 va_start(ap, fmt);
1611 if (-1 == vsnprintf(buf, sizeof(buf), fmt, ap))
1612 err(1, "vsnprintf");
1613 va_end(ap);
1614
1615 return((*tree->cb.roffmsg)
1616 (tree->arg, ROFF_ERROR, tree->cur, pos, buf));
1617 }
1618