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