]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Added regression tests.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.6 2008/11/26 16:50:34 kristaps Exp $ */
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
30 /* FIXME: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
31
32 /* FIXME: warn about empty lists. */
33
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 \
48 int tok, struct rofftree *tree, \
49 const char *argv[], enum roffd type
50
51 struct rofftree;
52
53 struct rofftok {
54 int (*cb)(ROFFCALL_ARGS); /* Callback. */
55 const int *args; /* Args (or NULL). */
56 const int *parents;
57 const int *children;
58 int ctx;
59 enum rofftype type; /* Type of macro. */
60 int flags;
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. */
65 };
66
67 struct roffarg {
68 int flags;
69 #define ROFF_VALUE (1 << 0) /* Has a value. */
70 };
71
72 struct roffnode {
73 int tok; /* Token id. */
74 struct roffnode *parent; /* Parent (or NULL). */
75 size_t line; /* Parsed at line. */
76 };
77
78 struct rofftree {
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. */
85 int state;
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;
96 };
97
98 static int roff_Dd(ROFFCALL_ARGS);
99 static int roff_Dt(ROFFCALL_ARGS);
100 static int roff_Os(ROFFCALL_ARGS);
101
102 static int roff_layout(ROFFCALL_ARGS);
103 static int roff_text(ROFFCALL_ARGS);
104 static int roff_comment(ROFFCALL_ARGS);
105
106 static struct roffnode *roffnode_new(int, struct rofftree *);
107 static void roffnode_free(int, struct rofftree *);
108
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);
118
119
120 static const int roffarg_An[] = {
121 ROFF_Split, ROFF_Nosplit, ROFF_ARGMAX };
122
123 static const int roffarg_Bd[] = {
124 ROFF_Ragged, ROFF_Unfilled, ROFF_Literal, ROFF_File,
125 ROFF_Offset, ROFF_ARGMAX };
126
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
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 };
137
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 */
236 };
237
238 /* Table of all known token arguments. */
239 static const struct roffarg tokenargs[ROFF_ARGMAX] = {
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 */
260 };
261
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;
389
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)
403 (tree->last->tok, tree, NULL, ROFF_EXIT))
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,
422 const struct md_rbuf *in, const struct roffcb *cb)
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;
435 tree->cb = cb;
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
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
525 static int
526 roffparse(struct rofftree *tree, char *buf, size_t sz)
527 {
528 int tok, t;
529 struct roffnode *n;
530 char *argv[ROFF_MAXARG];
531 const char **argvp;
532
533 assert(sz > 0);
534
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],
542 tree->rbuf->line);
543 return(0);
544 } else if (ROFF_COMMENT == tokens[tok].type)
545 return(1);
546
547 if ( ! roffargs(tok, buf, argv)) {
548 warnx("%s: too many args to `%s' (line %zu)",
549 tree->rbuf->name, toknames[tok],
550 tree->rbuf->line);
551 return(0);
552 } else
553 argvp = (const char **)argv + 1;
554
555 /*
556 * Prelude macros break some assumptions: branch now.
557 */
558
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)",
582 tree->rbuf->name, toknames[tok],
583 toknames[tree->last->tok],
584 tree->rbuf->line);
585 return(0);
586 }
587
588 /*
589 * Branch if we're not a layout token.
590 */
591
592 if (ROFF_LAYOUT != tokens[tok].type)
593 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
594
595 /*
596 * Check our scope rules.
597 */
598
599 if (0 == tokens[tok].ctx)
600 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
601
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));
615 }
616 do {
617 t = tree->last->tok;
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))
623 return(0);
624 } while (t != tok);
625
626 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
627 }
628
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);
639
640 return((*tokens[tok].cb)(tok, tree, argvp, ROFF_ENTER));
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 */
654 if (0 == strcmp(name, tokargnames[i]))
655 return((int)i);
656
657 return(ROFF_ARGMAX);
658 }
659
660
661 static int
662 rofffindtok(const char *buf)
663 {
664 char token[4];
665 size_t i;
666
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
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 */
688 if (0 == strcmp(toknames[i], token))
689 return((int)i);
690
691 return(ROFF_MAX);
692 }
693
694
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
706 static struct roffnode *
707 roffnode_new(int tokid, struct rofftree *tree)
708 {
709 struct roffnode *p;
710
711 if (NULL == (p = malloc(sizeof(struct roffnode)))) {
712 warn("malloc");
713 return(NULL);
714 }
715
716 p->line = tree->rbuf->line;
717 p->tok = tokid;
718 p->parent = tree->last;
719 tree->last = p;
720 return(p);
721 }
722
723
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
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
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
789 /* ARGSUSED */
790 static int
791 roff_Dd(ROFFCALL_ARGS)
792 {
793
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
800 assert(ROFF_PRELUDE & tree->state);
801 assert( ! (ROFF_BODY & tree->state));
802
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) {
808 warnx("%s: prelude `Dd' out-of-order (line %zu)",
809 tree->rbuf->name, tree->rbuf->line);
810 return(0);
811 }
812
813 /* TODO: parse date. */
814
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
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
833 assert(ROFF_PRELUDE & tree->state);
834 assert( ! (ROFF_BODY & tree->state));
835
836 if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
837 warnx("%s: prelude `Dt' out-of-order (line %zu)",
838 tree->rbuf->name, tree->rbuf->line);
839 return(0);
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);
844 }
845
846 /* TODO: parse date. */
847
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) {
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 }
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
877 /* TODO: extract OS. */
878
879 tree->state |= ROFF_PRELUDE_Os;
880 tree->state &= ~ROFF_PRELUDE;
881 tree->state |= ROFF_BODY;
882
883 assert(NULL == tree->last);
884
885 return(roff_layout(tok, tree, argv, type));
886 }
887
888
889 /* ARGSUSED */
890 static int
891 roff_layout(ROFFCALL_ARGS)
892 {
893 int i, c, argcp[ROFF_MAXARG];
894 char *v, *argvp[ROFF_MAXARG];
895
896 if (ROFF_PRELUDE & tree->state) {
897 warnx("%s: macro `%s' called in prelude (line %zu)",
898 tree->rbuf->name,
899 toknames[tok],
900 tree->rbuf->line);
901 return(0);
902 }
903
904 if (ROFF_EXIT == type) {
905 roffnode_free(tok, tree);
906 return((*tree->cb->roffblkout)(tok));
907 }
908
909 i = 0;
910
911 while (-1 != (c = roffnextopt(tok, &argv, &v))) {
912 if (ROFF_ARGMAX == c) {
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)",
920 tree->rbuf->name,
921 tokargnames[c],
922 toknames[tok],
923 tree->rbuf->line);
924 return(0);
925 }
926 argcp[i] = c;
927 argvp[i] = v;
928 i++;
929 argv++;
930 }
931
932 argcp[i] = ROFF_ARGMAX;
933 argvp[i] = NULL;
934
935 if (NULL == roffnode_new(tok, tree))
936 return(0);
937
938 if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
939 return(0);
940
941 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
942 /* TODO: print all tokens. */
943
944 if ( ! ((*tree->cb->roffout)(tok)))
945 return(0);
946 return((*tree->cb->roffblkin)(tok));
947 }
948
949 while (*argv) {
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
960 if ( ! ((*tree->cb->roffout)(tok)))
961 return(0);
962
963 return((*tree->cb->roffblkin)(tok));
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
974 if (ROFF_PRELUDE & tree->state) {
975 warnx("%s: macro `%s' called in prelude (line %zu)",
976 tree->rbuf->name,
977 toknames[tok],
978 tree->rbuf->line);
979 return(0);
980 }
981
982 i = 0;
983
984 while (-1 != (c = roffnextopt(tok, &argv, &v))) {
985 if (ROFF_ARGMAX == c) {
986 warnx("%s: error parsing `%s' args (line %zu)",
987 tree->rbuf->name,
988 toknames[tok],
989 tree->rbuf->line);
990 return(0);
991 }
992 argcp[i] = c;
993 argvp[i] = v;
994 i++;
995 argv++;
996 }
997
998 argcp[i] = ROFF_ARGMAX;
999 argvp[i] = NULL;
1000
1001 if ( ! (*tree->cb->roffin)(tok, argcp, argvp))
1002 return(0);
1003
1004 if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1005 /* TODO: print all tokens. */
1006 return((*tree->cb->roffout)(tok));
1007 }
1008
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
1020 return((*tree->cb->roffout)(tok));
1021 }
1022
1023
1024 /* ARGUSED */
1025 static int
1026 roff_comment(ROFFCALL_ARGS)
1027 {
1028
1029 return(1);
1030 }