version 1.2, 2010/12/28 13:47:38 |
version 1.22, 2018/12/12 21:54:35 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
|
* Copyright (c) 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 |
|
|
* 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 "config.h" |
|
|
|
#include <sys/types.h> |
|
|
|
#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 "tbl.h" |
|
#include "libmandoc.h" |
#include "libroff.h" |
#include "libroff.h" |
|
|
enum tbl_ident { |
#define KEY_DPOINT 0 |
KEY_CENTRE = 0, |
#define KEY_DELIM 1 |
KEY_DELIM, |
#define KEY_LINESIZE 2 |
KEY_EXPAND, |
#define KEY_TAB 3 |
KEY_BOX, |
|
KEY_DBOX, |
|
KEY_ALLBOX, |
|
KEY_TAB, |
|
KEY_LINESIZE, |
|
KEY_NOKEEP, |
|
KEY_DPOINT, |
|
KEY_NOSPACE, |
|
KEY_FRAME, |
|
KEY_DFRAME, |
|
KEY_MAX |
|
}; |
|
|
|
struct tbl_phrase { |
struct tbl_phrase { |
const char *name; |
const char *name; |
int key; |
int key; |
enum tbl_ident ident; |
|
}; |
}; |
|
|
/* Handle Commonwealth/American spellings. */ |
static const struct tbl_phrase keys[] = { |
#define KEY_MAXKEYS 14 |
{"decimalpoint", 0}, |
|
{"delim", 0}, |
static const struct tbl_phrase keys[KEY_MAXKEYS] = { |
{"linesize", 0}, |
{ "center", TBL_OPT_CENTRE, KEY_CENTRE}, |
{"tab", 0}, |
{ "centre", TBL_OPT_CENTRE, KEY_CENTRE}, |
{"allbox", TBL_OPT_ALLBOX | TBL_OPT_BOX}, |
{ "delim", 0, KEY_DELIM}, |
{"box", TBL_OPT_BOX}, |
{ "expand", TBL_OPT_EXPAND, KEY_EXPAND}, |
{"frame", TBL_OPT_BOX}, |
{ "box", TBL_OPT_BOX, KEY_BOX}, |
{"center", TBL_OPT_CENTRE}, |
{ "doublebox", TBL_OPT_DBOX, KEY_DBOX}, |
{"centre", TBL_OPT_CENTRE}, |
{ "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX}, |
{"doublebox", TBL_OPT_DBOX}, |
{ "frame", TBL_OPT_BOX, KEY_FRAME}, |
{"doubleframe", TBL_OPT_DBOX}, |
{ "doubleframe", TBL_OPT_DBOX, KEY_DFRAME}, |
{"expand", TBL_OPT_EXPAND}, |
{ "tab", 0, KEY_TAB}, |
{"nokeep", TBL_OPT_NOKEEP}, |
{ "linesize", 0, KEY_LINESIZE}, |
{"nospaces", TBL_OPT_NOSPACE}, |
{ "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP}, |
{"nowarn", TBL_OPT_NOWARN}, |
{ "decimalpoint", 0, KEY_DPOINT}, |
|
{ "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, |
|
}; |
}; |
|
|
static int arg(struct tbl *, int, const char *, int *, int); |
#define KEY_MAXKEYS ((int)(sizeof(keys)/sizeof(keys[0]))) |
static int opt(struct tbl *, int, const char *, int *); |
|
|
|
static int |
static void arg(struct tbl_node *, int, const char *, int *, int); |
arg(struct tbl *tbl, int ln, const char *p, int *pos, int key) |
|
{ |
|
int sv; |
|
|
|
again: |
|
sv = *pos; |
|
|
|
switch (tbl_next(tbl, p, pos)) { |
static void |
case (TBL_TOK_OPENPAREN): |
arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key) |
break; |
{ |
case (TBL_TOK_SPACE): |
int len, want; |
/* FALLTHROUGH */ |
|
case (TBL_TOK_TAB): |
|
goto again; |
|
default: |
|
return(0); |
|
} |
|
|
|
sv = *pos; |
while (p[*pos] == ' ' || p[*pos] == '\t') |
|
(*pos)++; |
|
|
switch (tbl_next(tbl, p, pos)) { |
/* Arguments are enclosed in parentheses. */ |
case (TBL_TOK__MAX): |
|
break; |
len = 0; |
default: |
if (p[*pos] == '(') { |
return(0); |
(*pos)++; |
|
while (p[*pos + len] != ')') |
|
len++; |
} |
} |
|
|
switch (key) { |
switch (key) { |
case (KEY_DELIM): |
case KEY_DELIM: |
/* FIXME: cache this value. */ |
mandoc_vmsg(MANDOCERR_TBLOPT_EQN, tbl->parse, |
if (2 != strlen(tbl->buf)) |
ln, *pos, "%.*s", len, p + *pos); |
return(0); |
want = 2; |
tbl->delims[0] = tbl->buf[0]; |
|
tbl->delims[1] = tbl->buf[1]; |
|
break; |
break; |
case (KEY_TAB): |
case KEY_TAB: |
/* FIXME: cache this value. */ |
want = 1; |
if (1 != strlen(tbl->buf)) |
if (len == want) |
return(0); |
tbl->opts.tab = p[*pos]; |
tbl->tab = tbl->buf[0]; |
|
break; |
break; |
case (KEY_LINESIZE): |
case KEY_LINESIZE: |
if ((tbl->linesize = atoi(tbl->buf)) <= 0) |
want = 0; |
return(0); |
|
break; |
break; |
case (KEY_DPOINT): |
case KEY_DPOINT: |
/* FIXME: cache this value. */ |
want = 1; |
if (1 != strlen(tbl->buf)) |
if (len == want) |
return(0); |
tbl->opts.decimal = p[*pos]; |
tbl->decimal = tbl->buf[0]; |
|
break; |
break; |
default: |
default: |
abort(); |
abort(); |
} |
} |
|
|
sv = *pos; |
if (len == 0) |
|
mandoc_msg(MANDOCERR_TBLOPT_NOARG, |
|
tbl->parse, ln, *pos, keys[key].name); |
|
else if (want && len != want) |
|
mandoc_vmsg(MANDOCERR_TBLOPT_ARGSZ, |
|
tbl->parse, ln, *pos, "%s want %d have %d", |
|
keys[key].name, want, len); |
|
|
switch (tbl_next(tbl, p, pos)) { |
*pos += len; |
case (TBL_TOK_CLOSEPAREN): |
if (p[*pos] == ')') |
break; |
(*pos)++; |
default: |
|
return(0); |
|
} |
|
|
|
return(1); |
|
} |
} |
|
|
|
/* |
static int |
* Parse one line of options up to the semicolon. |
opt(struct tbl *tbl, int ln, const char *p, int *pos) |
* Each option can be preceded by blanks and/or commas, |
|
* and some options are followed by arguments. |
|
*/ |
|
void |
|
tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs) |
{ |
{ |
int i, sv; |
int i, pos, len; |
|
|
again: |
pos = *offs; |
sv = *pos; |
for (;;) { |
|
while (p[pos] == ' ' || p[pos] == '\t' || p[pos] == ',') |
|
pos++; |
|
|
/* |
if (p[pos] == ';') { |
* EBNF describing this section: |
*offs = pos + 1; |
* |
return; |
* options ::= option_list [:space:]* [;][\n] |
} |
* option_list ::= option option_tail |
|
* option_tail ::= [:space:]+ option_list | |
|
* ::= epsilon |
|
* option ::= [:alpha:]+ args |
|
* args ::= [:space:]* [(] [:alpha:]+ [)] |
|
*/ |
|
|
|
switch (tbl_next(tbl, p, pos)) { |
/* Parse one option name. */ |
case (TBL_TOK__MAX): |
|
break; |
|
case (TBL_TOK_SPACE): |
|
/* FALLTHROUGH */ |
|
case (TBL_TOK_TAB): |
|
goto again; |
|
case (TBL_TOK_SEMICOLON): |
|
tbl->part = TBL_PART_LAYOUT; |
|
return(1); |
|
default: |
|
return(0); |
|
} |
|
|
|
for (i = 0; i < KEY_MAXKEYS; i++) { |
len = 0; |
/* FIXME: hashtable this? */ |
while (isalpha((unsigned char)p[pos + len])) |
if (strcasecmp(tbl->buf, keys[i].name)) |
len++; |
|
|
|
if (len == 0) { |
|
mandoc_vmsg(MANDOCERR_TBLOPT_ALPHA, |
|
tbl->parse, ln, pos, "%c", p[pos]); |
|
pos++; |
continue; |
continue; |
if (keys[i].key) |
} |
tbl->opts |= keys[i].key; |
|
else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) |
|
return(0); |
|
|
|
break; |
/* Look up the option name. */ |
} |
|
|
|
if (KEY_MAXKEYS == i) |
i = 0; |
return(0); |
while (i < KEY_MAXKEYS && |
|
(strncasecmp(p + pos, keys[i].name, len) || |
|
keys[i].name[len] != '\0')) |
|
i++; |
|
|
return(opt(tbl, ln, p, pos)); |
if (i == KEY_MAXKEYS) { |
} |
mandoc_vmsg(MANDOCERR_TBLOPT_BAD, tbl->parse, |
|
ln, pos, "%.*s", len, p + pos); |
|
pos += len; |
|
continue; |
|
} |
|
|
int |
/* Handle the option. */ |
tbl_option(struct tbl *tbl, int ln, const char *p) |
|
{ |
|
int pos; |
|
|
|
pos = 0; |
pos += len; |
return(opt(tbl, ln, p, &pos)); |
if (keys[i].key) |
|
tbl->opts.opts |= keys[i].key; |
|
else |
|
arg(tbl, ln, p, &pos, i); |
|
} |
} |
} |