Return to mdoc_macro.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.150, 2014/11/28 03:14:18 | version 1.196, 2015/04/29 14:48:53 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> | * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org> | * Copyright (c) 2010, 2012-2015 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 | ||
* copyright notice and this permission notice appear in all copies. | * copyright notice and this permission notice appear in all copies. | ||
* | * | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | ||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | ||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
|
|
||
#include <string.h> | #include <string.h> | ||
#include <time.h> | #include <time.h> | ||
#include "mdoc.h" | |||
#include "mandoc.h" | #include "mandoc.h" | ||
#include "libmdoc.h" | #include "roff.h" | ||
#include "mdoc.h" | |||
#include "libmandoc.h" | #include "libmandoc.h" | ||
#include "roff_int.h" | |||
#include "libmdoc.h" | |||
enum rew { /* see rew_dohalt() */ | static void blk_full(MACRO_PROT_ARGS); | ||
REWIND_NONE, | static void blk_exp_close(MACRO_PROT_ARGS); | ||
REWIND_THIS, | static void blk_part_exp(MACRO_PROT_ARGS); | ||
REWIND_MORE, | static void blk_part_imp(MACRO_PROT_ARGS); | ||
REWIND_FORCE, | static void ctx_synopsis(MACRO_PROT_ARGS); | ||
REWIND_LATER, | static void in_line_eoln(MACRO_PROT_ARGS); | ||
REWIND_ERROR | static void in_line_argn(MACRO_PROT_ARGS); | ||
}; | static void in_line(MACRO_PROT_ARGS); | ||
static void phrase_ta(MACRO_PROT_ARGS); | |||
static int blk_full(MACRO_PROT_ARGS); | static void append_delims(struct roff_man *, int, int *, char *); | ||
static int blk_exp_close(MACRO_PROT_ARGS); | static void dword(struct roff_man *, int, int, const char *, | ||
static int blk_part_exp(MACRO_PROT_ARGS); | enum mdelim, int); | ||
static int blk_part_imp(MACRO_PROT_ARGS); | static int find_pending(struct roff_man *, int, int, int, | ||
static int ctx_synopsis(MACRO_PROT_ARGS); | struct roff_node *); | ||
static int in_line_eoln(MACRO_PROT_ARGS); | static int lookup(struct roff_man *, int, int, int, const char *); | ||
static int in_line_argn(MACRO_PROT_ARGS); | static int macro_or_word(MACRO_PROT_ARGS, int); | ||
static int in_line(MACRO_PROT_ARGS); | static int parse_rest(struct roff_man *, int, int, int *, char *); | ||
static int phrase_ta(MACRO_PROT_ARGS); | static int rew_alt(int); | ||
static void rew_elem(struct roff_man *, int); | |||
static void rew_last(struct roff_man *, const struct roff_node *); | |||
static void rew_pending(struct roff_man *, | |||
const struct roff_node *); | |||
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 int make_pending(struct mdoc_node *, enum mdoct, | |||
struct mdoc *, int, int); | |||
static int phrase(struct mdoc *, int, int, char *); | |||
static enum mdoct rew_alt(enum mdoct); | |||
static enum rew rew_dohalt(enum mdoct, enum mdoc_type, | |||
const struct mdoc_node *); | |||
static void rew_elem(struct mdoc *, enum mdoct); | |||
static void rew_last(struct mdoc *, const struct mdoc_node *); | |||
static void rew_sub(enum mdoc_type, struct mdoc *, | |||
enum mdoct, int, int); | |||
const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { | const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { | ||
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */ | { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */ | ||
{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */ | { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ | ||
|
|
||
* closing out open [implicit] scopes. Obviously, open explicit scopes | * closing out open [implicit] scopes. Obviously, open explicit scopes | ||
* are errors. | * are errors. | ||
*/ | */ | ||
int | void | ||
mdoc_macroend(struct mdoc *mdoc) | mdoc_endparse(struct roff_man *mdoc) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
/* Scan for open explicit scopes. */ | /* Scan for open explicit scopes. */ | ||
n = MDOC_VALID & mdoc->last->flags ? | n = mdoc->last->flags & MDOC_VALID ? | ||
mdoc->last->parent : mdoc->last; | mdoc->last->parent : mdoc->last; | ||
for ( ; n; n = n->parent) | for ( ; n; n = n->parent) | ||
if (MDOC_BLOCK == n->type && | if (n->type == ROFFT_BLOCK && | ||
MDOC_EXPLICIT & mdoc_macros[n->tok].flags) | mdoc_macros[n->tok].flags & MDOC_EXPLICIT) | ||
mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse, | mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse, | ||
n->line, n->pos, mdoc_macronames[n->tok]); | n->line, n->pos, mdoc_macronames[n->tok]); | ||
/* Rewind to the first. */ | /* Rewind to the first. */ | ||
rew_last(mdoc, mdoc->first); | rew_last(mdoc, mdoc->first); | ||
return(1); | |||
} | } | ||
/* | /* | ||
* 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 == TOKEN_NONE. | |||
*/ | */ | ||
static enum mdoct | static int | ||
lookup(enum mdoct from, const char *p) | lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p) | ||
{ | { | ||
int res; | |||
if ( ! (MDOC_PARSED & mdoc_macros[from].flags)) | if (from == TOKEN_NONE || mdoc_macros[from].flags & MDOC_PARSED) { | ||
return(MDOC_MAX); | res = mdoc_hash_find(p); | ||
return(lookup_raw(p)); | if (res != TOKEN_NONE) { | ||
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(TOKEN_NONE); | |||
} | } | ||
/* | /* | ||
* Lookup a macro following the initial line macro. | * Rewind up to and including a specific node. | ||
*/ | */ | ||
static enum mdoct | |||
lookup_raw(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); | |||
return(MDOC_MAX); | |||
} | |||
static void | static void | ||
rew_last(struct mdoc *mdoc, const struct mdoc_node *to) | rew_last(struct roff_man *mdoc, const struct roff_node *to) | ||
{ | { | ||
struct mdoc_node *n, *np; | struct roff_node *n, *np; | ||
assert(to); | if (to->flags & MDOC_VALID) | ||
mdoc->next = MDOC_NEXT_SIBLING; | return; | ||
mdoc->next = ROFF_NEXT_SIBLING; | |||
while (mdoc->last != to) { | while (mdoc->last != to) { | ||
/* | /* | ||
* Save the parent here, because we may delete the | * Save the parent here, because we may delete the | ||
|
|
||
} | } | ||
/* | /* | ||
* Rewind up to a specific block, including all blocks that broke it. | |||
*/ | |||
static void | |||
rew_pending(struct roff_man *mdoc, const struct roff_node *n) | |||
{ | |||
for (;;) { | |||
rew_last(mdoc, n); | |||
if (mdoc->last == n) { | |||
switch (n->type) { | |||
case ROFFT_HEAD: | |||
roff_body_alloc(mdoc, n->line, n->pos, | |||
n->tok); | |||
return; | |||
case ROFFT_BLOCK: | |||
break; | |||
default: | |||
return; | |||
} | |||
if ( ! (n->flags & MDOC_BROKEN)) | |||
return; | |||
} else | |||
n = mdoc->last; | |||
for (;;) { | |||
if ((n = n->parent) == NULL) | |||
return; | |||
if (n->type == ROFFT_BLOCK || | |||
n->type == ROFFT_HEAD) { | |||
if (n->flags & MDOC_ENDED) | |||
break; | |||
else | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
/* | |||
* For a block closing macro, return the corresponding opening one. | * For a block closing macro, return the corresponding opening one. | ||
* Otherwise, return the macro itself. | * Otherwise, return the macro itself. | ||
*/ | */ | ||
static enum mdoct | static int | ||
rew_alt(enum mdoct tok) | rew_alt(int tok) | ||
{ | { | ||
switch (tok) { | switch (tok) { | ||
case MDOC_Ac: | case MDOC_Ac: | ||
|
|
||
/* NOTREACHED */ | /* NOTREACHED */ | ||
} | } | ||
/* | |||
* Rewinding to tok, how do we have to handle *p? | |||
* REWIND_NONE: *p would delimit tok, but no tok scope is open | |||
* inside *p, so there is no need to rewind anything at all. | |||
* REWIND_THIS: *p matches tok, so rewind *p and nothing else. | |||
* REWIND_MORE: *p is implicit, rewind it and keep searching for tok. | |||
* REWIND_FORCE: *p is explicit, but tok is full, force rewinding *p. | |||
* REWIND_LATER: *p is explicit and still open, postpone rewinding. | |||
* REWIND_ERROR: No tok block is open at all. | |||
*/ | |||
static enum rew | |||
rew_dohalt(enum mdoct tok, enum mdoc_type type, | |||
const struct mdoc_node *p) | |||
{ | |||
/* | |||
* No matching token, no delimiting block, no broken block. | |||
* This can happen when full implicit macros are called for | |||
* the first time but try to rewind their previous | |||
* instance anyway. | |||
*/ | |||
if (MDOC_ROOT == p->type) | |||
return(MDOC_BLOCK == type && | |||
MDOC_EXPLICIT & mdoc_macros[tok].flags ? | |||
REWIND_ERROR : REWIND_NONE); | |||
/* | |||
* When starting to rewind, skip plain text | |||
* and nodes that have already been rewound. | |||
*/ | |||
if (MDOC_TEXT == p->type || MDOC_VALID & p->flags) | |||
return(REWIND_MORE); | |||
/* | |||
* The easiest case: Found a matching token. | |||
* This applies to both blocks and elements. | |||
*/ | |||
tok = rew_alt(tok); | |||
if (tok == p->tok) | |||
return(p->end ? REWIND_NONE : | |||
type == p->type ? REWIND_THIS : REWIND_MORE); | |||
/* | |||
* While elements do require rewinding for themselves, | |||
* they never affect rewinding of other nodes. | |||
*/ | |||
if (MDOC_ELEM == p->type) | |||
return(REWIND_MORE); | |||
/* | |||
* Blocks delimited by our target token get REWIND_MORE. | |||
* Blocks delimiting our target token get REWIND_NONE. | |||
*/ | |||
switch (tok) { | |||
case MDOC_Bl: | |||
if (MDOC_It == p->tok) | |||
return(REWIND_MORE); | |||
break; | |||
case MDOC_It: | |||
if (MDOC_BODY == p->type && MDOC_Bl == p->tok) | |||
return(REWIND_NONE); | |||
break; | |||
/* | |||
* XXX Badly nested block handling still fails badly | |||
* when one block is breaking two blocks of the same type. | |||
* This is an incomplete and extremely ugly workaround, | |||
* required to let the OpenBSD tree build. | |||
*/ | |||
case MDOC_Oo: | |||
if (MDOC_Op == p->tok) | |||
return(REWIND_MORE); | |||
break; | |||
case MDOC_Nm: | |||
return(REWIND_NONE); | |||
case MDOC_Nd: | |||
/* FALLTHROUGH */ | |||
case MDOC_Ss: | |||
if (MDOC_BODY == p->type && MDOC_Sh == p->tok) | |||
return(REWIND_NONE); | |||
/* FALLTHROUGH */ | |||
case MDOC_Sh: | |||
if (MDOC_ROOT == p->parent->type) | |||
return(REWIND_THIS); | |||
if (MDOC_Nd == p->tok || MDOC_Ss == p->tok || | |||
MDOC_Sh == p->tok) | |||
return(REWIND_MORE); | |||
break; | |||
default: | |||
break; | |||
} | |||
/* | |||
* Default block rewinding rules. | |||
* In particular, always skip block end markers, | |||
* and let all blocks rewind Nm children. | |||
* Do not warn again when closing a block, | |||
* since closing the body already warned. | |||
*/ | |||
if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok || | |||
MDOC_BLOCK == type || (MDOC_BLOCK == p->type && | |||
! (MDOC_EXPLICIT & mdoc_macros[tok].flags))) | |||
return(REWIND_MORE); | |||
/* | |||
* By default, closing out full blocks | |||
* forces closing of broken explicit blocks, | |||
* while closing out partial blocks | |||
* allows delayed rewinding by default. | |||
*/ | |||
return (&blk_full == mdoc_macros[tok].fp ? | |||
REWIND_FORCE : REWIND_LATER); | |||
} | |||
static void | static void | ||
rew_elem(struct mdoc *mdoc, enum mdoct tok) | rew_elem(struct roff_man *mdoc, int tok) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
n = mdoc->last; | n = mdoc->last; | ||
if (MDOC_ELEM != n->type) | if (n->type != ROFFT_ELEM) | ||
n = n->parent; | n = n->parent; | ||
assert(MDOC_ELEM == n->type); | assert(n->type == ROFFT_ELEM); | ||
assert(tok == n->tok); | assert(tok == n->tok); | ||
rew_last(mdoc, n); | rew_last(mdoc, n); | ||
} | } | ||
/* | /* | ||
* We are trying to close a block identified by tok, | * If there is an open sub-block of the target requiring | ||
* but the child block *broken is still open. | * explicit close-out, postpone closing out the target until | ||
* Thus, postpone closing the tok block | * the rew_pending() call closing out the sub-block. | ||
* until the rew_sub call closing *broken. | |||
*/ | */ | ||
static int | static int | ||
make_pending(struct mdoc_node *broken, enum mdoct tok, | find_pending(struct roff_man *mdoc, int tok, int line, int ppos, | ||
struct mdoc *mdoc, int line, int ppos) | struct roff_node *target) | ||
{ | { | ||
struct mdoc_node *breaker; | struct roff_node *n; | ||
int irc; | |||
/* | irc = 0; | ||
* Iterate backwards, searching for the block matching tok, | for (n = mdoc->last; n != NULL && n != target; n = n->parent) { | ||
* that is, the block breaking the *broken block. | if (n->flags & MDOC_ENDED) { | ||
*/ | if ( ! (n->flags & MDOC_VALID)) | ||
for (breaker = broken->parent; breaker; breaker = breaker->parent) { | n->flags |= MDOC_BROKEN; | ||
/* | |||
* 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 (breaker == broken->pending) { | |||
broken = breaker; | |||
continue; | continue; | ||
} | } | ||
if (n->type == ROFFT_BLOCK && | |||
if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker)) | mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { | ||
continue; | irc = 1; | ||
if (MDOC_BODY == broken->type) | n->flags = MDOC_BROKEN; | ||
broken = broken->parent; | if (target->type == ROFFT_HEAD) | ||
target->flags = MDOC_ENDED; | |||
/* | else if ( ! (target->flags & MDOC_ENDED)) { | ||
* Found the breaker. | mandoc_vmsg(MANDOCERR_BLK_NEST, | ||
* If another, outer breaker is already pending on | mdoc->parse, line, ppos, | ||
* the *broken block, we must not clobber the link | "%s breaks %s", mdoc_macronames[tok], | ||
* to the outer breaker, but make it pending on the | mdoc_macronames[n->tok]); | ||
* new, now inner breaker. | mdoc_endbody_alloc(mdoc, line, ppos, | ||
* Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]" | tok, target, ENDBODY_NOSPACE); | ||
* becomes "[A breaker=[B->A broken=[C A] tok=B] C]" | } | ||
* and finally "[A [B->A [C->B A] B] C]". | |||
*/ | |||
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]" | |||
*/ | |||
taker = breaker; | |||
while (taker->pending) | |||
taker = taker->pending; | |||
taker->pending = broken->pending; | |||
} | } | ||
broken->pending = breaker; | |||
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos, | |||
"%s breaks %s", mdoc_macronames[tok], | |||
mdoc_macronames[broken->tok]); | |||
return(1); | |||
} | } | ||
return(irc); | |||
/* | |||
* Found no matching block for tok. | |||
* Are you trying to close a block that is not open? | |||
*/ | |||
return(0); | |||
} | } | ||
static void | |||
rew_sub(enum mdoc_type t, struct mdoc *mdoc, | |||
enum mdoct tok, int line, int ppos) | |||
{ | |||
struct mdoc_node *n; | |||
n = mdoc->last; | |||
while (n) { | |||
switch (rew_dohalt(tok, t, n)) { | |||
case REWIND_NONE: | |||
return; | |||
case REWIND_THIS: | |||
n->lastline = line - | |||
(mdoc->flags & MDOC_NEWLINE && | |||
! (mdoc_macros[tok].flags & MDOC_EXPLICIT)); | |||
break; | |||
case REWIND_FORCE: | |||
mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, | |||
line, ppos, "%s breaks %s", | |||
mdoc_macronames[tok], | |||
mdoc_macronames[n->tok]); | |||
/* FALLTHROUGH */ | |||
case REWIND_MORE: | |||
n->lastline = line - | |||
(mdoc->flags & MDOC_NEWLINE ? 1 : 0); | |||
n = n->parent; | |||
continue; | |||
case REWIND_LATER: | |||
if (make_pending(n, tok, mdoc, line, ppos) || | |||
t != MDOC_BLOCK) | |||
return; | |||
/* FALLTHROUGH */ | |||
case REWIND_ERROR: | |||
mandoc_msg(MANDOCERR_BLK_NOTOPEN, | |||
mdoc->parse, line, ppos, | |||
mdoc_macronames[tok]); | |||
return; | |||
} | |||
break; | |||
} | |||
assert(n); | |||
rew_last(mdoc, n); | |||
/* | |||
* The current block extends an enclosing block. | |||
* Now that the current block ends, close the enclosing block, too. | |||
*/ | |||
while ((n = n->pending) != NULL) { | |||
rew_last(mdoc, n); | |||
if (n->type == MDOC_HEAD) | |||
mdoc_body_alloc(mdoc, n->line, n->pos, n->tok); | |||
} | |||
} | |||
/* | /* | ||
* Allocate a word and check whether it's punctuation or not. | * Allocate a word and check whether it's punctuation or not. | ||
* Punctuation consists of those tokens found in mdoc_isdelim(). | * Punctuation consists of those tokens found in mdoc_isdelim(). | ||
*/ | */ | ||
static void | static void | ||
dword(struct mdoc *mdoc, int line, int col, const char *p, | dword(struct roff_man *mdoc, int line, int col, const char *p, | ||
enum mdelim d, int may_append) | enum mdelim d, int may_append) | ||
{ | { | ||
|
|
||
if (may_append && | if (may_append && | ||
! (mdoc->flags & (MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF)) && | ! (mdoc->flags & (MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF)) && | ||
d == DELIM_NONE && mdoc->last->type == MDOC_TEXT && | d == DELIM_NONE && mdoc->last->type == ROFFT_TEXT && | ||
mdoc_isdelim(mdoc->last->string) == DELIM_NONE) { | mdoc_isdelim(mdoc->last->string) == DELIM_NONE) { | ||
mdoc_word_append(mdoc, p); | roff_word_append(mdoc, p); | ||
return; | return; | ||
} | } | ||
mdoc_word_alloc(mdoc, line, col, p); | roff_word_alloc(mdoc, line, col, p); | ||
/* | /* | ||
* If the word consists of a bare delimiter, | * If the word consists of a bare delimiter, | ||
|
|
||
} | } | ||
static void | static void | ||
append_delims(struct mdoc *mdoc, int line, int *pos, char *buf) | append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) | ||
{ | { | ||
int la; | |||
enum margserr ac; | |||
char *p; | char *p; | ||
int la; | |||
if (buf[*pos] == '\0') | if (buf[*pos] == '\0') | ||
return; | return; | ||
for (;;) { | for (;;) { | ||
la = *pos; | la = *pos; | ||
ac = mdoc_zargs(mdoc, line, pos, buf, &p); | if (mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p) == | ||
if (ac == ARGS_EOLN) | ARGS_EOLN) | ||
break; | break; | ||
dword(mdoc, line, la, p, DELIM_MAX, 1); | dword(mdoc, line, la, p, DELIM_MAX, 1); | ||
|
|
||
} | } | ||
/* | /* | ||
* Close out block partial/full explicit. | * Parse one word. | ||
* If it is a macro, call it and return 1. | |||
* Otherwise, allocate it and return 0. | |||
*/ | */ | ||
static int | static int | ||
macro_or_word(MACRO_PROT_ARGS, int parsed) | |||
{ | |||
char *p; | |||
int ntok; | |||
p = buf + ppos; | |||
ntok = TOKEN_NONE; | |||
if (*p == '"') | |||
p++; | |||
else if (parsed && ! (mdoc->flags & MDOC_PHRASELIT)) | |||
ntok = lookup(mdoc, tok, line, ppos, p); | |||
if (ntok == TOKEN_NONE) { | |||
dword(mdoc, line, ppos, p, DELIM_MAX, tok == TOKEN_NONE || | |||
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 == TOKEN_NONE) | |||
append_delims(mdoc, line, pos, buf); | |||
return(1); | |||
} | |||
} | |||
/* | |||
* Close out block partial/full explicit. | |||
*/ | |||
static void | |||
blk_exp_close(MACRO_PROT_ARGS) | blk_exp_close(MACRO_PROT_ARGS) | ||
{ | { | ||
struct mdoc_node *body; /* Our own body. */ | struct roff_node *body; /* Our own body. */ | ||
struct mdoc_node *endbody; /* Our own end marker. */ | struct roff_node *endbody; /* Our own end marker. */ | ||
struct mdoc_node *later; /* A sub-block starting later. */ | struct roff_node *itblk; /* An It block starting later. */ | ||
struct mdoc_node *n; /* For searching backwards. */ | struct roff_node *later; /* A sub-block starting later. */ | ||
struct roff_node *n; /* Search back to our block. */ | |||
struct roff_node *target; /* For find_pending(). */ | |||
int j, lastarg, maxargs, flushed, nl; | int j, lastarg, maxargs, nl, pending; | ||
enum margserr ac; | enum margserr ac; | ||
enum mdoct atok, ntok; | int atok, ntok; | ||
char *p; | char *p; | ||
nl = MDOC_NEWLINE & mdoc->flags; | nl = MDOC_NEWLINE & mdoc->flags; | ||
|
|
||
break; | break; | ||
case MDOC_Ek: | case MDOC_Ek: | ||
mdoc->flags &= ~MDOC_KEEP; | mdoc->flags &= ~MDOC_KEEP; | ||
/* FALLTHROUGH */ | |||
default: | default: | ||
maxargs = 0; | maxargs = 0; | ||
break; | break; | ||
|
|
||
* Search backwards for beginnings of blocks, | * Search backwards for beginnings of blocks, | ||
* both of our own and of pending sub-blocks. | * both of our own and of pending sub-blocks. | ||
*/ | */ | ||
atok = rew_alt(tok); | atok = rew_alt(tok); | ||
body = endbody = later = NULL; | body = endbody = itblk = later = NULL; | ||
for (n = mdoc->last; n; n = n->parent) { | for (n = mdoc->last; n; n = n->parent) { | ||
if (n->flags & MDOC_VALID) | if (n->flags & MDOC_ENDED) { | ||
if ( ! (n->flags & MDOC_VALID)) | |||
n->flags |= MDOC_BROKEN; | |||
continue; | continue; | ||
} | |||
/* Remember the start of our own body. */ | /* Remember the start of our own body. */ | ||
if (n->type == MDOC_BODY && atok == n->tok) { | |||
if (n->type == ROFFT_BODY && atok == n->tok) { | |||
if (n->end == ENDBODY_NOT) | if (n->end == ENDBODY_NOT) | ||
body = n; | body = n; | ||
continue; | continue; | ||
} | } | ||
if (n->type != MDOC_BLOCK || n->tok == MDOC_Nm) | if (n->type != ROFFT_BLOCK || n->tok == MDOC_Nm) | ||
continue; | continue; | ||
if (n->tok == MDOC_It) { | |||
itblk = n; | |||
continue; | |||
} | |||
if (atok == n->tok) { | if (atok == n->tok) { | ||
assert(body); | assert(body); | ||
|
|
||
* When there is no pending sub block, | * When there is no pending sub block, | ||
* just proceed to closing out. | * just proceed to closing out. | ||
*/ | */ | ||
if (later == NULL) | |||
if (later == NULL || | |||
(tok == MDOC_El && itblk == NULL)) | |||
break; | break; | ||
/* | /* | ||
* When there is a pending sub block, | * When there is a pending sub block, postpone | ||
* postpone closing out the current block | * closing out the current block until the | ||
* until the rew_sub() closing out the sub-block. | * rew_pending() closing out the sub-block. | ||
*/ | |||
make_pending(later, tok, mdoc, line, ppos); | |||
/* | |||
* Mark the place where the formatting - but not | * Mark the place where the formatting - but not | ||
* the scope - of the current block ends. | * the scope - of the current block ends. | ||
*/ | */ | ||
mdoc_endbody_alloc(mdoc, line, ppos, | |||
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, | |||
line, ppos, "%s breaks %s", | |||
mdoc_macronames[atok], | |||
mdoc_macronames[later->tok]); | |||
endbody = mdoc_endbody_alloc(mdoc, line, ppos, | |||
atok, body, ENDBODY_SPACE); | atok, body, ENDBODY_SPACE); | ||
if (maxargs) { | |||
endbody = mdoc->last; | if (tok == MDOC_El) | ||
mdoc->next = MDOC_NEXT_CHILD; | itblk->flags |= MDOC_ENDED | MDOC_BROKEN; | ||
} | |||
/* | |||
* If a block closing macro taking arguments | |||
* breaks another block, put the arguments | |||
* into the end marker. | |||
*/ | |||
if (maxargs) | |||
mdoc->next = ROFF_NEXT_CHILD; | |||
break; | break; | ||
} | } | ||
/* | /* Explicit blocks close out description lines. */ | ||
* When finding an open sub block, remember the last | |||
* open explicit block, or, in case there are only | if (n->tok == MDOC_Nd) { | ||
* implicit ones, the first open implicit block. | rew_last(mdoc, n); | ||
*/ | |||
if (later && | |||
mdoc_macros[later->tok].flags & MDOC_EXPLICIT) | |||
continue; | continue; | ||
if (n->tok != MDOC_It) | } | ||
/* Breaking an open sub block. */ | |||
n->flags |= MDOC_BROKEN; | |||
if (later == NULL) | |||
later = n; | later = n; | ||
} | } | ||
if (body == NULL) { | |||
mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse, | |||
line, ppos, mdoc_macronames[tok]); | |||
if (later != NULL) | |||
later->flags &= ~MDOC_BROKEN; | |||
if (maxargs && endbody == NULL) { | |||
/* | |||
* Stray .Ec without previous .Eo: | |||
* Break the output line, keep the arguments. | |||
*/ | |||
roff_elem_alloc(mdoc, line, ppos, MDOC_br); | |||
rew_elem(mdoc, MDOC_br); | |||
} | |||
} else if (endbody == NULL) { | |||
rew_last(mdoc, body); | |||
if (maxargs) | |||
mdoc_tail_alloc(mdoc, line, ppos, atok); | |||
} | |||
if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) { | if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) { | ||
if (buf[*pos] != '\0') | if (buf[*pos] != '\0') | ||
mandoc_vmsg(MANDOCERR_ARG_SKIP, | mandoc_vmsg(MANDOCERR_ARG_SKIP, | ||
mdoc->parse, line, ppos, | mdoc->parse, line, ppos, | ||
"%s %s", mdoc_macronames[tok], | "%s %s", mdoc_macronames[tok], | ||
buf + *pos); | buf + *pos); | ||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | if (endbody == NULL && n != NULL) | ||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | rew_pending(mdoc, n); | ||
return(1); | return; | ||
} | } | ||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | |||
if (maxargs && endbody == NULL) { | if (endbody != NULL) | ||
if (n == NULL) { | n = endbody; | ||
/* | |||
* Stray .Ec without previous .Eo: | |||
* Break the output line, ignore any arguments. | |||
*/ | |||
mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL); | |||
rew_elem(mdoc, MDOC_br); | |||
} else | |||
mdoc_tail_alloc(mdoc, line, ppos, atok); | |||
} | |||
flushed = n == NULL; | |||
for (j = 0; ; j++) { | for (j = 0; ; j++) { | ||
lastarg = *pos; | lastarg = *pos; | ||
if (j == maxargs && ! flushed) { | if (j == maxargs && n != NULL) | ||
if (endbody == NULL) | rew_last(mdoc, n); | ||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | |||
else | |||
rew_last(mdoc, endbody); | |||
flushed = 1; | |||
} | |||
ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ||
if (ac == ARGS_PUNCT || ac == ARGS_EOLN) | if (ac == ARGS_PUNCT || ac == ARGS_EOLN) | ||
break; | break; | ||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p); | ntok = ac == ARGS_QWORD ? TOKEN_NONE : | ||
lookup(mdoc, tok, line, lastarg, p); | |||
if (ntok == MDOC_MAX) { | if (ntok == TOKEN_NONE) { | ||
dword(mdoc, line, lastarg, p, DELIM_MAX, | dword(mdoc, line, lastarg, p, DELIM_MAX, | ||
MDOC_JOIN & mdoc_macros[tok].flags); | MDOC_JOIN & mdoc_macros[tok].flags); | ||
continue; | continue; | ||
} | } | ||
if ( ! flushed) { | if (n != NULL) | ||
if (endbody == NULL) | rew_last(mdoc, n); | ||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | |||
else | |||
rew_last(mdoc, endbody); | |||
flushed = 1; | |||
} | |||
mdoc->flags &= ~MDOC_NEWLINE; | mdoc->flags &= ~MDOC_NEWLINE; | ||
mdoc_macro(mdoc, ntok, line, lastarg, pos, buf); | |||
if ( ! mdoc_macro(mdoc, ntok, line, lastarg, pos, buf)) | |||
return(0); | |||
break; | break; | ||
} | } | ||
if ( ! flushed) { | if (n != NULL) { | ||
if (endbody == NULL) | if (n != mdoc->last && n->flags & MDOC_BROKEN) { | ||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | target = n; | ||
else | do | ||
rew_last(mdoc, endbody); | target = target->parent; | ||
while ( ! (target->flags & MDOC_ENDED)); | |||
pending = find_pending(mdoc, ntok, line, ppos, | |||
target); | |||
} else | |||
pending = 0; | |||
if ( ! pending) | |||
rew_pending(mdoc, n); | |||
} | } | ||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
return(1); | |||
} | } | ||
static int | static void | ||
in_line(MACRO_PROT_ARGS) | in_line(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, scope, cnt, firstarg, mayopen, nc, nl; | int la, scope, cnt, firstarg, mayopen, nc, nl; | ||
enum margverr av; | int ntok; | ||
enum mdoct ntok; | |||
enum margserr ac; | enum margserr ac; | ||
enum mdelim d; | enum mdelim d; | ||
struct mdoc_arg *arg; | struct mdoc_arg *arg; | ||
|
|
||
break; | break; | ||
} | } | ||
for (arg = NULL;; ) { | mdoc_argv(mdoc, line, tok, &arg, pos, buf); | ||
la = *pos; | |||
av = mdoc_argv(mdoc, line, tok, &arg, pos, buf); | |||
if (ARGV_WORD == av) { | |||
*pos = la; | |||
break; | |||
} | |||
if (ARGV_EOLN == av) | |||
break; | |||
if (ARGV_ARG == av) | |||
continue; | |||
mdoc_argv_free(arg); | |||
return(0); | |||
} | |||
d = DELIM_NONE; | d = DELIM_NONE; | ||
firstarg = 1; | firstarg = 1; | ||
mayopen = 1; | mayopen = 1; | ||
|
|
||
*/ | */ | ||
if (ac == ARGS_PUNCT) { | if (ac == ARGS_PUNCT) { | ||
if (cnt == 0 && nc == 0) | if (cnt == 0 && (nc == 0 || tok == MDOC_An)) | ||
mdoc->flags |= MDOC_NODELIMC; | mdoc->flags |= MDOC_NODELIMC; | ||
break; | break; | ||
} | } | ||
ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ? | ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ? | ||
MDOC_MAX : lookup(tok, p); | TOKEN_NONE : lookup(mdoc, tok, line, la, p); | ||
/* | /* | ||
* In this case, we've located a submacro and must | * In this case, we've located a submacro and must | ||
|
|
||
* or raise a warning. | * or raise a warning. | ||
*/ | */ | ||
if (ntok != MDOC_MAX) { | if (ntok != TOKEN_NONE) { | ||
if (scope) | if (scope) | ||
rew_elem(mdoc, tok); | rew_elem(mdoc, tok); | ||
if (nc && ! cnt) { | if (nc && ! cnt) { | ||
|
|
||
mdoc->parse, line, ppos, | mdoc->parse, line, ppos, | ||
mdoc_macronames[tok]); | mdoc_macronames[tok]); | ||
} | } | ||
mdoc_macro(mdoc, ntok, line, la, pos, buf); | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | |||
return(0); | |||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
return(1); | return; | ||
} | } | ||
/* | /* | ||
|
|
||
} | } | ||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
return(1); | |||
} | } | ||
static int | static void | ||
blk_full(MACRO_PROT_ARGS) | blk_full(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, nl, nparsed; | int la, nl, parsed; | ||
struct mdoc_arg *arg; | struct mdoc_arg *arg; | ||
struct mdoc_node *head; /* save of head macro */ | struct roff_node *blk; /* Our own or a broken block. */ | ||
struct mdoc_node *body; /* save of body macro */ | struct roff_node *head; /* Our own head. */ | ||
struct mdoc_node *n; | struct roff_node *body; /* Our own body. */ | ||
enum mdoct ntok; | struct roff_node *n; | ||
enum margserr ac, lac; | enum margserr ac, lac; | ||
enum margverr av; | |||
char *p; | char *p; | ||
nl = MDOC_NEWLINE & mdoc->flags; | nl = MDOC_NEWLINE & mdoc->flags; | ||
/* Skip items outside lists. */ | if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | |||
line, ppos, mdoc_macronames[tok]); | |||
return; | |||
} | |||
if (tok == MDOC_It) { | if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) { | ||
for (n = mdoc->last; n; n = n->parent) | |||
if (n->tok == MDOC_Bl && | /* Here, tok is one of Sh Ss Nm Nd It. */ | ||
! (n->flags & MDOC_VALID)) | |||
blk = NULL; | |||
for (n = mdoc->last; n != NULL; n = n->parent) { | |||
if (n->flags & MDOC_ENDED) { | |||
if ( ! (n->flags & MDOC_VALID)) | |||
n->flags |= MDOC_BROKEN; | |||
continue; | |||
} | |||
if (n->type != ROFFT_BLOCK) | |||
continue; | |||
if (tok == MDOC_It && n->tok == MDOC_Bl) { | |||
if (blk != NULL) { | |||
mandoc_vmsg(MANDOCERR_BLK_BROKEN, | |||
mdoc->parse, line, ppos, | |||
"It breaks %s", | |||
mdoc_macronames[blk->tok]); | |||
rew_pending(mdoc, blk); | |||
} | |||
break; | break; | ||
if (n == NULL) { | } | ||
if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { | |||
switch (tok) { | |||
case MDOC_Sh: | |||
/* FALLTHROUGH */ | |||
case MDOC_Ss: | |||
mandoc_vmsg(MANDOCERR_BLK_BROKEN, | |||
mdoc->parse, line, ppos, | |||
"%s breaks %s", | |||
mdoc_macronames[tok], | |||
mdoc_macronames[n->tok]); | |||
rew_pending(mdoc, n); | |||
n = mdoc->last; | |||
continue; | |||
case MDOC_It: | |||
/* Delay in case it's astray. */ | |||
blk = n; | |||
continue; | |||
default: | |||
break; | |||
} | |||
break; | |||
} | |||
/* Here, n is one of Sh Ss Nm Nd It. */ | |||
if (tok != MDOC_Sh && (n->tok == MDOC_Sh || | |||
(tok != MDOC_Ss && (n->tok == MDOC_Ss || | |||
(tok != MDOC_It && n->tok == MDOC_It))))) | |||
break; | |||
/* Item breaking an explicit block. */ | |||
if (blk != NULL) { | |||
mandoc_vmsg(MANDOCERR_BLK_BROKEN, | |||
mdoc->parse, line, ppos, | |||
"It breaks %s", | |||
mdoc_macronames[blk->tok]); | |||
rew_pending(mdoc, blk); | |||
blk = NULL; | |||
} | |||
/* Close out prior implicit scopes. */ | |||
rew_last(mdoc, n); | |||
} | |||
/* Skip items outside lists. */ | |||
if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { | |||
mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, | mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, | ||
line, ppos, "It %s", buf + *pos); | line, ppos, "It %s", buf + *pos); | ||
mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL); | roff_elem_alloc(mdoc, line, ppos, MDOC_br); | ||
rew_elem(mdoc, MDOC_br); | rew_elem(mdoc, MDOC_br); | ||
return(1); | return; | ||
} | } | ||
} | } | ||
/* Close out prior implicit scope. */ | |||
if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) { | |||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | |||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | |||
} | |||
/* | /* | ||
* This routine accommodates implicitly- and explicitly-scoped | * This routine accommodates implicitly- and explicitly-scoped | ||
* macro openings. Implicit ones first close out prior scope | * macro openings. Implicit ones first close out prior scope | ||
|
|
||
* regular child nodes. | * regular child nodes. | ||
*/ | */ | ||
for (arg = NULL;; ) { | mdoc_argv(mdoc, line, tok, &arg, pos, buf); | ||
la = *pos; | blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg); | ||
av = mdoc_argv(mdoc, line, tok, &arg, pos, buf); | |||
if (ARGV_WORD == av) { | |||
*pos = la; | |||
break; | |||
} | |||
if (ARGV_EOLN == av) | |||
break; | |||
if (ARGV_ARG == av) | |||
continue; | |||
mdoc_argv_free(arg); | |||
return(0); | |||
} | |||
mdoc_block_alloc(mdoc, line, ppos, tok, arg); | |||
head = body = NULL; | head = body = NULL; | ||
/* | /* | ||
* Exception: Heads of `It' macros in `-diag' lists are not | * Exception: Heads of `It' macros in `-diag' lists are not | ||
* parsed, even though `It' macros in general are parsed. | * 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 | * The `Nd' macro has all arguments in its body: it's a hybrid | ||
* of block partial-explicit and full-implicit. Stupid. | * of block partial-explicit and full-implicit. Stupid. | ||
*/ | */ | ||
if (tok == MDOC_Nd) { | if (tok == MDOC_Nd) { | ||
head = mdoc_head_alloc(mdoc, line, ppos, tok); | head = roff_head_alloc(mdoc, line, ppos, tok); | ||
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos); | rew_last(mdoc, head); | ||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | body = roff_body_alloc(mdoc, line, ppos, tok); | ||
} | } | ||
if (tok == MDOC_Bk) | if (tok == MDOC_Bk) | ||
|
|
||
la = *pos; | la = *pos; | ||
lac = ac; | lac = ac; | ||
ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ||
if (ac == ARGS_PUNCT) | |||
break; | |||
if (ac == ARGS_EOLN) { | if (ac == ARGS_EOLN) { | ||
if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE) | if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE) | ||
break; | break; | ||
|
|
||
* reopen our scope if the last parse was a | * reopen our scope if the last parse was a | ||
* phrase or partial phrase. | * phrase or partial phrase. | ||
*/ | */ | ||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | if (body != NULL) | ||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | rew_last(mdoc, body); | ||
body = roff_body_alloc(mdoc, line, ppos, tok); | |||
break; | break; | ||
} | } | ||
if (tok == MDOC_Bd || tok == MDOC_Bk) { | |||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, | |||
mdoc->parse, line, la, "%s ... %s", | |||
mdoc_macronames[tok], buf + la); | |||
break; | |||
} | |||
if (tok == MDOC_Rs) { | |||
mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, | |||
line, la, "Rs %s", buf + la); | |||
break; | |||
} | |||
if (ac == ARGS_PUNCT) | |||
break; | |||
/* | /* | ||
* Emit leading punctuation (i.e., punctuation before | * Emit leading punctuation (i.e., punctuation before | ||
* the MDOC_HEAD) for non-phrase types. | * the ROFFT_HEAD) for non-phrase types. | ||
*/ | */ | ||
if (head == NULL && | if (head == NULL && | ||
|
|
||
/* Open a head if one hasn't been opened. */ | /* Open a head if one hasn't been opened. */ | ||
if (head == NULL) | if (head == NULL) | ||
head = mdoc_head_alloc(mdoc, line, ppos, tok); | head = roff_head_alloc(mdoc, line, ppos, tok); | ||
if (ac == ARGS_PHRASE || | if (ac == ARGS_PHRASE || | ||
ac == ARGS_PEND || | ac == ARGS_PEND || | ||
ac == ARGS_PPHRASE) { | ac == ARGS_PPHRASE) { | ||
/* | /* | ||
* If we haven't opened a body yet, rewind the | * If we haven't opened a body yet, rewind the | ||
* head; if we have, rewind that instead. | * head; if we have, rewind that instead. | ||
*/ | */ | ||
rew_sub(body ? MDOC_BODY : MDOC_HEAD, | rew_last(mdoc, body == NULL ? head : body); | ||
mdoc, tok, line, ppos); | body = roff_body_alloc(mdoc, line, ppos, tok); | ||
/* Then allocate our body context. */ | |||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | |||
/* | /* | ||
* Process phrases: set whether we're in a | * Process phrases: set whether we're in a | ||
* partial-phrase (this effects line handling) | * partial-phrase (this effects line handling) | ||
|
|
||
mdoc->flags |= MDOC_PPHRASE; | mdoc->flags |= MDOC_PPHRASE; | ||
if (ac == ARGS_PEND && lac == ARGS_PPHRASE) | if (ac == ARGS_PEND && lac == ARGS_PPHRASE) | ||
mdoc->flags |= MDOC_PPHRASE; | mdoc->flags |= MDOC_PPHRASE; | ||
parse_rest(mdoc, TOKEN_NONE, line, &la, buf); | |||
if ( ! phrase(mdoc, line, la, buf)) | |||
return(0); | |||
mdoc->flags &= ~MDOC_PPHRASE; | mdoc->flags &= ~MDOC_PPHRASE; | ||
continue; | continue; | ||
} | } | ||
ntok = nparsed || ac == ARGS_QWORD ? | if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed)) | ||
MDOC_MAX : lookup(tok, p); | break; | ||
if (ntok == MDOC_MAX) { | |||
dword(mdoc, line, la, p, DELIM_MAX, | |||
MDOC_JOIN & mdoc_macros[tok].flags); | |||
continue; | |||
} | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | |||
return(0); | |||
break; | |||
} | } | ||
if (blk->flags & MDOC_VALID) | |||
return; | |||
if (head == NULL) | if (head == NULL) | ||
head = mdoc_head_alloc(mdoc, line, ppos, tok); | head = roff_head_alloc(mdoc, line, ppos, tok); | ||
if (nl) | if (nl && tok != MDOC_Bd && tok != MDOC_Bl && tok != MDOC_Rs) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
if (body != NULL) | |||
/* If we've already opened our body, exit now. */ | |||
if (NULL != body) | |||
goto out; | goto out; | ||
if (find_pending(mdoc, tok, line, ppos, head)) | |||
return; | |||
/* | |||
* If there is an open (i.e., unvalidated) sub-block requiring | |||
* explicit close-out, postpone switching the current block from | |||
* head to body until the rew_sub() call closing out that | |||
* sub-block. | |||
*/ | |||
for (n = mdoc->last; n && n != head; n = n->parent) { | |||
if (MDOC_BLOCK == n->type && | |||
MDOC_EXPLICIT & mdoc_macros[n->tok].flags && | |||
! (MDOC_VALID & n->flags)) { | |||
n->pending = head; | |||
return(1); | |||
} | |||
} | |||
/* Close out scopes to remain in a consistent state. */ | /* Close out scopes to remain in a consistent state. */ | ||
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos); | rew_last(mdoc, head); | ||
mdoc_body_alloc(mdoc, line, ppos, tok); | body = roff_body_alloc(mdoc, line, ppos, tok); | ||
out: | out: | ||
if (mdoc->flags & MDOC_FREECOL) { | if (mdoc->flags & MDOC_FREECOL) { | ||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | rew_last(mdoc, body); | ||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | rew_last(mdoc, blk); | ||
mdoc->flags &= ~MDOC_FREECOL; | mdoc->flags &= ~MDOC_FREECOL; | ||
} | } | ||
return(1); | |||
} | } | ||
static int | static void | ||
blk_part_imp(MACRO_PROT_ARGS) | blk_part_imp(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, nl; | int la, nl; | ||
enum mdoct ntok; | |||
enum margserr ac; | enum margserr ac; | ||
char *p; | char *p; | ||
struct mdoc_node *blk; /* saved block context */ | struct roff_node *blk; /* saved block context */ | ||
struct mdoc_node *body; /* saved body context */ | struct roff_node *body; /* saved body context */ | ||
struct mdoc_node *n; | struct roff_node *n; | ||
nl = MDOC_NEWLINE & mdoc->flags; | nl = MDOC_NEWLINE & mdoc->flags; | ||
|
|
||
*/ | */ | ||
blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL); | blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL); | ||
mdoc_head_alloc(mdoc, line, ppos, tok); | rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); | ||
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos); | |||
/* | /* | ||
* Open the body scope "on-demand", that is, after we've | * Open the body scope "on-demand", that is, after we've | ||
|
|
||
} | } | ||
if (body == NULL) | if (body == NULL) | ||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | body = roff_body_alloc(mdoc, line, ppos, tok); | ||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p); | if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) | ||
break; | |||
if (ntok == MDOC_MAX) { | |||
dword(mdoc, line, la, p, DELIM_MAX, | |||
MDOC_JOIN & mdoc_macros[tok].flags); | |||
continue; | |||
} | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | |||
return(0); | |||
break; | |||
} | } | ||
/* Clean-ups to leave in a consistent state. */ | |||
if (body == NULL) | if (body == NULL) | ||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | body = roff_body_alloc(mdoc, line, ppos, tok); | ||
/* | if (find_pending(mdoc, tok, line, ppos, body)) | ||
* If there is an open sub-block requiring explicit close-out, | return; | ||
* postpone closing out the current block | |||
* until the rew_sub() call closing out the sub-block. | |||
*/ | |||
for (n = mdoc->last; n && n != body && n != blk->parent; | |||
n = n->parent) { | |||
if (n->type == MDOC_BLOCK && | |||
mdoc_macros[n->tok].flags & MDOC_EXPLICIT && | |||
! (n->flags & MDOC_VALID)) { | |||
make_pending(n, tok, mdoc, line, ppos); | |||
mdoc_endbody_alloc(mdoc, line, ppos, | |||
tok, body, ENDBODY_NOSPACE); | |||
return(1); | |||
} | |||
} | |||
assert(n == body); | |||
rew_sub(MDOC_BODY, mdoc, tok, line, ppos); | |||
/* Standard appending of delimiters. */ | rew_last(mdoc, body); | ||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
rew_pending(mdoc, blk); | |||
/* Rewind scope, if applicable. */ | |||
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos); | |||
/* Move trailing .Ns out of scope. */ | /* Move trailing .Ns out of scope. */ | ||
for (n = body->child; n && n->next; n = n->next) | for (n = body->child; n && n->next; n = n->next) | ||
/* Do nothing. */ ; | /* Do nothing. */ ; | ||
if (n && n->tok == MDOC_Ns) | if (n && n->tok == MDOC_Ns) | ||
mdoc_node_relink(mdoc, n); | mdoc_node_relink(mdoc, n); | ||
return(1); | |||
} | } | ||
static int | static void | ||
blk_part_exp(MACRO_PROT_ARGS) | blk_part_exp(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, nl; | int la, nl; | ||
enum margserr ac; | enum margserr ac; | ||
struct mdoc_node *head; /* keep track of head */ | struct roff_node *head; /* keep track of head */ | ||
struct mdoc_node *body; /* keep track of body */ | |||
char *p; | char *p; | ||
enum mdoct ntok; | |||
nl = MDOC_NEWLINE & mdoc->flags; | nl = MDOC_NEWLINE & mdoc->flags; | ||
|
|
||
* case of `Eo'); and a body that may be empty. | * case of `Eo'); and a body that may be empty. | ||
*/ | */ | ||
mdoc_block_alloc(mdoc, line, ppos, tok, NULL); | roff_block_alloc(mdoc, line, ppos, tok); | ||
for (head = body = NULL; ; ) { | head = NULL; | ||
for (;;) { | |||
la = *pos; | la = *pos; | ||
ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ||
if (ac == ARGS_PUNCT || ac == ARGS_EOLN) | if (ac == ARGS_PUNCT || ac == ARGS_EOLN) | ||
|
|
||
if (head == NULL && ac != ARGS_QWORD && | if (head == NULL && ac != ARGS_QWORD && | ||
mdoc_isdelim(p) == DELIM_OPEN) { | mdoc_isdelim(p) == DELIM_OPEN) { | ||
assert(NULL == body); | |||
dword(mdoc, line, la, p, DELIM_OPEN, 0); | dword(mdoc, line, la, p, DELIM_OPEN, 0); | ||
continue; | continue; | ||
} | } | ||
if (head == NULL) { | if (head == NULL) { | ||
assert(body == NULL); | head = roff_head_alloc(mdoc, line, ppos, tok); | ||
head = mdoc_head_alloc(mdoc, line, ppos, tok); | if (tok == MDOC_Eo) /* Not parsed. */ | ||
} | |||
/* | |||
* `Eo' gobbles any data into the head, but most other | |||
* macros just immediately close out and begin the body. | |||
*/ | |||
if (body == NULL) { | |||
assert(head); | |||
/* No check whether it's a macro! */ | |||
if (tok == MDOC_Eo) | |||
dword(mdoc, line, la, p, DELIM_MAX, 0); | dword(mdoc, line, la, p, DELIM_MAX, 0); | ||
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos); | rew_last(mdoc, head); | ||
body = mdoc_body_alloc(mdoc, line, ppos, tok); | roff_body_alloc(mdoc, line, ppos, tok); | ||
if (tok == MDOC_Eo) | if (tok == MDOC_Eo) | ||
continue; | continue; | ||
} | } | ||
assert(head != NULL && body != NULL); | |||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p); | if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) | ||
if (ntok == MDOC_MAX) { | break; | ||
dword(mdoc, line, la, p, DELIM_MAX, | |||
MDOC_JOIN & mdoc_macros[tok].flags); | |||
continue; | |||
} | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | |||
return(0); | |||
break; | |||
} | } | ||
/* Clean-up to leave in a consistent state. */ | /* Clean-up to leave in a consistent state. */ | ||
if (head == NULL) | if (head == NULL) { | ||
mdoc_head_alloc(mdoc, line, ppos, tok); | rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); | ||
roff_body_alloc(mdoc, line, ppos, tok); | |||
if (body == NULL) { | |||
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos); | |||
mdoc_body_alloc(mdoc, line, ppos, tok); | |||
} | } | ||
/* Standard appending of delimiters. */ | |||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
return(1); | |||
} | } | ||
static int | static void | ||
in_line_argn(MACRO_PROT_ARGS) | in_line_argn(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, flushed, j, maxargs, nl; | |||
enum margserr ac; | |||
enum margverr av; | |||
struct mdoc_arg *arg; | struct mdoc_arg *arg; | ||
char *p; | char *p; | ||
enum mdoct ntok; | enum margserr ac; | ||
int ntok; | |||
int state; /* arg#; -1: not yet open; -2: closed */ | |||
int la, maxargs, nl; | |||
nl = MDOC_NEWLINE & mdoc->flags; | nl = mdoc->flags & MDOC_NEWLINE; | ||
/* | /* | ||
* A line macro that has a fixed number of arguments (maxargs). | * A line macro that has a fixed number of arguments (maxargs). | ||
|
|
||
break; | break; | ||
} | } | ||
for (arg = NULL; ; ) { | mdoc_argv(mdoc, line, tok, &arg, pos, buf); | ||
la = *pos; | |||
av = mdoc_argv(mdoc, line, tok, &arg, pos, buf); | |||
if (ARGV_WORD == av) { | state = -1; | ||
*pos = la; | p = NULL; | ||
break; | for (;;) { | ||
} | |||
if (ARGV_EOLN == av) | |||
break; | |||
if (ARGV_ARG == av) | |||
continue; | |||
mdoc_argv_free(arg); | |||
return(0); | |||
} | |||
for (flushed = j = 0; ; ) { | |||
la = *pos; | la = *pos; | ||
ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | ||
if (ac == ARGS_PUNCT || ac == ARGS_EOLN) | |||
break; | |||
if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) && | if (ac == ARGS_WORD && state == -1 && | ||
ac != ARGS_QWORD && j == 0 && | ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) && | ||
mdoc_isdelim(p) == DELIM_OPEN) { | mdoc_isdelim(p) == DELIM_OPEN) { | ||
dword(mdoc, line, la, p, DELIM_OPEN, 0); | dword(mdoc, line, la, p, DELIM_OPEN, 0); | ||
continue; | continue; | ||
} else if (j == 0) | } | ||
mdoc_elem_alloc(mdoc, line, ppos, tok, arg); | |||
if (j == maxargs && ! flushed) { | if (state == -1 && tok != MDOC_In && | ||
tok != MDOC_St && tok != MDOC_Xr) { | |||
mdoc_elem_alloc(mdoc, line, ppos, tok, arg); | |||
state = 0; | |||
} | |||
if (ac == ARGS_PUNCT || ac == ARGS_EOLN) { | |||
if (abs(state) < 2 && tok == MDOC_Pf) | |||
mandoc_vmsg(MANDOCERR_PF_SKIP, | |||
mdoc->parse, line, ppos, "Pf %s", | |||
p == NULL ? "at eol" : p); | |||
break; | |||
} | |||
if (state == maxargs) { | |||
rew_elem(mdoc, tok); | rew_elem(mdoc, tok); | ||
flushed = 1; | state = -2; | ||
} | } | ||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p); | ntok = (ac == ARGS_QWORD || (tok == MDOC_Pf && state == 0)) ? | ||
TOKEN_NONE : lookup(mdoc, tok, line, la, p); | |||
if (ntok != MDOC_MAX) { | if (ntok != TOKEN_NONE) { | ||
if ( ! flushed) | if (state >= 0) { | ||
rew_elem(mdoc, tok); | rew_elem(mdoc, tok); | ||
flushed = 1; | state = -2; | ||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | } | ||
return(0); | mdoc_macro(mdoc, ntok, line, la, pos, buf); | ||
j++; | |||
break; | break; | ||
} | } | ||
if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) && | if (ac == ARGS_QWORD || | ||
ac != ARGS_QWORD && ! flushed && | mdoc_macros[tok].flags & MDOC_IGNDELIM || | ||
mdoc_isdelim(p) != DELIM_NONE) { | mdoc_isdelim(p) == DELIM_NONE) { | ||
if (state == -1) { | |||
mdoc_elem_alloc(mdoc, line, ppos, tok, arg); | |||
state = 1; | |||
} else if (state >= 0) | |||
state++; | |||
} else if (state >= 0) { | |||
rew_elem(mdoc, tok); | rew_elem(mdoc, tok); | ||
flushed = 1; | state = -2; | ||
} | } | ||
dword(mdoc, line, la, p, DELIM_MAX, | dword(mdoc, line, la, p, DELIM_MAX, | ||
MDOC_JOIN & mdoc_macros[tok].flags); | MDOC_JOIN & mdoc_macros[tok].flags); | ||
j++; | |||
} | } | ||
if (j == 0) | if (state == -1) { | ||
mdoc_elem_alloc(mdoc, line, ppos, tok, arg); | mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | ||
line, ppos, mdoc_macronames[tok]); | |||
return; | |||
} | |||
/* Close out in a consistent state. */ | if (state == 0 && tok == MDOC_Pf) | ||
append_delims(mdoc, line, pos, buf); | |||
if ( ! flushed) | if (state >= 0) | ||
rew_elem(mdoc, tok); | rew_elem(mdoc, tok); | ||
if (nl) | if (nl) | ||
append_delims(mdoc, line, pos, buf); | append_delims(mdoc, line, pos, buf); | ||
return(1); | |||
} | } | ||
static int | static void | ||
in_line_eoln(MACRO_PROT_ARGS) | in_line_eoln(MACRO_PROT_ARGS) | ||
{ | { | ||
int la; | struct roff_node *n; | ||
enum margserr ac; | struct mdoc_arg *arg; | ||
enum margverr av; | |||
struct mdoc_arg *arg; | |||
char *p; | |||
enum mdoct ntok; | |||
assert( ! (MDOC_PARSED & mdoc_macros[tok].flags)); | if ((tok == MDOC_Pp || tok == MDOC_Lp) && | ||
! (mdoc->flags & MDOC_SYNOPSIS)) { | |||
n = mdoc->last; | |||
if (mdoc->next == ROFF_NEXT_SIBLING) | |||
n = n->parent; | |||
if (n->tok == MDOC_Nm) | |||
rew_last(mdoc, mdoc->last->parent); | |||
} | |||
if (tok == MDOC_Pp) | if (buf[*pos] == '\0' && | ||
rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos); | (tok == MDOC_Fd || mdoc_macronames[tok][0] == '%')) { | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | |||
/* Parse macro arguments. */ | line, ppos, mdoc_macronames[tok]); | ||
return; | |||
for (arg = NULL; ; ) { | |||
la = *pos; | |||
av = mdoc_argv(mdoc, line, tok, &arg, pos, buf); | |||
if (ARGV_WORD == av) { | |||
*pos = la; | |||
break; | |||
} | |||
if (ARGV_EOLN == av) | |||
break; | |||
if (ARGV_ARG == av) | |||
continue; | |||
mdoc_argv_free(arg); | |||
return(0); | |||
} | } | ||
/* Open element scope. */ | mdoc_argv(mdoc, line, tok, &arg, pos, buf); | ||
mdoc_elem_alloc(mdoc, line, ppos, tok, arg); | 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 roff_man *mdoc, int tok, int line, int *pos, char *buf) | |||
{ | |||
int la; | |||
for (;;) { | for (;;) { | ||
la = *pos; | la = *pos; | ||
ac = mdoc_args(mdoc, line, pos, buf, tok, &p); | if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN) | ||
if (ac == ARGS_EOLN) | return(0); | ||
break; | if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) | ||
return(1); | |||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p); | |||
if (ntok == MDOC_MAX) { | |||
dword(mdoc, line, la, p, DELIM_MAX, | |||
MDOC_JOIN & mdoc_macros[tok].flags); | |||
continue; | |||
} | |||
rew_elem(mdoc, tok); | |||
return(mdoc_macro(mdoc, ntok, line, la, pos, buf)); | |||
} | } | ||
/* Close out (no delimiters). */ | |||
rew_elem(mdoc, tok); | |||
return(1); | |||
} | } | ||
static int | static void | ||
ctx_synopsis(MACRO_PROT_ARGS) | ctx_synopsis(MACRO_PROT_ARGS) | ||
{ | { | ||
int nl; | |||
nl = MDOC_NEWLINE & mdoc->flags; | if (~mdoc->flags & (MDOC_SYNOPSIS | MDOC_NEWLINE)) | ||
in_line(mdoc, tok, line, ppos, pos, buf); | |||
/* If we're not in the SYNOPSIS, go straight to in-line. */ | else if (tok == MDOC_Nm) | ||
if ( ! (MDOC_SYNOPSIS & mdoc->flags)) | blk_full(mdoc, tok, line, ppos, pos, buf); | ||
return(in_line(mdoc, tok, line, ppos, pos, buf)); | else { | ||
assert(tok == MDOC_Vt); | |||
/* If we're a nested call, same place. */ | blk_part_imp(mdoc, tok, line, ppos, pos, buf); | ||
if ( ! nl) | } | ||
return(in_line(mdoc, tok, line, ppos, pos, buf)); | |||
/* | |||
* XXX: this will open a block scope; however, if later we end | |||
* up formatting the block scope, then child nodes will inherit | |||
* the formatting. Be careful. | |||
*/ | |||
if (MDOC_Nm == tok) | |||
return(blk_full(mdoc, tok, line, ppos, pos, buf)); | |||
assert(MDOC_Vt == tok); | |||
return(blk_part_imp(mdoc, tok, line, ppos, pos, buf)); | |||
} | } | ||
/* | /* | ||
|
|
||
* They're unusual because they're basically free-form text until a | * They're unusual because they're basically free-form text until a | ||
* macro is encountered. | * macro is encountered. | ||
*/ | */ | ||
static int | static void | ||
phrase(struct mdoc *mdoc, int line, int ppos, char *buf) | phrase_ta(MACRO_PROT_ARGS) | ||
{ | { | ||
int la, pos; | struct roff_node *body, *n; | ||
enum margserr ac; | |||
enum mdoct ntok; | |||
char *p; | |||
for (pos = ppos; ; ) { | /* Make sure we are in a column list or ignore this macro. */ | ||
la = pos; | |||
ac = mdoc_zargs(mdoc, line, &pos, buf, &p); | body = NULL; | ||
if (ac == ARGS_EOLN) | for (n = mdoc->last; n != NULL; n = n->parent) { | ||
break; | if (n->flags & MDOC_ENDED) | ||
ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup_raw(p); | |||
if (ntok == MDOC_MAX) { | |||
dword(mdoc, line, la, p, DELIM_MAX, 1); | |||
continue; | continue; | ||
} | if (n->tok == MDOC_It && n->type == ROFFT_BODY) | ||
body = n; | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, &pos, buf)) | if (n->tok == MDOC_Bl) | ||
return(0); | break; | ||
append_delims(mdoc, line, &pos, buf); | |||
return(1); | |||
} | } | ||
return(1); | if (n == NULL || n->norm->Bl.type != LIST_column) { | ||
} | |||
static int | |||
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 (NULL != n && MDOC_Bl != n->tok) | |||
n = n->parent; | |||
if (NULL == n || LIST_column != n->norm->Bl.type) { | |||
mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse, | mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse, | ||
line, ppos, "Ta"); | line, ppos, "Ta"); | ||
return(1); | return; | ||
} | } | ||
/* Advance to the next column. */ | /* Advance to the next column. */ | ||
rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos); | rew_last(mdoc, body); | ||
mdoc_body_alloc(mdoc, line, ppos, MDOC_It); | roff_body_alloc(mdoc, line, ppos, MDOC_It); | ||
parse_rest(mdoc, TOKEN_NONE, line, pos, buf); | |||
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) { | |||
dword(mdoc, line, la, p, DELIM_MAX, | |||
MDOC_JOIN & mdoc_macros[tok].flags); | |||
continue; | |||
} | |||
if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) | |||
return(0); | |||
append_delims(mdoc, line, pos, buf); | |||
return(1); | |||
} | |||
return(1); | |||
} | } |