Return to man_macro.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.22, 2009/08/19 12:15:58 | version 1.75, 2012/11/17 00:26:33 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org> | |||
* | * | ||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||
|
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | */ | ||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <assert.h> | #include <assert.h> | ||
#include <ctype.h> | #include <ctype.h> | ||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include "man.h" | |||
#include "mandoc.h" | |||
#include "libmandoc.h" | |||
#include "libman.h" | #include "libman.h" | ||
#define REW_REWIND (0) /* See rew_scope(). */ | enum rew { | ||
#define REW_NOHALT (1) /* See rew_scope(). */ | REW_REWIND, | ||
#define REW_HALT (2) /* See rew_scope(). */ | REW_NOHALT, | ||
REW_HALT | |||
}; | |||
static int in_line_eoln(MACRO_PROT_ARGS); | |||
static int blk_imp(MACRO_PROT_ARGS); | |||
static int blk_close(MACRO_PROT_ARGS); | static int blk_close(MACRO_PROT_ARGS); | ||
static int blk_exp(MACRO_PROT_ARGS); | |||
static int blk_imp(MACRO_PROT_ARGS); | |||
static int in_line_eoln(MACRO_PROT_ARGS); | |||
static int man_args(struct man *, int, | |||
int *, char *, char **); | |||
static int rew_scope(enum man_type, struct man *, int); | static int rew_scope(enum man_type, | ||
static int rew_dohalt(int, enum man_type, | struct man *, enum mant); | ||
static enum rew rew_dohalt(enum mant, enum man_type, | |||
const struct man_node *); | const struct man_node *); | ||
static int rew_block(int, enum man_type, | static enum rew rew_block(enum mant, enum man_type, | ||
const struct man_node *); | const struct man_node *); | ||
static void rew_warn(struct man *, | |||
struct man_node *, enum mandocerr); | |||
const struct man_macro __man_macros[MAN_MAX] = { | const struct man_macro __man_macros[MAN_MAX] = { | ||
{ in_line_eoln, 0 }, /* br */ | { in_line_eoln, MAN_NSCOPED }, /* br */ | ||
{ in_line_eoln, 0 }, /* TH */ | { in_line_eoln, MAN_BSCOPE }, /* TH */ | ||
{ blk_imp, 0 }, /* SH */ | { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */ | ||
{ blk_imp, 0 }, /* SS */ | { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */ | ||
{ blk_imp, MAN_SCOPED }, /* TP */ | { blk_imp, MAN_BSCOPE | MAN_SCOPED | MAN_FSCOPED }, /* TP */ | ||
{ blk_imp, 0 }, /* LP */ | { blk_imp, MAN_BSCOPE }, /* LP */ | ||
{ blk_imp, 0 }, /* PP */ | { blk_imp, MAN_BSCOPE }, /* PP */ | ||
{ blk_imp, 0 }, /* P */ | { blk_imp, MAN_BSCOPE }, /* P */ | ||
{ blk_imp, 0 }, /* IP */ | { blk_imp, MAN_BSCOPE }, /* IP */ | ||
{ blk_imp, 0 }, /* HP */ | { blk_imp, MAN_BSCOPE }, /* HP */ | ||
{ in_line_eoln, MAN_SCOPED }, /* SM */ | { in_line_eoln, MAN_SCOPED }, /* SM */ | ||
{ in_line_eoln, MAN_SCOPED }, /* SB */ | { in_line_eoln, MAN_SCOPED }, /* SB */ | ||
{ in_line_eoln, 0 }, /* BI */ | { in_line_eoln, 0 }, /* BI */ | ||
|
|
||
{ in_line_eoln, MAN_SCOPED }, /* I */ | { in_line_eoln, MAN_SCOPED }, /* I */ | ||
{ in_line_eoln, 0 }, /* IR */ | { in_line_eoln, 0 }, /* IR */ | ||
{ in_line_eoln, 0 }, /* RI */ | { in_line_eoln, 0 }, /* RI */ | ||
{ in_line_eoln, 0 }, /* na */ | { in_line_eoln, MAN_NSCOPED }, /* na */ | ||
{ in_line_eoln, 0 }, /* i */ | { in_line_eoln, MAN_NSCOPED }, /* sp */ | ||
{ in_line_eoln, 0 }, /* sp */ | { in_line_eoln, MAN_BSCOPE }, /* nf */ | ||
{ in_line_eoln, 0 }, /* nf */ | { in_line_eoln, MAN_BSCOPE }, /* fi */ | ||
{ in_line_eoln, 0 }, /* fi */ | |||
{ in_line_eoln, 0 }, /* r */ | |||
{ blk_close, 0 }, /* RE */ | { blk_close, 0 }, /* RE */ | ||
{ blk_imp, MAN_EXPLICIT }, /* RS */ | { blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* RS */ | ||
{ in_line_eoln, 0 }, /* DT */ | |||
{ in_line_eoln, 0 }, /* UC */ | |||
{ in_line_eoln, 0 }, /* PD */ | |||
{ in_line_eoln, 0 }, /* AT */ | |||
{ in_line_eoln, 0 }, /* in */ | |||
{ in_line_eoln, 0 }, /* ft */ | |||
{ in_line_eoln, 0 }, /* OP */ | |||
{ in_line_eoln, MAN_BSCOPE }, /* EX */ | |||
{ in_line_eoln, MAN_BSCOPE }, /* EE */ | |||
}; | }; | ||
const struct man_macro * const man_macros = __man_macros; | const struct man_macro * const man_macros = __man_macros; | ||
/* | |||
* Warn when "n" is an explicit non-roff macro. | |||
*/ | |||
static void | |||
rew_warn(struct man *man, struct man_node *n, enum mandocerr er) | |||
{ | |||
if (er == MANDOCERR_MAX || MAN_BLOCK != n->type) | |||
return; | |||
if (MAN_VALID & n->flags) | |||
return; | |||
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags)) | |||
return; | |||
assert(er < MANDOCERR_FATAL); | |||
man_nmsg(man, n, er); | |||
} | |||
/* | |||
* Rewind scope. If a code "er" != MANDOCERR_MAX has been provided, it | |||
* will be used if an explicit block scope is being closed out. | |||
*/ | |||
int | int | ||
man_unscope(struct man *m, const struct man_node *n) | man_unscope(struct man *man, const struct man_node *to, | ||
enum mandocerr er) | |||
{ | { | ||
struct man_node *n; | |||
assert(n); | assert(to); | ||
m->next = MAN_NEXT_SIBLING; | |||
man->next = MAN_NEXT_SIBLING; | |||
/* LINTED */ | /* LINTED */ | ||
while (m->last != n) { | while (man->last != to) { | ||
if ( ! man_valid_post(m)) | /* | ||
* Save the parent here, because we may delete the | |||
* man->last node in the post-validation phase and reset | |||
* it to man->last->parent, causing a step in the closing | |||
* out to be lost. | |||
*/ | |||
n = man->last->parent; | |||
rew_warn(man, man->last, er); | |||
if ( ! man_valid_post(man)) | |||
return(0); | return(0); | ||
if ( ! man_action_post(m)) | man->last = n; | ||
return(0); | assert(man->last); | ||
m->last = m->last->parent; | |||
assert(m->last); | |||
} | } | ||
if ( ! man_valid_post(m)) | rew_warn(man, man->last, er); | ||
if ( ! man_valid_post(man)) | |||
return(0); | return(0); | ||
return(man_action_post(m)); | |||
return(1); | |||
} | } | ||
static int | static enum rew | ||
rew_block(int ntok, enum man_type type, const struct man_node *n) | rew_block(enum mant ntok, enum man_type type, const struct man_node *n) | ||
{ | { | ||
if (MAN_BLOCK == type && ntok == n->parent->tok && | if (MAN_BLOCK == type && ntok == n->parent->tok && | ||
|
|
||
* section (all less sections), and scoped to subsections (all less | * section (all less sections), and scoped to subsections (all less | ||
* sections and subsections). | * sections and subsections). | ||
*/ | */ | ||
static int | static enum rew | ||
rew_dohalt(int tok, enum man_type type, const struct man_node *n) | rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n) | ||
{ | { | ||
int c; | enum rew c; | ||
/* We cannot progress beyond the root ever. */ | |||
if (MAN_ROOT == n->type) | if (MAN_ROOT == n->type) | ||
return(REW_HALT); | return(REW_HALT); | ||
assert(n->parent); | assert(n->parent); | ||
/* Normal nodes shouldn't go to the level of the root. */ | |||
if (MAN_ROOT == n->parent->type) | if (MAN_ROOT == n->parent->type) | ||
return(REW_REWIND); | return(REW_REWIND); | ||
/* Already-validated nodes should be closed out. */ | |||
if (MAN_VALID & n->flags) | if (MAN_VALID & n->flags) | ||
return(REW_NOHALT); | return(REW_NOHALT); | ||
/* Rewind to ourselves, first. */ | /* First: rewind to ourselves. */ | ||
if (type == n->type && tok == n->tok) | if (type == n->type && tok == n->tok) { | ||
return(REW_REWIND); | if (MAN_EXPLICIT & man_macros[n->tok].flags) | ||
return(REW_HALT); | |||
else | |||
return(REW_REWIND); | |||
} | |||
/* | |||
* Next follow the implicit scope-smashings as defined by man.7: | |||
* section, sub-section, etc. | |||
*/ | |||
switch (tok) { | switch (tok) { | ||
case (MAN_SH): | case (MAN_SH): | ||
break; | break; | ||
|
|
||
return(c); | return(c); | ||
break; | break; | ||
case (MAN_RS): | case (MAN_RS): | ||
/* Preserve empty paragraphs before RS. */ | |||
if (0 == n->nchild && (MAN_P == n->tok || | |||
MAN_PP == n->tok || MAN_LP == n->tok)) | |||
return(REW_HALT); | |||
/* Rewind to a subsection, if a block. */ | /* Rewind to a subsection, if a block. */ | ||
if (REW_NOHALT != (c = rew_block(MAN_SS, type, n))) | if (REW_NOHALT != (c = rew_block(MAN_SS, type, n))) | ||
return(c); | return(c); | ||
|
|
||
* scopes. When a scope is closed, it must be validated and actioned. | * scopes. When a scope is closed, it must be validated and actioned. | ||
*/ | */ | ||
static int | static int | ||
rew_scope(enum man_type type, struct man *m, int tok) | rew_scope(enum man_type type, struct man *man, enum mant tok) | ||
{ | { | ||
struct man_node *n; | struct man_node *n; | ||
int c; | enum rew c; | ||
/* LINTED */ | /* LINTED */ | ||
for (n = m->last; n; n = n->parent) { | for (n = man->last; n; n = n->parent) { | ||
/* | /* | ||
* Whether we should stop immediately (REW_HALT), stop | * Whether we should stop immediately (REW_HALT), stop | ||
* and rewind until this point (REW_REWIND), or keep | * and rewind until this point (REW_REWIND), or keep | ||
|
|
||
break; | break; | ||
} | } | ||
/* Rewind until the current point. */ | /* | ||
* Rewind until the current point. Warn if we're a roff | |||
* instruction that's mowing over explicit scopes. | |||
*/ | |||
assert(n); | assert(n); | ||
return(man_unscope(m, n)); | |||
return(man_unscope(man, n, MANDOCERR_MAX)); | |||
} | } | ||
/* | |||
* Close out a generic explicit macro. | |||
*/ | |||
/* ARGSUSED */ | /* ARGSUSED */ | ||
int | int | ||
blk_close(MACRO_PROT_ARGS) | blk_close(MACRO_PROT_ARGS) | ||
{ | { | ||
int ntok; | enum mant ntok; | ||
const struct man_node *nn; | const struct man_node *nn; | ||
switch (tok) { | switch (tok) { | ||
|
|
||
/* NOTREACHED */ | /* NOTREACHED */ | ||
} | } | ||
for (nn = m->last->parent; nn; nn = nn->parent) | for (nn = man->last->parent; nn; nn = nn->parent) | ||
if (ntok == nn->tok) | if (ntok == nn->tok && MAN_BLOCK == nn->type) | ||
break; | break; | ||
if (NULL == nn) | if (NULL != nn) | ||
if ( ! man_pwarn(m, line, ppos, WNOSCOPE)) | man_unscope(man, nn, MANDOCERR_MAX); | ||
return(0); | else | ||
man_pmsg(man, line, ppos, MANDOCERR_NOSCOPE); | |||
if ( ! rew_scope(MAN_BODY, m, ntok)) | return(1); | ||
} | |||
/* ARGSUSED */ | |||
int | |||
blk_exp(MACRO_PROT_ARGS) | |||
{ | |||
struct man_node *n; | |||
int la; | |||
char *p; | |||
/* Close out prior implicit scopes. */ | |||
if ( ! rew_scope(MAN_BLOCK, man, tok)) | |||
return(0); | return(0); | ||
if ( ! rew_scope(MAN_BLOCK, m, ntok)) | |||
if ( ! man_block_alloc(man, line, ppos, tok)) | |||
return(0); | return(0); | ||
m->next = MAN_NEXT_SIBLING; | if ( ! man_head_alloc(man, line, ppos, tok)) | ||
return(1); | return(0); | ||
for (;;) { | |||
la = *pos; | |||
if ( ! man_args(man, line, pos, buf, &p)) | |||
break; | |||
if ( ! man_word_alloc(man, line, la, p)) | |||
return(0); | |||
} | |||
assert(man); | |||
assert(tok != MAN_MAX); | |||
for (n = man->last; n; n = n->parent) { | |||
if (n->tok != tok) | |||
continue; | |||
assert(MAN_HEAD == n->type); | |||
man_unscope(man, n, MANDOCERR_MAX); | |||
break; | |||
} | |||
return(man_body_alloc(man, line, ppos, tok)); | |||
} | } | ||
/* | /* | ||
* Parse an implicit-block macro. These contain a MAN_HEAD and a | * Parse an implicit-block macro. These contain a MAN_HEAD and a | ||
* MAN_BODY contained within a MAN_BLOCK. Rules for closing out other | * 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 | * scopes, such as `SH' closing out an `SS', are defined in the rew | ||
* routines. | * routines. | ||
*/ | */ | ||
/* ARGSUSED */ | |||
int | int | ||
blk_imp(MACRO_PROT_ARGS) | blk_imp(MACRO_PROT_ARGS) | ||
{ | { | ||
int w, la; | int la; | ||
char *p; | char *p; | ||
struct man_node *n; | |||
/* Close out prior scopes. */ | /* Close out prior scopes. */ | ||
if ( ! rew_scope(MAN_BODY, m, tok)) | if ( ! rew_scope(MAN_BODY, man, tok)) | ||
return(0); | return(0); | ||
if ( ! rew_scope(MAN_BLOCK, m, tok)) | if ( ! rew_scope(MAN_BLOCK, man, tok)) | ||
return(0); | return(0); | ||
/* Allocate new block & head scope. */ | /* Allocate new block & head scope. */ | ||
if ( ! man_block_alloc(m, line, ppos, tok)) | if ( ! man_block_alloc(man, line, ppos, tok)) | ||
return(0); | return(0); | ||
if ( ! man_head_alloc(m, line, ppos, tok)) | if ( ! man_head_alloc(man, line, ppos, tok)) | ||
return(0); | return(0); | ||
n = man->last; | |||
/* Add line arguments. */ | /* Add line arguments. */ | ||
for (;;) { | for (;;) { | ||
la = *pos; | la = *pos; | ||
w = man_args(m, line, pos, buf, &p); | if ( ! man_args(man, line, pos, buf, &p)) | ||
if (-1 == w) | |||
return(0); | |||
if (0 == w) | |||
break; | break; | ||
if ( ! man_word_alloc(man, line, la, p)) | |||
if ( ! man_word_alloc(m, line, la, p)) | |||
return(0); | return(0); | ||
} | } | ||
/* Close out head and open body (unless MAN_SCOPE). */ | /* Close out head and open body (unless MAN_SCOPE). */ | ||
if (MAN_SCOPED & man_macros[tok].flags) { | if (MAN_SCOPED & man_macros[tok].flags) { | ||
m->flags |= MAN_BLINE; | /* If we're forcing scope (`TP'), keep it open. */ | ||
return(1); | if (MAN_FSCOPED & man_macros[tok].flags) { | ||
} else if ( ! rew_scope(MAN_HEAD, m, tok)) | man->flags |= MAN_BLINE; | ||
return(0); | return(1); | ||
} else if (n == man->last) { | |||
man->flags |= MAN_BLINE; | |||
return(1); | |||
} | |||
} | |||
return(man_body_alloc(m, line, ppos, tok)); | if ( ! rew_scope(MAN_HEAD, man, tok)) | ||
return(0); | |||
return(man_body_alloc(man, line, ppos, tok)); | |||
} | } | ||
/* ARGSUSED */ | |||
int | int | ||
in_line_eoln(MACRO_PROT_ARGS) | in_line_eoln(MACRO_PROT_ARGS) | ||
{ | { | ||
int w, la; | int la; | ||
char *p; | char *p; | ||
struct man_node *n; | struct man_node *n; | ||
if ( ! man_elem_alloc(m, line, ppos, tok)) | if ( ! man_elem_alloc(man, line, ppos, tok)) | ||
return(0); | return(0); | ||
n = m->last; | n = man->last; | ||
for (;;) { | for (;;) { | ||
la = *pos; | la = *pos; | ||
w = man_args(m, line, pos, buf, &p); | if ( ! man_args(man, line, pos, buf, &p)) | ||
if (-1 == w) | |||
return(0); | |||
if (0 == w) | |||
break; | break; | ||
if ( ! man_word_alloc(man, line, la, p)) | |||
if ( ! man_word_alloc(m, line, la, p)) | |||
return(0); | return(0); | ||
} | } | ||
if (n == m->last && (MAN_SCOPED & man_macros[tok].flags)) { | /* | ||
m->flags |= MAN_ELINE; | * If no arguments are specified and this is MAN_SCOPED (i.e., | ||
* next-line scoped), then set our mode to indicate that we're | |||
* waiting for terms to load into our context. | |||
*/ | |||
if (n == man->last && MAN_SCOPED & man_macros[tok].flags) { | |||
assert( ! (MAN_NSCOPED & man_macros[tok].flags)); | |||
man->flags |= MAN_ELINE; | |||
return(1); | return(1); | ||
} | } | ||
/* Set ignorable context, if applicable. */ | |||
if (MAN_NSCOPED & man_macros[tok].flags) { | |||
assert( ! (MAN_SCOPED & man_macros[tok].flags)); | |||
man->flags |= MAN_ILINE; | |||
} | |||
assert(MAN_ROOT != man->last->type); | |||
man->next = MAN_NEXT_SIBLING; | |||
/* | /* | ||
* Note that when TH is pruned, we'll be back at the root, so | * Rewind our element scope. Note that when TH is pruned, we'll | ||
* make sure that we don't clobber as its sibling. | * be back at the root, so make sure that we don't clobber as | ||
* its sibling. | |||
*/ | */ | ||
for ( ; m->last; m->last = m->last->parent) { | for ( ; man->last; man->last = man->last->parent) { | ||
if (m->last == n) | if (man->last == n) | ||
break; | break; | ||
if (m->last->type == MAN_ROOT) | if (man->last->type == MAN_ROOT) | ||
break; | break; | ||
if ( ! man_valid_post(m)) | if ( ! man_valid_post(man)) | ||
return(0); | return(0); | ||
if ( ! man_action_post(m)) | |||
return(0); | |||
} | } | ||
assert(m->last); | assert(man->last); | ||
/* | /* | ||
* Same here regarding whether we're back at the root. | * Same here regarding whether we're back at the root. | ||
*/ | */ | ||
if (m->last->type != MAN_ROOT && ! man_valid_post(m)) | if (man->last->type != MAN_ROOT && ! man_valid_post(man)) | ||
return(0); | 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; | |||
return(1); | return(1); | ||
} | } | ||
int | int | ||
man_macroend(struct man *m) | man_macroend(struct man *man) | ||
{ | { | ||
struct man_node *n; | |||
n = MAN_VALID & m->last->flags ? | return(man_unscope(man, man->first, MANDOCERR_SCOPEEXIT)); | ||
m->last->parent : m->last; | } | ||
for ( ; n; n = n->parent) { | static int | ||
if (MAN_BLOCK != n->type) | man_args(struct man *man, int line, int *pos, char *buf, char **v) | ||
continue; | { | ||
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags)) | char *start; | ||
continue; | |||
return(man_nerr(m, n, WEXITSCOPE)); | |||
} | |||
return(man_unscope(m, m->first)); | assert(*pos); | ||
} | *v = start = buf + *pos; | ||
assert(' ' != *start); | |||
if ('\0' == *start) | |||
return(0); | |||
*v = mandoc_getarg(man->parse, v, line, pos); | |||
return(1); | |||
} |