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