=================================================================== RCS file: /cvs/mandoc/man_macro.c,v retrieving revision 1.4 retrieving revision 1.21 diff -u -p -r1.4 -r1.21 --- mandoc/man_macro.c 2009/03/23 15:41:09 1.4 +++ mandoc/man_macro.c 2009/08/19 09:14:50 1.21 @@ -1,144 +1,351 @@ -/* $Id: man_macro.c,v 1.4 2009/03/23 15:41:09 kristaps Exp $ */ +/* $Id: man_macro.c,v 1.21 2009/08/19 09:14:50 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009 Kristaps Dzonsons * * 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. + * 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. + * 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 #include -#include /* XXX */ #include -#include #include #include "libman.h" -static int man_args(struct man *, int, - int *, char *, char **); +#define REW_REWIND (0) /* See rew_scope(). */ +#define REW_NOHALT (1) /* See rew_scope(). */ +#define REW_HALT (2) /* See rew_scope(). */ +static int in_line_eoln(MACRO_PROT_ARGS); +static int blk_imp(MACRO_PROT_ARGS); +static int blk_close(MACRO_PROT_ARGS); +static int rew_scope(enum man_type, struct man *, int); +static int rew_dohalt(int, enum man_type, + const struct man_node *); +static int rew_block(int, enum man_type, + const struct man_node *); + +const struct man_macro __man_macros[MAN_MAX] = { + { in_line_eoln, 0 }, /* br */ + { in_line_eoln, 0 }, /* TH */ + { blk_imp, 0 }, /* SH */ + { blk_imp, 0 }, /* SS */ + { blk_imp, MAN_SCOPED }, /* TP */ + { blk_imp, 0 }, /* LP */ + { blk_imp, 0 }, /* PP */ + { blk_imp, 0 }, /* P */ + { blk_imp, 0 }, /* IP */ + { blk_imp, 0 }, /* HP */ + { in_line_eoln, MAN_SCOPED }, /* SM */ + { in_line_eoln, MAN_SCOPED }, /* SB */ + { in_line_eoln, 0 }, /* BI */ + { in_line_eoln, 0 }, /* IB */ + { in_line_eoln, 0 }, /* BR */ + { in_line_eoln, 0 }, /* RB */ + { in_line_eoln, MAN_SCOPED }, /* R */ + { in_line_eoln, MAN_SCOPED }, /* B */ + { in_line_eoln, MAN_SCOPED }, /* I */ + { in_line_eoln, 0 }, /* IR */ + { in_line_eoln, 0 }, /* RI */ + { in_line_eoln, 0 }, /* na */ + { in_line_eoln, 0 }, /* i */ + { in_line_eoln, 0 }, /* sp */ + { in_line_eoln, 0 }, /* nf */ + { in_line_eoln, 0 }, /* fi */ + { in_line_eoln, 0 }, /* r */ + { blk_close, 0 }, /* RE */ + { blk_imp, 0 }, /* RS */ +}; + +const struct man_macro * const man_macros = __man_macros; + + int -man_macro(struct man *man, int tok, int line, - int ppos, int *pos, char *buf) +man_unscope(struct man *m, const struct man_node *n) { - int w, la; - char *p; - struct man_node *n; - if ( ! man_elem_alloc(man, line, ppos, tok)) + assert(n); + m->next = MAN_NEXT_SIBLING; + + /* LINTED */ + while (m->last != n) { + if ( ! man_valid_post(m)) + return(0); + if ( ! man_action_post(m)) + return(0); + m->last = m->last->parent; + assert(m->last); + } + + if ( ! man_valid_post(m)) return(0); - n = man->last; - man->next = MAN_NEXT_CHILD; + return(man_action_post(m)); +} - for (;;) { - la = *pos; - w = man_args(man, line, pos, buf, &p); - if (-1 == w) - return(0); - if (0 == w) - break; +static int +rew_block(int ntok, enum man_type type, const struct man_node *n) +{ - if ( ! man_word_alloc(man, line, la, p)) - return(0); - man->next = MAN_NEXT_SIBLING; + if (MAN_BLOCK == type && ntok == n->parent->tok && + MAN_BODY == n->parent->type) + return(REW_REWIND); + return(ntok == n->tok ? REW_HALT : REW_NOHALT); +} + + +/* + * There are three scope levels: scoped to the root (all), scoped to the + * section (all less sections), and scoped to subsections (all less + * sections and subsections). + */ +static int +rew_dohalt(int tok, enum man_type type, const struct man_node *n) +{ + int c; + + if (MAN_ROOT == n->type) + return(REW_HALT); + assert(n->parent); + if (MAN_ROOT == n->parent->type) + return(REW_REWIND); + if (MAN_VALID & n->flags) + return(REW_NOHALT); + + /* Rewind to ourselves, first. */ + if (type == n->type && tok == n->tok) + return(REW_REWIND); + + switch (tok) { + case (MAN_SH): + break; + case (MAN_SS): + /* Rewind to a section, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_SH, type, n))) + return(c); + break; + case (MAN_RS): + /* Rewind to a subsection, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_SS, type, n))) + return(c); + /* Rewind to a section, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_SH, type, n))) + return(c); + break; + default: + /* Rewind to an offsetter, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_RS, type, n))) + return(c); + /* Rewind to a subsection, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_SS, type, n))) + return(c); + /* Rewind to a section, if a block. */ + if (REW_NOHALT != (c = rew_block(MAN_SH, type, n))) + return(c); + break; } - /* TODO: validate & actions. */ + return(REW_NOHALT); +} - man->last = n; - man->next = MAN_NEXT_SIBLING; - return(1); +/* + * Rewinding entails ascending the parse tree until a coherent point, + * for example, the `SH' macro will close out any intervening `SS' + * scopes. When a scope is closed, it must be validated and actioned. + */ +static int +rew_scope(enum man_type type, struct man *m, int tok) +{ + struct man_node *n; + int c; + + /* LINTED */ + for (n = m->last; n; n = n->parent) { + /* + * Whether we should stop immediately (REW_HALT), stop + * and rewind until this point (REW_REWIND), or keep + * rewinding (REW_NOHALT). + */ + c = rew_dohalt(tok, type, n); + if (REW_HALT == c) + return(1); + if (REW_REWIND == c) + break; + } + + /* Rewind until the current point. */ + + assert(n); + return(man_unscope(m, n)); } +/* ARGSUSED */ int -man_macroend(struct man *m) +blk_close(MACRO_PROT_ARGS) { + int ntok; + const struct man_node *nn; - /* TODO: validate & actions. */ + switch (tok) { + case (MAN_RE): + ntok = MAN_RS; + break; + default: + abort(); + /* NOTREACHED */ + } + + for (nn = m->last->parent; nn; nn = nn->parent) + if (ntok == nn->tok) + break; + + if (NULL == nn) + if ( ! man_pwarn(m, line, ppos, WNOSCOPE)) + return(0); + + if ( ! rew_scope(MAN_BODY, m, ntok)) + return(0); + if ( ! rew_scope(MAN_BLOCK, m, ntok)) + return(0); + m->next = MAN_NEXT_SIBLING; return(1); } -/* ARGSUSED */ -static int -man_args(struct man *man, int line, - int *pos, char *buf, char **v) +/* + * Parse an implicit-block macro. These contain a MAN_HEAD and a + * MAN_BODY contained within a MAN_BLOCK. Rules for closing out other + * scopes, such as `SH' closing out an `SS', are defined in the rew + * routines. + */ +int +blk_imp(MACRO_PROT_ARGS) { + int w, la; + char *p; - if (0 == buf[*pos]) + /* Close out prior scopes. */ + + if ( ! rew_scope(MAN_BODY, m, tok)) return(0); + if ( ! rew_scope(MAN_BLOCK, m, tok)) + return(0); - /* First parse non-quoted strings. */ + /* Allocate new block & head scope. */ - if ('\"' != buf[*pos]) { - *v = &buf[*pos]; + if ( ! man_block_alloc(m, line, ppos, tok)) + return(0); + if ( ! man_head_alloc(m, line, ppos, tok)) + return(0); - while (buf[*pos]) { - if (' ' == buf[*pos]) - if ('\\' != buf[*pos - 1]) - break; - (*pos)++; - } + /* Add line arguments. */ - if (0 == buf[*pos]) - return(1); + for (;;) { + la = *pos; + w = man_args(m, line, pos, buf, &p); - buf[(*pos)++] = 0; + if (-1 == w) + return(0); + if (0 == w) + break; - if (0 == buf[*pos]) - return(1); + if ( ! man_word_alloc(m, line, la, p)) + return(0); + } - while (buf[*pos] && ' ' == buf[*pos]) - (*pos)++; + /* Close out head and open body (unless MAN_SCOPE). */ - if (buf[*pos]) - return(1); + if (MAN_SCOPED & man_macros[tok].flags) { + m->flags |= MAN_BLINE; + return(1); + } else if ( ! rew_scope(MAN_HEAD, m, tok)) + return(0); - warnx("tail whitespace"); - return(-1); + return(man_body_alloc(m, line, ppos, tok)); +} + + +int +in_line_eoln(MACRO_PROT_ARGS) +{ + int w, la; + char *p; + struct man_node *n; + + if ( ! man_elem_alloc(m, line, ppos, tok)) + return(0); + + n = m->last; + + for (;;) { + la = *pos; + w = man_args(m, line, pos, buf, &p); + + if (-1 == w) + return(0); + if (0 == w) + break; + + if ( ! man_word_alloc(m, line, la, p)) + return(0); } + if (n == m->last && (MAN_SCOPED & man_macros[tok].flags)) { + m->flags |= MAN_ELINE; + return(1); + } + /* - * If we're a quoted string (and quoted strings are allowed), - * then parse ahead to the next quote. If none's found, it's an - * error. After, parse to the next word. + * Note that when TH is pruned, we'll be back at the root, so + * make sure that we don't clobber as its sibling. */ - *v = &buf[++(*pos)]; + /* FIXME: clean this to use man_unscope(). */ - while (buf[*pos] && '\"' != buf[*pos]) - (*pos)++; - - if (0 == buf[*pos]) { - warnx("unterminated quotation"); - return(-1); + for ( ; m->last; m->last = m->last->parent) { + if (m->last == n) + break; + if (m->last->type == MAN_ROOT) + break; + if ( ! man_valid_post(m)) + return(0); + if ( ! man_action_post(m)) + return(0); } - buf[(*pos)++] = 0; - if (0 == buf[*pos]) - return(1); + assert(m->last); - while (buf[*pos] && ' ' == buf[*pos]) - (*pos)++; + /* + * Same here regarding whether we're back at the root. + */ - if (buf[*pos]) - return(1); + if (m->last->type != MAN_ROOT && ! man_valid_post(m)) + return(0); + if (m->last->type != MAN_ROOT && ! man_action_post(m)) + return(0); + if (m->last->type != MAN_ROOT) + m->next = MAN_NEXT_SIBLING; - warnx("tail whitespace"); - return(-1); + return(1); } + + +int +man_macroend(struct man *m) +{ + + return(man_unscope(m, m->first)); +} +