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