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