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