]> git.cameronkatri.com Git - mandoc.git/blob - tbl_opts.c
Have mandoc-db use config.h for strlcat(). Then create the btree
[mandoc.git] / tbl_opts.c
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 }