version 1.2, 2008/11/23 19:10:03 |
version 1.3, 2008/11/23 22:30:53 |
|
|
#include "libmdocml.h" |
#include "libmdocml.h" |
#include "private.h" |
#include "private.h" |
|
|
enum roffd { |
enum roffd { |
ROFF_ENTER = 0, |
ROFF_ENTER = 0, |
ROFF_EXIT |
ROFF_EXIT |
}; |
}; |
|
|
enum rofftype { |
enum rofftype { |
ROFF_NONE = 0, |
ROFF_TITLE, |
ROFF_LAYOUT |
ROFF_COMMENT, |
|
ROFF_TEXT, |
|
ROFF_LAYOUT |
}; |
}; |
|
|
struct rofftree; |
|
|
|
#define ROFFCALL_ARGS const struct md_args *arg, \ |
#define ROFFCALL_ARGS const struct md_args *arg, \ |
struct md_mbuf *out, \ |
struct md_mbuf *out, \ |
const struct md_rbuf *in, \ |
const struct md_rbuf *in, \ |
const char *buf, size_t sz, \ |
const char *buf, size_t sz, \ |
size_t pos, enum roffd type, \ |
size_t pos, enum roffd type, \ |
struct rofftree *tree |
struct rofftree *tree |
typedef int (*roffcall)(ROFFCALL_ARGS); |
|
|
|
static int roff_Dd(ROFFCALL_ARGS); |
struct rofftree; |
static int roff_Dt(ROFFCALL_ARGS); |
|
static int roff_Os(ROFFCALL_ARGS); |
|
static int roff_Sh(ROFFCALL_ARGS); |
|
|
|
struct rofftok { |
struct rofftok { |
char id; |
int id; |
#define ROFF___ 0 |
|
#define ROFF_Dd 1 |
|
#define ROFF_Dt 2 |
|
#define ROFF_Os 3 |
|
#define ROFF_Sh 4 |
|
#define ROFF_Max 5 |
|
char name[2]; |
char name[2]; |
roffcall cb; |
int (*cb)(ROFFCALL_ARGS); |
enum rofftype type; |
enum rofftype type; |
int flags; |
int flags; |
#define ROFF_NESTED (1 << 0) |
#define ROFF_NESTED (1 << 0) /* FIXME: test. */ |
|
#define ROFF_PARSED (1 << 1) /* FIXME: test. */ |
|
#define ROFF_CALLABLE (1 << 2) /* FIXME: test. */ |
}; |
}; |
|
|
static const struct rofftok tokens[ROFF_Max] = { |
|
{ ROFF___, "\\\"", NULL, ROFF_NONE, 0 }, |
|
{ ROFF_Dd, "Dd", roff_Dd, ROFF_NONE, 0 }, |
|
{ ROFF_Dt, "Dt", roff_Dt, ROFF_NONE, 0 }, |
|
{ ROFF_Os, "Os", roff_Os, ROFF_LAYOUT, 0 }, |
|
{ ROFF_Sh, "Sh", roff_Sh, ROFF_LAYOUT, 0 }, |
|
}; |
|
|
|
struct roffnode { |
struct roffnode { |
int tok; |
int tok; |
struct roffnode *parent; |
struct roffnode *parent; |
/* TODO: line number at acquisition. */ |
size_t line; |
}; |
}; |
|
|
struct rofftree { |
struct rofftree { |
struct roffnode *last; |
struct roffnode *last; |
time_t date; |
time_t date; |
char title[256]; |
char title[256]; |
char section[256]; |
char section[256]; |
char volume[256]; |
char volume[256]; |
int state; |
int state; |
#define ROFF_PRELUDE_Os (1 << 1) |
#define ROFF_PRELUDE (1 << 1) |
#define ROFF_PRELUDE_Dt (1 << 2) |
#define ROFF_PRELUDE_Os (1 << 2) |
#define ROFF_PRELUDE_Dd (1 << 3) |
#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 rofffind(const char *); |
static int roffparse(const struct md_args *, |
static int roffparse(const struct md_args *, |
struct md_mbuf *, |
struct md_mbuf *, |
Line 108 static void dbg_enter(const struct md_args *, int); |
|
Line 114 static void dbg_enter(const struct md_args *, int); |
|
static void dbg_leave(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 |
int |
md_exit_html4_strict(const struct md_args *args, struct md_mbuf *out, |
md_exit_html4_strict(const struct md_args *args, struct md_mbuf *out, |
const struct md_rbuf *in, void *data) |
const struct md_rbuf *in, int error, void *data) |
{ |
{ |
struct rofftree *tree; |
struct rofftree *tree; |
int error; |
|
|
|
assert(args); |
assert(args); |
assert(data); |
assert(data); |
tree = (struct rofftree *)data; |
tree = (struct rofftree *)data; |
error = 0; |
|
|
|
|
if (-1 == error) |
|
out = NULL; |
|
|
|
/* LINTED */ |
while (tree->last) |
while (tree->last) |
if ( ! (*tokens[tree->last->tok].cb) |
if ( ! (*tokens[tree->last->tok].cb)(args, out, in, |
(args, error ? NULL : out, in, NULL, |
NULL, 0, 0, ROFF_EXIT, tree)) |
0, 0, ROFF_EXIT, tree)) |
out = NULL; |
error = 1; |
|
|
|
|
if (out && (ROFF_PRELUDE & tree->state)) { |
|
warnx("%s: prelude never finished", in->name); |
|
error = 1; |
|
} |
|
|
free(tree); |
free(tree); |
|
|
return(error ? 0 : 1); |
return(error ? 0 : 1); |
} |
} |
|
|
Line 149 md_init_html4_strict(const struct md_args *args, struc |
|
Line 174 md_init_html4_strict(const struct md_args *args, struc |
|
return(0); |
return(0); |
} |
} |
|
|
|
tree->state = ROFF_PRELUDE; |
|
|
*data = tree; |
*data = tree; |
return(1); |
return(1); |
} |
} |
Line 229 roffparse(const struct md_args *args, struct md_mbuf * |
|
Line 256 roffparse(const struct md_args *args, struct md_mbuf * |
|
*/ |
*/ |
|
|
if (3 > sz) { |
if (3 > sz) { |
warnx("%s: malformed input (line %zu, col 1)", |
warnx("%s: malformed line (line %zu)", |
in->name, in->line); |
in->name, in->line); |
return(0); |
return(0); |
} else if (ROFF_Max == (tokid = rofffind(buf + 1))) { |
} else if (ROFF_Max == (tokid = rofffind(buf + 1))) { |
warnx("%s: unknown token `%c%c' (line %zu, col 1)", |
warnx("%s: unknown line token `%c%c' (line %zu)", |
in->name, *(buf + 1), |
in->name, *(buf + 1), |
*(buf + 2), in->line); |
*(buf + 2), in->line); |
return(0); |
return(0); |
} else if (NULL == tokens[tokid].cb) |
} |
return(1); /* Skip token. */ |
|
|
|
pos = 3; |
/* Domain cross-contamination (and sanity) checks. */ |
|
|
|
switch (tokens[tokid].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[tokid].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: text token `%s' in prelude (line %zu)", |
|
in->name, tokens[tokid].name, in->line); |
|
return(0); |
|
default: |
|
return(1); |
|
} |
|
|
/* |
/* |
|
* Text-domain checks. |
|
*/ |
|
|
|
if (ROFF_TEXT == tokens[tokid].type && |
|
! (ROFF_PARSED & tokens[tokid].flags)) { |
|
warnx("%s: text token `%s' not callable (line %zu)", |
|
in->name, tokens[tokid].name, in->line); |
|
return(0); |
|
} |
|
|
|
/* |
* If this is a non-nestable layout token and we're below a |
* If this is a non-nestable layout token and we're below a |
* token of the same type, then recurse upward to the token, |
* token of the same type, then recurse upward to the token, |
* closing out the interim scopes. |
* closing out the interim scopes. |
Line 253 roffparse(const struct md_args *args, struct md_mbuf * |
|
Line 315 roffparse(const struct md_args *args, struct md_mbuf * |
|
*/ |
*/ |
|
|
node = NULL; |
node = NULL; |
|
pos = 3; |
|
|
if (ROFF_LAYOUT == tokens[tokid].type && |
if (ROFF_LAYOUT == tokens[tokid].type && |
! (ROFF_NESTED & tokens[tokid].flags)) { |
! (ROFF_NESTED & tokens[tokid].flags)) { |
Line 264 roffparse(const struct md_args *args, struct md_mbuf * |
|
Line 327 roffparse(const struct md_args *args, struct md_mbuf * |
|
|
|
if ( ! (ROFF_NESTED & tokens[node->tok].flags)) |
if ( ! (ROFF_NESTED & tokens[node->tok].flags)) |
continue; |
continue; |
warnx("%s: scope of %s broken by %s " |
warnx("%s: scope of %s (line %zu) broken by " |
"(line %zu, col %zu)", |
"%s (line %zu)", in->name, |
in->name, tokens[tokid].name, |
tokens[tokid].name, |
|
node->line, |
tokens[node->tok].name, |
tokens[node->tok].name, |
in->line, pos); |
in->line); |
return(0); |
return(0); |
} |
} |
} |
} |
|
|
if (node) { |
if (node) { |
assert(ROFF_LAYOUT == tokens[tokid].type); |
assert(ROFF_LAYOUT == tokens[tokid].type); |
assert( ! (ROFF_NESTED & tokens[tokid].flags)); |
assert( ! (ROFF_NESTED & tokens[tokid].flags)); |
Line 279 roffparse(const struct md_args *args, struct md_mbuf * |
|
Line 344 roffparse(const struct md_args *args, struct md_mbuf * |
|
|
|
/* Clear up to last scoped token. */ |
/* Clear up to last scoped token. */ |
|
|
|
/* LINTED */ |
do { |
do { |
t = tree->last->tok; |
t = tree->last->tok; |
if ( ! (*tokens[tree->last->tok].cb) |
if ( ! (*tokens[tree->last->tok].cb) |
Line 302 rofffind(const char *name) |
|
Line 368 rofffind(const char *name) |
|
|
|
assert(name); |
assert(name); |
/* FIXME: use a table, this is slow but ok for now. */ |
/* FIXME: use a table, this is slow but ok for now. */ |
|
|
|
/* LINTED */ |
for (i = 0; i < ROFF_Max; i++) |
for (i = 0; i < ROFF_Max; i++) |
|
/* LINTED */ |
if (0 == strncmp(name, tokens[i].name, 2)) |
if (0 == strncmp(name, tokens[i].name, 2)) |
return(i); |
return((int)i); |
|
|
return(ROFF_Max); |
return(ROFF_Max); |
} |
} |
|
|
|
|
/* ARGUSED */ |
static struct roffnode * |
static int |
roffnode_new(int tokid, size_t line, struct rofftree *tree) |
roff_Dd(ROFFCALL_ARGS) |
|
{ |
{ |
|
struct roffnode *p; |
|
|
|
if (NULL == (p = malloc(sizeof(struct roffnode)))) { |
|
warn("malloc"); |
|
return(NULL); |
|
} |
|
|
assert(in); |
p->line = line; |
assert(tree); |
p->tok = tokid; |
assert(arg); |
p->parent = tree->last; |
assert(out); |
tree->last = p; |
assert(buf); |
return(p); |
assert(sz > 0); |
} |
assert(pos > 0); |
|
assert(type == ROFF_ENTER); |
|
|
|
if (tree->last) { |
|
warnx("%s: superfluous prelude (line %zu, col %zu)", |
|
in->name, in->line, pos); |
|
return(0); |
|
} |
|
|
|
if (0 != tree->state) { |
static void |
warnx("%s: bad manual prelude (line %zu, col %zu)", |
roffnode_free(int tokid, struct rofftree *tree) |
in->name, in->line, pos); |
{ |
return(1); |
struct roffnode *p; |
} |
|
|
|
/* TODO: parse date from buffer. */ |
assert(tree->last); |
|
assert(tree->last->tok == tokid); |
|
|
tree->date = time(NULL); |
p = tree->last; |
tree->state |= ROFF_PRELUDE_Dd; |
tree->last = tree->last->parent; |
|
free(p); |
|
} |
|
|
(void)printf("Dd\n"); |
|
|
|
return(1); |
static int dbg_lvl = 0; /* FIXME: de-globalise. */ |
|
|
|
|
|
static void |
|
dbg_enter(const struct md_args *args, int tokid) |
|
{ |
|
int i; |
|
|
|
assert(args); |
|
if ( ! (args->dbg & MD_DBG_TREE)) |
|
return; |
|
|
|
assert(tokid >= 0 && tokid <= ROFF_Max); |
|
|
|
/* LINTED */ |
|
for (i = 0; i < dbg_lvl; i++) |
|
(void)printf(" "); |
|
|
|
(void)printf("%s\n", tokens[tokid].name); |
|
|
|
if (ROFF_LAYOUT == tokens[tokid].type) |
|
dbg_lvl++; |
} |
} |
|
|
|
|
static int |
static void |
roff_Dt(ROFFCALL_ARGS) |
dbg_leave(const struct md_args *args, int tokid) |
{ |
{ |
|
int i; |
|
|
assert(in); |
assert(args); |
assert(tree); |
if ( ! (args->dbg & MD_DBG_TREE)) |
assert(arg); |
return; |
assert(out); |
if (ROFF_LAYOUT != tokens[tokid].type) |
assert(buf); |
return; |
assert(sz > 0); |
|
assert(pos > 0); |
|
assert(type == ROFF_ENTER); |
|
|
|
if (tree->last) { |
assert(tokid >= 0 && tokid <= ROFF_Max); |
warnx("%s: superfluous prelude (line %zu, col %zu)", |
assert(dbg_lvl > 0); |
in->name, in->line, pos); |
|
|
dbg_lvl--; |
|
|
|
/* LINTED */ |
|
for (i = 0; i < dbg_lvl; i++) |
|
(void)printf(" "); |
|
|
|
(void)printf("%s\n", tokens[tokid].name); |
|
} |
|
|
|
|
|
static int |
|
roff_Dd(ROFFCALL_ARGS) |
|
{ |
|
|
|
assert(ROFF_PRELUDE & tree->state); |
|
if (ROFF_PRELUDE_Dt & tree->state || |
|
ROFF_PRELUDE_Dd & tree->state) { |
|
warnx("%s: bad prelude ordering (line %zu)", |
|
in->name, in->line); |
return(0); |
return(0); |
} |
} |
|
|
|
assert(NULL == tree->last); |
|
tree->state |= ROFF_PRELUDE_Dd; |
|
|
|
dbg_enter(arg, ROFF_Dd); |
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
roff_Dt(ROFFCALL_ARGS) |
|
{ |
|
|
|
assert(ROFF_PRELUDE & tree->state); |
if ( ! (ROFF_PRELUDE_Dd & tree->state) || |
if ( ! (ROFF_PRELUDE_Dd & tree->state) || |
(ROFF_PRELUDE_Os & tree->state) || |
|
(ROFF_PRELUDE_Dt & tree->state)) { |
(ROFF_PRELUDE_Dt & tree->state)) { |
warnx("%s: bad manual prelude (line %zu, col %zu)", |
warnx("%s: bad prelude ordering (line %zu)", |
in->name, in->line, pos); |
in->name, in->line); |
return(1); |
return(0); |
} |
} |
|
|
/* TODO: parse titles from buffer. */ |
assert(NULL == tree->last); |
|
|
tree->state |= ROFF_PRELUDE_Dt; |
tree->state |= ROFF_PRELUDE_Dt; |
|
|
(void)printf("Dt\n"); |
dbg_enter(arg, ROFF_Dt); |
|
|
return(1); |
return(1); |
} |
} |
|
|
Line 387 roff_Dt(ROFFCALL_ARGS) |
|
Line 503 roff_Dt(ROFFCALL_ARGS) |
|
static int |
static int |
roff_Os(ROFFCALL_ARGS) |
roff_Os(ROFFCALL_ARGS) |
{ |
{ |
struct roffnode *node; |
|
|
|
assert(arg); |
|
assert(tree); |
|
assert(in); |
|
|
|
if (ROFF_EXIT == type) { |
if (ROFF_EXIT == type) { |
assert(tree->last); |
roffnode_free(ROFF_Os, tree); |
assert(tree->last->tok == ROFF_Os); |
|
|
|
/* TODO: flush out ML footer. */ |
|
|
|
node = tree->last; |
|
tree->last = node->parent; |
|
free(node); |
|
|
|
dbg_leave(arg, ROFF_Os); |
dbg_leave(arg, ROFF_Os); |
|
|
return(1); |
return(1); |
} |
} |
|
|
assert(out); |
assert(ROFF_PRELUDE & tree->state); |
assert(buf); |
if ( ! (ROFF_PRELUDE_Dt & tree->state) || |
assert(sz > 0); |
! (ROFF_PRELUDE_Dd & tree->state)) { |
assert(pos > 0); |
warnx("%s: bad prelude ordering (line %zu)", |
|
in->name, in->line); |
if (tree->last) { |
|
warnx("%s: superfluous prelude (line %zu, col %zu)", |
|
in->name, in->line, pos); |
|
return(0); |
return(0); |
} |
} |
|
|
if ((ROFF_PRELUDE_Os & tree->state) || |
assert(NULL == tree->last); |
! (ROFF_PRELUDE_Dt & tree->state) || |
if (NULL == roffnode_new(ROFF_Os, in->line, tree)) |
! (ROFF_PRELUDE_Dd & tree->state)) { |
|
warnx("%s: bad manual prelude (line %zu, col %zu)", |
|
in->name, in->line, pos); |
|
return(1); |
|
} |
|
|
|
node = malloc(sizeof(struct roffnode)); |
|
if (NULL == node) { |
|
warn("malloc"); |
|
return(0); |
return(0); |
} |
|
node->tok = ROFF_Os; |
|
node->parent = NULL; |
|
|
|
tree->state |= ROFF_PRELUDE_Os; |
tree->state |= ROFF_PRELUDE_Os; |
tree->last = node; |
tree->state &= ~ROFF_PRELUDE; |
|
tree->state |= ROFF_BODY; |
|
|
dbg_enter(arg, ROFF_Os); |
dbg_enter(arg, ROFF_Os); |
|
|
return(1); |
return(1); |
} |
} |
|
|
|
|
static int |
static int |
roff_Sh(ROFFCALL_ARGS) |
roff_Sh(ROFFCALL_ARGS) |
{ |
{ |
struct roffnode *node; |
|
|
|
assert(arg); |
|
assert(tree); |
|
assert(tree->last); |
|
assert(in); |
|
|
|
if (ROFF_EXIT == type) { |
if (ROFF_EXIT == type) { |
assert(tree->last->tok == ROFF_Sh); |
roffnode_free(ROFF_Sh, tree); |
|
|
node = tree->last; |
|
tree->last = node->parent; |
|
free(node); |
|
|
|
dbg_leave(arg, ROFF_Sh); |
dbg_leave(arg, ROFF_Sh); |
|
|
return(1); |
return(1); |
} |
} |
|
|
assert(out); |
if (NULL == roffnode_new(ROFF_Sh, in->line, tree)) |
assert(buf); |
|
assert(sz > 0); |
|
assert(pos > 0); |
|
|
|
node = malloc(sizeof(struct roffnode)); |
|
if (NULL == node) { |
|
warn("malloc"); |
|
return(0); |
return(0); |
} |
|
node->tok = ROFF_Sh; |
|
node->parent = tree->last; |
|
|
|
tree->last = node; |
|
|
|
dbg_enter(arg, ROFF_Sh); |
dbg_enter(arg, ROFF_Sh); |
|
|
return(1); |
return(1); |
} |
} |
|
|
|
|
static int dbg_lvl = 0; /* FIXME: de-globalise. */ |
static int |
|
roff_Li(ROFFCALL_ARGS) |
|
|
static void |
|
dbg_enter(const struct md_args *args, int tokid) |
|
{ |
{ |
int i; |
|
|
|
assert(args); |
return(1); |
if ( ! (args->dbg & MD_DBG_TREE)) |
|
return; |
|
|
|
assert(tokid >= 0 && tokid <= ROFF_Max); |
|
|
|
for (i = 0; i < dbg_lvl; i++) |
|
(void)printf(" "); |
|
|
|
(void)printf("%s\n", tokens[tokid].name); |
|
|
|
if (ROFF_LAYOUT == tokens[tokid].type) |
|
dbg_lvl++; |
|
} |
} |
|
|
|
|
static void |
static int |
dbg_leave(const struct md_args *args, int tokid) |
roff_An(ROFFCALL_ARGS) |
{ |
{ |
int i; |
|
|
|
assert(args); |
return(1); |
if ( ! (args->dbg & MD_DBG_TREE)) |
|
return; |
|
|
|
assert(tokid >= 0 && tokid <= ROFF_Max); |
|
assert(dbg_lvl > 0); |
|
|
|
dbg_lvl--; |
|
for (i = 0; i < dbg_lvl; i++) |
|
(void)printf(" "); |
|
|
|
(void)printf("%s\n", tokens[tokid].name); |
|
} |
} |
|
|