Return to mdoc_argv.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.15, 2009/07/17 12:27:49 | version 1.56, 2010/07/13 23:53:20 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* | * | ||
* 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 <sys/types.h> | #include <sys/types.h> | ||
#include <assert.h> | #include <assert.h> | ||
|
|
||
#include <stdio.h> | #include <stdio.h> | ||
#include <string.h> | #include <string.h> | ||
#include "mandoc.h" | |||
#include "libmdoc.h" | #include "libmdoc.h" | ||
#include "libmandoc.h" | |||
/* | /* | ||
* Routines to parse arguments of macros. Arguments follow the syntax | * Routines to parse arguments of macros. Arguments follow the syntax | ||
|
|
||
* There's no limit to the number or arguments that may be allocated. | * There's no limit to the number or arguments that may be allocated. | ||
*/ | */ | ||
/* FIXME .Bf Li raises "macro-like parameter". */ | |||
#define ARGS_QUOTED (1 << 0) | |||
#define ARGS_DELIM (1 << 1) | |||
#define ARGS_TABSEP (1 << 2) | |||
#define ARGV_NONE (1 << 0) | #define ARGV_NONE (1 << 0) | ||
#define ARGV_SINGLE (1 << 1) | #define ARGV_SINGLE (1 << 1) | ||
#define ARGV_MULTI (1 << 2) | #define ARGV_MULTI (1 << 2) | ||
|
|
||
#define MULTI_STEP 5 | #define MULTI_STEP 5 | ||
static int argv_a2arg(int, const char *); | static int argv_a2arg(enum mdoct, const char *); | ||
static int args(struct mdoc *, int, int *, | static enum margserr args(struct mdoc *, int, int *, | ||
char *, int, char **); | char *, int, char **); | ||
static int argv(struct mdoc *, int, | static int argv(struct mdoc *, int, | ||
struct mdoc_argv *, int *, char *); | struct mdoc_argv *, int *, char *); | ||
|
|
||
ARGV_NONE, /* MDOC_Ragged */ | ARGV_NONE, /* MDOC_Ragged */ | ||
ARGV_NONE, /* MDOC_Unfilled */ | ARGV_NONE, /* MDOC_Unfilled */ | ||
ARGV_NONE, /* MDOC_Literal */ | ARGV_NONE, /* MDOC_Literal */ | ||
ARGV_NONE, /* MDOC_File */ | ARGV_SINGLE, /* MDOC_File */ | ||
ARGV_SINGLE, /* MDOC_Offset */ | ARGV_OPT_SINGLE, /* MDOC_Offset */ | ||
ARGV_NONE, /* MDOC_Bullet */ | ARGV_NONE, /* MDOC_Bullet */ | ||
ARGV_NONE, /* MDOC_Dash */ | ARGV_NONE, /* MDOC_Dash */ | ||
ARGV_NONE, /* MDOC_Hyphen */ | ARGV_NONE, /* MDOC_Hyphen */ | ||
|
|
||
ARGV_MULTI, /* MDOC_Column */ | ARGV_MULTI, /* MDOC_Column */ | ||
ARGV_SINGLE, /* MDOC_Width */ | ARGV_SINGLE, /* MDOC_Width */ | ||
ARGV_NONE, /* MDOC_Compact */ | ARGV_NONE, /* MDOC_Compact */ | ||
ARGV_OPT_SINGLE, /* MDOC_Std */ | ARGV_NONE, /* MDOC_Std */ | ||
ARGV_NONE, /* MDOC_Filled */ | ARGV_NONE, /* MDOC_Filled */ | ||
ARGV_NONE, /* MDOC_Words */ | ARGV_NONE, /* MDOC_Words */ | ||
ARGV_NONE, /* MDOC_Emphasis */ | ARGV_NONE, /* MDOC_Emphasis */ | ||
|
|
||
0, /* Dd */ | 0, /* Dd */ | ||
0, /* Dt */ | 0, /* Dt */ | ||
0, /* Os */ | 0, /* Os */ | ||
ARGS_QUOTED, /* Sh */ | 0, /* Sh */ | ||
ARGS_QUOTED, /* Ss */ | 0, /* Ss */ | ||
ARGS_DELIM, /* Pp */ | ARGS_DELIM, /* Pp */ | ||
ARGS_DELIM, /* D1 */ | ARGS_DELIM, /* D1 */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Dl */ | ARGS_DELIM, /* Dl */ | ||
0, /* Bd */ | 0, /* Bd */ | ||
0, /* Ed */ | 0, /* Ed */ | ||
ARGS_QUOTED, /* Bl */ | 0, /* Bl */ | ||
0, /* El */ | 0, /* El */ | ||
0, /* It */ | 0, /* It */ | ||
ARGS_DELIM, /* Ad */ | ARGS_DELIM, /* Ad */ | ||
ARGS_DELIM, /* An */ | ARGS_DELIM, /* An */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Ar */ | ARGS_DELIM, /* Ar */ | ||
ARGS_QUOTED, /* Cd */ | 0, /* Cd */ | ||
ARGS_DELIM, /* Cm */ | ARGS_DELIM, /* Cm */ | ||
ARGS_DELIM, /* Dv */ | ARGS_DELIM, /* Dv */ | ||
ARGS_DELIM, /* Er */ | ARGS_DELIM, /* Er */ | ||
ARGS_DELIM, /* Ev */ | ARGS_DELIM, /* Ev */ | ||
0, /* Ex */ | 0, /* Ex */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Fa */ | ARGS_DELIM, /* Fa */ | ||
0, /* Fd */ | 0, /* Fd */ | ||
ARGS_DELIM, /* Fl */ | ARGS_DELIM, /* Fl */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Fn */ | ARGS_DELIM, /* Fn */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Ft */ | ARGS_DELIM, /* Ft */ | ||
ARGS_DELIM, /* Ic */ | ARGS_DELIM, /* Ic */ | ||
0, /* In */ | 0, /* In */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Li */ | ARGS_DELIM, /* Li */ | ||
ARGS_QUOTED, /* Nd */ | 0, /* Nd */ | ||
ARGS_DELIM, /* Nm */ | ARGS_DELIM, /* Nm */ | ||
ARGS_DELIM, /* Op */ | ARGS_DELIM, /* Op */ | ||
0, /* Ot */ | 0, /* Ot */ | ||
|
|
||
ARGS_DELIM, /* Va */ | ARGS_DELIM, /* Va */ | ||
ARGS_DELIM, /* Vt */ | ARGS_DELIM, /* Vt */ | ||
ARGS_DELIM, /* Xr */ | ARGS_DELIM, /* Xr */ | ||
ARGS_QUOTED, /* %A */ | 0, /* %A */ | ||
ARGS_QUOTED, /* %B */ | 0, /* %B */ | ||
ARGS_QUOTED, /* %D */ | 0, /* %D */ | ||
ARGS_QUOTED, /* %I */ | 0, /* %I */ | ||
ARGS_QUOTED, /* %J */ | 0, /* %J */ | ||
ARGS_QUOTED, /* %N */ | 0, /* %N */ | ||
ARGS_QUOTED, /* %O */ | 0, /* %O */ | ||
ARGS_QUOTED, /* %P */ | 0, /* %P */ | ||
ARGS_QUOTED, /* %R */ | 0, /* %R */ | ||
ARGS_QUOTED, /* %T */ | 0, /* %T */ | ||
ARGS_QUOTED, /* %V */ | 0, /* %V */ | ||
ARGS_DELIM, /* Ac */ | ARGS_DELIM, /* Ac */ | ||
0, /* Ao */ | 0, /* Ao */ | ||
ARGS_DELIM, /* Aq */ | ARGS_DELIM, /* Aq */ | ||
|
|
||
ARGS_DELIM, /* Sq */ | ARGS_DELIM, /* Sq */ | ||
0, /* Sm */ | 0, /* Sm */ | ||
ARGS_DELIM, /* Sx */ | ARGS_DELIM, /* Sx */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Sy */ | ARGS_DELIM, /* Sy */ | ||
ARGS_DELIM, /* Tn */ | ARGS_DELIM, /* Tn */ | ||
ARGS_DELIM, /* Ux */ | ARGS_DELIM, /* Ux */ | ||
ARGS_DELIM, /* Xc */ | ARGS_DELIM, /* Xc */ | ||
0, /* Xo */ | 0, /* Xo */ | ||
ARGS_QUOTED, /* Fo */ | 0, /* Fo */ | ||
0, /* Fc */ | 0, /* Fc */ | ||
0, /* Oo */ | 0, /* Oo */ | ||
ARGS_DELIM, /* Oc */ | ARGS_DELIM, /* Oc */ | ||
|
|
||
0, /* Ud */ | 0, /* Ud */ | ||
0, /* Lb */ | 0, /* Lb */ | ||
ARGS_DELIM, /* Lp */ | ARGS_DELIM, /* Lp */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Lk */ | ARGS_DELIM, /* Lk */ | ||
ARGS_DELIM | ARGS_QUOTED, /* Mt */ | ARGS_DELIM, /* Mt */ | ||
ARGS_DELIM, /* Brq */ | ARGS_DELIM, /* Brq */ | ||
0, /* Bro */ | 0, /* Bro */ | ||
ARGS_DELIM, /* Brc */ | ARGS_DELIM, /* Brc */ | ||
ARGS_QUOTED, /* %C */ | 0, /* %C */ | ||
0, /* Es */ | 0, /* Es */ | ||
0, /* En */ | 0, /* En */ | ||
0, /* Dx */ | 0, /* Dx */ | ||
ARGS_QUOTED, /* %Q */ | 0, /* %Q */ | ||
0, /* br */ | 0, /* br */ | ||
0, /* sp */ | 0, /* sp */ | ||
0, /* %U */ | |||
0, /* Ta */ | |||
}; | }; | ||
|
|
||
* [value0...], which may either have a single mandatory value, at least | * [value0...], which may either have a single mandatory value, at least | ||
* one mandatory value, an optional single value, or no value. | * one mandatory value, an optional single value, or no value. | ||
*/ | */ | ||
int | enum margverr | ||
mdoc_argv(struct mdoc *m, int line, int tok, | mdoc_argv(struct mdoc *m, int line, enum mdoct tok, | ||
struct mdoc_arg **v, int *pos, char *buf) | struct mdoc_arg **v, int *pos, char *buf) | ||
{ | { | ||
int i; | |||
char *p, sv; | char *p, sv; | ||
struct mdoc_argv tmp; | struct mdoc_argv tmp; | ||
struct mdoc_arg *arg; | struct mdoc_arg *arg; | ||
if (0 == buf[*pos]) | if ('\0' == buf[*pos]) | ||
return(ARGV_EOLN); | return(ARGV_EOLN); | ||
assert(' ' != buf[*pos]); | assert(' ' != buf[*pos]); | ||
/* Parse through to the first unescaped space. */ | /* Parse through to the first unescaped space. */ | ||
i = *pos; | |||
p = &buf[++(*pos)]; | p = &buf[++(*pos)]; | ||
assert(*pos > 0); | assert(*pos > 0); | ||
|
|
||
/* XXX - save zeroed byte, if not an argument. */ | /* XXX - save zeroed byte, if not an argument. */ | ||
sv = 0; | sv = '\0'; | ||
if (buf[*pos]) { | if (buf[*pos]) { | ||
sv = buf[*pos]; | sv = buf[*pos]; | ||
buf[(*pos)++] = 0; | buf[(*pos)++] = '\0'; | ||
} | } | ||
(void)memset(&tmp, 0, sizeof(struct mdoc_argv)); | (void)memset(&tmp, 0, sizeof(struct mdoc_argv)); | ||
|
|
||
if ( ! argv(m, line, &tmp, pos, buf)) | if ( ! argv(m, line, &tmp, pos, buf)) | ||
return(ARGV_ERROR); | return(ARGV_ERROR); | ||
if (NULL == (arg = *v)) { | if (NULL == (arg = *v)) | ||
*v = calloc(1, sizeof(struct mdoc_arg)); | arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg)); | ||
if (NULL == *v) { | |||
(void)mdoc_nerr(m, m->last, EMALLOC); | |||
return(ARGV_ERROR); | |||
} | |||
arg = *v; | |||
} | |||
arg->argc++; | arg->argc++; | ||
arg->argv = realloc(arg->argv, arg->argc * | arg->argv = mandoc_realloc | ||
sizeof(struct mdoc_argv)); | (arg->argv, arg->argc * sizeof(struct mdoc_argv)); | ||
if (NULL == arg->argv) { | |||
(void)mdoc_nerr(m, m->last, EMALLOC); | |||
return(ARGV_ERROR); | |||
} | |||
(void)memcpy(&arg->argv[(int)arg->argc - 1], | (void)memcpy(&arg->argv[(int)arg->argc - 1], | ||
&tmp, sizeof(struct mdoc_argv)); | &tmp, sizeof(struct mdoc_argv)); | ||
|
|
||
void | void | ||
mdoc_argv_free(struct mdoc_arg *p) | mdoc_argv_free(struct mdoc_arg *p) | ||
{ | { | ||
int i, j; | int i; | ||
if (NULL == p) | if (NULL == p) | ||
return; | return; | ||
|
|
||
} | } | ||
assert(p->argc); | assert(p->argc); | ||
/* LINTED */ | for (i = (int)p->argc - 1; i >= 0; i--) | ||
for (i = 0; i < (int)p->argc; i++) { | mdoc_argn_free(p, i); | ||
if (0 == p->argv[i].sz) | |||
continue; | |||
/* LINTED */ | |||
for (j = 0; j < (int)p->argv[i].sz; j++) | |||
free(p->argv[i].value[j]); | |||
free(p->argv[i].value); | |||
} | |||
free(p->argv); | free(p->argv); | ||
free(p); | free(p); | ||
} | } | ||
int | void | ||
mdoc_args(struct mdoc *m, int line, | mdoc_argn_free(struct mdoc_arg *p, int iarg) | ||
int *pos, char *buf, int tok, char **v) | |||
{ | { | ||
int fl, c, i; | struct mdoc_argv *arg = &p->argv[iarg]; | ||
struct mdoc_node *n; | int j; | ||
fl = (0 == tok) ? 0 : mdoc_argflags[tok]; | if (arg->sz && arg->value) { | ||
for (j = (int)arg->sz - 1; j >= 0; j--) | |||
free(arg->value[j]); | |||
free(arg->value); | |||
} | |||
/* | for (--p->argc; iarg < (int)p->argc; iarg++) | ||
* Override per-macro argument flags with context-specific ones. | p->argv[iarg] = p->argv[iarg+1]; | ||
* As of now, this is only valid for `It' depending on its list | } | ||
* context. | |||
*/ | |||
switch (tok) { | |||
case (MDOC_It): | |||
for (n = m->last; n; n = n->parent) | |||
if (MDOC_BLOCK == n->type && MDOC_Bl == n->tok) | |||
break; | |||
assert(n); | enum margserr | ||
c = (int)(n->args ? n->args->argc : 0); | mdoc_zargs(struct mdoc *m, int line, int *pos, | ||
assert(c > 0); | char *buf, int flags, char **v) | ||
{ | |||
/* | return(args(m, line, pos, buf, flags, v)); | ||
* Using `Bl -column' adds ARGS_TABSEP to the arguments | } | ||
* and invalidates ARGS_DELIM. Using `Bl -diag' allows | |||
* for quoted arguments. | |||
*/ | |||
/* LINTED */ | |||
for (i = 0; i < c; i++) { | enum margserr | ||
switch (n->args->argv[i].arg) { | mdoc_args(struct mdoc *m, int line, int *pos, | ||
case (MDOC_Column): | char *buf, enum mdoct tok, char **v) | ||
fl |= ARGS_TABSEP; | { | ||
fl &= ~ARGS_DELIM; | int fl; | ||
i = c; | struct mdoc_node *n; | ||
break; | |||
case (MDOC_Diag): | fl = mdoc_argflags[tok]; | ||
fl |= ARGS_QUOTED; | |||
i = c; | if (MDOC_It != tok) | ||
break; | return(args(m, line, pos, buf, fl, v)); | ||
default: | |||
break; | /* | ||
} | * We know that we're in an `It', so it's reasonable to expect | ||
} | * us to be sitting in a `Bl'. Someday this may not be the case | ||
break; | * (if we allow random `It's sitting out there), so provide a | ||
default: | * safe fall-back into the default behaviour. | ||
break; | */ | ||
for (n = m->last; n; n = n->parent) | |||
if (MDOC_Bl == n->tok) | |||
break; | |||
assert(n->data.Bl); | |||
if (n && LIST_column == n->data.Bl->type) { | |||
fl |= ARGS_TABSEP; | |||
fl &= ~ARGS_DELIM; | |||
} | } | ||
return(args(m, line, pos, buf, fl, v)); | return(args(m, line, pos, buf, fl, v)); | ||
} | } | ||
static int | static enum margserr | ||
args(struct mdoc *m, int line, int *pos, | args(struct mdoc *m, int line, int *pos, | ||
char *buf, int fl, char **v) | char *buf, int fl, char **v) | ||
{ | { | ||
int i; | int i; | ||
char *p, *pp; | char *p, *pp; | ||
enum margserr rc; | |||
enum mdelim d; | |||
assert(*pos > 0); | /* | ||
* Parse out the terms (like `val' in `.Xx -arg val' or simply | |||
* `.Xx val'), which can have all sorts of properties: | |||
* | |||
* ARGS_DELIM: use special handling if encountering trailing | |||
* delimiters in the form of [[::delim::][ ]+]+. | |||
* | |||
* ARGS_NOWARN: don't post warnings. This is only used when | |||
* re-parsing delimiters, as the warnings have already been | |||
* posted. | |||
* | |||
* ARGS_TABSEP: use special handling for tab/`Ta' separated | |||
* phrases like in `Bl -column'. | |||
*/ | |||
if (0 == buf[*pos]) | assert(' ' != buf[*pos]); | ||
return(ARGS_EOLN); | |||
if ('\"' == buf[*pos] && ! (fl & ARGS_QUOTED)) | if ('\0' == buf[*pos]) { | ||
if ( ! mdoc_pwarn(m, line, *pos, EQUOTPARM)) | if (MDOC_PPHRASE & m->flags) | ||
return(ARGS_ERROR); | return(ARGS_EOLN); | ||
/* | |||
* If we're not in a partial phrase and the flag for | |||
* being a phrase literal is still set, the punctuation | |||
* is unterminated. | |||
*/ | |||
if (MDOC_PHRASELIT & m->flags) | |||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE)) | |||
return(ARGS_ERROR); | |||
m->flags &= ~MDOC_PHRASELIT; | |||
return(ARGS_EOLN); | |||
} | |||
/* | /* | ||
* If the first character is a delimiter and we're to look for | * If the first character is a closing delimiter and we're to | ||
* delimited strings, then pass down the buffer seeing if it | * look for delimited strings, then pass down the buffer seeing | ||
* follows the pattern of [[::delim::][ ]+]+. | * if it follows the pattern of [[::delim::][ ]+]+. Note that | ||
* we ONLY care about closing delimiters. | |||
*/ | */ | ||
if ((fl & ARGS_DELIM) && mdoc_iscdelim(buf[*pos])) { | if ((fl & ARGS_DELIM) && DELIM_CLOSE == mdoc_iscdelim(buf[*pos])) { | ||
for (i = *pos; buf[i]; ) { | for (i = *pos; buf[i]; ) { | ||
if ( ! mdoc_iscdelim(buf[i])) | d = mdoc_iscdelim(buf[i]); | ||
if (DELIM_NONE == d || DELIM_OPEN == d) | |||
break; | break; | ||
i++; | i++; | ||
/* There must be at least one space... */ | if ('\0' == buf[i] || ' ' != buf[i]) | ||
if (0 == buf[i] || ' ' != buf[i]) | |||
break; | break; | ||
i++; | i++; | ||
while (buf[i] && ' ' == buf[i]) | while (buf[i] && ' ' == buf[i]) | ||
i++; | i++; | ||
} | } | ||
if (0 == buf[i]) { | |||
if ('\0' == buf[i]) { | |||
*v = &buf[*pos]; | *v = &buf[*pos]; | ||
if (i && ' ' != buf[i - 1]) | |||
return(ARGS_PUNCT); | |||
if (ARGS_NOWARN & fl) | |||
return(ARGS_PUNCT); | |||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) | |||
return(ARGS_ERROR); | |||
return(ARGS_PUNCT); | return(ARGS_PUNCT); | ||
} | } | ||
} | } | ||
/* First parse non-quoted strings. */ | *v = &buf[*pos]; | ||
if ('\"' != buf[*pos] || ! (ARGS_QUOTED & fl)) { | /* | ||
*v = &buf[*pos]; | * First handle TABSEP items, restricted to `Bl -column'. This | ||
* ignores conventional token parsing and instead uses tabs or | |||
* `Ta' macros to separate phrases. Phrases are parsed again | |||
* for arguments at a later phase. | |||
*/ | |||
/* | if (ARGS_TABSEP & fl) { | ||
* Thar be dragons here! If we're tab-separated, search | /* Scan ahead to tab (can't be escaped). */ | ||
* ahead for either a tab or the `Ta' macro. | p = strchr(*v, '\t'); | ||
* If a `Ta' is detected, it must be space-buffered before and | pp = NULL; | ||
* after. If either of these hold true, then prune out the | |||
* extra spaces and call it an argument. | |||
*/ | |||
if (ARGS_TABSEP & fl) { | /* Scan ahead to unescaped `Ta'. */ | ||
/* Scan ahead to unescaped tab. */ | if ( ! (MDOC_PHRASELIT & m->flags)) | ||
p = strchr(*v, '\t'); | |||
/* Scan ahead to unescaped `Ta'. */ | |||
for (pp = *v; ; pp++) { | for (pp = *v; ; pp++) { | ||
if (NULL == (pp = strstr(pp, "Ta"))) | if (NULL == (pp = strstr(pp, "Ta"))) | ||
break; | break; | ||
if (pp > *v && ' ' != *(pp - 1)) | if (pp > *v && ' ' != *(pp - 1)) | ||
continue; | continue; | ||
if (' ' == *(pp + 2) || 0 == *(pp + 2)) | if (' ' == *(pp + 2) || '\0' == *(pp + 2)) | ||
break; | break; | ||
} | } | ||
/* Choose delimiter tab/Ta. */ | /* By default, assume a phrase. */ | ||
rc = ARGS_PHRASE; | |||
if (p && pp) | /* | ||
p = (p < pp ? p : pp); | * Adjust new-buffer position to be beyond delimiter | ||
else if ( ! p && pp) | * mark (e.g., Ta -> end + 2). | ||
p = pp; | */ | ||
if (p && pp) { | |||
*pos += pp < p ? 2 : 1; | |||
rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE; | |||
p = pp < p ? pp : p; | |||
} else if (p && ! pp) { | |||
rc = ARGS_PPHRASE; | |||
*pos += 1; | |||
} else if (pp && ! p) { | |||
p = pp; | |||
*pos += 2; | |||
} else { | |||
rc = ARGS_PEND; | |||
p = strchr(*v, 0); | |||
} | |||
/* Strip delimiter's preceding whitespace. */ | /* Whitespace check for eoln case... */ | ||
if ('\0' == *p && ' ' == *(p - 1) && ! (ARGS_NOWARN & fl)) | |||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) | |||
return(ARGS_ERROR); | |||
if (p && p > *v) { | *pos += (int)(p - *v); | ||
pp = p - 1; | |||
while (pp > *v && ' ' == *pp) | |||
pp--; | |||
if (pp == *v && ' ' == *pp) | |||
*pp = 0; | |||
else if (' ' == *pp) | |||
*(pp + 1) = 0; | |||
} | |||
/* ...in- and proceding whitespace. */ | /* Strip delimiter's preceding whitespace. */ | ||
pp = p - 1; | |||
while (pp > *v && ' ' == *pp) { | |||
if (pp > *v && '\\' == *(pp - 1)) | |||
break; | |||
pp--; | |||
} | |||
*(pp + 1) = 0; | |||
if (p && ('\t' != *p)) { | /* Strip delimiter's proceeding whitespace. */ | ||
*p++ = 0; | for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++) | ||
*p++ = 0; | /* Skip ahead. */ ; | ||
} else if (p) | |||
*p++ = 0; | |||
if (p) { | return(rc); | ||
while (' ' == *p) | } | ||
p++; | |||
if (0 != *p) | |||
*(p - 1) = 0; | |||
*pos += (int)(p - *v); | |||
} | |||
if (p && 0 == *p) | /* | ||
if ( ! mdoc_pwarn(m, line, *pos, ECOLEMPTY)) | * Process a quoted literal. A quote begins with a double-quote | ||
return(ARGS_ERROR); | * and ends with a double-quote NOT preceded by a double-quote. | ||
if (p && 0 == *p && p > *v && ' ' == *(p - 1)) | * Whitespace is NOT involved in literal termination. | ||
if ( ! mdoc_pwarn(m, line, *pos, ETAILWS)) | */ | ||
return(ARGS_ERROR); | |||
if (p) | if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) { | ||
return(ARGS_PHRASE); | if ( ! (MDOC_PHRASELIT & m->flags)) | ||
*v = &buf[++(*pos)]; | |||
/* Configure the eoln case, too. */ | if (MDOC_PPHRASE & m->flags) | ||
m->flags |= MDOC_PHRASELIT; | |||
p = strchr(*v, 0); | for ( ; buf[*pos]; (*pos)++) { | ||
assert(p); | if ('\"' != buf[*pos]) | ||
continue; | |||
if ('\"' != buf[*pos + 1]) | |||
break; | |||
(*pos)++; | |||
} | |||
if (p > *v && ' ' == *(p - 1)) | if ('\0' == buf[*pos]) { | ||
if ( ! mdoc_pwarn(m, line, *pos, ETAILWS)) | if (ARGS_NOWARN & fl || MDOC_PPHRASE & m->flags) | ||
return(ARGS_ERROR); | return(ARGS_QWORD); | ||
*pos += (int)(p - *v); | if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE)) | ||
return(ARGS_ERROR); | |||
return(ARGS_QWORD); | |||
} | |||
return(ARGS_PHRASE); | m->flags &= ~MDOC_PHRASELIT; | ||
} | buf[(*pos)++] = '\0'; | ||
/* Do non-tabsep look-ahead here. */ | |||
if ( ! (ARGS_TABSEP & fl)) | |||
while (buf[*pos]) { | |||
if (' ' == buf[*pos]) | |||
if ('\\' != buf[*pos - 1]) | |||
break; | |||
(*pos)++; | |||
} | |||
if (0 == buf[*pos]) | if ('\0' == buf[*pos]) | ||
return(ARGS_WORD); | return(ARGS_QWORD); | ||
buf[(*pos)++] = 0; | while (' ' == buf[*pos]) | ||
(*pos)++; | |||
if (0 == buf[*pos]) | if (0 == buf[*pos] && ! (ARGS_NOWARN & fl)) | ||
return(ARGS_WORD); | if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) | ||
return(ARGS_ERROR); | |||
if ( ! (ARGS_TABSEP & fl)) | return(ARGS_QWORD); | ||
while (buf[*pos] && ' ' == buf[*pos]) | |||
(*pos)++; | |||
if (buf[*pos]) | |||
return(ARGS_WORD); | |||
if ( ! mdoc_pwarn(m, line, *pos, ETAILWS)) | |||
return(ARGS_ERROR); | |||
return(ARGS_WORD); | |||
} | } | ||
/* | /* | ||
* If we're a quoted string (and quoted strings are allowed), | * A non-quoted term progresses until either the end of line or | ||
* then parse ahead to the next quote. If none's found, it's an | * a non-escaped whitespace. | ||
* error. After, parse to the next word. | |||
*/ | */ | ||
*v = &buf[++(*pos)]; | for ( ; buf[*pos]; (*pos)++) | ||
if (*pos && ' ' == buf[*pos] && '\\' != buf[*pos - 1]) | |||
break; | |||
while (buf[*pos] && '\"' != buf[*pos]) | if ('\0' == buf[*pos]) | ||
(*pos)++; | return(ARGS_WORD); | ||
if (0 == buf[*pos]) { | buf[(*pos)++] = '\0'; | ||
(void)mdoc_perr(m, line, *pos, EQUOTTERM); | |||
return(ARGS_ERROR); | |||
} | |||
buf[(*pos)++] = 0; | while (' ' == buf[*pos]) | ||
if (0 == buf[*pos]) | |||
return(ARGS_QWORD); | |||
while (buf[*pos] && ' ' == buf[*pos]) | |||
(*pos)++; | (*pos)++; | ||
if (buf[*pos]) | if ('\0' == buf[*pos] && ! (ARGS_NOWARN & fl)) | ||
return(ARGS_QWORD); | if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) | ||
return(ARGS_ERROR); | |||
if ( ! mdoc_pwarn(m, line, *pos, ETAILWS)) | return(ARGS_WORD); | ||
return(ARGS_ERROR); | |||
return(ARGS_QWORD); | |||
} | } | ||
static int | static int | ||
argv_a2arg(int tok, const char *argv) | argv_a2arg(enum mdoct tok, const char *p) | ||
{ | { | ||
/* | /* | ||
|
|
||
switch (tok) { | switch (tok) { | ||
case (MDOC_An): | case (MDOC_An): | ||
if (0 == strcmp(argv, "split")) | if (0 == strcmp(p, "split")) | ||
return(MDOC_Split); | return(MDOC_Split); | ||
else if (0 == strcmp(argv, "nosplit")) | else if (0 == strcmp(p, "nosplit")) | ||
return(MDOC_Nosplit); | return(MDOC_Nosplit); | ||
break; | break; | ||
case (MDOC_Bd): | case (MDOC_Bd): | ||
if (0 == strcmp(argv, "ragged")) | if (0 == strcmp(p, "ragged")) | ||
return(MDOC_Ragged); | return(MDOC_Ragged); | ||
else if (0 == strcmp(argv, "unfilled")) | else if (0 == strcmp(p, "unfilled")) | ||
return(MDOC_Unfilled); | return(MDOC_Unfilled); | ||
else if (0 == strcmp(argv, "filled")) | else if (0 == strcmp(p, "filled")) | ||
return(MDOC_Filled); | return(MDOC_Filled); | ||
else if (0 == strcmp(argv, "literal")) | else if (0 == strcmp(p, "literal")) | ||
return(MDOC_Literal); | return(MDOC_Literal); | ||
else if (0 == strcmp(argv, "file")) | else if (0 == strcmp(p, "file")) | ||
return(MDOC_File); | return(MDOC_File); | ||
else if (0 == strcmp(argv, "offset")) | else if (0 == strcmp(p, "offset")) | ||
return(MDOC_Offset); | return(MDOC_Offset); | ||
else if (0 == strcmp(argv, "compact")) | else if (0 == strcmp(p, "compact")) | ||
return(MDOC_Compact); | return(MDOC_Compact); | ||
else if (0 == strcmp(p, "centered")) | |||
return(MDOC_Centred); | |||
break; | break; | ||
case (MDOC_Bf): | case (MDOC_Bf): | ||
if (0 == strcmp(argv, "emphasis")) | if (0 == strcmp(p, "emphasis")) | ||
return(MDOC_Emphasis); | return(MDOC_Emphasis); | ||
else if (0 == strcmp(argv, "literal")) | else if (0 == strcmp(p, "literal")) | ||
return(MDOC_Literal); | return(MDOC_Literal); | ||
else if (0 == strcmp(argv, "symbolic")) | else if (0 == strcmp(p, "symbolic")) | ||
return(MDOC_Symbolic); | return(MDOC_Symbolic); | ||
break; | break; | ||
case (MDOC_Bk): | case (MDOC_Bk): | ||
if (0 == strcmp(argv, "words")) | if (0 == strcmp(p, "words")) | ||
return(MDOC_Words); | return(MDOC_Words); | ||
break; | break; | ||
case (MDOC_Bl): | case (MDOC_Bl): | ||
if (0 == strcmp(argv, "bullet")) | if (0 == strcmp(p, "bullet")) | ||
return(MDOC_Bullet); | return(MDOC_Bullet); | ||
else if (0 == strcmp(argv, "dash")) | else if (0 == strcmp(p, "dash")) | ||
return(MDOC_Dash); | return(MDOC_Dash); | ||
else if (0 == strcmp(argv, "hyphen")) | else if (0 == strcmp(p, "hyphen")) | ||
return(MDOC_Hyphen); | return(MDOC_Hyphen); | ||
else if (0 == strcmp(argv, "item")) | else if (0 == strcmp(p, "item")) | ||
return(MDOC_Item); | return(MDOC_Item); | ||
else if (0 == strcmp(argv, "enum")) | else if (0 == strcmp(p, "enum")) | ||
return(MDOC_Enum); | return(MDOC_Enum); | ||
else if (0 == strcmp(argv, "tag")) | else if (0 == strcmp(p, "tag")) | ||
return(MDOC_Tag); | return(MDOC_Tag); | ||
else if (0 == strcmp(argv, "diag")) | else if (0 == strcmp(p, "diag")) | ||
return(MDOC_Diag); | return(MDOC_Diag); | ||
else if (0 == strcmp(argv, "hang")) | else if (0 == strcmp(p, "hang")) | ||
return(MDOC_Hang); | return(MDOC_Hang); | ||
else if (0 == strcmp(argv, "ohang")) | else if (0 == strcmp(p, "ohang")) | ||
return(MDOC_Ohang); | return(MDOC_Ohang); | ||
else if (0 == strcmp(argv, "inset")) | else if (0 == strcmp(p, "inset")) | ||
return(MDOC_Inset); | return(MDOC_Inset); | ||
else if (0 == strcmp(argv, "column")) | else if (0 == strcmp(p, "column")) | ||
return(MDOC_Column); | return(MDOC_Column); | ||
else if (0 == strcmp(argv, "width")) | else if (0 == strcmp(p, "width")) | ||
return(MDOC_Width); | return(MDOC_Width); | ||
else if (0 == strcmp(argv, "offset")) | else if (0 == strcmp(p, "offset")) | ||
return(MDOC_Offset); | return(MDOC_Offset); | ||
else if (0 == strcmp(argv, "compact")) | else if (0 == strcmp(p, "compact")) | ||
return(MDOC_Compact); | return(MDOC_Compact); | ||
else if (0 == strcmp(argv, "nested")) | else if (0 == strcmp(p, "nested")) | ||
return(MDOC_Nested); | return(MDOC_Nested); | ||
break; | break; | ||
case (MDOC_Rv): | case (MDOC_Rv): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case (MDOC_Ex): | case (MDOC_Ex): | ||
if (0 == strcmp(argv, "std")) | if (0 == strcmp(p, "std")) | ||
return(MDOC_Std); | return(MDOC_Std); | ||
break; | break; | ||
default: | default: | ||
|
|
||
argv_multi(struct mdoc *m, int line, | argv_multi(struct mdoc *m, int line, | ||
struct mdoc_argv *v, int *pos, char *buf) | struct mdoc_argv *v, int *pos, char *buf) | ||
{ | { | ||
int c; | enum margserr ac; | ||
char *p; | char *p; | ||
for (v->sz = 0; ; v->sz++) { | for (v->sz = 0; ; v->sz++) { | ||
if ('-' == buf[*pos]) | if ('-' == buf[*pos]) | ||
break; | break; | ||
c = args(m, line, pos, buf, ARGS_QUOTED, &p); | ac = args(m, line, pos, buf, 0, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == ac) | ||
return(0); | return(0); | ||
else if (ARGS_EOLN == c) | else if (ARGS_EOLN == ac) | ||
break; | break; | ||
if (0 == v->sz % MULTI_STEP) { | if (0 == v->sz % MULTI_STEP) | ||
v->value = realloc(v->value, | v->value = mandoc_realloc(v->value, | ||
(v->sz + MULTI_STEP) * sizeof(char *)); | (v->sz + MULTI_STEP) * sizeof(char *)); | ||
if (NULL == v->value) { | |||
(void)mdoc_nerr(m, m->last, EMALLOC); | v->value[(int)v->sz] = mandoc_strdup(p); | ||
return(ARGV_ERROR); | |||
} | |||
} | |||
if (NULL == (v->value[(int)v->sz] = strdup(p))) | |||
return(mdoc_nerr(m, m->last, EMALLOC)); | |||
} | } | ||
return(1); | return(1); | ||
|
|
||
argv_opt_single(struct mdoc *m, int line, | argv_opt_single(struct mdoc *m, int line, | ||
struct mdoc_argv *v, int *pos, char *buf) | struct mdoc_argv *v, int *pos, char *buf) | ||
{ | { | ||
int c; | enum margserr ac; | ||
char *p; | char *p; | ||
if ('-' == buf[*pos]) | if ('-' == buf[*pos]) | ||
return(1); | return(1); | ||
c = args(m, line, pos, buf, ARGS_QUOTED, &p); | ac = args(m, line, pos, buf, 0, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_ERROR == ac) | ||
return(0); | return(0); | ||
if (ARGS_EOLN == c) | if (ARGS_EOLN == ac) | ||
return(1); | return(1); | ||
v->sz = 1; | v->sz = 1; | ||
if (NULL == (v->value = calloc(1, sizeof(char *)))) | v->value = mandoc_malloc(sizeof(char *)); | ||
return(mdoc_nerr(m, m->last, EMALLOC)); | v->value[0] = mandoc_strdup(p); | ||
if (NULL == (v->value[0] = strdup(p))) | |||
return(mdoc_nerr(m, m->last, EMALLOC)); | |||
return(1); | return(1); | ||
} | } | ||
|
|
||
argv_single(struct mdoc *m, int line, | argv_single(struct mdoc *m, int line, | ||
struct mdoc_argv *v, int *pos, char *buf) | struct mdoc_argv *v, int *pos, char *buf) | ||
{ | { | ||
int c, ppos; | int ppos; | ||
enum margserr ac; | |||
char *p; | char *p; | ||
ppos = *pos; | ppos = *pos; | ||
c = args(m, line, pos, buf, ARGS_QUOTED, &p); | ac = args(m, line, pos, buf, 0, &p); | ||
if (ARGS_ERROR == c) | if (ARGS_EOLN == ac) { | ||
mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT); | |||
return(0); | return(0); | ||
if (ARGS_EOLN == c) | } else if (ARGS_ERROR == ac) | ||
return(mdoc_perr(m, line, ppos, EARGVAL)); | return(0); | ||
v->sz = 1; | v->sz = 1; | ||
if (NULL == (v->value = calloc(1, sizeof(char *)))) | v->value = mandoc_malloc(sizeof(char *)); | ||
return(mdoc_nerr(m, m->last, EMALLOC)); | v->value[0] = mandoc_strdup(p); | ||
if (NULL == (v->value[0] = strdup(p))) | |||
return(mdoc_nerr(m, m->last, EMALLOC)); | |||
return(1); | return(1); | ||
} | } |