]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.6 2008/11/26 16:50:34 kristaps Exp $ */
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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
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.
27 #include "libmdocml.h"
30 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
32 /* FIXME: warn about empty lists. */
34 #define ROFF_MAXARG 10
47 #define ROFFCALL_ARGS \
48 int tok, struct rofftree *tree, \
49 const char *argv[], enum roffd type
54 int (*cb
)(ROFFCALL_ARGS
); /* Callback. */
55 const int *args
; /* Args (or NULL). */
59 enum rofftype type
; /* Type of macro. */
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. */
69 #define ROFF_VALUE (1 << 0) /* Has a value. */
73 int tok
; /* Token id. */
74 struct roffnode
*parent
; /* Parent (or NULL). */
75 size_t line
; /* Parsed at line. */
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. */
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. */
95 const struct roffcb
*cb
;
98 static int roff_Dd(ROFFCALL_ARGS
);
99 static int roff_Dt(ROFFCALL_ARGS
);
100 static int roff_Os(ROFFCALL_ARGS
);
102 static int roff_layout(ROFFCALL_ARGS
);
103 static int roff_text(ROFFCALL_ARGS
);
104 static int roff_comment(ROFFCALL_ARGS
);
106 static struct roffnode
*roffnode_new(int, struct rofftree
*);
107 static void roffnode_free(int, struct rofftree
*);
109 static int rofffindtok(const char *);
110 static int rofffindarg(const char *);
111 static int rofffindcallable(const char *);
112 static int roffargs(int, char *, char **);
113 static int roffargok(int, int);
114 static int roffnextopt(int, const char ***, char **);
115 static int roffparse(struct rofftree
*, char *, size_t);
116 static int textparse(const struct rofftree
*,
117 const char *, size_t);
120 static const int roffarg_An
[] = {
121 ROFF_Split
, ROFF_Nosplit
, ROFF_ARGMAX
};
123 static const int roffarg_Bd
[] = {
124 ROFF_Ragged
, ROFF_Unfilled
, ROFF_Literal
, ROFF_File
,
125 ROFF_Offset
, ROFF_ARGMAX
};
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
};
132 static const int roffchild_Bl
[] = { ROFF_It
, ROFF_El
, ROFF_MAX
};
134 static const int roffparent_El
[] = { ROFF_Bl
, ROFF_It
, ROFF_MAX
};
136 static const int roffparent_It
[] = { ROFF_Bl
, ROFF_MAX
};
138 /* Table of all known tokens. */
139 static const struct rofftok tokens
[ROFF_MAX
] = {
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 */
238 /* Table of all known token arguments. */
239 static const struct roffarg tokenargs
[ROFF_ARGMAX
] = {
243 { 0 }, /* unfilled */
245 { ROFF_VALUE
}, /* file */
246 { ROFF_VALUE
}, /* offset */
262 const char *const toknamesp
[ROFF_MAX
] =
265 "Dd", /* Title macros. */
268 "Sh", /* Layout macros */
278 "Ad", /* Text macros. */
306 "\%A", /* General text macros. */
359 "Xc", /* FIXME: do not support! */
360 "Xo", /* FIXME: do not support! */
363 const char *const tokargnamesp
[ROFF_ARGMAX
] =
387 const char *const *toknames
= toknamesp
;
388 const char *const *tokargnames
= tokargnamesp
;
392 roff_free(struct rofftree
*tree
, int flush
)
402 if ( ! (*tokens
[tree
->last
->tok
].cb
)
403 (tree
->last
->tok
, tree
, NULL
, ROFF_EXIT
))
404 /* Disallow flushing. */
407 error
= tree
->mbuf
? 0 : 1;
409 if (tree
->mbuf
&& (ROFF_PRELUDE
& tree
->state
)) {
410 warnx("%s: prelude never finished",
416 return(error
? 0 : 1);
421 roff_alloc(const struct md_args
*args
, struct md_mbuf
*out
,
422 const struct md_rbuf
*in
, const struct roffcb
*cb
)
424 struct rofftree
*tree
;
426 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
)))) {
431 tree
->state
= ROFF_PRELUDE
;
442 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
446 warnx("%s: blank line (line %zu)",
450 } else if ('.' != *buf
)
451 return(textparse(tree
, buf
, sz
));
453 return(roffparse(tree
, buf
, sz
));
458 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
461 if (NULL
== tree
->last
) {
462 warnx("%s: unexpected text (line %zu)",
466 } else if (NULL
== tree
->last
->parent
) {
467 warnx("%s: disallowed text (line %zu)",
480 roffargs(int tok
, char *buf
, char **argv
)
484 (void)tok
;/* FIXME: quotable strings? */
486 assert(tok
>= 0 && tok
< ROFF_MAX
);
490 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
492 while (*buf
&& ! isspace(*buf
))
498 while (*buf
&& isspace(*buf
))
506 return(ROFF_MAXARG
> i
);
512 roffscan(int tok
, const int *tokv
)
517 for ( ; ROFF_MAX
!= *tokv
; tokv
++)
526 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
530 char *argv
[ROFF_MAXARG
];
535 if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
536 warnx("%s: unknown line macro (line %zu)",
537 tree
->rbuf
->name
, tree
->rbuf
->line
);
539 } else if (NULL
== tokens
[tok
].cb
) {
540 warnx("%s: macro `%s' not supported (line %zu)",
541 tree
->rbuf
->name
, toknames
[tok
],
544 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
547 if ( ! roffargs(tok
, buf
, argv
)) {
548 warnx("%s: too many args to `%s' (line %zu)",
549 tree
->rbuf
->name
, toknames
[tok
],
553 argvp
= (const char **)argv
+ 1;
556 * Prelude macros break some assumptions: branch now.
559 if (ROFF_PRELUDE
& tree
->state
) {
560 assert(NULL
== tree
->last
);
561 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
565 assert(ROFF_BODY
& tree
->state
);
568 * First check that our possible parents and parent's possible
569 * children are satisfied.
572 if ( ! roffscan(tree
->last
->tok
, tokens
[tok
].parents
)) {
573 warnx("%s: invalid parent `%s' for `%s' (line %zu)",
575 toknames
[tree
->last
->tok
],
576 toknames
[tok
], tree
->rbuf
->line
);
580 if ( ! roffscan(tok
, tokens
[tree
->last
->tok
].children
)) {
581 warnx("%s: invalid child `%s' for `%s' (line %zu)",
582 tree
->rbuf
->name
, toknames
[tok
],
583 toknames
[tree
->last
->tok
],
589 * Branch if we're not a layout token.
592 if (ROFF_LAYOUT
!= tokens
[tok
].type
)
593 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
596 * Check our scope rules.
599 if (0 == tokens
[tok
].ctx
)
600 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
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
);
611 (void)printf("scope: new `%s'\n",
614 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
619 (void)printf("scope: closing `%s'\n",
622 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
626 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
629 assert(tok
!= tokens
[tok
].ctx
&& 0 != tokens
[tok
].ctx
);
634 (void)printf("scope: closing `%s'\n", toknames
[t
]);
636 if ( ! (*tokens
[t
].cb
)(t
, tree
, NULL
, ROFF_EXIT
))
638 } while (t
!= tokens
[tok
].ctx
);
640 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
645 rofffindarg(const char *name
)
649 /* FIXME: use a table, this is slow but ok for now. */
652 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
654 if (0 == strcmp(name
, tokargnames
[i
]))
662 rofffindtok(const char *buf
)
667 for (i
= 0; *buf
&& ! isspace(*buf
) && i
< 3; i
++, buf
++)
672 (void)printf("lookup: macro too long: `%s'\n", buf
);
680 (void)printf("lookup: `%s'\n", token
);
683 /* FIXME: use a table, this is slow but ok for now. */
686 for (i
= 0; i
< ROFF_MAX
; i
++)
688 if (0 == strcmp(toknames
[i
], token
))
696 rofffindcallable(const char *name
)
700 if (ROFF_MAX
== (c
= rofffindtok(name
)))
702 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
706 static struct roffnode
*
707 roffnode_new(int tokid
, struct rofftree
*tree
)
711 if (NULL
== (p
= malloc(sizeof(struct roffnode
)))) {
716 p
->line
= tree
->rbuf
->line
;
718 p
->parent
= tree
->last
;
725 roffargok(int tokid
, int argid
)
729 if (NULL
== (c
= tokens
[tokid
].args
))
732 for ( ; ROFF_ARGMAX
!= *c
; c
++)
741 roffnode_free(int tokid
, struct rofftree
*tree
)
746 assert(tree
->last
->tok
== tokid
);
749 tree
->last
= tree
->last
->parent
;
755 roffnextopt(int tok
, const char ***in
, char **val
)
757 const char *arg
, **argv
;
764 if (NULL
== (arg
= *argv
))
769 /* FIXME: should we let this slide... ? */
771 if (ROFF_ARGMAX
== (v
= rofffindarg(&arg
[1])))
774 /* FIXME: should we let this slide... ? */
776 if ( ! roffargok(tok
, v
))
778 if ( ! (ROFF_VALUE
& tokenargs
[v
].flags
))
783 /* FIXME: what if this looks like a roff token or argument? */
785 return(*argv
? v
: ROFF_ARGMAX
);
791 roff_Dd(ROFFCALL_ARGS
)
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
));
800 assert(ROFF_PRELUDE
& tree
->state
);
801 assert( ! (ROFF_BODY
& tree
->state
));
803 if (ROFF_PRELUDE_Dd
& tree
->state
) {
804 warnx("%s: prelude `Dd' repeated (line %zu)",
805 tree
->rbuf
->name
, tree
->rbuf
->line
);
807 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
808 warnx("%s: prelude `Dd' out-of-order (line %zu)",
809 tree
->rbuf
->name
, tree
->rbuf
->line
);
813 /* TODO: parse date. */
815 assert(NULL
== tree
->last
);
816 tree
->state
|= ROFF_PRELUDE_Dd
;
824 roff_Dt(ROFFCALL_ARGS
)
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
));
833 assert(ROFF_PRELUDE
& tree
->state
);
834 assert( ! (ROFF_BODY
& tree
->state
));
836 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
837 warnx("%s: prelude `Dt' out-of-order (line %zu)",
838 tree
->rbuf
->name
, tree
->rbuf
->line
);
840 } else if (ROFF_PRELUDE_Dt
& tree
->state
) {
841 warnx("%s: prelude `Dt' repeated (line %zu)",
842 tree
->rbuf
->name
, tree
->rbuf
->line
);
846 /* TODO: parse date. */
848 assert(NULL
== tree
->last
);
849 tree
->state
|= ROFF_PRELUDE_Dt
;
857 roff_Os(ROFFCALL_ARGS
)
860 if (ROFF_EXIT
== type
) {
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
));
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
);
877 /* TODO: extract OS. */
879 tree
->state
|= ROFF_PRELUDE_Os
;
880 tree
->state
&= ~ROFF_PRELUDE
;
881 tree
->state
|= ROFF_BODY
;
883 assert(NULL
== tree
->last
);
885 return(roff_layout(tok
, tree
, argv
, type
));
891 roff_layout(ROFFCALL_ARGS
)
893 int i
, c
, argcp
[ROFF_MAXARG
];
894 char *v
, *argvp
[ROFF_MAXARG
];
896 if (ROFF_PRELUDE
& tree
->state
) {
897 warnx("%s: macro `%s' called in prelude (line %zu)",
904 if (ROFF_EXIT
== type
) {
905 roffnode_free(tok
, tree
);
906 return((*tree
->cb
->roffblkout
)(tok
));
911 while (-1 != (c
= roffnextopt(tok
, &argv
, &v
))) {
912 if (ROFF_ARGMAX
== c
) {
913 warnx("%s: error parsing `%s' args (line %zu)",
918 } else if ( ! roffargok(tok
, c
)) {
919 warnx("%s: arg `%s' not for `%s' (line %zu)",
932 argcp
[i
] = ROFF_ARGMAX
;
935 if (NULL
== roffnode_new(tok
, tree
))
938 if ( ! (*tree
->cb
->roffin
)(tok
, argcp
, argvp
))
941 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
942 /* TODO: print all tokens. */
944 if ( ! ((*tree
->cb
->roffout
)(tok
)))
946 return((*tree
->cb
->roffblkin
)(tok
));
950 if (2 >= strlen(*argv
) && ROFF_MAX
!=
951 (c
= rofffindcallable(*argv
)))
952 if ( ! (*tokens
[c
].cb
)(c
, tree
,
953 argv
+ 1, ROFF_ENTER
))
956 /* TODO: print token. */
960 if ( ! ((*tree
->cb
->roffout
)(tok
)))
963 return((*tree
->cb
->roffblkin
)(tok
));
969 roff_text(ROFFCALL_ARGS
)
971 int i
, c
, argcp
[ROFF_MAXARG
];
972 char *v
, *argvp
[ROFF_MAXARG
];
974 if (ROFF_PRELUDE
& tree
->state
) {
975 warnx("%s: macro `%s' called in prelude (line %zu)",
984 while (-1 != (c
= roffnextopt(tok
, &argv
, &v
))) {
985 if (ROFF_ARGMAX
== c
) {
986 warnx("%s: error parsing `%s' args (line %zu)",
998 argcp
[i
] = ROFF_ARGMAX
;
1001 if ( ! (*tree
->cb
->roffin
)(tok
, argcp
, argvp
))
1004 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
1005 /* TODO: print all tokens. */
1006 return((*tree
->cb
->roffout
)(tok
));
1010 if (2 >= strlen(*argv
) && ROFF_MAX
!=
1011 (c
= rofffindcallable(*argv
)))
1012 if ( ! (*tokens
[c
].cb
)(c
, tree
,
1013 argv
+ 1, ROFF_ENTER
))
1016 /* TODO: print token. */
1020 return((*tree
->cb
->roffout
)(tok
));
1026 roff_comment(ROFFCALL_ARGS
)