summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile21
-rw-r--r--dummy.c55
-rw-r--r--html4_strict.c622
-rw-r--r--libmdocml.c23
-rw-r--r--mdocml.c4
-rw-r--r--private.h38
-rw-r--r--roff.c701
7 files changed, 796 insertions, 668 deletions
diff --git a/Makefile b/Makefile
index 93d27a3d..e0242577 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,15 @@
CFLAGS += -W -Wall -Wno-unused-parameter -g
LINTFLAGS += -c -e -f -u
-LNS = mdocml.ln html4_strict.ln dummy.ln libmdocml.ln
+LNS = mdocml.ln html4_strict.ln dummy.ln libmdocml.ln roff.ln
LLNS = llib-lmdocml.ln
LIBS = libmdocml.a
-OBJS = mdocml.o html4_strict.o dummy.o libmdocml.o
+OBJS = mdocml.o html4_strict.o dummy.o libmdocml.o roff.o
-SRCS = mdocml.c html4_strict.c dummy.c libmdocml.c
+SRCS = mdocml.c html4_strict.c dummy.c libmdocml.c roff.c
HEADS = libmdocml.h private.h
@@ -38,8 +38,8 @@ mdocml.tgz: $(INSTALL)
( cd .dist/ && tar zcf ../mdocml.tgz mdocml/ )
rm -rf .dist/
-llib-lmdocml.ln: mdocml.ln libmdocml.ln html4_strict.ln dummy.ln
- $(LINT) $(LINTFLAGS) -Cmdocml mdocml.ln libmdocml.ln html4_strict.ln dummy.ln
+llib-lmdocml.ln: mdocml.ln libmdocml.ln html4_strict.ln dummy.ln roff.ln
+ $(LINT) $(LINTFLAGS) -Cmdocml mdocml.ln libmdocml.ln html4_strict.ln dummy.ln roff.ln
mdocml.ln: mdocml.c
@@ -47,8 +47,8 @@ mdocml.o: mdocml.c
mdocml.c: libmdocml.h
-libmdocml.a: libmdocml.o html4_strict.o dummy.o
- $(AR) rs $@ libmdocml.o html4_strict.o dummy.o
+libmdocml.a: libmdocml.o html4_strict.o dummy.o roff.o
+ $(AR) rs $@ libmdocml.o html4_strict.o dummy.o roff.o
html4_strict.ln: html4_strict.c
@@ -56,8 +56,15 @@ html4_strict.o: html4_strict.c
html4_strict.c: private.h libmdocml.h
+roff.ln: roff.c
+
+roff.o: roff.c
+
+roff.c: private.h libmdocml.h
+
libmdocml.ln: libmdocml.c
libmdocml.o: libmdocml.c
libmdocml.c: private.h libmdocml.h
+
diff --git a/dummy.c b/dummy.c
index 808d2b9f..7e9cfdf2 100644
--- a/dummy.c
+++ b/dummy.c
@@ -17,28 +17,57 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
+#include <err.h>
#include <stdlib.h>
#include "libmdocml.h"
#include "private.h"
+struct md_dummy {
+ struct rofftree *tree;
+};
+
+
+int
+md_line_dummy(void *arg, char *buf, size_t sz)
+{
+ struct md_dummy *p;
+
+ p = (struct md_dummy *)arg;
+ return(roff_engine(p->tree, buf, sz));
+}
+
+
int
-md_line_dummy(const struct md_args *args,
- struct md_mbuf *out, const struct md_rbuf *in,
- char *buf, size_t sz, void *data)
+md_exit_dummy(void *data, int flush)
+{
+ int c;
+ struct md_dummy *p;
+
+ p = (struct md_dummy *)data;
+ c = roff_free(p->tree, flush);
+ free(p);
+
+ return(c);
+}
+
+
+void *
+md_init_dummy(const struct md_args *args,
+ struct md_mbuf *mbuf, const struct md_rbuf *rbuf)
{
+ struct md_dummy *p;
- assert(buf);
- assert(out);
- assert(in);
- assert(args);
- assert(NULL == data);
+ if (NULL == (p = malloc(sizeof(struct md_dummy)))) {
+ warn("malloc");
+ return(NULL);
+ }
- if ( ! md_buf_puts(out, buf, sz))
- return(0);
- if ( ! md_buf_putchar(out, '\n'))
- return(0);
+ if (NULL == (p->tree = roff_alloc(args, mbuf, rbuf))) {
+ free(p);
+ return(NULL);
+ }
- return(1);
+ return(p);
}
diff --git a/html4_strict.c b/html4_strict.c
index 717d007a..2cd61e36 100644
--- a/html4_strict.c
+++ b/html4_strict.c
@@ -17,642 +17,32 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
-#include <ctype.h>
-#include <err.h>
#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
#include "libmdocml.h"
#include "private.h"
-#define ROFF_MAXARG 10
-
-enum roffd {
- ROFF_ENTER = 0,
- ROFF_EXIT
-};
-
-enum rofftype {
- ROFF_TITLE,
- ROFF_COMMENT,
- ROFF_TEXT,
- ROFF_LAYOUT
-};
-
-#define ROFFCALL_ARGS \
- const struct md_args *arg, struct md_mbuf *out, \
- const struct md_rbuf *in, const char *argv[], \
- enum roffd type, struct rofftree *tree
-
-struct rofftree;
-
-struct rofftok {
- int id;
- char name[2];
- int (*cb)(ROFFCALL_ARGS);
- enum rofftype type;
- int flags;
-#define ROFF_NESTED (1 << 0)
-#define ROFF_PARSED (1 << 1)
-#define ROFF_CALLABLE (1 << 2)
-#define ROFF_QUOTES (1 << 3)
-};
-
-struct roffnode {
- int tok;
- struct roffnode *parent;
- size_t line;
-};
-
-struct rofftree {
- struct roffnode *last;
- time_t date;
- char title[256];
- char section[256];
- char volume[256];
- int state;
-#define ROFF_PRELUDE (1 << 1)
-#define ROFF_PRELUDE_Os (1 << 2)
-#define ROFF_PRELUDE_Dt (1 << 3)
-#define ROFF_PRELUDE_Dd (1 << 4)
-#define ROFF_BODY (1 << 5)
-};
-
-#define ROFF___ 0
-#define ROFF_Dd 1
-#define ROFF_Dt 2
-#define ROFF_Os 3
-#define ROFF_Sh 4
-#define ROFF_An 5
-#define ROFF_Li 6
-#define ROFF_MAX 7
-
-static int roff_Dd(ROFFCALL_ARGS);
-static int roff_Dt(ROFFCALL_ARGS);
-static int roff_Os(ROFFCALL_ARGS);
-static int roff_Sh(ROFFCALL_ARGS);
-static int roff_An(ROFFCALL_ARGS);
-static int roff_Li(ROFFCALL_ARGS);
-
-static struct roffnode *roffnode_new(int, size_t,
- struct rofftree *);
-static void roffnode_free(int, struct rofftree *);
-
-static int rofffind(const char *);
-static int roffargs(int, char *, char **);
-static int roffparse(const struct md_args *,
- struct md_mbuf *,
- const struct md_rbuf *,
- char *, size_t, struct rofftree *);
-static int textparse(struct md_mbuf *,
- const struct md_rbuf *,
- const char *, size_t,
- const struct rofftree *);
-
-static void dbg_enter(const struct md_args *, int);
-static void dbg_leave(const struct md_args *, int);
-
-
-static const struct rofftok tokens[ROFF_MAX] =
-{
-{ ROFF___, "\\\"", NULL, ROFF_COMMENT, 0 },
-{ ROFF_Dd, "Dd", roff_Dd, ROFF_TITLE, 0 },
-{ ROFF_Dt, "Dt", roff_Dt, ROFF_TITLE, 0 },
-{ ROFF_Os, "Os", roff_Os, ROFF_TITLE, 0 },
-{ ROFF_Sh, "Sh", roff_Sh, ROFF_LAYOUT, 0 },
-{ ROFF_An, "An", roff_An, ROFF_TEXT, ROFF_PARSED },
-{ ROFF_Li, "Li", roff_Li, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE },
-};
-
int
-md_exit_html4_strict(const struct md_args *args, struct md_mbuf *out,
- const struct md_rbuf *in, int error, void *data)
+md_line_html4_strict(void *data, char *buf, size_t sz)
{
- struct rofftree *tree;
-
- assert(args);
- assert(data);
- tree = (struct rofftree *)data;
-
- if (-1 == error)
- out = NULL;
-
- /* LINTED */
- while (tree->last)
- if ( ! (*tokens[tree->last->tok].cb)(args, out, in,
- NULL, ROFF_EXIT, tree))
- out = NULL;
-
- if (out && (ROFF_PRELUDE & tree->state)) {
- warnx("%s: prelude never finished", in->name);
- error = 1;
- }
-
- free(tree);
- return(error ? 0 : 1);
-}
-
-
-int
-md_init_html4_strict(const struct md_args *args, struct md_mbuf *out,
- const struct md_rbuf *in, void **data)
-{
- struct rofftree *tree;
-
- assert(args);
- assert(in);
- assert(out);
- assert(data);
-
- /* TODO: write HTML-DTD header. */
-
- if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
- warn("malloc");
- return(0);
- }
-
- tree->state = ROFF_PRELUDE;
-
- *data = tree;
return(1);
}
int
-md_line_html4_strict(const struct md_args *args, struct md_mbuf *out,
- const struct md_rbuf *in, char *buf,
- size_t sz, void *data)
-{
- struct rofftree *tree;
-
- assert(args);
- assert(in);
- assert(data);
-
- tree = (struct rofftree *)data;
-
- if (0 == sz) {
- warnx("%s: blank line (line %zu)", in->name, in->line);
- return(0);
- } else if ('.' != *buf)
- return(textparse(out, in, buf, sz, tree));
-
- return(roffparse(args, out, in, buf, sz, tree));
-}
-
-
-static int
-textparse(struct md_mbuf *out, const struct md_rbuf *in,
- const char *buf, size_t sz,
- const struct rofftree *tree)
-{
-
- assert(tree);
- assert(out);
- assert(in);
- assert(buf);
- assert(sz > 0);
-
- if (NULL == tree->last) {
- warnx("%s: unexpected text (line %zu)",
- in->name, in->line);
- return(0);
- } else if (NULL == tree->last->parent) {
- warnx("%s: disallowed text (line %zu)",
- in->name, in->line);
- return(0);
- }
-
- if ( ! md_buf_puts(out, buf, sz))
- return(0);
- return(md_buf_putstring(out, " "));
-}
-
-
-static int
-roffargs(int tok, char *buf, char **argv)
-{
- int i;
-
- (void)tok;/* FIXME: quotable strings? */
-
- assert(tok >= 0 && tok < ROFF_MAX);
- assert('.' == *buf);
-
- /* LINTED */
- for (i = 0; *buf && i < ROFF_MAXARG; i++) {
- argv[i] = buf++;
- while (*buf && ! isspace(*buf))
- buf++;
- if (NULL == *buf) {
- continue;
- }
- *buf++ = 0;
- while (*buf && isspace(*buf))
- buf++;
- }
-
- assert(i > 0);
- if (i < ROFF_MAXARG)
- argv[i] = NULL;
-
- return(ROFF_MAXARG > i);
-}
-
-
-static int
-roffparse(const struct md_args *args, struct md_mbuf *out,
- const struct md_rbuf *in, char *buf, size_t sz,
- struct rofftree *tree)
-{
- int tok, t;
- struct roffnode *node;
- char *argv[ROFF_MAXARG];
-
- assert(sz > 0);
-
- /*
- * Extract the token identifier from the buffer. If there's no
- * callback for the token (comment, etc.) then exit immediately.
- * We don't do any error handling (yet), so if the token doesn't
- * exist, die.
- */
-
- if (3 > sz) {
- warnx("%s: malformed line (line %zu)",
- in->name, in->line);
- return(0);
- } else if (ROFF_MAX == (tok = rofffind(buf + 1))) {
- warnx("%s: unknown line token `%c%c' (line %zu)",
- in->name, *(buf + 1),
- *(buf + 2), in->line);
- return(0);
- } else if (ROFF_COMMENT == tokens[tok].type)
- /* Ignore comment tokens. */
- return(1);
-
- if ( ! roffargs(tok, buf, argv)) {
- warnx("%s: too many arguments to `%s' (line %zu)",
- in->name, tokens[tok].name, in->line);
- return(0);
- }
-
- /* Domain cross-contamination (and sanity) checks. */
-
- switch (tokens[tok].type) {
- case (ROFF_TITLE):
- if (ROFF_PRELUDE & tree->state) {
- assert( ! (ROFF_BODY & tree->state));
- break;
- }
- assert(ROFF_BODY & tree->state);
- warnx("%s: prelude token `%s' in body (line %zu)",
- in->name, tokens[tok].name, in->line);
- return(0);
- case (ROFF_LAYOUT):
- /* FALLTHROUGH */
- case (ROFF_TEXT):
- if (ROFF_BODY & tree->state) {
- assert( ! (ROFF_PRELUDE & tree->state));
- break;
- }
- assert(ROFF_PRELUDE & tree->state);
- warnx("%s: body token `%s' in prelude (line %zu)",
- in->name, tokens[tok].name, in->line);
- return(0);
- case (ROFF_COMMENT):
- return(1);
- default:
- abort();
- }
-
- /*
- * Text-domain checks.
- */
-
- if (ROFF_TEXT == tokens[tok].type &&
- ! (ROFF_PARSED & tokens[tok].flags)) {
- warnx("%s: text token `%s' not callable (line %zu)",
- in->name, tokens[tok].name, in->line);
- return(0);
- }
-
- /*
- * If this is a non-nestable layout token and we're below a
- * token of the same type, then recurse upward to the token,
- * closing out the interim scopes.
- *
- * If there's a nested token on the chain, then raise an error
- * as nested tokens have corresponding "ending" tokens and we're
- * breaking their scope.
- */
-
- node = NULL;
-
- if (ROFF_LAYOUT == tokens[tok].type &&
- ! (ROFF_NESTED & tokens[tok].flags)) {
- for (node = tree->last; node; node = node->parent) {
- if (node->tok == tok)
- break;
-
- /* Don't break nested scope. */
-
- if ( ! (ROFF_NESTED & tokens[node->tok].flags))
- continue;
- warnx("%s: scope of %s (line %zu) broken by "
- "%s (line %zu)", in->name,
- tokens[tok].name,
- node->line,
- tokens[node->tok].name,
- in->line);
- return(0);
- }
- }
-
- if (node) {
- assert(ROFF_LAYOUT == tokens[tok].type);
- assert( ! (ROFF_NESTED & tokens[tok].flags));
- assert(node->tok == tok);
-
- /* Clear up to last scoped token. */
-
- /* LINTED */
- do {
- t = tree->last->tok;
- if ( ! (*tokens[tree->last->tok].cb)
- (args, out, in, NULL,
- ROFF_EXIT, tree))
- return(0);
- } while (t != tok);
- }
-
- /* Proceed with actual token processing. */
-
- return((*tokens[tok].cb)(args, out, in, (const char **)argv,
- ROFF_ENTER, tree));
-}
-
-
-static int
-rofffind(const char *name)
-{
- size_t i;
-
- assert(name);
- /* FIXME: use a table, this is slow but ok for now. */
-
- /* LINTED */
- for (i = 0; i < ROFF_MAX; i++)
- /* LINTED */
- if (0 == strncmp(name, tokens[i].name, 2))
- return((int)i);
-
- return(ROFF_MAX);
-}
-
-
-static struct roffnode *
-roffnode_new(int tokid, size_t line, struct rofftree *tree)
-{
- struct roffnode *p;
-
- if (NULL == (p = malloc(sizeof(struct roffnode)))) {
- warn("malloc");
- return(NULL);
- }
-
- p->line = line;
- p->tok = tokid;
- p->parent = tree->last;
- tree->last = p;
- return(p);
-}
-
-
-static void
-roffnode_free(int tokid, struct rofftree *tree)
-{
- struct roffnode *p;
-
- assert(tree->last);
- assert(tree->last->tok == tokid);
-
- p = tree->last;
- tree->last = tree->last->parent;
- free(p);
-}
-
-
-static int dbg_lvl = 0;
-
-
-static void
-dbg_enter(const struct md_args *args, int tokid)
-{
- int i;
- static char buf[72];
-
- assert(args);
- if ( ! (args->dbg & MD_DBG_TREE))
- return;
- assert(tokid >= 0 && tokid <= ROFF_MAX);
-
- buf[0] = 0;
-
- switch (tokens[tokid].type) {
- case (ROFF_LAYOUT):
- /* FALLTHROUGH */
- case (ROFF_TEXT):
- (void)strlcat(buf, "body: ", sizeof(buf));
- break;
- case (ROFF_TITLE):
- (void)strlcat(buf, "prelude: ", sizeof(buf));
- break;
- default:
- abort();
- }
-
- /* LINTED */
- for (i = 0; i < dbg_lvl; i++)
- (void)strlcat(buf, " ", sizeof(buf));
-
- (void)strlcat(buf, tokens[tokid].name, sizeof(buf));
-
- (void)printf("%s\n", buf);
-
- if (ROFF_LAYOUT == tokens[tokid].type)
- dbg_lvl++;
-}
-
-
-static void
-dbg_leave(const struct md_args *args, int tokid)
-{
- assert(args);
- if ( ! (args->dbg & MD_DBG_TREE))
- return;
- if (ROFF_LAYOUT != tokens[tokid].type)
- return;
-
- assert(tokid >= 0 && tokid <= ROFF_MAX);
- assert(dbg_lvl > 0);
- dbg_lvl--;
-}
-
-
-/* ARGSUSED */
-static int
-roff_Dd(ROFFCALL_ARGS)
-{
-
- dbg_enter(arg, ROFF_Dd);
-
- assert(ROFF_PRELUDE & tree->state);
- if (ROFF_PRELUDE_Dt & tree->state ||
- ROFF_PRELUDE_Dd & tree->state) {
- warnx("%s: prelude `Dd' out-of-order (line %zu)",
- in->name, in->line);
- return(0);
- }
-
- assert(NULL == tree->last);
- tree->state |= ROFF_PRELUDE_Dd;
-
- dbg_leave(arg, ROFF_Dd);
-
- return(1);
-}
-
-
-/* ARGSUSED */
-static int
-roff_Dt(ROFFCALL_ARGS)
-{
-
- dbg_enter(arg, ROFF_Dt);
-
- assert(ROFF_PRELUDE & tree->state);
- if ( ! (ROFF_PRELUDE_Dd & tree->state) ||
- (ROFF_PRELUDE_Dt & tree->state)) {
- warnx("%s: prelude `Dt' out-of-order (line %zu)",
- in->name, in->line);
- return(0);
- }
-
- assert(NULL == tree->last);
- tree->state |= ROFF_PRELUDE_Dt;
-
- dbg_leave(arg, ROFF_Dt);
-
- return(1);
-}
-
-
-/* ARGSUSED */
-static int
-roff_Os(ROFFCALL_ARGS)
+md_exit_html4_strict(void *data, int flush)
{
- if (ROFF_EXIT == type) {
- roffnode_free(ROFF_Os, tree);
- dbg_leave(arg, ROFF_Os);
- return(1);
- }
-
- dbg_enter(arg, ROFF_Os);
-
- assert(ROFF_PRELUDE & tree->state);
- if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
- ! (ROFF_PRELUDE_Dd & tree->state)) {
- warnx("%s: prelude `Os' out-of-order (line %zu)",
- in->name, in->line);
- return(0);
- }
-
- assert(NULL == tree->last);
- if (NULL == roffnode_new(ROFF_Os, in->line, tree))
- return(0);
-
- tree->state |= ROFF_PRELUDE_Os;
- tree->state &= ~ROFF_PRELUDE;
- tree->state |= ROFF_BODY;
-
- return(1);
-}
-
-
-/* ARGSUSED */
-static int
-roff_Sh(ROFFCALL_ARGS)
-{
-
- if (ROFF_EXIT == type) {
- roffnode_free(ROFF_Sh, tree);
- dbg_leave(arg, ROFF_Sh);
- return(1);
- }
-
- dbg_enter(arg, ROFF_Sh);
-
- if (NULL == roffnode_new(ROFF_Sh, in->line, tree))
- return(0);
-
- dbg_leave(arg, ROFF_Li);
-
return(1);
}
-/* ARGSUSED */
-static int
-roff_Li(ROFFCALL_ARGS)
+void *
+md_init_html4_strict(const struct md_args *args,
+ struct md_mbuf *mbuf, const struct md_rbuf *rbuf)
{
- dbg_enter(arg, ROFF_Li);
- dbg_leave(arg, ROFF_Li);
-
- return(1);
-}
-
-
-#if 0
-static int
-parse_args(void)
-{
- skip_whitespace();
-
- while (pos < sz) {
-
- if (is_arg) {
- } else if (parsable) {
- if (is_callable_token()) {
- }
- }
-
- skip_whitespace();
- }
-}
-#endif
-
-
-/* ARGSUSED */
-static int
-roff_An(ROFFCALL_ARGS)
-{
-
- dbg_enter(arg, ROFF_An);
-
- /* Do our ML stuff. */
-
- /*parse_args();*/
-
- /* Do our trailing whitespace stuff. */
-
- dbg_leave(arg, ROFF_An);
-
- return(1);
+ return(NULL);
}
diff --git a/libmdocml.c b/libmdocml.c
index 11936f91..6fc5d32e 100644
--- a/libmdocml.c
+++ b/libmdocml.c
@@ -136,13 +136,13 @@ md_run_leave(const struct md_args *args, struct md_mbuf *mbuf,
/* Run exiters. */
switch (args->type) {
case (MD_HTML4_STRICT):
- if ( ! md_exit_html4_strict(args, mbuf, rbuf, c, data))
- return(-1);
- break;
- case (MD_DUMMY):
+ if ( ! md_exit_html4_strict(data, -1 == c ? 0 : 1))
+ c = -1;
break;
default:
- abort();
+ if ( ! md_exit_dummy(data, -1 == c ? 0 : 1))
+ c = -1;
+ break;
}
/* Make final flush of buffer. */
@@ -200,7 +200,7 @@ again:
}
line[pos] = 0;
- if ( ! (*fp)(args, mbuf, rbuf, line, pos, p))
+ if ( ! (*fp)(p, line, pos))
return(md_run_leave(args, mbuf, rbuf, -1, p));
rbuf->line++;
pos = 0;
@@ -228,18 +228,17 @@ md_run(const struct md_args *args,
mbuf.pos = 0;
rbuf.line = 1;
- data = NULL;
/* Run initialisers. */
switch (args->type) {
case (MD_HTML4_STRICT):
- if ( ! md_init_html4_strict(args, &mbuf, &rbuf, &data))
- return(-1);
- break;
- case (MD_DUMMY):
+ data = md_init_html4_strict
+ (args, &mbuf, &rbuf);
break;
default:
- abort();
+ data = md_init_dummy
+ (args, &mbuf, &rbuf);
+ break;
}
/* Go into mainline. */
diff --git a/mdocml.c b/mdocml.c
index 1ba70fb8..d83bb4cd 100644
--- a/mdocml.c
+++ b/mdocml.c
@@ -55,6 +55,8 @@ main(int argc, char *argv[])
extern int optind;
out = in = NULL;
+
+ (void)memset(&args, 0, sizeof(struct md_args));
while (-1 != (c = getopt(argc, argv, "vo:")))
switch (c) {
@@ -75,7 +77,7 @@ main(int argc, char *argv[])
if (1 == argc)
in = *argv++;
- args.type = MD_HTML4_STRICT;
+ args.type = MD_DUMMY;
return(begin_io(&args, out ? out : "-", in ? in : "-"));
}
diff --git a/private.h b/private.h
index 7d166667..52312274 100644
--- a/private.h
+++ b/private.h
@@ -37,32 +37,32 @@ struct md_mbuf {
__BEGIN_DECLS
-typedef int (*md_init)(const struct md_args *, struct md_mbuf *,
- const struct md_rbuf *, void **);
-typedef int (*md_exit)(const struct md_args *, struct md_mbuf *,
- const struct md_rbuf *, int, void *);
-typedef int (*md_line)(const struct md_args *,
- struct md_mbuf *, const struct md_rbuf *,
- char *, size_t, void *);
+typedef void (*(*md_init)(const struct md_args *,
+ struct md_mbuf *, const struct md_rbuf *));
+typedef int (*md_line)(void *, char *, size_t);
+typedef int (*md_exit)(void *, int);
-int md_line_html4_strict(const struct md_args *,
- struct md_mbuf *, const struct md_rbuf *,
- char *, size_t, void *);
-int md_init_html4_strict(const struct md_args *,
- struct md_mbuf *, const struct md_rbuf *,
- void **);
-int md_exit_html4_strict(const struct md_args *,
- struct md_mbuf *, const struct md_rbuf *,
- int, void *);
+void *md_init_html4_strict(const struct md_args *,
+ struct md_mbuf *, const struct md_rbuf *);
+int md_line_html4_strict(void *, char *, size_t);
+int md_exit_html4_strict(void *, int);
-int md_line_dummy(const struct md_args *,
- struct md_mbuf *, const struct md_rbuf *,
- char *, size_t, void *);
+void *md_init_dummy(const struct md_args *,
+ struct md_mbuf *, const struct md_rbuf *);
+int md_line_dummy(void *, char *, size_t);
+int md_exit_dummy(void *, int);
int md_buf_puts(struct md_mbuf *, const char *, size_t);
int md_buf_putchar(struct md_mbuf *, char);
int md_buf_putstring(struct md_mbuf *, const char *);
+struct rofftree;
+
+struct rofftree *roff_alloc(const struct md_args *,
+ struct md_mbuf *, const struct md_rbuf *);
+int roff_engine(struct rofftree *, char *, size_t);
+int roff_free(struct rofftree *, int);
+
__END_DECLS
#endif /*!PRIVATE_H*/
diff --git a/roff.c b/roff.c
new file mode 100644
index 00000000..1f5b9a48
--- /dev/null
+++ b/roff.c
@@ -0,0 +1,701 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "libmdocml.h"
+#include "private.h"
+
+#define ROFF_MAXARG 10
+
+enum roffd {
+ ROFF_ENTER = 0,
+ ROFF_EXIT
+};
+
+enum rofftype {
+ ROFF_TITLE,
+ ROFF_COMMENT,
+ ROFF_TEXT,
+ ROFF_LAYOUT
+};
+
+#define ROFFCALL_ARGS \
+ struct rofftree *tree, const char *argv[], enum roffd type
+
+struct rofftree;
+
+struct rofftok {
+ char *name;
+ int (*cb)(ROFFCALL_ARGS);
+ enum rofftype type;
+ int flags;
+#define ROFF_NESTED (1 << 0)
+#define ROFF_PARSED (1 << 1)
+#define ROFF_CALLABLE (1 << 2)
+#define ROFF_QUOTES (1 << 3)
+};
+
+struct roffarg {
+ char *name;
+ int flags;
+#define ROFF_VALUE (1 << 0)
+};
+
+struct roffnode {
+ int tok;
+ struct roffnode *parent;
+ size_t line;
+};
+
+struct rofftree {
+ struct roffnode *last;
+ time_t date;
+ char title[256];
+ char section[256];
+ char volume[256];
+ int state;
+#define ROFF_PRELUDE (1 << 1)
+#define ROFF_PRELUDE_Os (1 << 2)
+#define ROFF_PRELUDE_Dt (1 << 3)
+#define ROFF_PRELUDE_Dd (1 << 4)
+#define ROFF_BODY (1 << 5)
+ struct md_mbuf *mbuf; /* NULL if ROFF_EXIT and error. */
+
+ const struct md_args *args;
+ const struct md_rbuf *rbuf;
+};
+
+#define ROFF___ 0
+#define ROFF_Dd 1
+#define ROFF_Dt 2
+#define ROFF_Os 3
+#define ROFF_Sh 4
+#define ROFF_An 5
+#define ROFF_Li 6
+#define ROFF_MAX 7
+
+static int roff_Dd(ROFFCALL_ARGS);
+static int roff_Dt(ROFFCALL_ARGS);
+static int roff_Os(ROFFCALL_ARGS);
+static int roff_Sh(ROFFCALL_ARGS);
+static int roff_An(ROFFCALL_ARGS);
+static int roff_Li(ROFFCALL_ARGS);
+
+static struct roffnode *roffnode_new(int, size_t,
+ struct rofftree *);
+static void roffnode_free(int, struct rofftree *);
+
+static int rofffindtok(const char *);
+static int rofffindarg(const char *);
+static int roffargs(int, char *, char **);
+static int roffparse(struct rofftree *, char *, size_t);
+static int textparse(const struct rofftree *,
+ const char *, size_t);
+
+static void dbg_enter(const struct md_args *, int);
+static void dbg_leave(const struct md_args *, int);
+
+
+static const struct rofftok tokens[ROFF_MAX] = {
+ { "\\\"", NULL, ROFF_COMMENT, 0 },
+ { "Dd", roff_Dd, ROFF_TITLE, 0 },
+ { "Dt", roff_Dt, ROFF_TITLE, 0 },
+ { "Os", roff_Os, ROFF_TITLE, 0 },
+ { "Sh", roff_Sh, ROFF_LAYOUT, 0 },
+ { "An", roff_An, ROFF_TEXT, ROFF_PARSED },
+ { "Li", roff_Li, ROFF_TEXT, ROFF_PARSED | ROFF_CALLABLE },
+};
+
+#define ROFF_Split 0
+#define ROFF_Nosplit 1
+#define ROFF_ARGMAX 2
+
+static const struct roffarg tokenargs[ROFF_ARGMAX] = {
+ { "split", 0 },
+ { "nosplit", 0 },
+};
+
+
+int
+roff_free(struct rofftree *tree, int flush)
+{
+ int error;
+
+ assert(tree->mbuf);
+ if ( ! flush)
+ tree->mbuf = NULL;
+
+ /* LINTED */
+ while (tree->last)
+ if ( ! (*tokens[tree->last->tok].cb)
+ (tree, NULL, ROFF_EXIT))
+ /* Disallow flushing. */
+ tree->mbuf = NULL;
+
+ error = tree->mbuf ? 0 : 1;
+
+ if (tree->mbuf && (ROFF_PRELUDE & tree->state)) {
+ warnx("%s: prelude never finished",
+ tree->rbuf->name);
+ error = 1;
+ }
+
+ free(tree);
+ return(error ? 0 : 1);
+}
+
+
+struct rofftree *
+roff_alloc(const struct md_args *args, struct md_mbuf *out,
+ const struct md_rbuf *in)
+{
+ struct rofftree *tree;
+
+ if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) {
+ warn("malloc");
+ return(NULL);
+ }
+
+ tree->state = ROFF_PRELUDE;
+ tree->args = args;
+ tree->mbuf = out;
+ tree->rbuf = in;
+
+ return(tree);
+}
+
+
+int
+roff_engine(struct rofftree *tree, char *buf, size_t sz)
+{
+
+ if (0 == sz) {
+ warnx("%s: blank line (line %zu)",
+ tree->rbuf->name,
+ tree->rbuf->line);
+ return(0);
+ } else if ('.' != *buf)
+ return(textparse(tree, buf, sz));
+
+ return(roffparse(tree, buf, sz));
+}
+
+
+static int
+textparse(const struct rofftree *tree, const char *buf, size_t sz)
+{
+
+ if (NULL == tree->last) {
+ warnx("%s: unexpected text (line %zu)",
+ tree->rbuf->name,
+ tree->rbuf->line);
+ return(0);
+ } else if (NULL == tree->last->parent) {
+ warnx("%s: disallowed text (line %zu)",
+ tree->rbuf->name,
+ tree->rbuf->line);
+ return(0);
+ }
+
+ /* Print text. */
+
+ return(1);
+}
+
+
+static int
+roffargs(int tok, char *buf, char **argv)
+{
+ int i;
+
+ (void)tok;/* FIXME: quotable strings? */
+
+ assert(tok >= 0 && tok < ROFF_MAX);
+ assert('.' == *buf);
+
+ /* LINTED */
+ for (i = 0; *buf && i < ROFF_MAXARG; i++) {
+ argv[i] = buf++;
+ while (*buf && ! isspace(*buf))
+ buf++;
+ if (0 == *buf) {
+ continue;
+ }
+ *buf++ = 0;
+ while (*buf && isspace(*buf))
+ buf++;
+ }
+
+ assert(i > 0);
+ if (i < ROFF_MAXARG)
+ argv[i] = NULL;
+
+ return(ROFF_MAXARG > i);
+}
+
+
+static int
+roffparse(struct rofftree *tree, char *buf, size_t sz)
+{
+ int tok, t;
+ struct roffnode *node;
+ char *argv[ROFF_MAXARG];
+ const char **argvp;
+
+ assert(sz > 0);
+
+ /*
+ * Extract the token identifier from the buffer. If there's no
+ * callback for the token (comment, etc.) then exit immediately.
+ * We don't do any error handling (yet), so if the token doesn't
+ * exist, die.
+ */
+
+ if (3 > sz) {
+ warnx("%s: malformed line (line %zu)",
+ tree->rbuf->name,
+ tree->rbuf->line);
+ return(0);
+ } else if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
+ warnx("%s: unknown line token `%c%c' (line %zu)",
+ tree->rbuf->name,
+ *(buf + 1), *(buf + 2),
+ tree->rbuf->line);
+ return(0);
+ } else if (ROFF_COMMENT == tokens[tok].type)
+ /* Ignore comment tokens. */
+ return(1);
+
+ if ( ! roffargs(tok, buf, argv)) {
+ warnx("%s: too many arguments to `%s' (line %zu)",
+ tree->rbuf->name, tokens[tok].name,
+ tree->rbuf->line);
+ return(0);
+ }
+
+ /* Domain cross-contamination (and sanity) checks. */
+
+ switch (tokens[tok].type) {
+ case (ROFF_TITLE):
+ if (ROFF_PRELUDE & tree->state) {
+ assert( ! (ROFF_BODY & tree->state));
+ break;
+ }
+ assert(ROFF_BODY & tree->state);
+ warnx("%s: prelude token `%s' in body (line %zu)",
+ tree->rbuf->name, tokens[tok].name,
+ tree->rbuf->line);
+ return(0);
+ case (ROFF_LAYOUT):
+ /* FALLTHROUGH */
+ case (ROFF_TEXT):
+ if (ROFF_BODY & tree->state) {
+ assert( ! (ROFF_PRELUDE & tree->state));
+ break;
+ }
+ assert(ROFF_PRELUDE & tree->state);
+ warnx("%s: body token `%s' in prelude (line %zu)",
+ tree->rbuf->name, tokens[tok].name,
+ tree->rbuf->line);
+ return(0);
+ case (ROFF_COMMENT):
+ return(1);
+ default:
+ abort();
+ }
+
+ /*
+ * If this is a non-nestable layout token and we're below a
+ * token of the same type, then recurse upward to the token,
+ * closing out the interim scopes.
+ *
+ * If there's a nested token on the chain, then raise an error
+ * as nested tokens have corresponding "ending" tokens and we're
+ * breaking their scope.
+ */
+
+ node = NULL;
+
+ if (ROFF_LAYOUT == tokens[tok].type &&
+ ! (ROFF_NESTED & tokens[tok].flags)) {
+ for (node = tree->last; node; node = node->parent) {
+ if (node->tok == tok)
+ break;
+
+ /* Don't break nested scope. */
+
+ if ( ! (ROFF_NESTED & tokens[node->tok].flags))
+ continue;
+ warnx("%s: scope of %s (line %zu) broken by "
+ "%s (line %zu)",
+ tree->rbuf->name,
+ tokens[tok].name,
+ node->line,
+ tokens[node->tok].name,
+ tree->rbuf->line);
+ return(0);
+ }
+ }
+
+ if (node) {
+ assert(ROFF_LAYOUT == tokens[tok].type);
+ assert( ! (ROFF_NESTED & tokens[tok].flags));
+ assert(node->tok == tok);
+
+ /* Clear up to last scoped token. */
+
+ /* LINTED */
+ do {
+ t = tree->last->tok;
+ if ( ! (*tokens[tree->last->tok].cb)
+ (tree, NULL, ROFF_EXIT))
+ return(0);
+ } while (t != tok);
+ }
+
+ /* Proceed with actual token processing. */
+
+ argvp = (const char **)&argv[1];
+ return((*tokens[tok].cb)(tree, argvp, ROFF_ENTER));
+}
+
+
+static int
+rofffindarg(const char *name)
+{
+ size_t i;
+
+ /* FIXME: use a table, this is slow but ok for now. */
+
+ /* LINTED */
+ for (i = 0; i < ROFF_ARGMAX; i++)
+ /* LINTED */
+ if (0 == strcmp(name, tokenargs[i].name))
+ return((int)i);
+
+ return(ROFF_ARGMAX);
+}
+
+
+static int
+rofffindtok(const char *name)
+{
+ size_t i;
+
+ /* FIXME: use a table, this is slow but ok for now. */
+
+ /* LINTED */
+ for (i = 0; i < ROFF_MAX; i++)
+ /* LINTED */
+ if (0 == strncmp(name, tokens[i].name, 2))
+ return((int)i);
+
+ return(ROFF_MAX);
+}
+
+
+/* FIXME: accept only struct rofftree *. */
+static struct roffnode *
+roffnode_new(int tokid, size_t line, struct rofftree *tree)
+{
+ struct roffnode *p;
+
+ if (NULL == (p = malloc(sizeof(struct roffnode)))) {
+ warn("malloc");
+ return(NULL);
+ }
+
+ p->line = line;
+ p->tok = tokid;
+ p->parent = tree->last;
+ tree->last = p;
+ return(p);
+}
+
+
+static void
+roffnode_free(int tokid, struct rofftree *tree)
+{
+ struct roffnode *p;
+
+ assert(tree->last);
+ assert(tree->last->tok == tokid);
+
+ p = tree->last;
+ tree->last = tree->last->parent;
+ free(p);
+}
+
+
+static int dbg_lvl = 0;
+
+
+static void
+dbg_enter(const struct md_args *args, int tokid)
+{
+ int i;
+ static char buf[72];
+
+ assert(args);
+ if ( ! (args->dbg & MD_DBG_TREE))
+ return;
+ assert(tokid >= 0 && tokid <= ROFF_MAX);
+
+ buf[0] = buf[71] = 0;
+
+ switch (tokens[tokid].type) {
+ case (ROFF_LAYOUT):
+ (void)strncat(buf, "[body-layout] ", sizeof(buf) - 1);
+ break;
+ case (ROFF_TEXT):
+ (void)strncat(buf, "[ body-text] ", sizeof(buf) - 1);
+ break;
+ case (ROFF_TITLE):
+ (void)strncat(buf, "[ prelude] ", sizeof(buf) - 1);
+ break;
+ default:
+ abort();
+ }
+
+ /* LINTED */
+ for (i = 0; i < dbg_lvl; i++)
+ (void)strncat(buf, " ", sizeof(buf) - 1);
+
+ (void)strncat(buf, tokens[tokid].name, sizeof(buf) - 1);
+
+ (void)printf("%s\n", buf);
+
+ dbg_lvl++;
+}
+
+
+/* FIXME: accept only struct rofftree *. */
+static void
+dbg_leave(const struct md_args *args, int tokid)
+{
+ assert(args);
+ if ( ! (args->dbg & MD_DBG_TREE))
+ return;
+
+ assert(tokid >= 0 && tokid <= ROFF_MAX);
+ assert(dbg_lvl > 0);
+ dbg_lvl--;
+}
+
+
+/* FIXME: accept only struct rofftree *. */
+/* ARGSUSED */
+static int
+roff_Dd(ROFFCALL_ARGS)
+{
+
+ dbg_enter(tree->args, ROFF_Dd);
+
+ assert(ROFF_PRELUDE & tree->state);
+ if (ROFF_PRELUDE_Dt & tree->state ||
+ ROFF_PRELUDE_Dd & tree->state) {
+ warnx("%s: prelude `Dd' out-of-order (line %zu)",
+ tree->rbuf->name, tree->rbuf->line);
+ return(0);
+ }
+
+ assert(NULL == tree->last);
+ tree->state |= ROFF_PRELUDE_Dd;
+
+ dbg_leave(tree->args, ROFF_Dd);
+
+ return(1);
+}
+
+
+/* ARGSUSED */
+static int
+roff_Dt(ROFFCALL_ARGS)
+{
+
+ dbg_enter(tree->args, ROFF_Dt);
+
+ assert(ROFF_PRELUDE & tree->state);
+ if ( ! (ROFF_PRELUDE_Dd & tree->state) ||
+ (ROFF_PRELUDE_Dt & tree->state)) {
+ warnx("%s: prelude `Dt' out-of-order (line %zu)",
+ tree->rbuf->name, tree->rbuf->line);
+ return(0);
+ }
+
+ assert(NULL == tree->last);
+ tree->state |= ROFF_PRELUDE_Dt;
+
+ dbg_leave(tree->args, ROFF_Dt);
+
+ return(1);
+}
+
+
+/* ARGSUSED */
+static int
+roff_Os(ROFFCALL_ARGS)
+{
+
+ if (ROFF_EXIT == type) {
+ roffnode_free(ROFF_Os, tree);
+ dbg_leave(tree->args, ROFF_Os);
+ return(1);
+ }
+
+ dbg_enter(tree->args, ROFF_Os);
+
+ assert(ROFF_PRELUDE & tree->state);
+ if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
+ ! (ROFF_PRELUDE_Dd & tree->state)) {
+ warnx("%s: prelude `Os' out-of-order (line %zu)",
+ tree->rbuf->name, tree->rbuf->line);
+ return(0);
+ }
+
+ assert(NULL == tree->last);
+ if (NULL == roffnode_new(ROFF_Os, tree->rbuf->line, tree))
+ return(0);
+
+ tree->state |= ROFF_PRELUDE_Os;
+ tree->state &= ~ROFF_PRELUDE;
+ tree->state |= ROFF_BODY;
+
+ return(1);
+}
+
+
+/* ARGSUSED */
+static int
+roff_Sh(ROFFCALL_ARGS)
+{
+
+ if (ROFF_EXIT == type) {
+ roffnode_free(ROFF_Sh, tree);
+ dbg_leave(tree->args, ROFF_Sh);
+ return(1);
+ }
+
+ dbg_enter(tree->args, ROFF_Sh);
+
+ if (NULL == roffnode_new(ROFF_Sh, tree->rbuf->line, tree))
+ return(0);
+
+ return(1);
+}
+
+
+/* ARGSUSED */
+static int
+roff_Li(ROFFCALL_ARGS)
+{
+
+ dbg_enter(tree->args, ROFF_Li);
+ dbg_leave(tree->args, ROFF_Li);
+
+ return(1);
+}
+
+
+static int
+roffnextopt(const char ***in, char **val)
+{
+ const char *arg, **argv;
+ int v;
+
+ *val = NULL;
+ argv = *in;
+ assert(argv);
+
+ if (NULL == (arg = *argv))
+ return(-1);
+ if ('-' != *arg)
+ return(-1);
+ if (ROFF_ARGMAX == (v = rofffindarg(&arg[1])))
+ return(-1);
+ if ( ! (ROFF_VALUE & tokenargs[v].flags))
+ return(v);
+
+ *in = ++argv;
+
+ /* FIXME: what if this looks like a roff token or argument? */
+
+ return(*argv ? v : ROFF_ARGMAX);
+}
+
+
+/* ARGSUSED */
+static int
+roff_An(ROFFCALL_ARGS)
+{
+ int c;
+ char *val;
+
+ dbg_enter(tree->args, ROFF_An);
+
+ while (-1 != (c = roffnextopt(&argv, &val))) {
+ switch (c) {
+ case (ROFF_Split):
+ /* Process argument. */
+ break;
+ case (ROFF_Nosplit):
+ /* Process argument. */
+ break;
+ default:
+ warnx("%s: error parsing `An' args (line %zu)",
+ tree->rbuf->name,
+ tree->rbuf->line);
+ return(0);
+ }
+ argv++;
+ }
+
+ /* Print header. */
+
+ while (*argv) {
+ if (/* is_parsable && */ 2 >= strlen(*argv)) {
+ if (ROFF_MAX != (c = rofffindtok(*argv))) {
+ if (ROFF_CALLABLE & tokens[c].flags) {
+ /* Call to token. */
+ if ( ! (*tokens[c].cb)(tree, (const char **)argv + 1, ROFF_ENTER))
+ return(0);
+ }
+ /* Print token. */
+ } else {
+ /* Print token. */
+ }
+ } else {
+ /* Print token. */
+ }
+ argv++;
+ }
+
+ /* Print footer. */
+
+ dbg_leave(tree->args, ROFF_An);
+
+ return(1);
+}
+