Annotation of mandoc/roff.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: roff.c,v 1.5 2008/11/25 16:49: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: */
19: #include <assert.h>
20: #include <ctype.h>
21: #include <err.h>
22: #include <stdlib.h>
23: #include <stdio.h>
24: #include <string.h>
25: #include <time.h>
26:
27: #include "libmdocml.h"
28: #include "private.h"
29:
1.6 ! kristaps 30: /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
! 31:
! 32: /* FIXME: warn about empty lists. */
! 33:
1.1 kristaps 34: #define ROFF_MAXARG 10
35:
36: enum roffd {
37: ROFF_ENTER = 0,
38: ROFF_EXIT
39: };
40:
41: enum rofftype {
42: ROFF_COMMENT,
43: ROFF_TEXT,
44: ROFF_LAYOUT
45: };
46:
47: #define ROFFCALL_ARGS \
1.2 kristaps 48: int tok, struct rofftree *tree, \
49: const char *argv[], enum roffd type
1.1 kristaps 50:
51: struct rofftree;
52:
53: struct rofftok {
1.5 kristaps 54: int (*cb)(ROFFCALL_ARGS); /* Callback. */
55: const int *args; /* Args (or NULL). */
1.6 ! kristaps 56: const int *parents;
! 57: const int *children;
! 58: int ctx;
1.5 kristaps 59: enum rofftype type; /* Type of macro. */
1.1 kristaps 60: int flags;
1.5 kristaps 61: #define ROFF_NESTED (1 << 0) /* Nested-layout. */
62: #define ROFF_PARSED (1 << 1) /* "Parsed". */
63: #define ROFF_CALLABLE (1 << 2) /* "Callable". */
64: #define ROFF_QUOTES (1 << 3) /* Quoted args. */
1.1 kristaps 65: };
66:
67: struct roffarg {
68: int flags;
1.5 kristaps 69: #define ROFF_VALUE (1 << 0) /* Has a value. */
1.1 kristaps 70: };
71:
72: struct roffnode {
1.5 kristaps 73: int tok; /* Token id. */
74: struct roffnode *parent; /* Parent (or NULL). */
75: size_t line; /* Parsed at line. */
1.1 kristaps 76: };
77:
78: struct rofftree {
1.5 kristaps 79: struct roffnode *last; /* Last parsed node. */
80: time_t date; /* `Dd' results. */
81: char os[64]; /* `Os' results. */
82: char title[64]; /* `Dt' results. */
83: char section[64]; /* `Dt' results. */
84: char volume[64]; /* `Dt' results. */
1.1 kristaps 85: int state;
1.5 kristaps 86: #define ROFF_PRELUDE (1 << 1) /* In roff prelude. */
87: /* FIXME: if we had prev ptrs, this wouldn't be necessary. */
88: #define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */
89: #define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */
90: #define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */
91: #define ROFF_BODY (1 << 5) /* In roff body. */
92: struct md_mbuf *mbuf; /* Output (or NULL). */
93: const struct md_args *args; /* Global args. */
94: const struct md_rbuf *rbuf; /* Input. */
1.6 ! kristaps 95: const struct roffcb *cb;
1.1 kristaps 96: };
97:
98: static int roff_Dd(ROFFCALL_ARGS);
99: static int roff_Dt(ROFFCALL_ARGS);
100: static int roff_Os(ROFFCALL_ARGS);
1.6 ! kristaps 101:
1.2 kristaps 102: static int roff_layout(ROFFCALL_ARGS);
103: static int roff_text(ROFFCALL_ARGS);
1.6 ! kristaps 104: static int roff_comment(ROFFCALL_ARGS);
1.1 kristaps 105:
1.4 kristaps 106: static struct roffnode *roffnode_new(int, struct rofftree *);
1.1 kristaps 107: static void roffnode_free(int, struct rofftree *);
108:
109: static int rofffindtok(const char *);
110: static int rofffindarg(const char *);
1.2 kristaps 111: static int rofffindcallable(const char *);
1.1 kristaps 112: static int roffargs(int, char *, char **);
1.5 kristaps 113: static int roffargok(int, int);
114: static int roffnextopt(int, const char ***, char **);
1.1 kristaps 115: static int roffparse(struct rofftree *, char *, size_t);
116: static int textparse(const struct rofftree *,
117: const char *, size_t);
118:
1.6 ! kristaps 119:
1.5 kristaps 120: static const int roffarg_An[] = {
121: ROFF_Split, ROFF_Nosplit, ROFF_ARGMAX };
1.6 ! kristaps 122:
1.5 kristaps 123: static const int roffarg_Bd[] = {
124: ROFF_Ragged, ROFF_Unfilled, ROFF_Literal, ROFF_File,
125: ROFF_Offset, ROFF_ARGMAX };
1.6 ! kristaps 126:
1.5 kristaps 127: static const int roffarg_Bl[] = {
128: ROFF_Bullet, ROFF_Dash, ROFF_Hyphen, ROFF_Item, ROFF_Enum,
129: ROFF_Tag, ROFF_Diag, ROFF_Hang, ROFF_Ohang, ROFF_Inset,
130: ROFF_Column, ROFF_Offset, ROFF_ARGMAX };
131:
1.6 ! kristaps 132: static const int roffchild_Bl[] = { ROFF_It, ROFF_El, ROFF_MAX };
! 133:
! 134: static const int roffparent_El[] = { ROFF_Bl, ROFF_It, ROFF_MAX };
! 135:
! 136: static const int roffparent_It[] = { ROFF_Bl, ROFF_MAX };
1.1 kristaps 137:
1.5 kristaps 138: /* Table of all known tokens. */
1.4 kristaps 139: static const struct rofftok tokens[ROFF_MAX] = {
1.6 ! kristaps 140: {roff_comment, NULL, NULL, NULL, 0, ROFF_COMMENT, 0 }, /* \" */
! 141: { roff_Dd, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dd */
! 142: { roff_Dt, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dt */
! 143: { roff_Os, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Os */
! 144: { roff_layout, NULL, NULL, NULL, ROFF_Sh, ROFF_LAYOUT, ROFF_PARSED }, /* Sh */
! 145: { roff_layout, NULL, NULL, NULL, ROFF_Ss, ROFF_LAYOUT, ROFF_PARSED }, /* Ss */
! 146: { roff_text, NULL, NULL, NULL, ROFF_Pp, ROFF_TEXT, 0 }, /* Pp */
! 147: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* D1 */
! 148: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Dl */
! 149: { NULL, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Bd */
! 150: { NULL, NULL, NULL, NULL, 0, ROFF_LAYOUT, 0 }, /* Ed */
! 151: { roff_layout, roffarg_Bl, NULL, roffchild_Bl, 0, ROFF_LAYOUT, 0 }, /* Bl */
! 152: { roff_layout, NULL, roffparent_El, NULL, ROFF_Bl, ROFF_LAYOUT, 0 }, /* El */
! 153: { roff_layout, NULL, roffparent_It, NULL, ROFF_It, ROFF_LAYOUT, 0 }, /* It */
! 154: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ad */
! 155: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* An */
! 156: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ar */
! 157: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Cd */
! 158: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Cm */
! 159: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dv */
! 160: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Er */
! 161: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ev */
! 162: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ex */
! 163: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fa */
! 164: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Fd */
! 165: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fl */
! 166: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Fn */
! 167: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ft */
! 168: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ic */
! 169: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* In */
! 170: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Li */
! 171: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Nd */
! 172: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Nm */
! 173: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Op */
! 174: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ot */
! 175: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pa */
! 176: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rv */
! 177: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* St */
! 178: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Va */
! 179: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Vt */
! 180: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xr */
! 181: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %A */
! 182: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %B */
! 183: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %D */
! 184: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %I */
! 185: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE}, /* %J */
! 186: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %N */
! 187: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %O */
! 188: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %P */
! 189: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %R */
! 190: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* %T */
! 191: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* %V */
! 192: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ac */
! 193: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ao */
! 194: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Aq */
! 195: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* At */
! 196: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bc */
! 197: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Bf */
! 198: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bo */
! 199: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Bq */
! 200: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bsx */
! 201: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Bx */
! 202: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Db */
! 203: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dc */
! 204: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Do */
! 205: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Dq */
! 206: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ec */
! 207: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Ef */
! 208: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Em */
! 209: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Eo */
! 210: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Fx */
! 211: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ms */
! 212: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* No */
! 213: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ns */
! 214: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Nx */
! 215: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ox */
! 216: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pc */
! 217: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Pf */
! 218: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Po */
! 219: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Pq */
! 220: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qc */
! 221: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Ql */
! 222: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qo */
! 223: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Qq */
! 224: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Re */
! 225: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Rs */
! 226: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sc */
! 227: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* So */
! 228: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sq */
! 229: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, 0 }, /* Sm */
! 230: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sx */
! 231: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Sy */
! 232: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Tn */
! 233: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED }, /* Ux */
! 234: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xc */
! 235: { NULL, NULL, NULL, NULL, 0, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE }, /* Xo */
1.1 kristaps 236: };
237:
1.5 kristaps 238: /* Table of all known token arguments. */
1.4 kristaps 239: static const struct roffarg tokenargs[ROFF_ARGMAX] = {
1.5 kristaps 240: { 0 }, /* split */
241: { 0 }, /* nosplit */
242: { 0 }, /* ragged */
243: { 0 }, /* unfilled */
244: { 0 }, /* literal */
245: { ROFF_VALUE }, /* file */
246: { ROFF_VALUE }, /* offset */
247: { 0 }, /* bullet */
248: { 0 }, /* dash */
249: { 0 }, /* hyphen */
250: { 0 }, /* item */
251: { 0 }, /* enum */
252: { 0 }, /* tag */
253: { 0 }, /* diag */
254: { 0 }, /* hang */
255: { 0 }, /* ohang */
256: { 0 }, /* inset */
257: { 0 }, /* column */
258: { 0 }, /* width */
259: { 0 }, /* compact */
1.1 kristaps 260: };
261:
1.5 kristaps 262: const char *const toknamesp[ROFF_MAX] =
263: {
264: "\\\"",
265: "Dd", /* Title macros. */
266: "Dt",
267: "Os",
268: "Sh", /* Layout macros */
269: "Ss",
270: "Pp",
271: "D1",
272: "Dl",
273: "Bd",
274: "Ed",
275: "Bl",
276: "El",
277: "It",
278: "Ad", /* Text macros. */
279: "An",
280: "Ar",
281: "Cd",
282: "Cm",
283: "Dr",
284: "Er",
285: "Ev",
286: "Ex",
287: "Fa",
288: "Fd",
289: "Fl",
290: "Fn",
291: "Ft",
292: "Ex",
293: "Ic",
294: "In",
295: "Li",
296: "Nd",
297: "Nm",
298: "Op",
299: "Ot",
300: "Pa",
301: "Rv",
302: "St",
303: "Va",
304: "Vt",
305: "Xr",
306: "\%A", /* General text macros. */
307: "\%B",
308: "\%D",
309: "\%I",
310: "\%J",
311: "\%N",
312: "\%O",
313: "\%P",
314: "\%R",
315: "\%T",
316: "\%V",
317: "Ac",
318: "Ao",
319: "Aq",
320: "At",
321: "Bc",
322: "Bf",
323: "Bo",
324: "Bq",
325: "Bsx",
326: "Bx",
327: "Db",
328: "Dc",
329: "Do",
330: "Dq",
331: "Ec",
332: "Ef",
333: "Em",
334: "Eo",
335: "Fx",
336: "Ms",
337: "No",
338: "Ns",
339: "Nx",
340: "Ox",
341: "Pc",
342: "Pf",
343: "Po",
344: "Pq",
345: "Qc",
346: "Ql",
347: "Qo",
348: "Qq",
349: "Re",
350: "Rs",
351: "Sc",
352: "So",
353: "Sq",
354: "Sm",
355: "Sx",
356: "Sy",
357: "Tn",
358: "Ux",
359: "Xc", /* FIXME: do not support! */
360: "Xo", /* FIXME: do not support! */
361: };
362:
363: const char *const tokargnamesp[ROFF_ARGMAX] =
364: {
365: "split",
366: "nosplit",
367: "ragged",
368: "unfilled",
369: "literal",
370: "file",
371: "offset",
372: "bullet",
373: "dash",
374: "hyphen",
375: "item",
376: "enum",
377: "tag",
378: "diag",
379: "hang",
380: "ohang",
381: "inset",
382: "column",
383: "width",
384: "compact",
385: };
386:
387: const char *const *toknames = toknamesp;
388: const char *const *tokargnames = tokargnamesp;
1.4 kristaps 389:
1.1 kristaps 390:
391: int
392: roff_free(struct rofftree *tree, int flush)
393: {
394: int error;
395:
396: assert(tree->mbuf);
397: if ( ! flush)
398: tree->mbuf = NULL;
399:
400: /* LINTED */
401: while (tree->last)
402: if ( ! (*tokens[tree->last->tok].cb)
1.2 kristaps 403: (tree->last->tok, tree, NULL, ROFF_EXIT))
1.1 kristaps 404: /* Disallow flushing. */
405: tree->mbuf = NULL;
406:
407: error = tree->mbuf ? 0 : 1;
408:
409: if (tree->mbuf && (ROFF_PRELUDE & tree->state)) {
410: warnx("%s: prelude never finished",
411: tree->rbuf->name);
412: error = 1;
413: }
414:
415: free(tree);
416: return(error ? 0 : 1);
417: }
418:
419:
420: struct rofftree *
421: roff_alloc(const struct md_args *args, struct md_mbuf *out,
1.6 ! kristaps 422: const struct md_rbuf *in, const struct roffcb *cb)
1.1 kristaps 423: {
424: struct rofftree *tree;
425:
426: if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
427: warn("malloc");
428: return(NULL);
429: }
430:
431: tree->state = ROFF_PRELUDE;
432: tree->args = args;
433: tree->mbuf = out;
434: tree->rbuf = in;
1.6 ! kristaps 435: tree->cb = cb;
1.1 kristaps 436:
437: return(tree);
438: }
439:
440:
441: int
442: roff_engine(struct rofftree *tree, char *buf, size_t sz)
443: {
444:
445: if (0 == sz) {
446: warnx("%s: blank line (line %zu)",
447: tree->rbuf->name,
448: tree->rbuf->line);
449: return(0);
450: } else if ('.' != *buf)
451: return(textparse(tree, buf, sz));
452:
453: return(roffparse(tree, buf, sz));
454: }
455:
456:
457: static int
458: textparse(const struct rofftree *tree, const char *buf, size_t sz)
459: {
460:
461: if (NULL == tree->last) {
462: warnx("%s: unexpected text (line %zu)",
463: tree->rbuf->name,
464: tree->rbuf->line);
465: return(0);
466: } else if (NULL == tree->last->parent) {
467: warnx("%s: disallowed text (line %zu)",
468: tree->rbuf->name,
469: tree->rbuf->line);
470: return(0);
471: }
472:
473: /* Print text. */
474:
475: return(1);
476: }
477:
478:
479: static int
480: roffargs(int tok, char *buf, char **argv)
481: {
482: int i;
483:
484: (void)tok;/* FIXME: quotable strings? */
485:
486: assert(tok >= 0 && tok < ROFF_MAX);
487: assert('.' == *buf);
488:
489: /* LINTED */
490: for (i = 0; *buf && i < ROFF_MAXARG; i++) {
491: argv[i] = buf++;
492: while (*buf && ! isspace(*buf))
493: buf++;
494: if (0 == *buf) {
495: continue;
496: }
497: *buf++ = 0;
498: while (*buf && isspace(*buf))
499: buf++;
500: }
501:
502: assert(i > 0);
503: if (i < ROFF_MAXARG)
504: argv[i] = NULL;
505:
506: return(ROFF_MAXARG > i);
507: }
508:
509:
1.6 ! kristaps 510: /* XXX */
! 511: static int
! 512: roffscan(int tok, const int *tokv)
! 513: {
! 514: if (NULL == tokv)
! 515: return(1);
! 516:
! 517: for ( ; ROFF_MAX != *tokv; tokv++)
! 518: if (tok == *tokv)
! 519: return(1);
! 520:
! 521: return(0);
! 522: }
! 523:
! 524:
1.1 kristaps 525: static int
526: roffparse(struct rofftree *tree, char *buf, size_t sz)
527: {
528: int tok, t;
1.6 ! kristaps 529: struct roffnode *n;
1.1 kristaps 530: char *argv[ROFF_MAXARG];
531: const char **argvp;
532:
533: assert(sz > 0);
534:
1.6 ! kristaps 535: if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
! 536: warnx("%s: unknown line macro (line %zu)",
! 537: tree->rbuf->name, tree->rbuf->line);
! 538: return(0);
! 539: } else if (NULL == tokens[tok].cb) {
! 540: warnx("%s: macro `%s' not supported (line %zu)",
! 541: tree->rbuf->name, toknames[tok],
1.1 kristaps 542: tree->rbuf->line);
543: return(0);
1.6 ! kristaps 544: } else if (ROFF_COMMENT == tokens[tok].type)
! 545: return(1);
1.5 kristaps 546:
1.6 ! kristaps 547: if ( ! roffargs(tok, buf, argv)) {
! 548: warnx("%s: too many args to `%s' (line %zu)",
! 549: tree->rbuf->name, toknames[tok],
1.1 kristaps 550: tree->rbuf->line);
551: return(0);
1.6 ! kristaps 552: } else
! 553: argvp = (const char **)argv + 1;
! 554:
! 555: /*
! 556: * Prelude macros break some assumptions: branch now.
! 557: */
1.1 kristaps 558:
1.6 ! kristaps 559: if (ROFF_PRELUDE & tree->state) {
! 560: assert(NULL == tree->last);
! 561: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
! 562: } else
! 563: assert(tree->last);
! 564:
! 565: assert(ROFF_BODY & tree->state);
! 566:
! 567: /*
! 568: * First check that our possible parents and parent's possible
! 569: * children are satisfied.
! 570: */
! 571:
! 572: if ( ! roffscan(tree->last->tok, tokens[tok].parents)) {
! 573: warnx("%s: invalid parent `%s' for `%s' (line %zu)",
! 574: tree->rbuf->name,
! 575: toknames[tree->last->tok],
! 576: toknames[tok], tree->rbuf->line);
! 577: return(0);
! 578: }
! 579:
! 580: if ( ! roffscan(tok, tokens[tree->last->tok].children)) {
! 581: warnx("%s: invalid child `%s' for `%s' (line %zu)",
1.4 kristaps 582: tree->rbuf->name, toknames[tok],
1.6 ! kristaps 583: toknames[tree->last->tok],
1.1 kristaps 584: tree->rbuf->line);
585: return(0);
586: }
587:
588: /*
1.6 ! kristaps 589: * Branch if we're not a layout token.
1.1 kristaps 590: */
591:
1.6 ! kristaps 592: if (ROFF_LAYOUT != tokens[tok].type)
! 593: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
1.1 kristaps 594:
1.6 ! kristaps 595: /*
! 596: * Check our scope rules.
! 597: */
1.1 kristaps 598:
1.6 ! kristaps 599: if (0 == tokens[tok].ctx)
! 600: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
1.1 kristaps 601:
1.6 ! kristaps 602: if (tok == tokens[tok].ctx) {
! 603: for (n = tree->last; n; n = n->parent) {
! 604: assert(0 == tokens[n->tok].ctx ||
! 605: n->tok == tokens[n->tok].ctx);
! 606: if (n->tok == tok)
! 607: break;
! 608: }
! 609: if (NULL == n) {
! 610: #ifdef DEBUG
! 611: (void)printf("scope: new `%s'\n",
! 612: toknames[tok]);
! 613: #endif
! 614: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
1.1 kristaps 615: }
616: do {
617: t = tree->last->tok;
1.6 ! kristaps 618: #ifdef DEBUG
! 619: (void)printf("scope: closing `%s'\n",
! 620: toknames[t]);
! 621: #endif
! 622: if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
1.1 kristaps 623: return(0);
624: } while (t != tok);
1.6 ! kristaps 625:
! 626: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
1.1 kristaps 627: }
628:
1.6 ! kristaps 629: assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
! 630:
! 631: do {
! 632: t = tree->last->tok;
! 633: #ifdef DEBUG
! 634: (void)printf("scope: closing `%s'\n", toknames[t]);
! 635: #endif
! 636: if ( ! (*tokens[t].cb)(t, tree, NULL, ROFF_EXIT))
! 637: return(0);
! 638: } while (t != tokens[tok].ctx);
1.1 kristaps 639:
1.2 kristaps 640: return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
1.1 kristaps 641: }
642:
643:
644: static int
645: rofffindarg(const char *name)
646: {
647: size_t i;
648:
649: /* FIXME: use a table, this is slow but ok for now. */
650:
651: /* LINTED */
652: for (i = 0; i < ROFF_ARGMAX; i++)
653: /* LINTED */
1.4 kristaps 654: if (0 == strcmp(name, tokargnames[i]))
1.1 kristaps 655: return((int)i);
656:
657: return(ROFF_ARGMAX);
658: }
659:
660:
661: static int
1.6 ! kristaps 662: rofffindtok(const char *buf)
1.1 kristaps 663: {
1.6 ! kristaps 664: char token[4];
1.1 kristaps 665: size_t i;
666:
1.6 ! kristaps 667: for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
! 668: token[i] = *buf;
! 669:
! 670: if (i == 3) {
! 671: #ifdef DEBUG
! 672: (void)printf("lookup: macro too long: `%s'\n", buf);
! 673: #endif
! 674: return(ROFF_MAX);
! 675: }
! 676:
! 677: token[i] = 0;
! 678:
! 679: #ifdef DEBUG
! 680: (void)printf("lookup: `%s'\n", token);
! 681: #endif
! 682:
1.1 kristaps 683: /* FIXME: use a table, this is slow but ok for now. */
684:
685: /* LINTED */
686: for (i = 0; i < ROFF_MAX; i++)
687: /* LINTED */
1.6 ! kristaps 688: if (0 == strcmp(toknames[i], token))
1.1 kristaps 689: return((int)i);
690:
691: return(ROFF_MAX);
692: }
693:
694:
1.2 kristaps 695: static int
696: rofffindcallable(const char *name)
697: {
698: int c;
699:
700: if (ROFF_MAX == (c = rofffindtok(name)))
701: return(ROFF_MAX);
702: return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
703: }
704:
705:
1.1 kristaps 706: static struct roffnode *
1.4 kristaps 707: roffnode_new(int tokid, struct rofftree *tree)
1.1 kristaps 708: {
709: struct roffnode *p;
710:
711: if (NULL == (p = malloc(sizeof(struct roffnode)))) {
712: warn("malloc");
713: return(NULL);
714: }
715:
1.4 kristaps 716: p->line = tree->rbuf->line;
1.1 kristaps 717: p->tok = tokid;
718: p->parent = tree->last;
719: tree->last = p;
720: return(p);
721: }
722:
723:
1.5 kristaps 724: static int
725: roffargok(int tokid, int argid)
726: {
727: const int *c;
728:
729: if (NULL == (c = tokens[tokid].args))
730: return(0);
731:
732: for ( ; ROFF_ARGMAX != *c; c++)
733: if (argid == *c)
734: return(1);
735:
736: return(0);
737: }
738:
739:
1.1 kristaps 740: static void
741: roffnode_free(int tokid, struct rofftree *tree)
742: {
743: struct roffnode *p;
744:
745: assert(tree->last);
746: assert(tree->last->tok == tokid);
747:
748: p = tree->last;
749: tree->last = tree->last->parent;
750: free(p);
751: }
752:
753:
1.6 ! kristaps 754: static int
! 755: roffnextopt(int tok, const char ***in, char **val)
! 756: {
! 757: const char *arg, **argv;
! 758: int v;
! 759:
! 760: *val = NULL;
! 761: argv = *in;
! 762: assert(argv);
! 763:
! 764: if (NULL == (arg = *argv))
! 765: return(-1);
! 766: if ('-' != *arg)
! 767: return(-1);
! 768:
! 769: /* FIXME: should we let this slide... ? */
! 770:
! 771: if (ROFF_ARGMAX == (v = rofffindarg(&arg[1])))
! 772: return(-1);
! 773:
! 774: /* FIXME: should we let this slide... ? */
! 775:
! 776: if ( ! roffargok(tok, v))
! 777: return(-1);
! 778: if ( ! (ROFF_VALUE & tokenargs[v].flags))
! 779: return(v);
! 780:
! 781: *in = ++argv;
! 782:
! 783: /* FIXME: what if this looks like a roff token or argument? */
! 784:
! 785: return(*argv ? v : ROFF_ARGMAX);
! 786: }
! 787:
! 788:
1.1 kristaps 789: /* ARGSUSED */
790: static int
791: roff_Dd(ROFFCALL_ARGS)
792: {
793:
1.4 kristaps 794: if (ROFF_BODY & tree->state) {
795: assert( ! (ROFF_PRELUDE & tree->state));
796: assert(ROFF_PRELUDE_Dd & tree->state);
797: return(roff_text(tok, tree, argv, type));
798: }
799:
1.1 kristaps 800: assert(ROFF_PRELUDE & tree->state);
1.4 kristaps 801: assert( ! (ROFF_BODY & tree->state));
802:
1.6 ! kristaps 803: if (ROFF_PRELUDE_Dd & tree->state) {
! 804: warnx("%s: prelude `Dd' repeated (line %zu)",
! 805: tree->rbuf->name, tree->rbuf->line);
! 806: return(0);
! 807: } else if (ROFF_PRELUDE_Dt & tree->state) {
1.1 kristaps 808: warnx("%s: prelude `Dd' out-of-order (line %zu)",
809: tree->rbuf->name, tree->rbuf->line);
810: return(0);
811: }
812:
1.4 kristaps 813: /* TODO: parse date. */
814:
1.1 kristaps 815: assert(NULL == tree->last);
816: tree->state |= ROFF_PRELUDE_Dd;
817:
818: return(1);
819: }
820:
821:
822: /* ARGSUSED */
823: static int
824: roff_Dt(ROFFCALL_ARGS)
825: {
826:
1.4 kristaps 827: if (ROFF_BODY & tree->state) {
828: assert( ! (ROFF_PRELUDE & tree->state));
829: assert(ROFF_PRELUDE_Dt & tree->state);
830: return(roff_text(tok, tree, argv, type));
831: }
832:
1.1 kristaps 833: assert(ROFF_PRELUDE & tree->state);
1.4 kristaps 834: assert( ! (ROFF_BODY & tree->state));
835:
1.6 ! kristaps 836: if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
1.1 kristaps 837: warnx("%s: prelude `Dt' out-of-order (line %zu)",
838: tree->rbuf->name, tree->rbuf->line);
839: return(0);
1.6 ! kristaps 840: } else if (ROFF_PRELUDE_Dt & tree->state) {
! 841: warnx("%s: prelude `Dt' repeated (line %zu)",
! 842: tree->rbuf->name, tree->rbuf->line);
! 843: return(0);
1.1 kristaps 844: }
845:
1.4 kristaps 846: /* TODO: parse date. */
847:
1.1 kristaps 848: assert(NULL == tree->last);
849: tree->state |= ROFF_PRELUDE_Dt;
850:
851: return(1);
852: }
853:
854:
855: /* ARGSUSED */
856: static int
857: roff_Os(ROFFCALL_ARGS)
858: {
859:
860: if (ROFF_EXIT == type) {
1.4 kristaps 861: assert(ROFF_PRELUDE_Os & tree->state);
862: return(roff_layout(tok, tree, argv, type));
863: } else if (ROFF_BODY & tree->state) {
864: assert( ! (ROFF_PRELUDE & tree->state));
865: assert(ROFF_PRELUDE_Os & tree->state);
866: return(roff_text(tok, tree, argv, type));
867: }
1.1 kristaps 868:
869: assert(ROFF_PRELUDE & tree->state);
870: if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
871: ! (ROFF_PRELUDE_Dd & tree->state)) {
872: warnx("%s: prelude `Os' out-of-order (line %zu)",
873: tree->rbuf->name, tree->rbuf->line);
874: return(0);
875: }
876:
1.4 kristaps 877: /* TODO: extract OS. */
1.1 kristaps 878:
879: tree->state |= ROFF_PRELUDE_Os;
880: tree->state &= ~ROFF_PRELUDE;
881: tree->state |= ROFF_BODY;
882:
1.4 kristaps 883: assert(NULL == tree->last);
884:
885: return(roff_layout(tok, tree, argv, type));
1.1 kristaps 886: }
887:
888:
889: /* ARGSUSED */
890: static int
1.2 kristaps 891: roff_layout(ROFFCALL_ARGS)
1.1 kristaps 892: {
1.2 kristaps 893: int i, c, argcp[ROFF_MAXARG];
894: char *v, *argvp[ROFF_MAXARG];
1.1 kristaps 895:
1.4 kristaps 896: if (ROFF_PRELUDE & tree->state) {
897: warnx("%s: macro `%s' called in prelude (line %zu)",
1.5 kristaps 898: tree->rbuf->name,
899: toknames[tok],
1.4 kristaps 900: tree->rbuf->line);
901: return(0);
902: }
903:
1.2 kristaps 904: if (ROFF_EXIT == type) {
905: roffnode_free(tok, tree);
1.6 ! kristaps 906: return((*tree->cb->roffblkout)(tok));
1.2 kristaps 907: }
1.1 kristaps 908:
1.2 kristaps 909: i = 0;
1.5 kristaps 910:
911: while (-1 != (c = roffnextopt(tok, &argv, &v))) {
1.2 kristaps 912: if (ROFF_ARGMAX == c) {
1.5 kristaps 913: warnx("%s: error parsing `%s' args (line %zu)",
914: tree->rbuf->name,
915: toknames[tok],
916: tree->rbuf->line);
917: return(0);
918: } else if ( ! roffargok(tok, c)) {
919: warnx("%s: arg `%s' not for `%s' (line %zu)",
1.1 kristaps 920: tree->rbuf->name,
1.5 kristaps 921: tokargnames[c],
1.4 kristaps 922: toknames[tok],
1.1 kristaps 923: tree->rbuf->line);
924: return(0);
925: }
1.2 kristaps 926: argcp[i] = c;
927: argvp[i] = v;
1.5 kristaps 928: i++;
1.1 kristaps 929: argv++;
930: }
931:
1.5 kristaps 932: argcp[i] = ROFF_ARGMAX;
933: argvp[i] = NULL;
934:
1.4 kristaps 935: if (NULL == roffnode_new(tok, tree))
1.2 kristaps 936: return(0);
937:
1.6 ! kristaps 938: if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
1.2 kristaps 939: return(0);
940:
941: if ( ! (ROFF_PARSED & tokens[tok].flags)) {
942: /* TODO: print all tokens. */
943:
1.6 ! kristaps 944: if ( ! ((*tree->cb->roffout)(tok)))
1.2 kristaps 945: return(0);
1.6 ! kristaps 946: return((*tree->cb->roffblkin)(tok));
1.2 kristaps 947: }
948:
1.1 kristaps 949: while (*argv) {
1.2 kristaps 950: if (2 >= strlen(*argv) && ROFF_MAX !=
951: (c = rofffindcallable(*argv)))
952: if ( ! (*tokens[c].cb)(c, tree,
953: argv + 1, ROFF_ENTER))
954: return(0);
955:
956: /* TODO: print token. */
957: argv++;
958: }
959:
1.6 ! kristaps 960: if ( ! ((*tree->cb->roffout)(tok)))
1.2 kristaps 961: return(0);
962:
1.6 ! kristaps 963: return((*tree->cb->roffblkin)(tok));
1.2 kristaps 964: }
965:
966:
967: /* ARGSUSED */
968: static int
969: roff_text(ROFFCALL_ARGS)
970: {
971: int i, c, argcp[ROFF_MAXARG];
972: char *v, *argvp[ROFF_MAXARG];
973:
1.4 kristaps 974: if (ROFF_PRELUDE & tree->state) {
975: warnx("%s: macro `%s' called in prelude (line %zu)",
1.5 kristaps 976: tree->rbuf->name,
977: toknames[tok],
1.4 kristaps 978: tree->rbuf->line);
979: return(0);
980: }
981:
1.2 kristaps 982: i = 0;
1.5 kristaps 983:
984: while (-1 != (c = roffnextopt(tok, &argv, &v))) {
1.2 kristaps 985: if (ROFF_ARGMAX == c) {
1.5 kristaps 986: warnx("%s: error parsing `%s' args (line %zu)",
1.2 kristaps 987: tree->rbuf->name,
1.4 kristaps 988: toknames[tok],
1.2 kristaps 989: tree->rbuf->line);
990: return(0);
1.5 kristaps 991: }
1.2 kristaps 992: argcp[i] = c;
993: argvp[i] = v;
1.5 kristaps 994: i++;
1.1 kristaps 995: argv++;
996: }
997:
1.5 kristaps 998: argcp[i] = ROFF_ARGMAX;
999: argvp[i] = NULL;
1000:
1.6 ! kristaps 1001: if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
1.2 kristaps 1002: return(0);
1.1 kristaps 1003:
1.2 kristaps 1004: if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1005: /* TODO: print all tokens. */
1.6 ! kristaps 1006: return((*tree->cb->roffout)(tok));
1.2 kristaps 1007: }
1.1 kristaps 1008:
1.2 kristaps 1009: while (*argv) {
1010: if (2 >= strlen(*argv) && ROFF_MAX !=
1011: (c = rofffindcallable(*argv)))
1012: if ( ! (*tokens[c].cb)(c, tree,
1013: argv + 1, ROFF_ENTER))
1014: return(0);
1015:
1016: /* TODO: print token. */
1017: argv++;
1018: }
1019:
1.6 ! kristaps 1020: return((*tree->cb->roffout)(tok));
! 1021: }
! 1022:
! 1023:
! 1024: /* ARGUSED */
! 1025: static int
! 1026: roff_comment(ROFFCALL_ARGS)
! 1027: {
! 1028:
! 1029: return(1);
1.1 kristaps 1030: }
CVSweb