1*95c635efSGarrett D'Amore /* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */ 2*95c635efSGarrett D'Amore /* 3*95c635efSGarrett D'Amore * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*95c635efSGarrett D'Amore * 5*95c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 6*95c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 7*95c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 8*95c635efSGarrett D'Amore * 9*95c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*95c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*95c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*95c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*95c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*95c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*95c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*95c635efSGarrett D'Amore */ 17*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H 18*95c635efSGarrett D'Amore #include "config.h" 19*95c635efSGarrett D'Amore #endif 20*95c635efSGarrett D'Amore 21*95c635efSGarrett D'Amore #include <ctype.h> 22*95c635efSGarrett D'Amore #include <stdio.h> 23*95c635efSGarrett D'Amore #include <stdlib.h> 24*95c635efSGarrett D'Amore #include <string.h> 25*95c635efSGarrett D'Amore 26*95c635efSGarrett D'Amore #include "mandoc.h" 27*95c635efSGarrett D'Amore #include "libmandoc.h" 28*95c635efSGarrett D'Amore #include "libroff.h" 29*95c635efSGarrett D'Amore 30*95c635efSGarrett D'Amore enum tbl_ident { 31*95c635efSGarrett D'Amore KEY_CENTRE = 0, 32*95c635efSGarrett D'Amore KEY_DELIM, 33*95c635efSGarrett D'Amore KEY_EXPAND, 34*95c635efSGarrett D'Amore KEY_BOX, 35*95c635efSGarrett D'Amore KEY_DBOX, 36*95c635efSGarrett D'Amore KEY_ALLBOX, 37*95c635efSGarrett D'Amore KEY_TAB, 38*95c635efSGarrett D'Amore KEY_LINESIZE, 39*95c635efSGarrett D'Amore KEY_NOKEEP, 40*95c635efSGarrett D'Amore KEY_DPOINT, 41*95c635efSGarrett D'Amore KEY_NOSPACE, 42*95c635efSGarrett D'Amore KEY_FRAME, 43*95c635efSGarrett D'Amore KEY_DFRAME, 44*95c635efSGarrett D'Amore KEY_MAX 45*95c635efSGarrett D'Amore }; 46*95c635efSGarrett D'Amore 47*95c635efSGarrett D'Amore struct tbl_phrase { 48*95c635efSGarrett D'Amore const char *name; 49*95c635efSGarrett D'Amore int key; 50*95c635efSGarrett D'Amore enum tbl_ident ident; 51*95c635efSGarrett D'Amore }; 52*95c635efSGarrett D'Amore 53*95c635efSGarrett D'Amore /* Handle Commonwealth/American spellings. */ 54*95c635efSGarrett D'Amore #define KEY_MAXKEYS 14 55*95c635efSGarrett D'Amore 56*95c635efSGarrett D'Amore /* Maximum length of key name string. */ 57*95c635efSGarrett D'Amore #define KEY_MAXNAME 13 58*95c635efSGarrett D'Amore 59*95c635efSGarrett D'Amore /* Maximum length of key number size. */ 60*95c635efSGarrett D'Amore #define KEY_MAXNUMSZ 10 61*95c635efSGarrett D'Amore 62*95c635efSGarrett D'Amore static const struct tbl_phrase keys[KEY_MAXKEYS] = { 63*95c635efSGarrett D'Amore { "center", TBL_OPT_CENTRE, KEY_CENTRE}, 64*95c635efSGarrett D'Amore { "centre", TBL_OPT_CENTRE, KEY_CENTRE}, 65*95c635efSGarrett D'Amore { "delim", 0, KEY_DELIM}, 66*95c635efSGarrett D'Amore { "expand", TBL_OPT_EXPAND, KEY_EXPAND}, 67*95c635efSGarrett D'Amore { "box", TBL_OPT_BOX, KEY_BOX}, 68*95c635efSGarrett D'Amore { "doublebox", TBL_OPT_DBOX, KEY_DBOX}, 69*95c635efSGarrett D'Amore { "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX}, 70*95c635efSGarrett D'Amore { "frame", TBL_OPT_BOX, KEY_FRAME}, 71*95c635efSGarrett D'Amore { "doubleframe", TBL_OPT_DBOX, KEY_DFRAME}, 72*95c635efSGarrett D'Amore { "tab", 0, KEY_TAB}, 73*95c635efSGarrett D'Amore { "linesize", 0, KEY_LINESIZE}, 74*95c635efSGarrett D'Amore { "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP}, 75*95c635efSGarrett D'Amore { "decimalpoint", 0, KEY_DPOINT}, 76*95c635efSGarrett D'Amore { "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, 77*95c635efSGarrett D'Amore }; 78*95c635efSGarrett D'Amore 79*95c635efSGarrett D'Amore static int arg(struct tbl_node *, int, 80*95c635efSGarrett D'Amore const char *, int *, enum tbl_ident); 81*95c635efSGarrett D'Amore static void opt(struct tbl_node *, int, 82*95c635efSGarrett D'Amore const char *, int *); 83*95c635efSGarrett D'Amore 84*95c635efSGarrett D'Amore static int 85*95c635efSGarrett D'Amore arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key) 86*95c635efSGarrett D'Amore { 87*95c635efSGarrett D'Amore int i; 88*95c635efSGarrett D'Amore char buf[KEY_MAXNUMSZ]; 89*95c635efSGarrett D'Amore 90*95c635efSGarrett D'Amore while (isspace((unsigned char)p[*pos])) 91*95c635efSGarrett D'Amore (*pos)++; 92*95c635efSGarrett D'Amore 93*95c635efSGarrett D'Amore /* Arguments always begin with a parenthesis. */ 94*95c635efSGarrett D'Amore 95*95c635efSGarrett D'Amore if ('(' != p[*pos]) { 96*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, 97*95c635efSGarrett D'Amore ln, *pos, NULL); 98*95c635efSGarrett D'Amore return(0); 99*95c635efSGarrett D'Amore } 100*95c635efSGarrett D'Amore 101*95c635efSGarrett D'Amore (*pos)++; 102*95c635efSGarrett D'Amore 103*95c635efSGarrett D'Amore /* 104*95c635efSGarrett D'Amore * The arguments can be ANY value, so we can't just stop at the 105*95c635efSGarrett D'Amore * next close parenthesis (the argument can be a closed 106*95c635efSGarrett D'Amore * parenthesis itself). 107*95c635efSGarrett D'Amore */ 108*95c635efSGarrett D'Amore 109*95c635efSGarrett D'Amore switch (key) { 110*95c635efSGarrett D'Amore case (KEY_DELIM): 111*95c635efSGarrett D'Amore if ('\0' == p[(*pos)++]) { 112*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, 113*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 114*95c635efSGarrett D'Amore return(0); 115*95c635efSGarrett D'Amore } 116*95c635efSGarrett D'Amore 117*95c635efSGarrett D'Amore if ('\0' == p[(*pos)++]) { 118*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, 119*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 120*95c635efSGarrett D'Amore return(0); 121*95c635efSGarrett D'Amore } 122*95c635efSGarrett D'Amore break; 123*95c635efSGarrett D'Amore case (KEY_TAB): 124*95c635efSGarrett D'Amore if ('\0' != (tbl->opts.tab = p[(*pos)++])) 125*95c635efSGarrett D'Amore break; 126*95c635efSGarrett D'Amore 127*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, 128*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 129*95c635efSGarrett D'Amore return(0); 130*95c635efSGarrett D'Amore case (KEY_LINESIZE): 131*95c635efSGarrett D'Amore for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) { 132*95c635efSGarrett D'Amore buf[i] = p[*pos]; 133*95c635efSGarrett D'Amore if ( ! isdigit((unsigned char)buf[i])) 134*95c635efSGarrett D'Amore break; 135*95c635efSGarrett D'Amore } 136*95c635efSGarrett D'Amore 137*95c635efSGarrett D'Amore if (i < KEY_MAXNUMSZ) { 138*95c635efSGarrett D'Amore buf[i] = '\0'; 139*95c635efSGarrett D'Amore tbl->opts.linesize = atoi(buf); 140*95c635efSGarrett D'Amore break; 141*95c635efSGarrett D'Amore } 142*95c635efSGarrett D'Amore 143*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 144*95c635efSGarrett D'Amore return(0); 145*95c635efSGarrett D'Amore case (KEY_DPOINT): 146*95c635efSGarrett D'Amore if ('\0' != (tbl->opts.decimal = p[(*pos)++])) 147*95c635efSGarrett D'Amore break; 148*95c635efSGarrett D'Amore 149*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, 150*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 151*95c635efSGarrett D'Amore return(0); 152*95c635efSGarrett D'Amore default: 153*95c635efSGarrett D'Amore abort(); 154*95c635efSGarrett D'Amore /* NOTREACHED */ 155*95c635efSGarrett D'Amore } 156*95c635efSGarrett D'Amore 157*95c635efSGarrett D'Amore /* End with a close parenthesis. */ 158*95c635efSGarrett D'Amore 159*95c635efSGarrett D'Amore if (')' == p[(*pos)++]) 160*95c635efSGarrett D'Amore return(1); 161*95c635efSGarrett D'Amore 162*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL); 163*95c635efSGarrett D'Amore return(0); 164*95c635efSGarrett D'Amore } 165*95c635efSGarrett D'Amore 166*95c635efSGarrett D'Amore static void 167*95c635efSGarrett D'Amore opt(struct tbl_node *tbl, int ln, const char *p, int *pos) 168*95c635efSGarrett D'Amore { 169*95c635efSGarrett D'Amore int i, sv; 170*95c635efSGarrett D'Amore char buf[KEY_MAXNAME]; 171*95c635efSGarrett D'Amore 172*95c635efSGarrett D'Amore /* 173*95c635efSGarrett D'Amore * Parse individual options from the stream as surrounded by 174*95c635efSGarrett D'Amore * this goto. Each pass through the routine parses out a single 175*95c635efSGarrett D'Amore * option and registers it. Option arguments are processed in 176*95c635efSGarrett D'Amore * the arg() function. 177*95c635efSGarrett D'Amore */ 178*95c635efSGarrett D'Amore 179*95c635efSGarrett D'Amore again: /* 180*95c635efSGarrett D'Amore * EBNF describing this section: 181*95c635efSGarrett D'Amore * 182*95c635efSGarrett D'Amore * options ::= option_list [:space:]* [;][\n] 183*95c635efSGarrett D'Amore * option_list ::= option option_tail 184*95c635efSGarrett D'Amore * option_tail ::= [:space:]+ option_list | 185*95c635efSGarrett D'Amore * ::= epsilon 186*95c635efSGarrett D'Amore * option ::= [:alpha:]+ args 187*95c635efSGarrett D'Amore * args ::= [:space:]* [(] [:alpha:]+ [)] 188*95c635efSGarrett D'Amore */ 189*95c635efSGarrett D'Amore 190*95c635efSGarrett D'Amore while (isspace((unsigned char)p[*pos])) 191*95c635efSGarrett D'Amore (*pos)++; 192*95c635efSGarrett D'Amore 193*95c635efSGarrett D'Amore /* Safe exit point. */ 194*95c635efSGarrett D'Amore 195*95c635efSGarrett D'Amore if (';' == p[*pos]) 196*95c635efSGarrett D'Amore return; 197*95c635efSGarrett D'Amore 198*95c635efSGarrett D'Amore /* Copy up to first non-alpha character. */ 199*95c635efSGarrett D'Amore 200*95c635efSGarrett D'Amore for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) { 201*95c635efSGarrett D'Amore buf[i] = (char)tolower((unsigned char)p[*pos]); 202*95c635efSGarrett D'Amore if ( ! isalpha((unsigned char)buf[i])) 203*95c635efSGarrett D'Amore break; 204*95c635efSGarrett D'Amore } 205*95c635efSGarrett D'Amore 206*95c635efSGarrett D'Amore /* Exit if buffer is empty (or overrun). */ 207*95c635efSGarrett D'Amore 208*95c635efSGarrett D'Amore if (KEY_MAXNAME == i || 0 == i) { 209*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 210*95c635efSGarrett D'Amore return; 211*95c635efSGarrett D'Amore } 212*95c635efSGarrett D'Amore 213*95c635efSGarrett D'Amore buf[i] = '\0'; 214*95c635efSGarrett D'Amore 215*95c635efSGarrett D'Amore while (isspace((unsigned char)p[*pos])) 216*95c635efSGarrett D'Amore (*pos)++; 217*95c635efSGarrett D'Amore 218*95c635efSGarrett D'Amore /* 219*95c635efSGarrett D'Amore * Look through all of the available keys to find one that 220*95c635efSGarrett D'Amore * matches the input. FIXME: hashtable this. 221*95c635efSGarrett D'Amore */ 222*95c635efSGarrett D'Amore 223*95c635efSGarrett D'Amore for (i = 0; i < KEY_MAXKEYS; i++) { 224*95c635efSGarrett D'Amore if (strcmp(buf, keys[i].name)) 225*95c635efSGarrett D'Amore continue; 226*95c635efSGarrett D'Amore 227*95c635efSGarrett D'Amore /* 228*95c635efSGarrett D'Amore * Note: this is more difficult to recover from, as we 229*95c635efSGarrett D'Amore * can be anywhere in the option sequence and it's 230*95c635efSGarrett D'Amore * harder to jump to the next. Meanwhile, just bail out 231*95c635efSGarrett D'Amore * of the sequence altogether. 232*95c635efSGarrett D'Amore */ 233*95c635efSGarrett D'Amore 234*95c635efSGarrett D'Amore if (keys[i].key) 235*95c635efSGarrett D'Amore tbl->opts.opts |= keys[i].key; 236*95c635efSGarrett D'Amore else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) 237*95c635efSGarrett D'Amore return; 238*95c635efSGarrett D'Amore 239*95c635efSGarrett D'Amore break; 240*95c635efSGarrett D'Amore } 241*95c635efSGarrett D'Amore 242*95c635efSGarrett D'Amore /* 243*95c635efSGarrett D'Amore * Allow us to recover from bad options by continuing to another 244*95c635efSGarrett D'Amore * parse sequence. 245*95c635efSGarrett D'Amore */ 246*95c635efSGarrett D'Amore 247*95c635efSGarrett D'Amore if (KEY_MAXKEYS == i) 248*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL); 249*95c635efSGarrett D'Amore 250*95c635efSGarrett D'Amore goto again; 251*95c635efSGarrett D'Amore /* NOTREACHED */ 252*95c635efSGarrett D'Amore } 253*95c635efSGarrett D'Amore 254*95c635efSGarrett D'Amore int 255*95c635efSGarrett D'Amore tbl_option(struct tbl_node *tbl, int ln, const char *p) 256*95c635efSGarrett D'Amore { 257*95c635efSGarrett D'Amore int pos; 258*95c635efSGarrett D'Amore 259*95c635efSGarrett D'Amore /* 260*95c635efSGarrett D'Amore * Table options are always on just one line, so automatically 261*95c635efSGarrett D'Amore * switch into the next input mode here. 262*95c635efSGarrett D'Amore */ 263*95c635efSGarrett D'Amore tbl->part = TBL_PART_LAYOUT; 264*95c635efSGarrett D'Amore 265*95c635efSGarrett D'Amore pos = 0; 266*95c635efSGarrett D'Amore opt(tbl, ln, p, &pos); 267*95c635efSGarrett D'Amore 268*95c635efSGarrett D'Amore /* Always succeed. */ 269*95c635efSGarrett D'Amore return(1); 270*95c635efSGarrett D'Amore } 271