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