1241675Suqs/* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * 5241675Suqs * Permission to use, copy, modify, and distribute this software for any 6241675Suqs * purpose with or without fee is hereby granted, provided that the above 7241675Suqs * copyright notice and this permission notice appear in all copies. 8241675Suqs * 9241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16241675Suqs */ 17241675Suqs#ifdef HAVE_CONFIG_H 18241675Suqs#include "config.h" 19241675Suqs#endif 20241675Suqs 21241675Suqs#include <ctype.h> 22241675Suqs#include <stdio.h> 23241675Suqs#include <stdlib.h> 24241675Suqs#include <string.h> 25241675Suqs 26241675Suqs#include "mandoc.h" 27241675Suqs#include "libmandoc.h" 28241675Suqs#include "libroff.h" 29241675Suqs 30241675Suqsenum tbl_ident { 31241675Suqs KEY_CENTRE = 0, 32241675Suqs KEY_DELIM, 33241675Suqs KEY_EXPAND, 34241675Suqs KEY_BOX, 35241675Suqs KEY_DBOX, 36241675Suqs KEY_ALLBOX, 37241675Suqs KEY_TAB, 38241675Suqs KEY_LINESIZE, 39241675Suqs KEY_NOKEEP, 40241675Suqs KEY_DPOINT, 41241675Suqs KEY_NOSPACE, 42241675Suqs KEY_FRAME, 43241675Suqs KEY_DFRAME, 44241675Suqs KEY_MAX 45241675Suqs}; 46241675Suqs 47241675Suqsstruct tbl_phrase { 48241675Suqs const char *name; 49241675Suqs int key; 50241675Suqs enum tbl_ident ident; 51241675Suqs}; 52241675Suqs 53241675Suqs/* Handle Commonwealth/American spellings. */ 54241675Suqs#define KEY_MAXKEYS 14 55241675Suqs 56241675Suqs/* Maximum length of key name string. */ 57241675Suqs#define KEY_MAXNAME 13 58241675Suqs 59241675Suqs/* Maximum length of key number size. */ 60241675Suqs#define KEY_MAXNUMSZ 10 61241675Suqs 62241675Suqsstatic const struct tbl_phrase keys[KEY_MAXKEYS] = { 63241675Suqs { "center", TBL_OPT_CENTRE, KEY_CENTRE}, 64241675Suqs { "centre", TBL_OPT_CENTRE, KEY_CENTRE}, 65241675Suqs { "delim", 0, KEY_DELIM}, 66241675Suqs { "expand", TBL_OPT_EXPAND, KEY_EXPAND}, 67241675Suqs { "box", TBL_OPT_BOX, KEY_BOX}, 68241675Suqs { "doublebox", TBL_OPT_DBOX, KEY_DBOX}, 69241675Suqs { "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX}, 70241675Suqs { "frame", TBL_OPT_BOX, KEY_FRAME}, 71241675Suqs { "doubleframe", TBL_OPT_DBOX, KEY_DFRAME}, 72241675Suqs { "tab", 0, KEY_TAB}, 73241675Suqs { "linesize", 0, KEY_LINESIZE}, 74241675Suqs { "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP}, 75241675Suqs { "decimalpoint", 0, KEY_DPOINT}, 76241675Suqs { "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, 77241675Suqs}; 78241675Suqs 79241675Suqsstatic int arg(struct tbl_node *, int, 80241675Suqs const char *, int *, enum tbl_ident); 81241675Suqsstatic void opt(struct tbl_node *, int, 82241675Suqs const char *, int *); 83241675Suqs 84241675Suqsstatic int 85241675Suqsarg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key) 86241675Suqs{ 87241675Suqs int i; 88241675Suqs char buf[KEY_MAXNUMSZ]; 89241675Suqs 90241675Suqs while (isspace((unsigned char)p[*pos])) 91241675Suqs (*pos)++; 92241675Suqs 93241675Suqs /* Arguments always begin with a parenthesis. */ 94241675Suqs 95241675Suqs if ('(' != p[*pos]) { 96241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, 97241675Suqs ln, *pos, NULL); 98241675Suqs return(0); 99241675Suqs } 100241675Suqs 101241675Suqs (*pos)++; 102241675Suqs 103241675Suqs /* 104241675Suqs * The arguments can be ANY value, so we can't just stop at the 105241675Suqs * next close parenthesis (the argument can be a closed 106241675Suqs * parenthesis itself). 107241675Suqs */ 108241675Suqs 109241675Suqs switch (key) { 110241675Suqs case (KEY_DELIM): 111241675Suqs if ('\0' == p[(*pos)++]) { 112241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, 113241675Suqs ln, *pos - 1, NULL); 114241675Suqs return(0); 115241675Suqs } 116241675Suqs 117241675Suqs if ('\0' == p[(*pos)++]) { 118241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, 119241675Suqs ln, *pos - 1, NULL); 120241675Suqs return(0); 121241675Suqs } 122241675Suqs break; 123241675Suqs case (KEY_TAB): 124241675Suqs if ('\0' != (tbl->opts.tab = p[(*pos)++])) 125241675Suqs break; 126241675Suqs 127241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, 128241675Suqs ln, *pos - 1, NULL); 129241675Suqs return(0); 130241675Suqs case (KEY_LINESIZE): 131241675Suqs for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) { 132241675Suqs buf[i] = p[*pos]; 133241675Suqs if ( ! isdigit((unsigned char)buf[i])) 134241675Suqs break; 135241675Suqs } 136241675Suqs 137241675Suqs if (i < KEY_MAXNUMSZ) { 138241675Suqs buf[i] = '\0'; 139241675Suqs tbl->opts.linesize = atoi(buf); 140241675Suqs break; 141241675Suqs } 142241675Suqs 143241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 144241675Suqs return(0); 145241675Suqs case (KEY_DPOINT): 146241675Suqs if ('\0' != (tbl->opts.decimal = p[(*pos)++])) 147241675Suqs break; 148241675Suqs 149241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, 150241675Suqs ln, *pos - 1, NULL); 151241675Suqs return(0); 152241675Suqs default: 153241675Suqs abort(); 154241675Suqs /* NOTREACHED */ 155241675Suqs } 156241675Suqs 157241675Suqs /* End with a close parenthesis. */ 158241675Suqs 159241675Suqs if (')' == p[(*pos)++]) 160241675Suqs return(1); 161241675Suqs 162241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL); 163241675Suqs return(0); 164241675Suqs} 165241675Suqs 166241675Suqsstatic void 167241675Suqsopt(struct tbl_node *tbl, int ln, const char *p, int *pos) 168241675Suqs{ 169241675Suqs int i, sv; 170241675Suqs char buf[KEY_MAXNAME]; 171241675Suqs 172241675Suqs /* 173241675Suqs * Parse individual options from the stream as surrounded by 174241675Suqs * this goto. Each pass through the routine parses out a single 175241675Suqs * option and registers it. Option arguments are processed in 176241675Suqs * the arg() function. 177241675Suqs */ 178241675Suqs 179241675Suqsagain: /* 180241675Suqs * EBNF describing this section: 181241675Suqs * 182241675Suqs * options ::= option_list [:space:]* [;][\n] 183241675Suqs * option_list ::= option option_tail 184241675Suqs * option_tail ::= [:space:]+ option_list | 185241675Suqs * ::= epsilon 186241675Suqs * option ::= [:alpha:]+ args 187241675Suqs * args ::= [:space:]* [(] [:alpha:]+ [)] 188241675Suqs */ 189241675Suqs 190241675Suqs while (isspace((unsigned char)p[*pos])) 191241675Suqs (*pos)++; 192241675Suqs 193241675Suqs /* Safe exit point. */ 194241675Suqs 195241675Suqs if (';' == p[*pos]) 196241675Suqs return; 197241675Suqs 198241675Suqs /* Copy up to first non-alpha character. */ 199241675Suqs 200241675Suqs for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) { 201241675Suqs buf[i] = (char)tolower((unsigned char)p[*pos]); 202241675Suqs if ( ! isalpha((unsigned char)buf[i])) 203241675Suqs break; 204241675Suqs } 205241675Suqs 206241675Suqs /* Exit if buffer is empty (or overrun). */ 207241675Suqs 208241675Suqs if (KEY_MAXNAME == i || 0 == i) { 209241675Suqs mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 210241675Suqs return; 211241675Suqs } 212241675Suqs 213241675Suqs buf[i] = '\0'; 214241675Suqs 215241675Suqs while (isspace((unsigned char)p[*pos])) 216241675Suqs (*pos)++; 217241675Suqs 218241675Suqs /* 219241675Suqs * Look through all of the available keys to find one that 220241675Suqs * matches the input. FIXME: hashtable this. 221241675Suqs */ 222241675Suqs 223241675Suqs for (i = 0; i < KEY_MAXKEYS; i++) { 224241675Suqs if (strcmp(buf, keys[i].name)) 225241675Suqs continue; 226241675Suqs 227241675Suqs /* 228241675Suqs * Note: this is more difficult to recover from, as we 229241675Suqs * can be anywhere in the option sequence and it's 230241675Suqs * harder to jump to the next. Meanwhile, just bail out 231241675Suqs * of the sequence altogether. 232241675Suqs */ 233241675Suqs 234241675Suqs if (keys[i].key) 235241675Suqs tbl->opts.opts |= keys[i].key; 236241675Suqs else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) 237241675Suqs return; 238241675Suqs 239241675Suqs break; 240241675Suqs } 241241675Suqs 242241675Suqs /* 243241675Suqs * Allow us to recover from bad options by continuing to another 244241675Suqs * parse sequence. 245241675Suqs */ 246241675Suqs 247241675Suqs if (KEY_MAXKEYS == i) 248241675Suqs mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL); 249241675Suqs 250241675Suqs goto again; 251241675Suqs /* NOTREACHED */ 252241675Suqs} 253241675Suqs 254241675Suqsint 255241675Suqstbl_option(struct tbl_node *tbl, int ln, const char *p) 256241675Suqs{ 257241675Suqs int pos; 258241675Suqs 259241675Suqs /* 260241675Suqs * Table options are always on just one line, so automatically 261241675Suqs * switch into the next input mode here. 262241675Suqs */ 263241675Suqs tbl->part = TBL_PART_LAYOUT; 264241675Suqs 265241675Suqs pos = 0; 266241675Suqs opt(tbl, ln, p, &pos); 267241675Suqs 268241675Suqs /* Always succeed. */ 269241675Suqs return(1); 270241675Suqs} 271