1 /* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 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 static int 85 arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key) 86 { 87 int i; 88 char buf[KEY_MAXNUMSZ]; 89 90 while (isspace((unsigned char)p[*pos])) 91 (*pos)++; 92 93 /* Arguments always begin with a parenthesis. */ 94 95 if ('(' != p[*pos]) { 96 mandoc_msg(MANDOCERR_TBL, tbl->parse, 97 ln, *pos, NULL); 98 return(0); 99 } 100 101 (*pos)++; 102 103 /* 104 * The arguments can be ANY value, so we can't just stop at the 105 * next close parenthesis (the argument can be a closed 106 * parenthesis itself). 107 */ 108 109 switch (key) { 110 case (KEY_DELIM): 111 if ('\0' == p[(*pos)++]) { 112 mandoc_msg(MANDOCERR_TBL, tbl->parse, 113 ln, *pos - 1, NULL); 114 return(0); 115 } 116 117 if ('\0' == p[(*pos)++]) { 118 mandoc_msg(MANDOCERR_TBL, tbl->parse, 119 ln, *pos - 1, NULL); 120 return(0); 121 } 122 break; 123 case (KEY_TAB): 124 if ('\0' != (tbl->opts.tab = p[(*pos)++])) 125 break; 126 127 mandoc_msg(MANDOCERR_TBL, tbl->parse, 128 ln, *pos - 1, NULL); 129 return(0); 130 case (KEY_LINESIZE): 131 for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) { 132 buf[i] = p[*pos]; 133 if ( ! isdigit((unsigned char)buf[i])) 134 break; 135 } 136 137 if (i < KEY_MAXNUMSZ) { 138 buf[i] = '\0'; 139 tbl->opts.linesize = atoi(buf); 140 break; 141 } 142 143 mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 144 return(0); 145 case (KEY_DPOINT): 146 if ('\0' != (tbl->opts.decimal = p[(*pos)++])) 147 break; 148 149 mandoc_msg(MANDOCERR_TBL, tbl->parse, 150 ln, *pos - 1, NULL); 151 return(0); 152 default: 153 abort(); 154 /* NOTREACHED */ 155 } 156 157 /* End with a close parenthesis. */ 158 159 if (')' == p[(*pos)++]) 160 return(1); 161 162 mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL); 163 return(0); 164 } 165 166 static void 167 opt(struct tbl_node *tbl, int ln, const char *p, int *pos) 168 { 169 int i, sv; 170 char buf[KEY_MAXNAME]; 171 172 /* 173 * Parse individual options from the stream as surrounded by 174 * this goto. Each pass through the routine parses out a single 175 * option and registers it. Option arguments are processed in 176 * the arg() function. 177 */ 178 179 again: /* 180 * EBNF describing this section: 181 * 182 * options ::= option_list [:space:]* [;][\n] 183 * option_list ::= option option_tail 184 * option_tail ::= [:space:]+ option_list | 185 * ::= epsilon 186 * option ::= [:alpha:]+ args 187 * args ::= [:space:]* [(] [:alpha:]+ [)] 188 */ 189 190 while (isspace((unsigned char)p[*pos])) 191 (*pos)++; 192 193 /* Safe exit point. */ 194 195 if (';' == p[*pos]) 196 return; 197 198 /* Copy up to first non-alpha character. */ 199 200 for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) { 201 buf[i] = (char)tolower((unsigned char)p[*pos]); 202 if ( ! isalpha((unsigned char)buf[i])) 203 break; 204 } 205 206 /* Exit if buffer is empty (or overrun). */ 207 208 if (KEY_MAXNAME == i || 0 == i) { 209 mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL); 210 return; 211 } 212 213 buf[i] = '\0'; 214 215 while (isspace((unsigned char)p[*pos])) 216 (*pos)++; 217 218 /* 219 * Look through all of the available keys to find one that 220 * matches the input. FIXME: hashtable this. 221 */ 222 223 for (i = 0; i < KEY_MAXKEYS; i++) { 224 if (strcmp(buf, keys[i].name)) 225 continue; 226 227 /* 228 * Note: this is more difficult to recover from, as we 229 * can be anywhere in the option sequence and it's 230 * harder to jump to the next. Meanwhile, just bail out 231 * of the sequence altogether. 232 */ 233 234 if (keys[i].key) 235 tbl->opts.opts |= keys[i].key; 236 else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) 237 return; 238 239 break; 240 } 241 242 /* 243 * Allow us to recover from bad options by continuing to another 244 * parse sequence. 245 */ 246 247 if (KEY_MAXKEYS == i) 248 mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL); 249 250 goto again; 251 /* NOTREACHED */ 252 } 253 254 int 255 tbl_option(struct tbl_node *tbl, int ln, const char *p) 256 { 257 int pos; 258 259 /* 260 * Table options are always on just one line, so automatically 261 * switch into the next input mode here. 262 */ 263 tbl->part = TBL_PART_LAYOUT; 264 265 pos = 0; 266 opt(tbl, ln, p, &pos); 267 268 /* Always succeed. */ 269 return(1); 270 }