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