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