![]() ![]() | ![]() |
version 1.8, 2008/12/28 21:25:09 | version 1.14, 2008/12/30 18:15:26 | ||
---|---|---|---|
|
|
||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <string.h> | #include <string.h> | ||
#ifdef __linux__ | |||
#include <time.h> | |||
#endif | |||
#include "private.h" | #include "private.h" | ||
/* FIXME: maxlineargs should be per LINE, no per TOKEN. */ | |||
/* FIXME: prologue check should be in macro_call. */ | |||
#define _CC(p) ((const char **)p) | #define _CC(p) ((const char **)p) | ||
static int scope_rewind_exp(struct mdoc *, int, int, int); | static int scope_rewind_exp(struct mdoc *, int, int, int); | ||
static int scope_rewind_imp(struct mdoc *, int, int); | static int scope_rewind_imp(struct mdoc *, int, int); | ||
static int append_text(struct mdoc *, int, | static int append_text(struct mdoc *, int, | ||
int, int, char *[]); | int, int, char *[]); | ||
static int append_const(struct mdoc *, int, int, int, char *[]); | |||
static int append_constarg(struct mdoc *, int, int, | |||
int, const struct mdoc_arg *); | |||
static int append_scoped(struct mdoc *, int, int, int, | static int append_scoped(struct mdoc *, int, int, int, | ||
const char *[], int, const struct mdoc_arg *); | const char *[], int, const struct mdoc_arg *); | ||
static int append_delims(struct mdoc *, int, int *, char *); | static int append_delims(struct mdoc *, int, int *, char *); | ||
|
|
||
static int | static int | ||
append_constarg(struct mdoc *mdoc, int tok, int pos, | |||
int argc, const struct mdoc_arg *argv) | |||
{ | |||
switch (tok) { | |||
default: | |||
break; | |||
} | |||
mdoc_elem_alloc(mdoc, pos, tok, argc, argv, 0, NULL); | |||
return(1); | |||
} | |||
/* | |||
* Append a node with implicit or explicit scoping ONLY. ALL macros | |||
* with the implicit- or explicit-scope callback must be included here. | |||
*/ | |||
static int | |||
append_scoped(struct mdoc *mdoc, int tok, int pos, | append_scoped(struct mdoc *mdoc, int tok, int pos, | ||
int sz, const char *args[], | int sz, const char *args[], | ||
int argc, const struct mdoc_arg *argv) | int argc, const struct mdoc_arg *argv) | ||
{ | { | ||
enum mdoc_sec sec; | enum mdoc_sec sec; | ||
struct mdoc_node *node; | |||
if ( ! mdoc_valid(mdoc, tok, pos, sz, args, argc, argv)) | |||
return(0); | |||
switch (tok) { | switch (tok) { | ||
/* ======= ADD MORE MACRO CHECKS BELOW. ======= */ | |||
case (MDOC_Sh): | case (MDOC_Sh): | ||
if (0 == sz) | |||
return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1)); | |||
sec = mdoc_atosec((size_t)sz, _CC(args)); | sec = mdoc_atosec((size_t)sz, _CC(args)); | ||
if (SEC_CUSTOM != sec && sec < mdoc->sec_lastn) | |||
if ( ! mdoc_warn(mdoc, tok, pos, WARN_SEC_OO)) | |||
return(0); | |||
if (SEC_BODY == mdoc->sec_last && SEC_NAME != sec) | |||
return(mdoc_err(mdoc, tok, pos, ERR_SEC_NAME)); | |||
if (SEC_CUSTOM != sec) | if (SEC_CUSTOM != sec) | ||
mdoc->sec_lastn = sec; | mdoc->sec_lastn = sec; | ||
mdoc->sec_last = sec; | mdoc->sec_last = sec; | ||
break; | break; | ||
case (MDOC_Ss): | |||
if (0 == sz) | |||
return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1)); | |||
break; | |||
case (MDOC_Bd): | |||
assert(mdoc->last); | |||
for (node = mdoc->last->parent; node; node = node->parent) { | |||
if (node->type != MDOC_BLOCK) | |||
continue; | |||
if (node->data.block.tok != MDOC_Bd) | |||
continue; | |||
return(mdoc_err(mdoc, tok, pos, ERR_SCOPE_NONEST)); | |||
} | |||
break; | |||
case (MDOC_Bl): | |||
break; | |||
/* ======= ADD MORE MACRO CHECKS ABOVE. ======= */ | |||
default: | default: | ||
abort(); | break; | ||
/* NOTREACHED */ | |||
} | } | ||
mdoc_block_alloc(mdoc, pos, tok, (size_t)argc, argv); | mdoc_block_alloc(mdoc, pos, tok, (size_t)argc, argv); | ||
|
|
||
static int | static int | ||
append_text(struct mdoc *mdoc, int tok, | append_const(struct mdoc *mdoc, int tok, | ||
int pos, int sz, char *args[]) | int pos, int sz, char *args[]) | ||
{ | { | ||
assert(sz >= 0); | if ( ! mdoc_valid(mdoc, tok, pos, sz, _CC(args), 0, NULL)) | ||
args[sz] = NULL; | return(0); | ||
switch (tok) { | switch (tok) { | ||
/* ======= ADD MORE MACRO CHECKS BELOW. ======= */ | case (MDOC_At): | ||
case (MDOC_Pp): | |||
if (0 == sz) | if (0 == sz) | ||
break; | break; | ||
if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_EQ0)) | |||
return(0); | |||
break; | |||
case (MDOC_Ft): | if (ATT_DEFAULT != mdoc_atoatt(args[0])) { | ||
/* FALLTHROUGH */ | mdoc_elem_alloc(mdoc, pos, tok, 0, | ||
case (MDOC_Li): | NULL, 1, _CC(&args[0])); | ||
/* FALLTHROUGH */ | } else { | ||
case (MDOC_Ms): | mdoc_elem_alloc(mdoc, pos, tok, | ||
/* FALLTHROUGH */ | 0, NULL, 0, NULL); | ||
case (MDOC_Pa): | mdoc_word_alloc(mdoc, pos, args[0]); | ||
/* FALLTHROUGH */ | } | ||
case (MDOC_Tn): | |||
if (0 < sz) | |||
break; | |||
if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_GE1)) | |||
return(0); | |||
break; | |||
case (MDOC_Ar): | if (1 == sz) | ||
/* FALLTHROUGH */ | return(1); | ||
case (MDOC_Cm): | mdoc_word_alloc(mdoc, pos, args[1]); | ||
/* FALLTHROUGH */ | return(1); | ||
case (MDOC_Fl): | |||
/* These can have no arguments. */ | |||
break; | |||
case (MDOC_Ad): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Em): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Er): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ev): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Fa): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Dv): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ic): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Va): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Vt): | |||
if (0 < sz) | |||
break; | |||
return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1)); | |||
/* ======= ADD MORE MACRO CHECKS ABOVE. ======= */ | |||
default: | default: | ||
abort(); | break; | ||
/* NOTREACHED */ | |||
} | } | ||
mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args)); | mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args)); | ||
|
|
||
} | } | ||
static int | |||
append_text(struct mdoc *mdoc, int tok, | |||
int pos, int sz, char *args[]) | |||
{ | |||
if ( ! mdoc_valid(mdoc, tok, pos, sz, _CC(args), 0, NULL)) | |||
return(0); | |||
mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args)); | |||
return(1); | |||
} | |||
int | int | ||
macro_text(MACRO_PROT_ARGS) | macro_text(MACRO_PROT_ARGS) | ||
{ | { | ||
int lastarg, lastpunct, c, j; | int lastarg, lastpunct, c, j; | ||
char *args[MDOC_LINEARG_MAX], *p; | char *args[MDOC_LINEARG_MAX]; | ||
if (SEC_PROLOGUE == mdoc->sec_lastn) | if (SEC_PROLOGUE == mdoc->sec_lastn) | ||
return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE)); | return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE)); | ||
|
|
||
* is non-terminal punctuation. | * is non-terminal punctuation. | ||
*/ | */ | ||
p = args[j]; | |||
if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args)) | if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args)) | ||
return(0); | return(0); | ||
mdoc_word_alloc(mdoc, lastarg, p); | mdoc_word_alloc(mdoc, lastarg, args[j]); | ||
j = 0; | j = 0; | ||
lastpunct = 1; | lastpunct = 1; | ||
|
|
||
} | } | ||
/* | |||
* Partial-line scope is identical to line scope (macro_scoped_line()) | |||
* except that trailing punctuation is appended to the BLOCK, instead of | |||
* contained within the HEAD. | |||
*/ | |||
int | int | ||
macro_scoped_pline(MACRO_PROT_ARGS) | macro_scoped_pline(MACRO_PROT_ARGS) | ||
{ | { | ||
|
|
||
/* Token pre-processing. */ | /* Token pre-processing. */ | ||
switch (tok) { | switch (tok) { | ||
case (MDOC_Ql): | |||
if ( ! mdoc_warn(mdoc, tok, ppos, WARN_COMPAT_TROFF)) | |||
return(0); | |||
break; | |||
default: | default: | ||
break; | break; | ||
} | } | ||
|
|
||
/* NOTREACHED */ | /* NOTREACHED */ | ||
} | } | ||
/* | |||
* A delimited-constant macro is similar to a general text macro: the | |||
* macro is followed by a 0 or 1 arguments (possibly-unspecified) then | |||
* terminating punctuation, other words, or another callable macro. | |||
*/ | |||
int | |||
macro_constant_delimited(MACRO_PROT_ARGS) | |||
{ | |||
int lastarg, flushed, c, maxargs; | |||
char *p; | |||
if (SEC_PROLOGUE == mdoc->sec_lastn) | |||
return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE)); | |||
/* Process line parameters. */ | |||
lastarg = ppos; | |||
flushed = 0; | |||
/* Token pre-processing. */ | |||
switch (tok) { | |||
case (MDOC_Ux): | |||
maxargs = 0; | |||
break; | |||
default: | |||
maxargs = 1; | |||
break; | |||
} | |||
again: | |||
lastarg = *pos; | |||
switch (mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p)) { | |||
case (ARGS_ERROR): | |||
return(0); | |||
case (ARGS_WORD): | |||
break; | |||
case (ARGS_PUNCT): | |||
if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p)) | |||
return(0); | |||
if (ppos > 1) | |||
return(1); | |||
return(append_delims(mdoc, tok, pos, buf)); | |||
case (ARGS_EOLN): | |||
if (flushed) | |||
return(1); | |||
return(append_const(mdoc, tok, ppos, 0, &p)); | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
/* Accepts no arguments: flush out symbol and continue. */ | |||
if (0 == maxargs) { | |||
if ( ! append_const(mdoc, tok, ppos, 0, &p)) | |||
return(0); | |||
flushed = 1; | |||
} | |||
if (MDOC_MAX != (c = mdoc_find(mdoc, p))) { | |||
if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p)) | |||
return(0); | |||
if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf)) | |||
return(0); | |||
if (ppos > 1) | |||
return(1); | |||
return(append_delims(mdoc, tok, pos, buf)); | |||
} | |||
/* | |||
* We only accept one argument; subsequent tokens are considered | |||
* as literal words (until a macro). | |||
*/ | |||
if ( ! flushed && ! mdoc_isdelim(p)) { | |||
if ( ! append_const(mdoc, tok, ppos, 1, &p)) | |||
return(0); | |||
flushed = 1; | |||
goto again; | |||
} else if ( ! flushed) { | |||
if ( ! append_const(mdoc, tok, ppos, 0, &p)) | |||
return(0); | |||
flushed = 1; | |||
} | |||
mdoc_word_alloc(mdoc, lastarg, p); | |||
goto again; | |||
/* NOTREACHED */ | |||
} | |||
int | |||
macro_constant(MACRO_PROT_ARGS) | |||
{ | |||
int lastarg, j; | |||
char *args[MDOC_LINEARG_MAX]; | |||
if (SEC_PROLOGUE == mdoc->sec_lastn) | |||
return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE)); | |||
j = 0; | |||
lastarg = ppos; | |||
again: | |||
if (j == MDOC_LINEARG_MAX) | |||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | |||
lastarg = *pos; | |||
switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[j])) { | |||
case (ARGS_ERROR): | |||
return(0); | |||
case (ARGS_WORD): | |||
break; | |||
case (ARGS_EOLN): | |||
return(append_const(mdoc, tok, ppos, j, args)); | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
if (MDOC_MAX != mdoc_find(mdoc, args[j])) | |||
if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE)) | |||
return(0); | |||
j++; | |||
goto again; | |||
/* NOTREACHED */ | |||
} | |||
int | |||
macro_constant_argv(MACRO_PROT_ARGS) | |||
{ | |||
int c, lastarg, j; | |||
struct mdoc_arg argv[MDOC_LINEARG_MAX]; | |||
if (SEC_PROLOGUE == mdoc->sec_lastn) | |||
return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE)); | |||
lastarg = *pos; | |||
for (j = 0; j < MDOC_LINEARG_MAX; j++) { | |||
lastarg = *pos; | |||
c = mdoc_argv(mdoc, tok, &argv[j], pos, buf); | |||
if (0 == c) | |||
break; | |||
else if (1 == c) | |||
continue; | |||
mdoc_argv_free(j, argv); | |||
return(0); | |||
} | |||
if (MDOC_LINEARG_MAX == j) { | |||
mdoc_argv_free(j, argv); | |||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | |||
} | |||
c = append_constarg(mdoc, tok, ppos, j, argv); | |||
mdoc_argv_free(j, argv); | |||
return(c); | |||
} |