]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.4 2008/11/25 12:14:02 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 #define ROFF_MAXARG 10
37 /* FIXME: prolog roffs can be text roffs, too. */
45 #define ROFFCALL_ARGS \
46 int tok, struct rofftree *tree, \
47 const char *argv[], enum roffd type
52 int (*cb
)(ROFFCALL_ARGS
);
55 #define ROFF_NESTED (1 << 0)
56 #define ROFF_PARSED (1 << 1)
57 #define ROFF_CALLABLE (1 << 2)
58 #define ROFF_QUOTES (1 << 3)
64 #define ROFF_VALUE (1 << 0)
69 struct roffnode
*parent
;
74 struct roffnode
*last
;
81 #define ROFF_PRELUDE (1 << 1)
82 #define ROFF_PRELUDE_Os (1 << 2)
83 #define ROFF_PRELUDE_Dt (1 << 3)
84 #define ROFF_PRELUDE_Dd (1 << 4)
85 #define ROFF_BODY (1 << 5)
90 roffblkout roffblkout
;
92 struct md_mbuf
*mbuf
; /* NULL if !flush. */
93 const struct md_args
*args
;
94 const struct md_rbuf
*rbuf
;
97 static int roff_Dd(ROFFCALL_ARGS
);
98 static int roff_Dt(ROFFCALL_ARGS
);
99 static int roff_Os(ROFFCALL_ARGS
);
101 static int roff_layout(ROFFCALL_ARGS
);
102 static int roff_text(ROFFCALL_ARGS
);
104 static struct roffnode
*roffnode_new(int, struct rofftree
*);
105 static void roffnode_free(int, struct rofftree
*);
107 static int rofffindtok(const char *);
108 static int rofffindarg(const char *);
109 static int rofffindcallable(const char *);
110 static int roffargs(int, char *, char **);
111 static int roffparse(struct rofftree
*, char *, size_t);
112 static int textparse(const struct rofftree
*,
113 const char *, size_t);
116 static const struct rofftok tokens
[ROFF_MAX
] = {
117 { NULL
, ROFF_COMMENT
, 0 },
118 { roff_Dd
, ROFF_TEXT
, 0 }, /* Dd */
119 { roff_Dt
, ROFF_TEXT
, 0 }, /* Dt */
120 { roff_Os
, ROFF_TEXT
, 0 }, /* Os */
121 { roff_layout
, ROFF_LAYOUT
, ROFF_PARSED
}, /* Sh */
122 { roff_layout
, ROFF_LAYOUT
, ROFF_PARSED
}, /* Ss XXX */
123 { roff_layout
, ROFF_LAYOUT
, 0 }, /* Pp */
124 { roff_layout
, ROFF_LAYOUT
, 0 }, /* D1 */
125 { roff_layout
, ROFF_LAYOUT
, 0 }, /* Dl */
126 { roff_layout
, ROFF_LAYOUT
, 0 }, /* Bd */
127 { roff_layout
, ROFF_LAYOUT
, 0 }, /* Ed */
128 { roff_layout
, ROFF_LAYOUT
, 0 }, /* Bl */
129 { roff_layout
, ROFF_LAYOUT
, 0 }, /* El */
130 { roff_layout
, ROFF_LAYOUT
, 0 }, /* It */
131 { roff_text
, ROFF_TEXT
, ROFF_PARSED
}, /* An */
132 { roff_text
, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
}, /* Li */
135 /* FIXME: multiple owners? */
137 static const struct roffarg tokenargs
[ROFF_ARGMAX
] = {
138 { ROFF_An
, 0 }, /* split */
139 { ROFF_An
, 0 }, /* nosplit */
140 { ROFF_Bd
, 0 }, /* ragged */
141 { ROFF_Bd
, 0 }, /* unfilled */
142 { ROFF_Bd
, 0 }, /* literal */
143 { ROFF_Bd
, ROFF_VALUE
}, /* file */
144 { ROFF_Bd
, ROFF_VALUE
}, /* offset */
145 { ROFF_Bl
, 0 }, /* bullet */
146 { ROFF_Bl
, 0 }, /* dash */
147 { ROFF_Bl
, 0 }, /* hyphen */
148 { ROFF_Bl
, 0 }, /* item */
149 { ROFF_Bl
, 0 }, /* enum */
150 { ROFF_Bl
, 0 }, /* tag */
151 { ROFF_Bl
, 0 }, /* diag */
152 { ROFF_Bl
, 0 }, /* hang */
153 { ROFF_Bl
, 0 }, /* ohang */
154 { ROFF_Bl
, 0 }, /* inset */
155 { ROFF_Bl
, 0 }, /* column */
158 static const char *const toknames
[ROFF_MAX
] = ROFF_NAMES
;
159 static const char *const tokargnames
[ROFF_ARGMAX
] = ROFF_ARGNAMES
;
163 roff_free(struct rofftree
*tree
, int flush
)
173 if ( ! (*tokens
[tree
->last
->tok
].cb
)
174 (tree
->last
->tok
, tree
, NULL
, ROFF_EXIT
))
175 /* Disallow flushing. */
178 error
= tree
->mbuf
? 0 : 1;
180 if (tree
->mbuf
&& (ROFF_PRELUDE
& tree
->state
)) {
181 warnx("%s: prelude never finished",
187 return(error
? 0 : 1);
192 roff_alloc(const struct md_args
*args
, struct md_mbuf
*out
,
193 const struct md_rbuf
*in
, roffin textin
,
194 roffout textout
, roffblkin blkin
, roffblkout blkout
)
196 struct rofftree
*tree
;
198 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
)))) {
203 tree
->state
= ROFF_PRELUDE
;
207 tree
->roffin
= textin
;
208 tree
->roffout
= textout
;
209 tree
->roffblkin
= blkin
;
210 tree
->roffblkout
= blkout
;
217 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
221 warnx("%s: blank line (line %zu)",
225 } else if ('.' != *buf
)
226 return(textparse(tree
, buf
, sz
));
228 return(roffparse(tree
, buf
, sz
));
233 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
236 if (NULL
== tree
->last
) {
237 warnx("%s: unexpected text (line %zu)",
241 } else if (NULL
== tree
->last
->parent
) {
242 warnx("%s: disallowed text (line %zu)",
255 roffargs(int tok
, char *buf
, char **argv
)
259 (void)tok
;/* FIXME: quotable strings? */
261 assert(tok
>= 0 && tok
< ROFF_MAX
);
265 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
267 while (*buf
&& ! isspace(*buf
))
273 while (*buf
&& isspace(*buf
))
281 return(ROFF_MAXARG
> i
);
286 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
289 struct roffnode
*node
;
290 char *argv
[ROFF_MAXARG
];
296 * Extract the token identifier from the buffer. If there's no
297 * callback for the token (comment, etc.) then exit immediately.
298 * We don't do any error handling (yet), so if the token doesn't
303 warnx("%s: malformed line (line %zu)",
307 } else if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
308 warnx("%s: unknown line token `%c%c' (line %zu)",
310 *(buf
+ 1), *(buf
+ 2),
313 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
314 /* Ignore comment tokens. */
317 if ( ! roffargs(tok
, buf
, argv
)) {
318 warnx("%s: too many arguments to `%s' (line %zu)",
319 tree
->rbuf
->name
, toknames
[tok
],
325 * If this is a non-nestable layout token and we're below a
326 * token of the same type, then recurse upward to the token,
327 * closing out the interim scopes.
329 * If there's a nested token on the chain, then raise an error
330 * as nested tokens have corresponding "ending" tokens and we're
331 * breaking their scope.
336 if (ROFF_LAYOUT
== tokens
[tok
].type
&&
337 ! (ROFF_NESTED
& tokens
[tok
].flags
)) {
338 for (node
= tree
->last
; node
; node
= node
->parent
) {
339 if (node
->tok
== tok
)
342 /* Don't break nested scope. */
344 if ( ! (ROFF_NESTED
& tokens
[node
->tok
].flags
))
346 warnx("%s: scope of %s (line %zu) broken by "
349 toknames
[tok
], node
->line
,
357 assert(ROFF_LAYOUT
== tokens
[tok
].type
);
358 assert( ! (ROFF_NESTED
& tokens
[tok
].flags
));
359 assert(node
->tok
== tok
);
361 /* Clear up to last scoped token. */
366 if ( ! (*tokens
[tree
->last
->tok
].cb
)
367 (tree
->last
->tok
, tree
, NULL
, ROFF_EXIT
))
372 /* Proceed with actual token processing. */
374 argvp
= (const char **)&argv
[1];
375 return((*tokens
[tok
].cb
)(tok
, tree
, argvp
, ROFF_ENTER
));
380 rofffindarg(const char *name
)
384 /* FIXME: use a table, this is slow but ok for now. */
387 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
389 if (0 == strcmp(name
, tokargnames
[i
]))
397 rofffindtok(const char *name
)
401 /* FIXME: use a table, this is slow but ok for now. */
404 for (i
= 0; i
< ROFF_MAX
; i
++)
406 if (0 == strncmp(name
, toknames
[i
], 2))
414 rofffindcallable(const char *name
)
418 if (ROFF_MAX
== (c
= rofffindtok(name
)))
420 return(ROFF_CALLABLE
& tokens
[c
].flags
? c
: ROFF_MAX
);
424 static struct roffnode
*
425 roffnode_new(int tokid
, struct rofftree
*tree
)
429 if (NULL
== (p
= malloc(sizeof(struct roffnode
)))) {
434 p
->line
= tree
->rbuf
->line
;
436 p
->parent
= tree
->last
;
443 roffnode_free(int tokid
, struct rofftree
*tree
)
448 assert(tree
->last
->tok
== tokid
);
451 tree
->last
= tree
->last
->parent
;
458 roff_Dd(ROFFCALL_ARGS
)
461 if (ROFF_BODY
& tree
->state
) {
462 assert( ! (ROFF_PRELUDE
& tree
->state
));
463 assert(ROFF_PRELUDE_Dd
& tree
->state
);
464 return(roff_text(tok
, tree
, argv
, type
));
467 assert(ROFF_PRELUDE
& tree
->state
);
468 assert( ! (ROFF_BODY
& tree
->state
));
470 if (ROFF_PRELUDE_Dd
& tree
->state
||
471 ROFF_PRELUDE_Dt
& tree
->state
) {
472 warnx("%s: prelude `Dd' out-of-order (line %zu)",
473 tree
->rbuf
->name
, tree
->rbuf
->line
);
477 /* TODO: parse date. */
479 assert(NULL
== tree
->last
);
480 tree
->state
|= ROFF_PRELUDE_Dd
;
488 roff_Dt(ROFFCALL_ARGS
)
491 if (ROFF_BODY
& tree
->state
) {
492 assert( ! (ROFF_PRELUDE
& tree
->state
));
493 assert(ROFF_PRELUDE_Dt
& tree
->state
);
494 return(roff_text(tok
, tree
, argv
, type
));
497 assert(ROFF_PRELUDE
& tree
->state
);
498 assert( ! (ROFF_BODY
& tree
->state
));
500 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
) ||
501 (ROFF_PRELUDE_Dt
& tree
->state
)) {
502 warnx("%s: prelude `Dt' out-of-order (line %zu)",
503 tree
->rbuf
->name
, tree
->rbuf
->line
);
507 /* TODO: parse date. */
509 assert(NULL
== tree
->last
);
510 tree
->state
|= ROFF_PRELUDE_Dt
;
518 roff_Os(ROFFCALL_ARGS
)
521 if (ROFF_EXIT
== type
) {
522 assert(ROFF_PRELUDE_Os
& tree
->state
);
523 return(roff_layout(tok
, tree
, argv
, type
));
524 } else if (ROFF_BODY
& tree
->state
) {
525 assert( ! (ROFF_PRELUDE
& tree
->state
));
526 assert(ROFF_PRELUDE_Os
& tree
->state
);
527 return(roff_text(tok
, tree
, argv
, type
));
530 assert(ROFF_PRELUDE
& tree
->state
);
531 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
532 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
533 warnx("%s: prelude `Os' out-of-order (line %zu)",
534 tree
->rbuf
->name
, tree
->rbuf
->line
);
538 /* TODO: extract OS. */
540 tree
->state
|= ROFF_PRELUDE_Os
;
541 tree
->state
&= ~ROFF_PRELUDE
;
542 tree
->state
|= ROFF_BODY
;
544 assert(NULL
== tree
->last
);
546 return(roff_layout(tok
, tree
, argv
, type
));
552 roffnextopt(const char ***in
, char **val
)
554 const char *arg
, **argv
;
561 if (NULL
== (arg
= *argv
))
565 if (ROFF_ARGMAX
== (v
= rofffindarg(&arg
[1])))
567 if ( ! (ROFF_VALUE
& tokenargs
[v
].flags
))
572 /* FIXME: what if this looks like a roff token or argument? */
574 return(*argv
? v
: ROFF_ARGMAX
);
580 roff_layout(ROFFCALL_ARGS
)
582 int i
, c
, argcp
[ROFF_MAXARG
];
583 char *v
, *argvp
[ROFF_MAXARG
];
585 if (ROFF_PRELUDE
& tree
->state
) {
586 warnx("%s: macro `%s' called in prelude (line %zu)",
587 tree
->rbuf
->name
, toknames
[tok
],
592 if (ROFF_EXIT
== type
) {
593 roffnode_free(tok
, tree
);
594 return((*tree
->roffblkout
)(tok
));
598 while (-1 != (c
= roffnextopt(&argv
, &v
))) {
599 if (ROFF_ARGMAX
== c
) {
600 warnx("%s: error parsing `%s' args (line %zu)",
611 if (NULL
== roffnode_new(tok
, tree
))
614 if ( ! (*tree
->roffin
)(tok
, argcp
, argvp
))
617 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
618 /* TODO: print all tokens. */
620 if ( ! ((*tree
->roffout
)(tok
)))
622 return((*tree
->roffblkin
)(tok
));
626 if (2 >= strlen(*argv
) && ROFF_MAX
!=
627 (c
= rofffindcallable(*argv
)))
628 if ( ! (*tokens
[c
].cb
)(c
, tree
,
629 argv
+ 1, ROFF_ENTER
))
632 /* TODO: print token. */
636 if ( ! ((*tree
->roffout
)(tok
)))
639 return((*tree
->roffblkin
)(tok
));
645 roff_text(ROFFCALL_ARGS
)
647 int i
, c
, argcp
[ROFF_MAXARG
];
648 char *v
, *argvp
[ROFF_MAXARG
];
650 if (ROFF_PRELUDE
& tree
->state
) {
651 warnx("%s: macro `%s' called in prelude (line %zu)",
652 tree
->rbuf
->name
, toknames
[tok
],
658 while (-1 != (c
= roffnextopt(&argv
, &v
))) {
659 if (ROFF_ARGMAX
== c
) {
660 warnx("%s: error parsing `%s' args (line %zu)",
671 if ( ! (*tree
->roffin
)(tok
, argcp
, argvp
))
674 if ( ! (ROFF_PARSED
& tokens
[tok
].flags
)) {
675 /* TODO: print all tokens. */
676 return((*tree
->roffout
)(tok
));
680 if (2 >= strlen(*argv
) && ROFF_MAX
!=
681 (c
= rofffindcallable(*argv
)))
682 if ( ! (*tokens
[c
].cb
)(c
, tree
,
683 argv
+ 1, ROFF_ENTER
))
686 /* TODO: print token. */
690 return((*tree
->roffout
)(tok
));