Return to macro.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.20, 2009/01/05 12:23:17 | version 1.37, 2009/01/15 17:38:57 | ||
---|---|---|---|
|
|
||
/* FIXME: maxlineargs should be per LINE, no per TOKEN. */ | /* FIXME: maxlineargs should be per LINE, no per TOKEN. */ | ||
static int rewind_elem(struct mdoc *, int, int); | static int rewind_alt(int); | ||
static int rewind_imp(struct mdoc *, int, int); | static int rewind_dohalt(int, enum mdoc_type, | ||
static int rewind_exp(struct mdoc *, int, int, int); | const struct mdoc_node *); | ||
static int rewind_line(struct mdoc *, int, int); | #define REWIND_REWIND (1 << 0) | ||
static int append_delims(struct mdoc *, int, int *, char *); | #define REWIND_NOHALT (1 << 1) | ||
#define REWIND_HALT (1 << 2) | |||
static int rewind_dobreak(int, enum mdoc_type, | |||
const struct mdoc_node *); | |||
static int rewind_elem(struct mdoc *, int); | |||
static int rewind_impblock(struct mdoc *, int); | |||
static int rewind_expblock(struct mdoc *, int); | |||
static int rewind_subblock(enum mdoc_type, struct mdoc *, int); | |||
static int rewind_last(int, int, | |||
struct mdoc *, struct mdoc_node *); | |||
static int append_delims(struct mdoc *, | |||
int, int, int *, char *); | |||
static int lookup(struct mdoc *, int, int, int, const char *); | |||
static int | static int | ||
rewind_elem(struct mdoc *mdoc, int ppos, int tok) | lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p) | ||
{ | { | ||
struct mdoc_node *n; | int res; | ||
n = mdoc->last; | res = mdoc_find(mdoc, p); | ||
if (MDOC_ELEM != n->type) | if (MDOC_PARSED & mdoc_macros[from].flags) | ||
n = n->parent; | return(res); | ||
assert(MDOC_ELEM == n->type); | if (MDOC_MAX == res) | ||
assert(tok == n->data.elem.tok); | return(res); | ||
mdoc->last = n; | if ( ! mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX_MACLIKE)) | ||
mdoc->next = MDOC_NEXT_SIBLING; | return(-1); | ||
return(mdoc_valid_post(mdoc, tok, ppos)); | return(MDOC_MAX); | ||
} | } | ||
static int | static int | ||
rewind_line(struct mdoc *mdoc, int ppos, int tok) | rewind_last(int tok, int type, struct mdoc *mdoc, struct mdoc_node *to) | ||
{ | { | ||
struct mdoc_node *n; | |||
int t; | |||
/* LINTED */ | assert(to); | ||
for (n = mdoc->last; n; n = n->parent) { | mdoc->next = MDOC_NEXT_SIBLING; | ||
if (MDOC_HEAD != n->type) | if (mdoc->last == to) { | ||
continue; | if ( ! mdoc_valid_post(mdoc)) | ||
if (tok == (t = n->data.head.tok)) | return(0); | ||
break; | if ( ! mdoc_action_post(mdoc)) | ||
if ( ! (MDOC_EXPLICIT & mdoc_macros[t].flags)) | return(0); | ||
continue; | mdoc_msg(mdoc, "rewound %s %s to %s %s", | ||
return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK)); | mdoc_type2a(type), | ||
mdoc_macronames[tok], | |||
mdoc_type2a(mdoc->last->type), | |||
mdoc_macronames[mdoc->last->tok]); | |||
return(1); | |||
} | } | ||
mdoc->last = n ? n : mdoc->last; | do { | ||
mdoc->next = MDOC_NEXT_SIBLING; | mdoc->last = mdoc->last->parent; | ||
assert(mdoc->last); | |||
if ( ! mdoc_valid_post(mdoc)) | |||
return(0); | |||
if ( ! mdoc_action_post(mdoc)) | |||
return(0); | |||
mdoc_msg(mdoc, "rewound %s %s to %s %s", | |||
mdoc_type2a(type), | |||
mdoc_macronames[tok], | |||
mdoc_type2a(mdoc->last->type), | |||
mdoc_macronames[mdoc->last->tok]); | |||
} while (mdoc->last != to); | |||
return(1); | return(1); | ||
} | } | ||
static int | static int | ||
rewind_exp(struct mdoc *mdoc, int ppos, int tok, int tt) | rewind_alt(int tok) | ||
{ | { | ||
struct mdoc_node *n; | switch (tok) { | ||
case (MDOC_Ac): | |||
assert(mdoc->last); | return(MDOC_Ao); | ||
case (MDOC_Bc): | |||
/* LINTED */ | return(MDOC_Bo); | ||
for (n = mdoc->last->parent; n; n = n->parent) { | case (MDOC_Dc): | ||
if (MDOC_BLOCK != n->type) | return(MDOC_Do); | ||
continue; | case (MDOC_Ec): | ||
if (tt == n->data.block.tok) | return(MDOC_Eo); | ||
break; | case (MDOC_Ed): | ||
return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK)); | return(MDOC_Bd); | ||
case (MDOC_Ef): | |||
return(MDOC_Bf); | |||
case (MDOC_Ek): | |||
return(MDOC_Bk); | |||
case (MDOC_El): | |||
return(MDOC_Bl); | |||
case (MDOC_Fc): | |||
return(MDOC_Fo); | |||
case (MDOC_Oc): | |||
return(MDOC_Oo); | |||
case (MDOC_Pc): | |||
return(MDOC_Po); | |||
case (MDOC_Qc): | |||
return(MDOC_Qo); | |||
case (MDOC_Re): | |||
return(MDOC_Rs); | |||
case (MDOC_Sc): | |||
return(MDOC_So); | |||
case (MDOC_Xc): | |||
return(MDOC_Xo); | |||
default: | |||
break; | |||
} | } | ||
abort(); | |||
if (NULL == (mdoc->last = n)) | /* NOTREACHED */ | ||
return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NOCTX)); | |||
mdoc->next = MDOC_NEXT_SIBLING; | |||
return(mdoc_valid_post(mdoc, tok, ppos)); | |||
} | } | ||
/* ARGSUSED */ | static int | ||
int | rewind_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p) | ||
macro_close_explicit(MACRO_PROT_ARGS) | |||
{ | { | ||
int tt; | |||
if (MDOC_ROOT == p->type) | |||
return(REWIND_HALT); | |||
if (MDOC_TEXT == p->type) | |||
return(REWIND_NOHALT); | |||
if (MDOC_ELEM == p->type) | |||
return(REWIND_NOHALT); | |||
switch (tok) { | switch (tok) { | ||
case (MDOC_El): | /* One-liner implicit-scope. */ | ||
tt = MDOC_Bl; | case (MDOC_Aq): | ||
/* FALLTHROUGH */ | |||
case (MDOC_Bq): | |||
/* FALLTHROUGH */ | |||
case (MDOC_D1): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Dl): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Dq): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Op): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Pq): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ql): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Qq): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Sq): | |||
assert(MDOC_BODY != type); | |||
assert(MDOC_TAIL != type); | |||
if (type == p->type && tok == p->tok) | |||
return(REWIND_REWIND); | |||
break; | break; | ||
case (MDOC_Ed): | |||
tt = MDOC_Bd; | /* Multi-line implicit-scope. */ | ||
case (MDOC_It): | |||
assert(MDOC_TAIL != type); | |||
if (type == p->type && tok == p->tok) | |||
return(REWIND_REWIND); | |||
if (MDOC_BODY == p->type && MDOC_Bl == p->tok) | |||
return(REWIND_HALT); | |||
break; | break; | ||
case (MDOC_Re): | case (MDOC_Sh): | ||
tt = MDOC_Rs; | if (type == p->type && tok == p->tok) | ||
return(REWIND_REWIND); | |||
break; | break; | ||
case (MDOC_Ss): | |||
assert(MDOC_TAIL != type); | |||
if (type == p->type && tok == p->tok) | |||
return(REWIND_REWIND); | |||
if (MDOC_BODY == p->type && MDOC_Sh == p->tok) | |||
return(REWIND_HALT); | |||
break; | |||
/* Multi-line explicit scope start. */ | |||
case (MDOC_Ao): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bd): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bf): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bk): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bl): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bo): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Do): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Eo): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Fo): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Oo): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Po): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Qo): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Rs): | |||
/* FALLTHROUGH */ | |||
case (MDOC_So): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Xo): | |||
if (type == p->type && tok == p->tok) | |||
return(REWIND_REWIND); | |||
break; | |||
/* Multi-line explicit scope close. */ | |||
case (MDOC_Ac): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Bc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Dc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ec): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ed): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ek): | |||
/* FALLTHROUGH */ | |||
case (MDOC_El): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Fc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Ef): | case (MDOC_Ef): | ||
tt = MDOC_Bf; | /* FALLTHROUGH */ | ||
case (MDOC_Oc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Pc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Qc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Re): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Sc): | |||
/* FALLTHROUGH */ | |||
case (MDOC_Xc): | |||
if (type == p->type && rewind_alt(tok) == p->tok) | |||
return(REWIND_REWIND); | |||
break; | break; | ||
default: | default: | ||
abort(); | abort(); | ||
/* NOTREACHED */ | /* NOTREACHED */ | ||
} | } | ||
if (0 != buf[*pos]) | return(REWIND_NOHALT); | ||
return(mdoc_err(mdoc, tok, ppos, ERR_ARGS_EQ0)); | |||
return(rewind_exp(mdoc, ppos, tok, tt)); | |||
} | } | ||
static int | static int | ||
rewind_imp(struct mdoc *mdoc, int ppos, int tok) | rewind_dobreak(int tok, enum mdoc_type type, const struct mdoc_node *p) | ||
{ | { | ||
assert(MDOC_ROOT != p->type); | |||
if (MDOC_ELEM == p->type) | |||
return(1); | |||
if (MDOC_TEXT == p->type) | |||
return(1); | |||
switch (tok) { | |||
/* Implicit rules. */ | |||
case (MDOC_It): | |||
return(MDOC_It == p->tok); | |||
case (MDOC_Ss): | |||
return(MDOC_Ss == p->tok); | |||
case (MDOC_Sh): | |||
if (MDOC_Ss == p->tok) | |||
return(1); | |||
return(MDOC_Sh == p->tok); | |||
/* Extra scope rules. */ | |||
case (MDOC_El): | |||
if (MDOC_It == p->tok) | |||
return(1); | |||
break; | |||
default: | |||
break; | |||
} | |||
if (MDOC_EXPLICIT & mdoc_macros[tok].flags) | |||
return(p->tok == rewind_alt(tok)); | |||
else if (MDOC_BLOCK == p->type) | |||
return(1); | |||
return(tok == p->tok); | |||
} | |||
static int | |||
rewind_elem(struct mdoc *mdoc, int tok) | |||
{ | |||
struct mdoc_node *n; | struct mdoc_node *n; | ||
int t; | |||
n = mdoc->last ? mdoc->last->parent : NULL; | n = mdoc->last; | ||
if (MDOC_ELEM != n->type) | |||
n = n->parent; | |||
assert(MDOC_ELEM == n->type); | |||
assert(tok == n->tok); | |||
return(rewind_last(tok, MDOC_ELEM, mdoc, n)); | |||
} | |||
static int | |||
rewind_subblock(enum mdoc_type type, struct mdoc *mdoc, int tok) | |||
{ | |||
struct mdoc_node *n; | |||
int c; | |||
c = rewind_dohalt(tok, type, mdoc->last); | |||
if (REWIND_HALT == c) | |||
return(1); | |||
if (REWIND_REWIND == c) | |||
return(rewind_last(tok, type, mdoc, mdoc->last)); | |||
/* LINTED */ | /* LINTED */ | ||
for ( ; n; n = n->parent) { | for (n = mdoc->last->parent; n; n = n->parent) { | ||
if (MDOC_BLOCK != n->type) | c = rewind_dohalt(tok, type, n); | ||
if (REWIND_HALT == c) | |||
return(1); | |||
if (REWIND_REWIND == c) | |||
break; | |||
else if (rewind_dobreak(tok, type, n)) | |||
continue; | continue; | ||
if (tok == (t = n->data.block.tok)) | return(mdoc_nerr(mdoc, n, "body scope broken")); | ||
} | |||
assert(n); | |||
return(rewind_last(tok, type, mdoc, n)); | |||
} | |||
static int | |||
rewind_expblock(struct mdoc *mdoc, int tok) | |||
{ | |||
struct mdoc_node *n; | |||
int c; | |||
c = rewind_dohalt(tok, MDOC_BLOCK, mdoc->last); | |||
if (REWIND_HALT == c) | |||
return(mdoc_err(mdoc, ERR_SCOPE_NOCTX)); | |||
if (REWIND_REWIND == c) | |||
return(rewind_last(tok, MDOC_BLOCK, mdoc, mdoc->last)); | |||
/* LINTED */ | |||
for (n = mdoc->last->parent; n; n = n->parent) { | |||
c = rewind_dohalt(tok, MDOC_BLOCK, n); | |||
if (REWIND_HALT == c) | |||
return(mdoc_err(mdoc, ERR_SCOPE_NOCTX)); | |||
if (REWIND_REWIND == c) | |||
break; | break; | ||
if ( ! (MDOC_EXPLICIT & mdoc_macros[t].flags)) | else if (rewind_dobreak(tok, MDOC_BLOCK, n)) | ||
continue; | continue; | ||
return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK)); | return(mdoc_nerr(mdoc, n, "macro scope broken")); | ||
} | } | ||
mdoc->last = n ? n : mdoc->last; | assert(n); | ||
mdoc->next = MDOC_NEXT_SIBLING; | return(rewind_last(tok, MDOC_BLOCK, mdoc, n)); | ||
return(1); | |||
/*return(mdoc_valid_post(mdoc, tok, ppos));*/ | |||
} | } | ||
static int | static int | ||
append_delims(struct mdoc *mdoc, int tok, int *pos, char *buf) | rewind_impblock(struct mdoc *mdoc, int tok) | ||
{ | { | ||
struct mdoc_node *n; | |||
int c; | |||
printf("1\n"); | |||
c = rewind_dohalt(tok, MDOC_BLOCK, mdoc->last); | |||
if (REWIND_HALT == c) | |||
return(1); | |||
if (REWIND_REWIND == c) | |||
return(rewind_last(tok, MDOC_BLOCK, mdoc, mdoc->last)); | |||
/* LINTED */ | |||
for (n = mdoc->last->parent; n; n = n->parent) { | |||
c = rewind_dohalt(tok, MDOC_BLOCK, n); | |||
if (REWIND_HALT == c) | |||
return(1); | |||
else if (REWIND_REWIND == c) | |||
break; | |||
else if (rewind_dobreak(tok, MDOC_BLOCK, n)) | |||
continue; | |||
return(mdoc_nerr(mdoc, n, "macro scope broken")); | |||
} | |||
assert(n); | |||
return(rewind_last(tok, MDOC_BLOCK, mdoc, n)); | |||
} | |||
static int | |||
append_delims(struct mdoc *mdoc, int tok, | |||
int line, int *pos, char *buf) | |||
{ | |||
int c, lastarg; | int c, lastarg; | ||
char *p; | char *p; | ||
|
|
||
for (;;) { | for (;;) { | ||
lastarg = *pos; | lastarg = *pos; | ||
c = mdoc_args(mdoc, tok, pos, buf, 0, &p); | c = mdoc_args(mdoc, line, pos, buf, 0, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == c) | ||
return(0); | return(0); | ||
else if (ARGS_EOLN == c) | else if (ARGS_EOLN == c) | ||
break; | break; | ||
assert(mdoc_isdelim(p)); | assert(mdoc_isdelim(p)); | ||
mdoc_word_alloc(mdoc, lastarg, p); | if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | ||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | mdoc->next = MDOC_NEXT_SIBLING; | ||
} | } | ||
|
|
||
} | } | ||
/* ARGSUSED */ | |||
int | |||
macro_scoped_close(MACRO_PROT_ARGS) | |||
{ | |||
int tt, j, c, lastarg, maxargs, flushed; | |||
char *p; | |||
switch (tok) { | |||
case (MDOC_Ec): | |||
maxargs = 1; | |||
break; | |||
default: | |||
maxargs = 0; | |||
break; | |||
} | |||
tt = rewind_alt(tok); | |||
mdoc_msg(mdoc, "parse-quiet: %s closing %s", | |||
mdoc_macronames[tok], mdoc_macronames[tt]); | |||
if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { | |||
if (0 == buf[*pos]) { | |||
if ( ! rewind_subblock(MDOC_BODY, mdoc, tok)) | |||
return(0); | |||
return(rewind_expblock(mdoc, tok)); | |||
} | |||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_EQ0)); | |||
} | |||
if ( ! rewind_subblock(MDOC_BODY, mdoc, tok)) | |||
return(0); | |||
lastarg = ppos; | |||
flushed = 0; | |||
if (maxargs > 0) { | |||
if ( ! mdoc_tail_alloc(mdoc, line, ppos, tt)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
} | |||
for (j = 0; j < MDOC_LINEARG_MAX; j++) { | |||
lastarg = *pos; | |||
if (j == maxargs && ! flushed) { | |||
if ( ! rewind_expblock(mdoc, tok)) | |||
return(0); | |||
flushed = 1; | |||
} | |||
c = mdoc_args(mdoc, line, pos, buf, ARGS_DELIM, &p); | |||
if (ARGS_ERROR == c) | |||
return(0); | |||
if (ARGS_PUNCT == c) | |||
break; | |||
if (ARGS_EOLN == c) | |||
break; | |||
if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) | |||
return(0); | |||
else if (MDOC_MAX != c) { | |||
if ( ! flushed) { | |||
if ( ! rewind_expblock(mdoc, tok)) | |||
return(0); | |||
flushed = 1; | |||
} | |||
if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) | |||
return(0); | |||
break; | |||
} | |||
if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | |||
} | |||
if (MDOC_LINEARG_MAX == j) | |||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | |||
if ( ! flushed && ! rewind_expblock(mdoc, tok)) | |||
return(0); | |||
if (ppos > 1) | |||
return(1); | |||
return(append_delims(mdoc, tok, line, pos, buf)); | |||
} | |||
/* | /* | ||
* A general text domain macro. When invoked, this opens a scope that | * A general text domain macro. When invoked, this opens a scope that | ||
* accepts words until either end-of-line, only-punctuation, or a | * accepts words until either end-of-line, only-punctuation, or a | ||
|
|
||
int | int | ||
macro_text(MACRO_PROT_ARGS) | macro_text(MACRO_PROT_ARGS) | ||
{ | { | ||
int lastarg, lastpunct, c, sz, fl, argc; | int la, lastpunct, c, sz, fl, argc; | ||
struct mdoc_arg argv[MDOC_LINEARG_MAX]; | struct mdoc_arg argv[MDOC_LINEARG_MAX]; | ||
char *p; | char *p; | ||
lastarg = ppos; | la = ppos; | ||
lastpunct = 0; | lastpunct = 0; | ||
for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | ||
lastarg = *pos; | la = *pos; | ||
c = mdoc_argv(mdoc, tok, &argv[argc], pos, buf); | c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf); | ||
if (ARGV_EOLN == c || ARGV_WORD == c) | if (ARGV_EOLN == c || ARGV_WORD == c) | ||
break; | break; | ||
else if (ARGV_ARG == c) | else if (ARGV_ARG == c) | ||
|
|
||
return(0); | return(0); | ||
} | } | ||
if ( ! mdoc_valid_pre(mdoc, tok, ppos, argc, argv)) { | if (MDOC_LINEARG_MAX == argc) { | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | |||
} | |||
c = mdoc_elem_alloc(mdoc, line, la, tok, argc, argv); | |||
if (0 == c) { | |||
mdoc_argv_free(argc, argv); | |||
return(0); | return(0); | ||
} | } | ||
mdoc->next = MDOC_NEXT_CHILD; | |||
fl = ARGS_DELIM; | fl = ARGS_DELIM; | ||
if (MDOC_QUOTABLE & mdoc_macros[tok].flags) | if (MDOC_QUOTABLE & mdoc_macros[tok].flags) | ||
fl |= ARGS_QUOTED; | fl |= ARGS_QUOTED; | ||
mdoc_elem_alloc(mdoc, lastarg, tok, argc, argv); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
for (lastpunct = sz = 0; sz + argc < MDOC_LINEARG_MAX; sz++) { | for (lastpunct = sz = 0; sz + argc < MDOC_LINEARG_MAX; sz++) { | ||
lastarg = *pos; | la = *pos; | ||
if (lastpunct) { | c = mdoc_args(mdoc, line, pos, buf, fl, &p); | ||
mdoc_elem_alloc(mdoc, lastarg, tok, argc, argv); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
lastpunct = 0; | |||
} | |||
c = mdoc_args(mdoc, tok, pos, buf, fl, &p); | |||
if (ARGS_ERROR == c) { | if (ARGS_ERROR == c) { | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(0); | return(0); | ||
|
|
||
if (ARGS_PUNCT == c) | if (ARGS_PUNCT == c) | ||
break; | break; | ||
if (MDOC_MAX != (c = mdoc_find(mdoc, p))) { | if (-1 == (c = lookup(mdoc, line, la, tok, p))) | ||
if ( ! rewind_elem(mdoc, ppos, tok)) { | return(0); | ||
else if (MDOC_MAX != c) { | |||
if (0 == lastpunct && ! rewind_elem(mdoc, tok)) { | |||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(0); | return(0); | ||
} | } | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf)) | |||
c = mdoc_macro(mdoc, c, line, la, pos, buf); | |||
if (0 == c) | |||
return(0); | return(0); | ||
if (ppos > 1) | if (ppos > 1) | ||
return(1); | return(1); | ||
return(append_delims(mdoc, tok, pos, buf)); | return(append_delims(mdoc, tok, line, pos, buf)); | ||
} | } | ||
if (mdoc_isdelim(p)) { | if (mdoc_isdelim(p)) { | ||
if ( ! rewind_elem(mdoc, ppos, tok)) { | if (0 == lastpunct && ! rewind_elem(mdoc, tok)) { | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(0); | return(0); | ||
} | } | ||
lastpunct = 1; | lastpunct = 1; | ||
} else if (lastpunct) { | |||
c = mdoc_elem_alloc(mdoc, line, | |||
la, tok, argc, argv); | |||
if (0 == c) { | |||
mdoc_argv_free(argc, argv); | |||
return(0); | |||
} | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
lastpunct = 0; | |||
} | } | ||
mdoc_word_alloc(mdoc, lastarg, p); | |||
if ( ! mdoc_word_alloc(mdoc, line, la, p)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | mdoc->next = MDOC_NEXT_SIBLING; | ||
} | } | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
if (sz == MDOC_LINEARG_MAX) | if (sz == MDOC_LINEARG_MAX) | ||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | ||
if ( ! rewind_elem(mdoc, ppos, tok)) | if (0 == lastpunct && ! rewind_elem(mdoc, tok)) | ||
return(0); | return(0); | ||
if (ppos > 1) | if (ppos > 1) | ||
return(1); | return(1); | ||
return(append_delims(mdoc, tok, pos, buf)); | return(append_delims(mdoc, tok, line, pos, buf)); | ||
} | } | ||
/* | /* | ||
* Multi-line-scoped macro. | * Implicit- or explicit-end multi-line scoped macro. | ||
*/ | */ | ||
int | int | ||
macro_scoped(MACRO_PROT_ARGS) | macro_scoped(MACRO_PROT_ARGS) | ||
{ | { | ||
int c, lastarg, argc; | int c, lastarg, argc, j, fl; | ||
struct mdoc_arg argv[MDOC_LINEARG_MAX]; | struct mdoc_arg argv[MDOC_LINEARG_MAX]; | ||
char *p; | |||
assert ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)); | assert ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)); | ||
if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) | if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) { | ||
if ( ! rewind_imp(mdoc, ppos, tok)) | if ( ! rewind_subblock(MDOC_BODY, mdoc, tok)) | ||
return(0); | return(0); | ||
if ( ! rewind_impblock(mdoc, tok)) | |||
return(0); | |||
} | |||
lastarg = ppos; | |||
for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
c = mdoc_argv(mdoc, tok, &argv[argc], pos, buf); | c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf); | ||
if (ARGV_EOLN == c || ARGV_WORD == c) | if (ARGV_EOLN == c || ARGV_WORD == c) | ||
break; | break; | ||
else if (ARGV_ARG == c) | else if (ARGV_ARG == c) | ||
|
|
||
return(0); | return(0); | ||
} | } | ||
if ( ! mdoc_valid_pre(mdoc, tok, ppos, argc, argv)) { | if (MDOC_LINEARG_MAX == argc) { | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(0); | return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | ||
} | } | ||
mdoc_block_alloc(mdoc, ppos, tok, (size_t)argc, argv); | c = mdoc_block_alloc(mdoc, line, ppos, | ||
mdoc->next = MDOC_NEXT_CHILD; | tok, (size_t)argc, argv); | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
/* XXX - Assumes header isn't parsed! */ | if (0 == c) | ||
return(0); | |||
if (0 != buf[*pos]) { | mdoc->next = MDOC_NEXT_CHILD; | ||
mdoc_head_alloc(mdoc, ppos, tok); | |||
if (0 == buf[*pos]) { | |||
if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
return(1); | |||
} | |||
mdoc_word_alloc(mdoc, lastarg, &buf[*pos]); | if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) | ||
mdoc->next = MDOC_NEXT_SIBLING; | return(0); | ||
mdoc->next = MDOC_NEXT_CHILD; | |||
if ( ! rewind_line(mdoc, ppos, tok)) | fl = ARGS_DELIM; | ||
if (MDOC_TABSEP & mdoc_macros[tok].flags) | |||
fl |= ARGS_TABSEP; | |||
for (j = 0; j < MDOC_LINEARG_MAX; j++) { | |||
lastarg = *pos; | |||
c = mdoc_args(mdoc, line, pos, buf, fl, &p); | |||
if (ARGS_ERROR == c) | |||
return(0); | return(0); | ||
if (ARGS_PUNCT == c) | |||
break; | |||
if (ARGS_EOLN == c) | |||
break; | |||
if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) | |||
return(0); | |||
else if (MDOC_MAX == c) { | |||
if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | |||
continue; | |||
} | |||
while (buf[*pos]) | if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) | ||
(*pos)++; | return(0); | ||
} | |||
#if 0 | |||
/* Post-processing. */ | |||
switch (tok) { | |||
case (MDOC_Sh): | |||
sec = mdoc_atosec((size_t)sz, _CC(args)); | |||
if (SEC_CUSTOM != sec) | |||
mdoc->sec_lastn = sec; | |||
mdoc->sec_last = sec; | |||
break; | break; | ||
default: | |||
break; | |||
} | } | ||
#endif | |||
mdoc_body_alloc(mdoc, ppos, tok); | if (j == MDOC_LINEARG_MAX) | ||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
if (1 == ppos && ! append_delims(mdoc, tok, line, pos, buf)) | |||
return(0); | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
return(1); | return(1); | ||
|
|
||
int lastarg, c, j; | int lastarg, c, j; | ||
char *p; | char *p; | ||
mdoc_block_alloc(mdoc, ppos, tok, 0, NULL); | if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL)) | ||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
mdoc_head_alloc(mdoc, ppos, tok); | if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) | ||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
/* XXX - no known argument macros. */ | /* XXX - no known argument macros. */ | ||
if ( ! mdoc_valid_pre(mdoc, tok, ppos, 0, NULL)) | |||
return(0); | |||
/* Process line parameters. */ | |||
for (lastarg = ppos, j = 0; j < MDOC_LINEARG_MAX; j++) { | for (lastarg = ppos, j = 0; j < MDOC_LINEARG_MAX; j++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p); | c = mdoc_args(mdoc, line, pos, buf, ARGS_DELIM, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == c) | ||
return(0); | return(0); | ||
|
|
||
if (ARGS_EOLN == c) | if (ARGS_EOLN == c) | ||
break; | break; | ||
if (MDOC_MAX == (c = mdoc_find(mdoc, p))) { | if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) | ||
mdoc_word_alloc(mdoc, lastarg, p); | return(0); | ||
else if (MDOC_MAX == c) { | |||
if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | mdoc->next = MDOC_NEXT_SIBLING; | ||
continue; | continue; | ||
} | } | ||
if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf)) | if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) | ||
return(0); | return(0); | ||
break; | break; | ||
} | } | ||
if (j == MDOC_LINEARG_MAX) | if (j == MDOC_LINEARG_MAX) | ||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | ||
if (1 == ppos) { | if (1 == ppos) { | ||
if ( ! rewind_line(mdoc, ppos, tok)) | if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | ||
return(0); | return(0); | ||
if ( ! append_delims(mdoc, tok, pos, buf)) | if ( ! append_delims(mdoc, tok, line, pos, buf)) | ||
return(0); | return(0); | ||
} else if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
return(rewind_impblock(mdoc, tok)); | |||
} | |||
/* | |||
* Constant-scope macros accept a fixed number of arguments and behave | |||
* like constant macros except that they're scoped across lines. | |||
*/ | |||
int | |||
macro_constant_scoped(MACRO_PROT_ARGS) | |||
{ | |||
int lastarg, flushed, j, c, maxargs; | |||
char *p; | |||
lastarg = ppos; | |||
flushed = 0; | |||
switch (tok) { | |||
case (MDOC_Eo): | |||
maxargs = 1; | |||
break; | |||
default: | |||
maxargs = 0; | |||
break; | |||
} | } | ||
return(rewind_imp(mdoc, ppos, tok)); | |||
if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, 0, NULL)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
if (0 == maxargs) { | |||
if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
flushed = 1; | |||
} else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
for (j = 0; j < MDOC_LINEARG_MAX; j++) { | |||
lastarg = *pos; | |||
if (j == maxargs && ! flushed) { | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
flushed = 1; | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
} | |||
c = mdoc_args(mdoc, line, pos, buf, ARGS_DELIM, &p); | |||
if (ARGS_ERROR == c) | |||
return(0); | |||
if (ARGS_PUNCT == c) | |||
break; | |||
if (ARGS_EOLN == c) | |||
break; | |||
if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) | |||
return(0); | |||
else if (MDOC_MAX != c) { | |||
if ( ! flushed) { | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
flushed = 1; | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
} | |||
if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) | |||
return(0); | |||
break; | |||
} | |||
if ( ! flushed && mdoc_isdelim(p)) { | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
flushed = 1; | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
} | |||
if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | |||
} | |||
if (MDOC_LINEARG_MAX == j) | |||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | |||
if ( ! flushed) { | |||
if ( ! rewind_subblock(MDOC_HEAD, mdoc, tok)) | |||
return(0); | |||
if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | |||
} | |||
if (ppos > 1) | |||
return(1); | |||
return(append_delims(mdoc, tok, line, pos, buf)); | |||
} | } | ||
|
|
||
int | int | ||
macro_constant_delimited(MACRO_PROT_ARGS) | macro_constant_delimited(MACRO_PROT_ARGS) | ||
{ | { | ||
int lastarg, flushed, j, c, maxargs; | int lastarg, flushed, j, c, maxargs, argc; | ||
struct mdoc_arg argv[MDOC_LINEARG_MAX]; | |||
char *p; | char *p; | ||
lastarg = ppos; | lastarg = ppos; | ||
|
|
||
case (MDOC_Ns): | case (MDOC_Ns): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case (MDOC_Ux): | case (MDOC_Ux): | ||
/* FALLTHROUGH */ | |||
case (MDOC_St): | |||
maxargs = 0; | maxargs = 0; | ||
break; | break; | ||
default: | default: | ||
|
|
||
break; | break; | ||
} | } | ||
mdoc_elem_alloc(mdoc, lastarg, tok, 0, NULL); | for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | ||
lastarg = *pos; | |||
c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf); | |||
if (ARGV_EOLN == c || ARGV_WORD == c) | |||
break; | |||
else if (ARGV_ARG == c) | |||
continue; | |||
mdoc_argv_free(argc, argv); | |||
return(0); | |||
} | |||
c = mdoc_elem_alloc(mdoc, line, lastarg, tok, argc, argv); | |||
mdoc_argv_free(argc, argv); | |||
if (0 == c) | |||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
for (j = 0; j < MDOC_LINEARG_MAX; j++) { | for (j = 0; j < MDOC_LINEARG_MAX; j++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
if (j == maxargs && ! flushed) { | if (j == maxargs && ! flushed) { | ||
if ( ! rewind_elem(mdoc, ppos, tok)) | if ( ! rewind_elem(mdoc, tok)) | ||
return(0); | return(0); | ||
flushed = 1; | flushed = 1; | ||
} | } | ||
c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p); | c = mdoc_args(mdoc, line, pos, buf, ARGS_DELIM, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == c) | ||
return(0); | return(0); | ||
if (ARGS_PUNCT == c) | if (ARGS_PUNCT == c) | ||
|
|
||
if (ARGS_EOLN == c) | if (ARGS_EOLN == c) | ||
break; | break; | ||
if (MDOC_MAX != (c = mdoc_find(mdoc, p))) { | if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) | ||
if ( ! flushed && ! rewind_elem(mdoc, ppos, tok)) | return(0); | ||
else if (MDOC_MAX != c) { | |||
if ( ! flushed && ! rewind_elem(mdoc, tok)) | |||
return(0); | return(0); | ||
flushed = 1; | flushed = 1; | ||
if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf)) | if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) | ||
return(0); | return(0); | ||
break; | break; | ||
} | } | ||
if (mdoc_isdelim(p)) { | if ( ! flushed && mdoc_isdelim(p)) { | ||
if ( ! rewind_elem(mdoc, ppos, tok)) | if ( ! rewind_elem(mdoc, tok)) | ||
return(0); | return(0); | ||
flushed = 1; | flushed = 1; | ||
} | } | ||
mdoc_word_alloc(mdoc, lastarg, p); | if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | ||
return(0); | |||
mdoc->next = MDOC_NEXT_SIBLING; | mdoc->next = MDOC_NEXT_SIBLING; | ||
} | } | ||
if ( ! flushed && rewind_elem(mdoc, ppos, tok)) | |||
return(0); | |||
if (MDOC_LINEARG_MAX == j) | if (MDOC_LINEARG_MAX == j) | ||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | ||
if ( ! flushed && rewind_elem(mdoc, tok)) | |||
return(0); | |||
if (ppos > 1) | if (ppos > 1) | ||
return(1); | return(1); | ||
return(append_delims(mdoc, tok, pos, buf)); | return(append_delims(mdoc, tok, line, pos, buf)); | ||
} | } | ||
|
|
||
struct mdoc_arg argv[MDOC_LINEARG_MAX]; | struct mdoc_arg argv[MDOC_LINEARG_MAX]; | ||
char *p; | char *p; | ||
/* FIXME: parsing macros! */ | |||
fl = 0; | fl = 0; | ||
if (MDOC_QUOTABLE & mdoc_macros[tok].flags) | if (MDOC_QUOTABLE & mdoc_macros[tok].flags) | ||
fl = ARGS_QUOTED; | fl = ARGS_QUOTED; | ||
for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | for (argc = 0; argc < MDOC_LINEARG_MAX; argc++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
c = mdoc_argv(mdoc, tok, &argv[argc], pos, buf); | c = mdoc_argv(mdoc, line, tok, &argv[argc], pos, buf); | ||
if (ARGV_EOLN == c) | if (ARGV_EOLN == c) | ||
break; | break; | ||
else if (ARGV_ARG == c) | else if (ARGV_ARG == c) | ||
|
|
||
return(0); | return(0); | ||
} | } | ||
if (MDOC_LINEARG_MAX == argc) { | c = mdoc_elem_alloc(mdoc, line, ppos, tok, argc, argv); | ||
mdoc_argv_free(argc, argv); | mdoc_argv_free(argc, argv); | ||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | |||
} | |||
mdoc_elem_alloc(mdoc, ppos, tok, argc, argv); | if (0 == c) | ||
return(0); | |||
mdoc->next = MDOC_NEXT_CHILD; | mdoc->next = MDOC_NEXT_CHILD; | ||
mdoc_argv_free(argc, argv); | if (MDOC_LINEARG_MAX == argc) | ||
return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | |||
for (sz = 0; sz + argc < MDOC_LINEARG_MAX; sz++) { | for (sz = 0; sz + argc < MDOC_LINEARG_MAX; sz++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
c = mdoc_args(mdoc, tok, pos, buf, fl, &p); | c = mdoc_args(mdoc, line, pos, buf, fl, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == c) | ||
return(0); | return(0); | ||
if (ARGS_EOLN == c) | if (ARGS_EOLN == c) | ||
break; | break; | ||
mdoc_word_alloc(mdoc, lastarg, p); | if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) | ||
mdoc->next = MDOC_NEXT_CHILD; | return(0); | ||
mdoc->next = MDOC_NEXT_SIBLING; | |||
} | } | ||
if (MDOC_LINEARG_MAX == sz + argc) | if (MDOC_LINEARG_MAX == sz + argc) | ||
return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY)); | return(mdoc_perr(mdoc, line, ppos, ERR_ARGS_MANY)); | ||
return(rewind_elem(mdoc, ppos, tok)); | return(rewind_elem(mdoc, tok)); | ||
} | } | ||
|
|
||
macro_obsolete(MACRO_PROT_ARGS) | macro_obsolete(MACRO_PROT_ARGS) | ||
{ | { | ||
return(mdoc_warn(mdoc, tok, ppos, WARN_IGN_OBSOLETE)); | return(mdoc_pwarn(mdoc, line, ppos, WARN_IGN_OBSOLETE)); | ||
} | |||
int | |||
macro_end(struct mdoc *mdoc) | |||
{ | |||
assert(mdoc->first); | |||
assert(mdoc->last); | |||
return(rewind_last(mdoc->last->tok, mdoc->last->type, | |||
mdoc, mdoc->first)); | |||
} | } |