]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
3e2b765675e29cfb4a47fd2fd4e6ba98b475a297
1 /* $Id: roff.c,v 1.1 2008/11/24 14:24:55 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
44 #define ROFFCALL_ARGS \
45 struct rofftree *tree, const char *argv[], enum roffd type
51 int (*cb
)(ROFFCALL_ARGS
);
54 #define ROFF_NESTED (1 << 0)
55 #define ROFF_PARSED (1 << 1)
56 #define ROFF_CALLABLE (1 << 2)
57 #define ROFF_QUOTES (1 << 3)
63 #define ROFF_VALUE (1 << 0)
68 struct roffnode
*parent
;
73 struct roffnode
*last
;
79 #define ROFF_PRELUDE (1 << 1)
80 #define ROFF_PRELUDE_Os (1 << 2)
81 #define ROFF_PRELUDE_Dt (1 << 3)
82 #define ROFF_PRELUDE_Dd (1 << 4)
83 #define ROFF_BODY (1 << 5)
84 struct md_mbuf
*mbuf
; /* NULL if ROFF_EXIT and error. */
86 const struct md_args
*args
;
87 const struct md_rbuf
*rbuf
;
99 static int roff_Dd(ROFFCALL_ARGS
);
100 static int roff_Dt(ROFFCALL_ARGS
);
101 static int roff_Os(ROFFCALL_ARGS
);
102 static int roff_Sh(ROFFCALL_ARGS
);
103 static int roff_An(ROFFCALL_ARGS
);
104 static int roff_Li(ROFFCALL_ARGS
);
106 static struct roffnode
*roffnode_new(int, size_t,
108 static void roffnode_free(int, struct rofftree
*);
110 static int rofffindtok(const char *);
111 static int rofffindarg(const char *);
112 static int roffargs(int, char *, char **);
113 static int roffparse(struct rofftree
*, char *, size_t);
114 static int textparse(const struct rofftree
*,
115 const char *, size_t);
117 static void dbg_enter(const struct md_args
*, int);
118 static void dbg_leave(const struct md_args
*, int);
121 static const struct rofftok tokens
[ROFF_MAX
] = {
122 { "\\\"", NULL
, ROFF_COMMENT
, 0 },
123 { "Dd", roff_Dd
, ROFF_TITLE
, 0 },
124 { "Dt", roff_Dt
, ROFF_TITLE
, 0 },
125 { "Os", roff_Os
, ROFF_TITLE
, 0 },
126 { "Sh", roff_Sh
, ROFF_LAYOUT
, 0 },
127 { "An", roff_An
, ROFF_TEXT
, ROFF_PARSED
},
128 { "Li", roff_Li
, ROFF_TEXT
, ROFF_PARSED
| ROFF_CALLABLE
},
132 #define ROFF_Nosplit 1
133 #define ROFF_ARGMAX 2
135 static const struct roffarg tokenargs
[ROFF_ARGMAX
] = {
142 roff_free(struct rofftree
*tree
, int flush
)
152 if ( ! (*tokens
[tree
->last
->tok
].cb
)
153 (tree
, NULL
, ROFF_EXIT
))
154 /* Disallow flushing. */
157 error
= tree
->mbuf
? 0 : 1;
159 if (tree
->mbuf
&& (ROFF_PRELUDE
& tree
->state
)) {
160 warnx("%s: prelude never finished",
166 return(error
? 0 : 1);
171 roff_alloc(const struct md_args
*args
, struct md_mbuf
*out
,
172 const struct md_rbuf
*in
)
174 struct rofftree
*tree
;
176 if (NULL
== (tree
= calloc(1, sizeof(struct rofftree
)))) {
181 tree
->state
= ROFF_PRELUDE
;
191 roff_engine(struct rofftree
*tree
, char *buf
, size_t sz
)
195 warnx("%s: blank line (line %zu)",
199 } else if ('.' != *buf
)
200 return(textparse(tree
, buf
, sz
));
202 return(roffparse(tree
, buf
, sz
));
207 textparse(const struct rofftree
*tree
, const char *buf
, size_t sz
)
210 if (NULL
== tree
->last
) {
211 warnx("%s: unexpected text (line %zu)",
215 } else if (NULL
== tree
->last
->parent
) {
216 warnx("%s: disallowed text (line %zu)",
229 roffargs(int tok
, char *buf
, char **argv
)
233 (void)tok
;/* FIXME: quotable strings? */
235 assert(tok
>= 0 && tok
< ROFF_MAX
);
239 for (i
= 0; *buf
&& i
< ROFF_MAXARG
; i
++) {
241 while (*buf
&& ! isspace(*buf
))
247 while (*buf
&& isspace(*buf
))
255 return(ROFF_MAXARG
> i
);
260 roffparse(struct rofftree
*tree
, char *buf
, size_t sz
)
263 struct roffnode
*node
;
264 char *argv
[ROFF_MAXARG
];
270 * Extract the token identifier from the buffer. If there's no
271 * callback for the token (comment, etc.) then exit immediately.
272 * We don't do any error handling (yet), so if the token doesn't
277 warnx("%s: malformed line (line %zu)",
281 } else if (ROFF_MAX
== (tok
= rofffindtok(buf
+ 1))) {
282 warnx("%s: unknown line token `%c%c' (line %zu)",
284 *(buf
+ 1), *(buf
+ 2),
287 } else if (ROFF_COMMENT
== tokens
[tok
].type
)
288 /* Ignore comment tokens. */
291 if ( ! roffargs(tok
, buf
, argv
)) {
292 warnx("%s: too many arguments to `%s' (line %zu)",
293 tree
->rbuf
->name
, tokens
[tok
].name
,
298 /* Domain cross-contamination (and sanity) checks. */
300 switch (tokens
[tok
].type
) {
302 if (ROFF_PRELUDE
& tree
->state
) {
303 assert( ! (ROFF_BODY
& tree
->state
));
306 assert(ROFF_BODY
& tree
->state
);
307 warnx("%s: prelude token `%s' in body (line %zu)",
308 tree
->rbuf
->name
, tokens
[tok
].name
,
314 if (ROFF_BODY
& tree
->state
) {
315 assert( ! (ROFF_PRELUDE
& tree
->state
));
318 assert(ROFF_PRELUDE
& tree
->state
);
319 warnx("%s: body token `%s' in prelude (line %zu)",
320 tree
->rbuf
->name
, tokens
[tok
].name
,
330 * If this is a non-nestable layout token and we're below a
331 * token of the same type, then recurse upward to the token,
332 * closing out the interim scopes.
334 * If there's a nested token on the chain, then raise an error
335 * as nested tokens have corresponding "ending" tokens and we're
336 * breaking their scope.
341 if (ROFF_LAYOUT
== tokens
[tok
].type
&&
342 ! (ROFF_NESTED
& tokens
[tok
].flags
)) {
343 for (node
= tree
->last
; node
; node
= node
->parent
) {
344 if (node
->tok
== tok
)
347 /* Don't break nested scope. */
349 if ( ! (ROFF_NESTED
& tokens
[node
->tok
].flags
))
351 warnx("%s: scope of %s (line %zu) broken by "
356 tokens
[node
->tok
].name
,
363 assert(ROFF_LAYOUT
== tokens
[tok
].type
);
364 assert( ! (ROFF_NESTED
& tokens
[tok
].flags
));
365 assert(node
->tok
== tok
);
367 /* Clear up to last scoped token. */
372 if ( ! (*tokens
[tree
->last
->tok
].cb
)
373 (tree
, NULL
, ROFF_EXIT
))
378 /* Proceed with actual token processing. */
380 argvp
= (const char **)&argv
[1];
381 return((*tokens
[tok
].cb
)(tree
, argvp
, ROFF_ENTER
));
386 rofffindarg(const char *name
)
390 /* FIXME: use a table, this is slow but ok for now. */
393 for (i
= 0; i
< ROFF_ARGMAX
; i
++)
395 if (0 == strcmp(name
, tokenargs
[i
].name
))
403 rofffindtok(const char *name
)
407 /* FIXME: use a table, this is slow but ok for now. */
410 for (i
= 0; i
< ROFF_MAX
; i
++)
412 if (0 == strncmp(name
, tokens
[i
].name
, 2))
419 /* FIXME: accept only struct rofftree *. */
420 static struct roffnode
*
421 roffnode_new(int tokid
, size_t line
, struct rofftree
*tree
)
425 if (NULL
== (p
= malloc(sizeof(struct roffnode
)))) {
432 p
->parent
= tree
->last
;
439 roffnode_free(int tokid
, struct rofftree
*tree
)
444 assert(tree
->last
->tok
== tokid
);
447 tree
->last
= tree
->last
->parent
;
452 static int dbg_lvl
= 0;
456 dbg_enter(const struct md_args
*args
, int tokid
)
462 if ( ! (args
->dbg
& MD_DBG_TREE
))
464 assert(tokid
>= 0 && tokid
<= ROFF_MAX
);
466 buf
[0] = buf
[71] = 0;
468 switch (tokens
[tokid
].type
) {
470 (void)strncat(buf
, "[body-layout] ", sizeof(buf
) - 1);
473 (void)strncat(buf
, "[ body-text] ", sizeof(buf
) - 1);
476 (void)strncat(buf
, "[ prelude] ", sizeof(buf
) - 1);
483 for (i
= 0; i
< dbg_lvl
; i
++)
484 (void)strncat(buf
, " ", sizeof(buf
) - 1);
486 (void)strncat(buf
, tokens
[tokid
].name
, sizeof(buf
) - 1);
488 (void)printf("%s\n", buf
);
494 /* FIXME: accept only struct rofftree *. */
496 dbg_leave(const struct md_args
*args
, int tokid
)
499 if ( ! (args
->dbg
& MD_DBG_TREE
))
502 assert(tokid
>= 0 && tokid
<= ROFF_MAX
);
508 /* FIXME: accept only struct rofftree *. */
511 roff_Dd(ROFFCALL_ARGS
)
514 dbg_enter(tree
->args
, ROFF_Dd
);
516 assert(ROFF_PRELUDE
& tree
->state
);
517 if (ROFF_PRELUDE_Dt
& tree
->state
||
518 ROFF_PRELUDE_Dd
& tree
->state
) {
519 warnx("%s: prelude `Dd' out-of-order (line %zu)",
520 tree
->rbuf
->name
, tree
->rbuf
->line
);
524 assert(NULL
== tree
->last
);
525 tree
->state
|= ROFF_PRELUDE_Dd
;
527 dbg_leave(tree
->args
, ROFF_Dd
);
535 roff_Dt(ROFFCALL_ARGS
)
538 dbg_enter(tree
->args
, ROFF_Dt
);
540 assert(ROFF_PRELUDE
& tree
->state
);
541 if ( ! (ROFF_PRELUDE_Dd
& tree
->state
) ||
542 (ROFF_PRELUDE_Dt
& tree
->state
)) {
543 warnx("%s: prelude `Dt' out-of-order (line %zu)",
544 tree
->rbuf
->name
, tree
->rbuf
->line
);
548 assert(NULL
== tree
->last
);
549 tree
->state
|= ROFF_PRELUDE_Dt
;
551 dbg_leave(tree
->args
, ROFF_Dt
);
559 roff_Os(ROFFCALL_ARGS
)
562 if (ROFF_EXIT
== type
) {
563 roffnode_free(ROFF_Os
, tree
);
564 dbg_leave(tree
->args
, ROFF_Os
);
568 dbg_enter(tree
->args
, ROFF_Os
);
570 assert(ROFF_PRELUDE
& tree
->state
);
571 if ( ! (ROFF_PRELUDE_Dt
& tree
->state
) ||
572 ! (ROFF_PRELUDE_Dd
& tree
->state
)) {
573 warnx("%s: prelude `Os' out-of-order (line %zu)",
574 tree
->rbuf
->name
, tree
->rbuf
->line
);
578 assert(NULL
== tree
->last
);
579 if (NULL
== roffnode_new(ROFF_Os
, tree
->rbuf
->line
, tree
))
582 tree
->state
|= ROFF_PRELUDE_Os
;
583 tree
->state
&= ~ROFF_PRELUDE
;
584 tree
->state
|= ROFF_BODY
;
592 roff_Sh(ROFFCALL_ARGS
)
595 if (ROFF_EXIT
== type
) {
596 roffnode_free(ROFF_Sh
, tree
);
597 dbg_leave(tree
->args
, ROFF_Sh
);
601 dbg_enter(tree
->args
, ROFF_Sh
);
603 if (NULL
== roffnode_new(ROFF_Sh
, tree
->rbuf
->line
, tree
))
612 roff_Li(ROFFCALL_ARGS
)
615 dbg_enter(tree
->args
, ROFF_Li
);
616 dbg_leave(tree
->args
, ROFF_Li
);
623 roffnextopt(const char ***in
, char **val
)
625 const char *arg
, **argv
;
632 if (NULL
== (arg
= *argv
))
636 if (ROFF_ARGMAX
== (v
= rofffindarg(&arg
[1])))
638 if ( ! (ROFF_VALUE
& tokenargs
[v
].flags
))
643 /* FIXME: what if this looks like a roff token or argument? */
645 return(*argv
? v
: ROFF_ARGMAX
);
651 roff_An(ROFFCALL_ARGS
)
656 dbg_enter(tree
->args
, ROFF_An
);
658 while (-1 != (c
= roffnextopt(&argv
, &val
))) {
661 /* Process argument. */
664 /* Process argument. */
667 warnx("%s: error parsing `An' args (line %zu)",
678 if (/* is_parsable && */ 2 >= strlen(*argv
)) {
679 if (ROFF_MAX
!= (c
= rofffindtok(*argv
))) {
680 if (ROFF_CALLABLE
& tokens
[c
].flags
) {
682 if ( ! (*tokens
[c
].cb
)(tree
, (const char **)argv
+ 1, ROFF_ENTER
))
697 dbg_leave(tree
->args
, ROFF_An
);