-/* $Id: mdoc_macro.c,v 1.151 2014/11/28 04:47:03 schwarze Exp $ */
+/* $Id: mdoc_macro.c,v 1.161 2014/12/22 23:27:32 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
static void dword(struct mdoc *, int, int, const char *,
enum mdelim, int);
static void append_delims(struct mdoc *, int, int *, char *);
-static enum mdoct lookup(enum mdoct, const char *);
-static enum mdoct lookup_raw(const char *);
+static enum mdoct lookup(struct mdoc *, enum mdoct,
+ int, int, const char *);
+static int macro_or_word(MACRO_PROT_ARGS, int);
static int make_pending(struct mdoc_node *, enum mdoct,
struct mdoc *, int, int);
-static void phrase(struct mdoc *, int, int, char *);
+static int parse_rest(struct mdoc *, enum mdoct,
+ int, int *, char *);
static enum mdoct rew_alt(enum mdoct);
static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
const struct mdoc_node *);
}
/*
- * Look up a macro from within a subsequent context.
+ * Look up the macro at *p called by "from",
+ * or as a line macro if from == MDOC_MAX.
*/
static enum mdoct
-lookup(enum mdoct from, const char *p)
-{
-
- if ( ! (MDOC_PARSED & mdoc_macros[from].flags))
- return(MDOC_MAX);
- return(lookup_raw(p));
-}
-
-/*
- * Lookup a macro following the initial line macro.
- */
-static enum mdoct
-lookup_raw(const char *p)
+lookup(struct mdoc *mdoc, enum mdoct from, int line, int ppos, const char *p)
{
enum mdoct res;
- if (MDOC_MAX == (res = mdoc_hash_find(p)))
- return(MDOC_MAX);
- if (MDOC_CALLABLE & mdoc_macros[res].flags)
- return(res);
+ if (from == MDOC_MAX || mdoc_macros[from].flags & MDOC_PARSED) {
+ res = mdoc_hash_find(p);
+ if (res != MDOC_MAX) {
+ if (mdoc_macros[res].flags & MDOC_CALLABLE)
+ return(res);
+ if (res != MDOC_br && res != MDOC_sp && res != MDOC_ll)
+ mandoc_msg(MANDOCERR_MACRO_CALL,
+ mdoc->parse, line, ppos, p);
+ }
+ }
return(MDOC_MAX);
}
* When starting to rewind, skip plain text
* and nodes that have already been rewound.
*/
- if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
+ if (p->type == MDOC_TEXT || p->flags & (MDOC_VALID | MDOC_BREAK))
return(REWIND_MORE);
/*
for (breaker = broken->parent; breaker; breaker = breaker->parent) {
/*
- * If the *broken block had already been broken before
- * and we encounter its breaker, make the tok block
- * pending on the inner breaker.
- * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
- * becomes "[A broken=[B [C->B B] tok=A] C]"
- * and finally "[A [B->A [C->B B] A] C]".
+ * If the *broken block (Z) is already broken and we
+ * encounter its breaker (B), make the tok block (A)
+ * pending on that inner breaker (B).
+ * Graphically, [A breaker=[B! broken=[Z->B B] tok=A] Z]
+ * becomes breaker=[A broken=[B! [Z->B B] tok=A] Z]
+ * and finally [A! [B!->A [Z->B B] A] Z].
+ * In these graphics, "->" indicates the "pending"
+ * pointer and "!" indicates the MDOC_BREAK flag.
+ * Each of the cases gets one additional pointer (B->A)
+ * and one additional flag (A!).
*/
if (breaker == broken->pending) {
broken = breaker;
/*
* Found the breaker.
- * If another, outer breaker is already pending on
- * the *broken block, we must not clobber the link
+ * If another, outer breaker (X) is already pending on
+ * the *broken block (B), we must not clobber the link
* to the outer breaker, but make it pending on the
- * new, now inner breaker.
- * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
- * becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
- * and finally "[A [B->A [C->B A] B] C]".
+ * new, now inner breaker (A).
+ * Graphically, [X! breaker=[A broken=[B->X X] tok=A] B]
+ * becomes [X! breaker=[A->X broken=[B X] tok=A] B]
+ * and finally [X! [A!->X [B->A X] A] B].
*/
if (broken->pending) {
struct mdoc_node *taker;
/*
- * If the breaker had also been broken before,
- * it cannot take on the outer breaker itself,
- * but must hand it on to its own breakers.
- * Graphically, this is the following situation:
- * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
- * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
+ * If the inner breaker (A) is already broken,
+ * too, it cannot take on the outer breaker (X)
+ * but must hand it on to its own breakers (Y):
+ * [X! [Y! breaker=[A->Y Y] broken=[B->X X] tok=A] B]
+ * [X! take=[Y!->X brea=[A->Y Y] brok=[B X] tok=A] B]
+ * and finally [X! [Y!->X [A!->Y Y] [B->A X] A] B].
*/
taker = breaker;
while (taker->pending)
taker = taker->pending;
taker->pending = broken->pending;
}
+
+ /*
+ * Now we have reduced the situation to the simplest
+ * case, which is just breaker=[A broken=[B tok=A] B]
+ * and becomes [A! [B->A A] B].
+ */
broken->pending = breaker;
+ breaker->flags |= MDOC_BREAK;
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[broken->tok]);
rew_sub(enum mdoc_type t, struct mdoc *mdoc,
enum mdoct tok, int line, int ppos)
{
- struct mdoc_node *n;
+ struct mdoc_node *n, *to;
+ to = NULL;
n = mdoc->last;
while (n) {
switch (rew_dohalt(tok, t, n)) {
case REWIND_NONE:
- return;
+ if (to == NULL)
+ return;
+ n = to;
+ break;
case REWIND_THIS:
n->lastline = line -
(mdoc->flags & MDOC_NEWLINE &&
case REWIND_MORE:
n->lastline = line -
(mdoc->flags & MDOC_NEWLINE ? 1 : 0);
+ to = n;
n = n->parent;
continue;
case REWIND_LATER:
static void
append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
{
- int la;
- enum margserr ac;
char *p;
+ int la;
if (buf[*pos] == '\0')
return;
for (;;) {
la = *pos;
- ac = mdoc_zargs(mdoc, line, pos, buf, &p);
- if (ac == ARGS_EOLN)
+ if (mdoc_args(mdoc, line, pos, buf, MDOC_MAX, &p) == ARGS_EOLN)
break;
dword(mdoc, line, la, p, DELIM_MAX, 1);
}
}
+/*
+ * Parse one word.
+ * If it is a macro, call it and return 1.
+ * Otherwise, allocate it and return 0.
+ */
+static int
+macro_or_word(MACRO_PROT_ARGS, int parsed)
+{
+ char *p;
+ enum mdoct ntok;
+
+ p = buf + ppos;
+ ntok = MDOC_MAX;
+ if (*p == '"')
+ p++;
+ else if (parsed && ! (mdoc->flags & MDOC_PHRASELIT))
+ ntok = lookup(mdoc, tok, line, ppos, p);
+
+ if (ntok == MDOC_MAX) {
+ dword(mdoc, line, ppos, p, DELIM_MAX, tok == MDOC_MAX ||
+ mdoc_macros[tok].flags & MDOC_JOIN);
+ return(0);
+ } else {
+ if (mdoc_macros[tok].fp == in_line_eoln)
+ rew_elem(mdoc, tok);
+ mdoc_macro(mdoc, ntok, line, ppos, pos, buf);
+ if (tok == MDOC_MAX)
+ append_delims(mdoc, line, pos, buf);
+ return(1);
+ }
+}
+
/*
* Close out block partial/full explicit.
*/
struct mdoc_node *later; /* A sub-block starting later. */
struct mdoc_node *n; /* For searching backwards. */
- int j, lastarg, maxargs, flushed, nl;
+ int flushed, have_it, j, lastarg, maxargs, nl;
enum margserr ac;
enum mdoct atok, ntok;
char *p;
* both of our own and of pending sub-blocks.
*/
+ have_it = 0;
atok = rew_alt(tok);
body = endbody = later = NULL;
for (n = mdoc->last; n; n = n->parent) {
- if (n->flags & MDOC_VALID)
+ if (n->flags & (MDOC_VALID | MDOC_BREAK))
continue;
/* Remember the start of our own body. */
if (n->type != MDOC_BLOCK || n->tok == MDOC_Nm)
continue;
+
+ if (n->tok == MDOC_It) {
+ have_it = 1;
+ continue;
+ }
+
if (atok == n->tok) {
assert(body);
* just proceed to closing out.
*/
- if (later == NULL)
+ if (later == NULL ||
+ (tok == MDOC_El && !have_it))
break;
/*
* implicit ones, the first open implicit block.
*/
- if (later &&
- mdoc_macros[later->tok].flags & MDOC_EXPLICIT)
- continue;
- if (n->tok != MDOC_It)
+ if (later == NULL ||
+ ! (mdoc_macros[later->tok].flags & MDOC_EXPLICIT))
later = n;
}
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
break;
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
+ ntok = ac == ARGS_QWORD ? MDOC_MAX :
+ lookup(mdoc, tok, line, lastarg, p);
if (ntok == MDOC_MAX) {
dword(mdoc, line, lastarg, p, DELIM_MAX,
in_line(MACRO_PROT_ARGS)
{
int la, scope, cnt, firstarg, mayopen, nc, nl;
- enum margverr av;
enum mdoct ntok;
enum margserr ac;
enum mdelim d;
break;
}
- for (arg = NULL;; ) {
- la = *pos;
- av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- if (av == ARGV_ARG)
- continue;
- if (av == ARGV_WORD)
- *pos = la;
- break;
- }
+ mdoc_argv(mdoc, line, tok, &arg, pos, buf);
d = DELIM_NONE;
firstarg = 1;
}
ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ?
- MDOC_MAX : lookup(tok, p);
+ MDOC_MAX : lookup(mdoc, tok, line, la, p);
/*
* In this case, we've located a submacro and must
static void
blk_full(MACRO_PROT_ARGS)
{
- int la, nl, nparsed;
+ int la, nl, parsed;
struct mdoc_arg *arg;
struct mdoc_node *head; /* save of head macro */
struct mdoc_node *body; /* save of body macro */
struct mdoc_node *n;
- enum mdoct ntok;
enum margserr ac, lac;
- enum margverr av;
char *p;
nl = MDOC_NEWLINE & mdoc->flags;
if (tok == MDOC_It) {
for (n = mdoc->last; n; n = n->parent)
- if (n->tok == MDOC_Bl &&
- ! (n->flags & MDOC_VALID))
+ if (n->tok == MDOC_Bl && n->type == MDOC_BLOCK &&
+ ! (n->flags & (MDOC_VALID | MDOC_BREAK)))
break;
if (n == NULL) {
mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
* regular child nodes.
*/
- for (arg = NULL;; ) {
- la = *pos;
- av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- if (av == ARGV_ARG)
- continue;
- if (av == ARGV_WORD)
- *pos = la;
- break;
- }
-
+ mdoc_argv(mdoc, line, tok, &arg, pos, buf);
mdoc_block_alloc(mdoc, line, ppos, tok, arg);
head = body = NULL;
* parsed, even though `It' macros in general are parsed.
*/
- nparsed = tok == MDOC_It &&
- mdoc->last->parent->tok == MDOC_Bl &&
- mdoc->last->parent->norm->Bl.type == LIST_diag;
+ parsed = tok != MDOC_It ||
+ mdoc->last->parent->tok != MDOC_Bl ||
+ mdoc->last->parent->norm->Bl.type != LIST_diag;
/*
* The `Nd' macro has all arguments in its body: it's a hybrid
mdoc->flags |= MDOC_PPHRASE;
if (ac == ARGS_PEND && lac == ARGS_PPHRASE)
mdoc->flags |= MDOC_PPHRASE;
- phrase(mdoc, line, la, buf);
+ parse_rest(mdoc, MDOC_MAX, line, &la, buf);
mdoc->flags &= ~MDOC_PPHRASE;
continue;
}
- ntok = nparsed || ac == ARGS_QWORD ?
- MDOC_MAX : lookup(tok, p);
-
- if (ntok != MDOC_MAX) {
- mdoc_macro(mdoc, ntok, line, la, pos, buf);
+ if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed))
break;
- }
- dword(mdoc, line, la, p, DELIM_MAX,
- MDOC_JOIN & mdoc_macros[tok].flags);
}
if (head == NULL)
blk_part_imp(MACRO_PROT_ARGS)
{
int la, nl;
- enum mdoct ntok;
enum margserr ac;
char *p;
struct mdoc_node *blk; /* saved block context */
if (body == NULL)
body = mdoc_body_alloc(mdoc, line, ppos, tok);
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
-
- if (ntok != MDOC_MAX) {
- mdoc_macro(mdoc, ntok, line, la, pos, buf);
+ if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
break;
- }
- dword(mdoc, line, la, p, DELIM_MAX,
- MDOC_JOIN & mdoc_macros[tok].flags);
}
if (body == NULL)
body = mdoc_body_alloc(mdoc, line, ppos, tok);
struct mdoc_node *head; /* keep track of head */
struct mdoc_node *body; /* keep track of body */
char *p;
- enum mdoct ntok;
nl = MDOC_NEWLINE & mdoc->flags;
}
assert(head != NULL && body != NULL);
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
- if (ntok != MDOC_MAX) {
- mdoc_macro(mdoc, ntok, line, la, pos, buf);
+ if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
break;
- }
- dword(mdoc, line, la, p, DELIM_MAX,
- MDOC_JOIN & mdoc_macros[tok].flags);
}
/* Clean-up to leave in a consistent state. */
{
int la, flushed, j, maxargs, nl;
enum margserr ac;
- enum margverr av;
struct mdoc_arg *arg;
char *p;
enum mdoct ntok;
- nl = MDOC_NEWLINE & mdoc->flags;
+ nl = mdoc->flags & MDOC_NEWLINE;
/*
* A line macro that has a fixed number of arguments (maxargs).
break;
}
- for (arg = NULL; ; ) {
- la = *pos;
- av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- if (av == ARGV_ARG)
- continue;
- if (av == ARGV_WORD)
- *pos = la;
- break;
- }
+ mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- for (flushed = j = 0; ; ) {
+ p = NULL;
+ flushed = j = 0;
+ for (;;) {
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
- if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
+ if (ac == ARGS_PUNCT || ac == ARGS_EOLN) {
+ if (j < 2 && tok == MDOC_Pf)
+ mandoc_vmsg(MANDOCERR_PF_SKIP,
+ mdoc->parse, line, ppos, "Pf %s",
+ p == NULL ? "at eol" : p);
break;
+ }
if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
ac != ARGS_QWORD && j == 0 &&
flushed = 1;
}
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
+ ntok = (ac == ARGS_QWORD || (tok == MDOC_Pf && j == 0)) ?
+ MDOC_MAX : lookup(mdoc, tok, line, la, p);
if (ntok != MDOC_MAX) {
if ( ! flushed)
j++;
}
- if (j == 0)
+ if (j == 0) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+ if (ac == ARGS_PUNCT && tok == MDOC_Pf)
+ append_delims(mdoc, line, pos, buf);
+ }
if ( ! flushed)
rew_elem(mdoc, tok);
if (nl)
static void
in_line_eoln(MACRO_PROT_ARGS)
{
- int la;
- enum margserr ac;
- enum margverr av;
struct mdoc_arg *arg;
- char *p;
- enum mdoct ntok;
-
- assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
if (tok == MDOC_Pp)
rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos);
- /* Parse macro arguments. */
-
- for (arg = NULL; ; ) {
- la = *pos;
- av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- if (av == ARGV_ARG)
- continue;
- if (av == ARGV_WORD)
- *pos = la;
- break;
- }
-
- /* Open element scope. */
-
+ mdoc_argv(mdoc, line, tok, &arg, pos, buf);
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+ if (parse_rest(mdoc, tok, line, pos, buf))
+ return;
+ rew_elem(mdoc, tok);
+}
- /* Parse argument terms. */
+/*
+ * The simplest argument parser available: Parse the remaining
+ * words until the end of the phrase or line and return 0
+ * or until the next macro, call that macro, and return 1.
+ */
+static int
+parse_rest(struct mdoc *mdoc, enum mdoct tok, int line, int *pos, char *buf)
+{
+ int la;
for (;;) {
la = *pos;
- ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
- if (ac == ARGS_EOLN)
- break;
-
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
-
- if (ntok != MDOC_MAX) {
- rew_elem(mdoc, tok);
- mdoc_macro(mdoc, ntok, line, la, pos, buf);
- return;
- }
- dword(mdoc, line, la, p, DELIM_MAX,
- MDOC_JOIN & mdoc_macros[tok].flags);
+ if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN)
+ return(0);
+ if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
+ return(1);
}
-
- /* Close out (no delimiters). */
-
- rew_elem(mdoc, tok);
}
static void
* They're unusual because they're basically free-form text until a
* macro is encountered.
*/
-static void
-phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
-{
- int la, pos;
- enum margserr ac;
- enum mdoct ntok;
- char *p;
-
- for (pos = ppos; ; ) {
- la = pos;
-
- ac = mdoc_zargs(mdoc, line, &pos, buf, &p);
- if (ac == ARGS_EOLN)
- break;
-
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup_raw(p);
-
- if (ntok != MDOC_MAX) {
- mdoc_macro(mdoc, ntok, line, la, &pos, buf);
- append_delims(mdoc, line, &pos, buf);
- return;
- }
- dword(mdoc, line, la, p, DELIM_MAX, 1);
- }
-}
-
static void
phrase_ta(MACRO_PROT_ARGS)
{
struct mdoc_node *n;
- int la;
- enum mdoct ntok;
- enum margserr ac;
- char *p;
/* Make sure we are in a column list or ignore this macro. */
n = mdoc->last;
- while (n != NULL && n->tok != MDOC_Bl)
+ while (n != NULL &&
+ (n->tok != MDOC_Bl || n->flags & (MDOC_VALID | MDOC_BREAK)))
n = n->parent;
if (n == NULL || n->norm->Bl.type != LIST_column) {
mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos);
mdoc_body_alloc(mdoc, line, ppos, MDOC_It);
-
- for (;;) {
- la = *pos;
- ac = mdoc_zargs(mdoc, line, pos, buf, &p);
- if (ac == ARGS_EOLN)
- break;
-
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup_raw(p);
-
- if (ntok != MDOC_MAX) {
- mdoc_macro(mdoc, ntok, line, la, pos, buf);
- append_delims(mdoc, line, pos, buf);
- return;
- }
- dword(mdoc, line, la, p, DELIM_MAX,
- MDOC_JOIN & mdoc_macros[tok].flags);
- }
+ parse_rest(mdoc, MDOC_MAX, line, pos, buf);
}