version 1.2, 2010/12/28 13:47:38 |
version 1.8, 2011/01/09 05:38:23 |
|
|
* 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. |
*/ |
*/ |
|
#include <ctype.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
|
#include "mandoc.h" |
#include "libroff.h" |
#include "libroff.h" |
|
|
enum tbl_ident { |
enum tbl_ident { |
Line 46 struct tbl_phrase { |
|
Line 48 struct tbl_phrase { |
|
/* Handle Commonwealth/American spellings. */ |
/* Handle Commonwealth/American spellings. */ |
#define KEY_MAXKEYS 14 |
#define KEY_MAXKEYS 14 |
|
|
|
/* Maximum length of key name string. */ |
|
#define KEY_MAXNAME 13 |
|
|
|
/* Maximum length of key number size. */ |
|
#define KEY_MAXNUMSZ 10 |
|
|
static const struct tbl_phrase keys[KEY_MAXKEYS] = { |
static const struct tbl_phrase keys[KEY_MAXKEYS] = { |
{ "center", TBL_OPT_CENTRE, KEY_CENTRE}, |
{ "center", TBL_OPT_CENTRE, KEY_CENTRE}, |
{ "centre", TBL_OPT_CENTRE, KEY_CENTRE}, |
{ "centre", TBL_OPT_CENTRE, KEY_CENTRE}, |
Line 63 static const struct tbl_phrase keys[KEY_MAXKEYS] = { |
|
Line 71 static const struct tbl_phrase keys[KEY_MAXKEYS] = { |
|
{ "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, |
{ "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, |
}; |
}; |
|
|
static int arg(struct tbl *, int, const char *, int *, int); |
static int arg(struct tbl_node *, int, |
static int opt(struct tbl *, int, const char *, int *); |
const char *, int *, enum tbl_ident); |
|
static void opt(struct tbl_node *, int, |
|
const char *, int *); |
|
|
static int |
static int |
arg(struct tbl *tbl, int ln, const char *p, int *pos, int key) |
arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key) |
{ |
{ |
int sv; |
int i; |
|
char buf[KEY_MAXNUMSZ]; |
|
|
again: |
while (isspace((unsigned char)p[*pos])) |
sv = *pos; |
(*pos)++; |
|
|
switch (tbl_next(tbl, p, pos)) { |
/* Arguments always begin with a parenthesis. */ |
case (TBL_TOK_OPENPAREN): |
|
break; |
if ('(' != p[*pos]) { |
case (TBL_TOK_SPACE): |
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos); |
/* FALLTHROUGH */ |
|
case (TBL_TOK_TAB): |
|
goto again; |
|
default: |
|
return(0); |
return(0); |
} |
} |
|
|
sv = *pos; |
(*pos)++; |
|
|
switch (tbl_next(tbl, p, pos)) { |
/* |
case (TBL_TOK__MAX): |
* The arguments can be ANY value, so we can't just stop at the |
break; |
* next close parenthesis (the argument can be a closed |
default: |
* parenthesis itself). |
return(0); |
*/ |
} |
|
|
|
switch (key) { |
switch (key) { |
case (KEY_DELIM): |
case (KEY_DELIM): |
/* FIXME: cache this value. */ |
if ('\0' == p[(*pos)++]) { |
if (2 != strlen(tbl->buf)) |
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); |
return(0); |
return(0); |
tbl->delims[0] = tbl->buf[0]; |
} |
tbl->delims[1] = tbl->buf[1]; |
|
break; |
if ('\0' == p[(*pos)++]) { |
case (KEY_TAB): |
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); |
/* FIXME: cache this value. */ |
|
if (1 != strlen(tbl->buf)) |
|
return(0); |
return(0); |
tbl->tab = tbl->buf[0]; |
} |
break; |
break; |
|
case (KEY_TAB): |
|
if ('\0' != (tbl->opts.tab = p[(*pos)++])) |
|
break; |
|
|
|
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); |
|
return(0); |
case (KEY_LINESIZE): |
case (KEY_LINESIZE): |
if ((tbl->linesize = atoi(tbl->buf)) <= 0) |
for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) { |
return(0); |
buf[i] = p[*pos]; |
break; |
if ( ! isdigit((unsigned char)buf[i])) |
|
break; |
|
} |
|
|
|
if (i < KEY_MAXNUMSZ) { |
|
buf[i] = '\0'; |
|
tbl->opts.linesize = atoi(buf); |
|
break; |
|
} |
|
|
|
(*tbl->msg)(MANDOCERR_TBL, tbl->data, ln, *pos, NULL); |
|
return(0); |
case (KEY_DPOINT): |
case (KEY_DPOINT): |
/* FIXME: cache this value. */ |
if ('\0' != (tbl->opts.decimal = p[(*pos)++])) |
if (1 != strlen(tbl->buf)) |
break; |
return(0); |
|
tbl->decimal = tbl->buf[0]; |
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); |
break; |
return(0); |
default: |
default: |
abort(); |
abort(); |
|
/* NOTREACHED */ |
} |
} |
|
|
sv = *pos; |
/* End with a close parenthesis. */ |
|
|
switch (tbl_next(tbl, p, pos)) { |
if (')' == p[(*pos)++]) |
case (TBL_TOK_CLOSEPAREN): |
return(1); |
break; |
|
default: |
|
return(0); |
|
} |
|
|
|
return(1); |
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); |
|
return(0); |
} |
} |
|
|
|
static void |
static int |
opt(struct tbl_node *tbl, int ln, const char *p, int *pos) |
opt(struct tbl *tbl, int ln, const char *p, int *pos) |
|
{ |
{ |
int i, sv; |
int i, sv; |
|
char buf[KEY_MAXNAME]; |
|
|
again: |
|
sv = *pos; |
|
|
|
/* |
/* |
|
* Parse individual options from the stream as surrounded by |
|
* this goto. Each pass through the routine parses out a single |
|
* option and registers it. Option arguments are processed in |
|
* the arg() function. |
|
*/ |
|
|
|
again: /* |
* EBNF describing this section: |
* EBNF describing this section: |
* |
* |
* options ::= option_list [:space:]* [;][\n] |
* options ::= option_list [:space:]* [;][\n] |
|
|
* args ::= [:space:]* [(] [:alpha:]+ [)] |
* args ::= [:space:]* [(] [:alpha:]+ [)] |
*/ |
*/ |
|
|
switch (tbl_next(tbl, p, pos)) { |
while (isspace((unsigned char)p[*pos])) |
case (TBL_TOK__MAX): |
(*pos)++; |
break; |
|
case (TBL_TOK_SPACE): |
/* Safe exit point. */ |
/* FALLTHROUGH */ |
|
case (TBL_TOK_TAB): |
if (';' == p[*pos]) |
goto again; |
return; |
case (TBL_TOK_SEMICOLON): |
|
tbl->part = TBL_PART_LAYOUT; |
/* Copy up to first non-alpha character. */ |
return(1); |
|
default: |
for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) { |
return(0); |
buf[i] = tolower((unsigned char)p[*pos]); |
|
if ( ! isalpha((unsigned char)buf[i])) |
|
break; |
} |
} |
|
|
|
/* Exit if buffer is empty (or overrun). */ |
|
|
|
if (KEY_MAXNAME == i || 0 == i) { |
|
TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos); |
|
return; |
|
} |
|
|
|
buf[i] = '\0'; |
|
|
|
while (isspace((unsigned char)p[*pos])) |
|
(*pos)++; |
|
|
|
/* |
|
* Look through all of the available keys to find one that |
|
* matches the input. FIXME: hashtable this. |
|
*/ |
|
|
for (i = 0; i < KEY_MAXKEYS; i++) { |
for (i = 0; i < KEY_MAXKEYS; i++) { |
/* FIXME: hashtable this? */ |
if (strcmp(buf, keys[i].name)) |
if (strcasecmp(tbl->buf, keys[i].name)) |
|
continue; |
continue; |
|
|
|
/* |
|
* Note: this is more difficult to recover from, as we |
|
* can be anywhere in the option sequence and it's |
|
* harder to jump to the next. Meanwhile, just bail out |
|
* of the sequence altogether. |
|
*/ |
|
|
if (keys[i].key) |
if (keys[i].key) |
tbl->opts |= keys[i].key; |
tbl->opts.opts |= keys[i].key; |
else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) |
else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) |
return(0); |
return; |
|
|
break; |
break; |
} |
} |
|
|
|
/* |
|
* Allow us to recover from bad options by continuing to another |
|
* parse sequence. |
|
*/ |
|
|
if (KEY_MAXKEYS == i) |
if (KEY_MAXKEYS == i) |
return(0); |
TBL_MSG(tbl, MANDOCERR_TBLOPT, ln, sv); |
|
|
return(opt(tbl, ln, p, pos)); |
goto again; |
|
/* NOTREACHED */ |
} |
} |
|
|
int |
int |
tbl_option(struct tbl *tbl, int ln, const char *p) |
tbl_option(struct tbl_node *tbl, int ln, const char *p) |
{ |
{ |
int pos; |
int pos; |
|
|
|
/* |
|
* Table options are always on just one line, so automatically |
|
* switch into the next input mode here. |
|
*/ |
|
tbl->part = TBL_PART_LAYOUT; |
|
|
pos = 0; |
pos = 0; |
return(opt(tbl, ln, p, &pos)); |
opt(tbl, ln, p, &pos); |
|
|
|
/* Always succeed. */ |
|
return(1); |
} |
} |