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