version 1.60, 2008/12/10 12:09:47 |
version 1.64, 2008/12/12 10:11:10 |
|
|
/* TODO: (warn) some sections need specific elements. */ |
/* TODO: (warn) some sections need specific elements. */ |
/* TODO: (warn) NAME section has particular order. */ |
/* TODO: (warn) NAME section has particular order. */ |
/* TODO: macros with a set number of arguments? */ |
/* TODO: macros with a set number of arguments? */ |
/* TODO: validate Dt macro arguments. */ |
|
/* FIXME: Bl -diag supposed to ignore callable children. */ |
/* FIXME: Bl -diag supposed to ignore callable children. */ |
|
|
struct roffnode { |
struct roffnode { |
Line 71 struct rofftree { |
|
Line 70 struct rofftree { |
|
char title[64]; /* `Dt' results. */ |
char title[64]; /* `Dt' results. */ |
enum roffmsec section; |
enum roffmsec section; |
enum roffvol volume; |
enum roffvol volume; |
int state; |
|
#define ROFF_PRELUDE (1 << 1) /* In roff prelude. */ /* FIXME: put into asec. */ |
|
#define ROFF_PRELUDE_Os (1 << 2) /* `Os' is parsed. */ |
|
#define ROFF_PRELUDE_Dt (1 << 3) /* `Dt' is parsed. */ |
|
#define ROFF_PRELUDE_Dd (1 << 4) /* `Dd' is parsed. */ |
|
#define ROFF_BODY (1 << 5) /* In roff body. */ |
|
struct roffcb cb; /* Callbacks. */ |
struct roffcb cb; /* Callbacks. */ |
void *arg; /* Callbacks' arg. */ |
void *arg; /* Callbacks' arg. */ |
int csec; /* Current section. */ |
int csec; /* Current section. */ |
int asec; /* Thus-far sections. */ |
int asec; /* Thus-far sections. */ |
|
int literal; /* Literal mode. */ |
}; |
}; |
|
|
static struct roffnode *roffnode_new(int, struct rofftree *); |
static struct roffnode *roffnode_new(int, struct rofftree *); |
Line 100 static int rofffindarg(const char *); |
|
Line 94 static int rofffindarg(const char *); |
|
static int rofffindcallable(const char *); |
static int rofffindcallable(const char *); |
static int roffispunct(const char *); |
static int roffispunct(const char *); |
static int roffchecksec(struct rofftree *, |
static int roffchecksec(struct rofftree *, |
const char *, int); |
const char *, int, int); |
static int roffargs(const struct rofftree *, |
static int roffargs(const struct rofftree *, |
int, char *, char **); |
int, char *, char **); |
static int roffargok(int, int); |
static int roffargok(int, int); |
Line 140 roff_free(struct rofftree *tree, int flush) |
|
Line 134 roff_free(struct rofftree *tree, int flush) |
|
|
|
error = 1; |
error = 1; |
|
|
if (ROFF_PRELUDE & tree->state) { |
if ( ! (ROFFSec_NAME & tree->asec)) { |
(void)roff_err(tree, NULL, "prelude never finished"); |
|
goto end; |
|
} else if ( ! (ROFFSec_NAME & tree->asec)) { |
|
(void)roff_err(tree, NULL, "missing `NAME' section"); |
(void)roff_err(tree, NULL, "missing `NAME' section"); |
goto end; |
goto end; |
} else if ( ! (ROFFSec_NMASK & tree->asec)) |
} else if ( ! (ROFFSec_NMASK & tree->asec)) |
Line 193 roff_alloc(const struct roffcb *cb, void *args) |
|
Line 184 roff_alloc(const struct roffcb *cb, void *args) |
|
if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) |
if (NULL == (tree = calloc(1, sizeof(struct rofftree)))) |
err(1, "calloc"); |
err(1, "calloc"); |
|
|
tree->state = ROFF_PRELUDE; |
|
tree->arg = args; |
tree->arg = args; |
tree->section = ROFF_MSEC_MAX; |
tree->section = ROFF_MSEC_MAX; |
|
|
Line 226 textparse(struct rofftree *tree, char *buf) |
|
Line 216 textparse(struct rofftree *tree, char *buf) |
|
|
|
/* TODO: literal parsing. */ |
/* TODO: literal parsing. */ |
|
|
if ( ! (ROFF_BODY & tree->state)) |
if ( ! (ROFFSec_NAME & tree->asec)) |
return(roff_err(tree, buf, "data not in body")); |
return(roff_err(tree, buf, "data before `NAME' section")); |
|
|
|
if (tree->literal) |
|
return(roffdata(tree, 0, buf)); |
|
|
/* LINTED */ |
/* LINTED */ |
while (*buf) { |
while (*buf) { |
while (*buf && isspace(*buf)) |
while (*buf && isspace(*buf)) |
Line 354 roffparse(struct rofftree *tree, char *buf) |
|
Line 347 roffparse(struct rofftree *tree, char *buf) |
|
* Prelude macros break some assumptions, so branch now. |
* Prelude macros break some assumptions, so branch now. |
*/ |
*/ |
|
|
if (ROFF_PRELUDE & tree->state) { |
if ( ! (ROFFSec_PR_Dd & tree->asec)) { |
assert(NULL == tree->last); |
assert(NULL == tree->last); |
return(roffcall(tree, tok, argvp)); |
return(roffcall(tree, tok, argvp)); |
} |
} |
|
|
assert(ROFF_BODY & tree->state); |
|
|
|
/* |
/* |
* First check that our possible parents and parent's possible |
* First check that our possible parents and parent's possible |
* children are satisfied. |
* children are satisfied. |
Line 527 rofffindtok(const char *buf) |
|
Line 518 rofffindtok(const char *buf) |
|
|
|
|
|
static int |
static int |
roffchecksec(struct rofftree *tree, const char *start, int sec) |
roffchecksec(struct rofftree *tree, |
|
const char *start, int sec, int fail) |
{ |
{ |
|
|
switch (sec) { |
switch (sec) { |
|
case(ROFFSec_PR_Dd): |
|
return(1); |
|
case(ROFFSec_PR_Dt): |
|
if (ROFFSec_PR_Dd & tree->asec) |
|
return(1); |
|
break; |
|
case(ROFFSec_PR_Os): |
|
if (ROFFSec_PR_Dt & tree->asec) |
|
return(1); |
|
break; |
|
case(ROFFSec_NAME): |
|
if (ROFFSec_PR_Os & tree->asec) |
|
return(1); |
|
break; |
case(ROFFSec_SYNOP): |
case(ROFFSec_SYNOP): |
if (ROFFSec_NAME & tree->asec) |
if (ROFFSec_NAME & tree->asec) |
return(1); |
return(1); |
Line 591 roffchecksec(struct rofftree *tree, const char *start, |
|
Line 597 roffchecksec(struct rofftree *tree, const char *start, |
|
return(1); |
return(1); |
} |
} |
|
|
|
if (fail) |
|
return(0); |
return(roff_warnp(tree, start, ROFF_Sh, WRN_SECORD)); |
return(roff_warnp(tree, start, ROFF_Sh, WRN_SECORD)); |
} |
} |
|
|
|
|
/* FIXME: move this into literals.c (or similar). */ |
|
static int |
static int |
roffispunct(const char *p) |
roffispunct(const char *p) |
{ |
{ |
Line 919 roff_Dd(ROFFCALL_ARGS) |
|
Line 926 roff_Dd(ROFFCALL_ARGS) |
|
char *p, buf[32]; |
char *p, buf[32]; |
size_t sz; |
size_t sz; |
|
|
if (ROFF_BODY & tree->state) { |
if (ROFFSec_PR_Os & tree->asec) |
assert( ! (ROFF_PRELUDE & tree->state)); |
|
assert(ROFF_PRELUDE_Dd & tree->state); |
|
return(roff_text(tok, tree, argv, type)); |
return(roff_text(tok, tree, argv, type)); |
} |
if (ROFFSec_PR_Dd & tree->asec) |
|
|
assert(ROFF_PRELUDE & tree->state); |
|
assert( ! (ROFF_BODY & tree->state)); |
|
|
|
if (ROFF_PRELUDE_Dd & tree->state) |
|
return(roff_errp(tree, *argv, tok, ERR_PR_REP)); |
return(roff_errp(tree, *argv, tok, ERR_PR_REP)); |
if (ROFF_PRELUDE_Dt & tree->state) |
if ( ! roffchecksec(tree, *argv, ROFFSec_PR_Dd, 1)) |
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
|
|
assert(NULL == tree->last); |
assert(NULL == tree->last); |
|
|
argv++; |
argv++; |
|
tree->asec |= (tree->csec = ROFFSec_PR_Dd); |
|
|
/* |
/* |
* This is a bit complex because there are many forms the date |
* This is a bit complex because there are many forms the date |
Line 947 roff_Dd(ROFFCALL_ARGS) |
|
Line 947 roff_Dd(ROFFCALL_ARGS) |
|
t = time(NULL); |
t = time(NULL); |
if (NULL == localtime_r(&t, &tree->tm)) |
if (NULL == localtime_r(&t, &tree->tm)) |
err(1, "localtime_r"); |
err(1, "localtime_r"); |
tree->state |= ROFF_PRELUDE_Dd; |
|
return(1); |
return(1); |
} |
} |
|
|
Line 961 roff_Dd(ROFFCALL_ARGS) |
|
Line 960 roff_Dd(ROFFCALL_ARGS) |
|
continue; |
continue; |
return(roff_errp(tree, p, tok, ERR_BADARG)); |
return(roff_errp(tree, p, tok, ERR_BADARG)); |
} |
} |
if (strptime(buf, "%b%d,%Y", &tree->tm)) { |
if (strptime(buf, "%b%d,%Y", &tree->tm)) |
tree->state |= ROFF_PRELUDE_Dd; |
|
return(1); |
return(1); |
} |
|
return(roff_errp(tree, p, tok, ERR_BADARG)); |
return(roff_errp(tree, p, tok, ERR_BADARG)); |
} |
} |
|
|
Line 982 roff_Dd(ROFFCALL_ARGS) |
|
Line 979 roff_Dd(ROFFCALL_ARGS) |
|
if (NULL == strptime(buf, "%b %d %Y", &tree->tm)) |
if (NULL == strptime(buf, "%b %d %Y", &tree->tm)) |
return(roff_errp(tree, p, tok, ERR_BADARG)); |
return(roff_errp(tree, p, tok, ERR_BADARG)); |
|
|
tree->state |= ROFF_PRELUDE_Dd; |
|
return(1); |
return(1); |
} |
} |
|
|
Line 993 roff_Dt(ROFFCALL_ARGS) |
|
Line 989 roff_Dt(ROFFCALL_ARGS) |
|
{ |
{ |
size_t sz; |
size_t sz; |
|
|
if (ROFF_BODY & tree->state) { |
if (ROFFSec_PR_Os & tree->asec) |
assert( ! (ROFF_PRELUDE & tree->state)); |
|
assert(ROFF_PRELUDE_Dt & tree->state); |
|
return(roff_text(tok, tree, argv, type)); |
return(roff_text(tok, tree, argv, type)); |
} |
if (ROFFSec_PR_Dt & tree->asec) |
|
|
assert(ROFF_PRELUDE & tree->state); |
|
assert( ! (ROFF_BODY & tree->state)); |
|
|
|
if ( ! (ROFF_PRELUDE_Dd & tree->state)) |
|
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
|
if (ROFF_PRELUDE_Dt & tree->state) |
|
return(roff_errp(tree, *argv, tok, ERR_PR_REP)); |
return(roff_errp(tree, *argv, tok, ERR_PR_REP)); |
|
if ( ! roffchecksec(tree, *argv, ROFFSec_PR_Dt, 1)) |
|
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
|
|
argv++; |
argv++; |
|
tree->asec |= (tree->csec = ROFFSec_PR_Dt); |
sz = sizeof(tree->title); |
sz = sizeof(tree->title); |
|
|
if (NULL == *argv) |
if (NULL == *argv) |
Line 1065 roff_Dt(ROFFCALL_ARGS) |
|
Line 1055 roff_Dt(ROFFCALL_ARGS) |
|
return(roff_errp(tree, *argv, tok, ERR_BADARG)); |
return(roff_errp(tree, *argv, tok, ERR_BADARG)); |
|
|
assert(NULL == tree->last); |
assert(NULL == tree->last); |
tree->state |= ROFF_PRELUDE_Dt; |
|
|
|
return(1); |
return(1); |
} |
} |
Line 1144 roff_Os(ROFFCALL_ARGS) |
|
Line 1133 roff_Os(ROFFCALL_ARGS) |
|
char *p; |
char *p; |
size_t sz; |
size_t sz; |
|
|
if (ROFF_BODY & tree->state) { |
if (ROFFSec_PR_Os & tree->asec) |
assert( ! (ROFF_PRELUDE & tree->state)); |
|
assert(ROFF_PRELUDE_Os & tree->state); |
|
return(roff_text(tok, tree, argv, type)); |
return(roff_text(tok, tree, argv, type)); |
} |
if ( ! roffchecksec(tree, *argv, ROFFSec_PR_Os, 1)) |
|
|
assert(ROFF_PRELUDE & tree->state); |
|
if ( ! (ROFF_PRELUDE_Dt & tree->state) || |
|
! (ROFF_PRELUDE_Dd & tree->state)) |
|
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
return(roff_errp(tree, *argv, tok, ERR_PR_OOO)); |
|
|
tree->os[0] = 0; |
|
|
|
p = *++argv; |
p = *++argv; |
sz = sizeof(tree->os); |
sz = sizeof(tree->os); |
|
tree->asec |= (tree->csec = ROFFSec_PR_Os); |
|
|
|
tree->os[0] = 0; |
|
|
while (*argv) |
while (*argv) |
if (strlcat(tree->os, *argv++, sz) >= sz) |
if (strlcat(tree->os, *argv++, sz) >= sz) |
return(roff_errp(tree, p, tok, ERR_ARGLEN)); |
return(roff_errp(tree, p, tok, ERR_ARGLEN)); |
Line 1168 roff_Os(ROFFCALL_ARGS) |
|
Line 1152 roff_Os(ROFFCALL_ARGS) |
|
if (strlcpy(tree->os, "LOCAL", sz) >= sz) |
if (strlcpy(tree->os, "LOCAL", sz) >= sz) |
return(roff_errp(tree, p, tok, ERR_ARGLEN)); |
return(roff_errp(tree, p, tok, ERR_ARGLEN)); |
|
|
tree->state |= ROFF_PRELUDE_Os; |
|
tree->state &= ~ROFF_PRELUDE; |
|
tree->state |= ROFF_BODY; |
|
|
|
assert(ROFF_MSEC_MAX != tree->section); |
assert(ROFF_MSEC_MAX != tree->section); |
assert(0 != tree->title[0]); |
assert(0 != tree->title[0]); |
assert(0 != tree->os[0]); |
assert(0 != tree->os[0]); |
|
|
roff_layout(ROFFCALL_ARGS) |
roff_layout(ROFFCALL_ARGS) |
{ |
{ |
int i, c, argcp[ROFF_MAXLINEARG]; |
int i, c, argcp[ROFF_MAXLINEARG]; |
char *argvp[ROFF_MAXLINEARG]; |
char *argvp[ROFF_MAXLINEARG], *p; |
|
|
/* |
/* |
* The roff_layout function is for multi-line macros. A layout |
* The roff_layout function is for multi-line macros. A layout |
Line 1205 roff_layout(ROFFCALL_ARGS) |
|
Line 1185 roff_layout(ROFFCALL_ARGS) |
|
|
|
assert( ! (ROFF_CALLABLE & tokens[tok].flags)); |
assert( ! (ROFF_CALLABLE & tokens[tok].flags)); |
|
|
if (ROFF_PRELUDE & tree->state) |
if ( ! (ROFFSec_PR_Os & tree->asec)) |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
|
|
if (ROFF_EXIT == type) { |
if (ROFF_EXIT == type) { |
Line 1215 roff_layout(ROFFCALL_ARGS) |
|
Line 1195 roff_layout(ROFFCALL_ARGS) |
|
return((*tree->cb.roffblkout)(tree->arg, tok)); |
return((*tree->cb.roffblkout)(tree->arg, tok)); |
} |
} |
|
|
argv++; |
p = *argv++; |
assert( ! (ROFF_CALLABLE & tokens[tok].flags)); |
assert( ! (ROFF_CALLABLE & tokens[tok].flags)); |
|
|
if ( ! roffparseopts(tree, tok, &argv, argcp, argvp)) |
if ( ! roffparseopts(tree, tok, &argv, argcp, argvp)) |
Line 1252 roff_layout(ROFFCALL_ARGS) |
|
Line 1232 roff_layout(ROFFCALL_ARGS) |
|
/* +++ Begin run macro-specific hooks over argv. */ |
/* +++ Begin run macro-specific hooks over argv. */ |
|
|
switch (tok) { |
switch (tok) { |
|
case (ROFF_Bd): |
|
tree->literal++; |
|
break; |
|
|
case (ROFF_Sh): |
case (ROFF_Sh): |
if (NULL == *argv) { |
if (NULL == *argv) { |
argv--; |
argv--; |
Line 1268 roff_layout(ROFFCALL_ARGS) |
|
Line 1252 roff_layout(ROFFCALL_ARGS) |
|
if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec)) |
if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec)) |
return(roff_err(tree, *argv, "`NAME' section " |
return(roff_err(tree, *argv, "`NAME' section " |
"must be first")); |
"must be first")); |
if ( ! roffchecksec(tree, *argv, tree->csec)) |
if ( ! roffchecksec(tree, *argv, tree->csec, 0)) |
return(0); |
return(0); |
|
|
tree->asec |= tree->csec; |
tree->asec |= tree->csec; |
|
|
|
if ( ! roffspecial(tree, tok, p, argcp, |
|
(const char **)argvp, 0, argv)) |
|
return(0); |
break; |
break; |
default: |
default: |
break; |
break; |
Line 1369 roff_ordered(ROFFCALL_ARGS) |
|
Line 1357 roff_ordered(ROFFCALL_ARGS) |
|
* .Xr arg1 arg2 punctuation |
* .Xr arg1 arg2 punctuation |
*/ |
*/ |
|
|
if (ROFF_PRELUDE & tree->state) |
if ( ! (ROFFSec_PR_Os & tree->asec)) |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
|
|
first = (*argv == tree->cur); |
first = (*argv == tree->cur); |
Line 1422 roff_ordered(ROFFCALL_ARGS) |
|
Line 1410 roff_ordered(ROFFCALL_ARGS) |
|
} |
} |
|
|
|
|
|
static int |
|
macro_default(struct rofftree *tree, int tok, char *args[]) |
|
{ |
|
char **p; |
|
char *argv[ROFF_MAXLINEARG]; |
|
int i, argc[ROFF_MAXLINEARG]; |
|
|
|
if ( ! roffparseopts(tree, tok, &args, argc, argv)) |
|
return(0); |
|
|
|
if ( ! (ROFF_PARSED & tokens[tok].flags)) |
|
return((*tree->cb.macro)(tree->arg, tok, argc, argv, 0, args)); |
|
|
|
p = args; |
|
|
|
while (*args) { |
|
c = rofffindcallable(*args); |
|
if (ROFF_MAX == c) { |
|
if (roffispunct(*args)) { |
|
|
|
|
|
} |
|
} |
|
|
|
if (ROFF_MAX != (c = rofffindcallable(*argv))) { |
|
if ( ! (ROFF_LSCOPE & tokens[tok].flags)) |
|
if ( ! (*tree->cb.roffout)(tree->arg, tok)) |
|
return(0); |
|
|
|
if ( ! roffcall(tree, c, argv)) |
|
return(0); |
|
if (ROFF_LSCOPE & tokens[tok].flags) |
|
if ( ! (*tree->cb.roffout)(tree->arg, tok)) |
|
return(0); |
|
break; |
|
} |
|
|
|
if ( ! roffispunct(*argv)) { |
|
if ( ! roffdata(tree, i++, *argv++)) |
|
return(0); |
|
continue; |
|
} |
|
|
|
i = 1; |
|
for (j = 0; argv[j]; j++) |
|
if ( ! roffispunct(argv[j])) |
|
break; |
|
|
|
if (argv[j]) { |
|
if (ROFF_LSCOPE & tokens[tok].flags) { |
|
if ( ! roffdata(tree, 0, *argv++)) |
|
return(0); |
|
continue; |
|
} |
|
if ( ! (*tree->cb.roffout)(tree->arg, tok)) |
|
return(0); |
|
if ( ! roffdata(tree, 0, *argv++)) |
|
return(0); |
|
if ( ! (*tree->cb.roffin)(tree->arg, tok, |
|
argcp, |
|
(const char **)argvp)) |
|
return(0); |
|
|
|
i = 0; |
|
continue; |
|
} |
|
|
|
if ( ! (*tree->cb.roffout)(tree->arg, tok)) |
|
return(0); |
|
break; |
|
} |
|
} |
|
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
static int |
static int |
roff_text(ROFFCALL_ARGS) |
roff_text(ROFFCALL_ARGS) |
Line 1441 roff_text(ROFFCALL_ARGS) |
|
Line 1503 roff_text(ROFFCALL_ARGS) |
|
* <fl> v W f </fl> ; |
* <fl> v W f </fl> ; |
*/ |
*/ |
|
|
if (ROFF_PRELUDE & tree->state) |
if ( ! (ROFFSec_PR_Os & tree->asec)) |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
return(roff_errp(tree, *argv, tok, ERR_NOT_PR)); |
|
|
first = (*argv == tree->cur); |
first = (*argv == tree->cur); |
|
|
roff_noop(ROFFCALL_ARGS) |
roff_noop(ROFFCALL_ARGS) |
{ |
{ |
|
|
|
switch (tok) { |
|
case (ROFF_Ed): |
|
tree->literal--; |
|
break; |
|
default: |
|
break; |
|
} |
|
|
return(1); |
return(1); |
} |
} |
|
|
Line 1549 roff_depr(ROFFCALL_ARGS) |
|
Line 1619 roff_depr(ROFFCALL_ARGS) |
|
} |
} |
|
|
|
|
|
/* FIXME: push this into the filter. */ |
static int |
static int |
roff_warnp(const struct rofftree *tree, const char *pos, |
roff_warnp(const struct rofftree *tree, const char *pos, |
int tok, enum rofferr type) |
int tok, enum rofferr type) |
Line 1583 roff_warn(const struct rofftree *tree, const char *pos |
|
Line 1654 roff_warn(const struct rofftree *tree, const char *pos |
|
} |
} |
|
|
|
|
|
/* FIXME: push this into the filter. */ |
static int |
static int |
roff_errp(const struct rofftree *tree, const char *pos, |
roff_errp(const struct rofftree *tree, const char *pos, |
int tok, enum rofferr type) |
int tok, enum rofferr type) |