1241675Suqs/*	$Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4241675Suqs *
5241675Suqs * Permission to use, copy, modify, and distribute this software for any
6241675Suqs * purpose with or without fee is hereby granted, provided that the above
7241675Suqs * copyright notice and this permission notice appear in all copies.
8241675Suqs *
9241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16241675Suqs */
17241675Suqs#ifdef HAVE_CONFIG_H
18241675Suqs#include "config.h"
19241675Suqs#endif
20241675Suqs
21241675Suqs#include <ctype.h>
22241675Suqs#include <stdio.h>
23241675Suqs#include <stdlib.h>
24241675Suqs#include <string.h>
25241675Suqs
26241675Suqs#include "mandoc.h"
27241675Suqs#include "libmandoc.h"
28241675Suqs#include "libroff.h"
29241675Suqs
30241675Suqsenum	tbl_ident {
31241675Suqs	KEY_CENTRE = 0,
32241675Suqs	KEY_DELIM,
33241675Suqs	KEY_EXPAND,
34241675Suqs	KEY_BOX,
35241675Suqs	KEY_DBOX,
36241675Suqs	KEY_ALLBOX,
37241675Suqs	KEY_TAB,
38241675Suqs	KEY_LINESIZE,
39241675Suqs	KEY_NOKEEP,
40241675Suqs	KEY_DPOINT,
41241675Suqs	KEY_NOSPACE,
42241675Suqs	KEY_FRAME,
43241675Suqs	KEY_DFRAME,
44241675Suqs	KEY_MAX
45241675Suqs};
46241675Suqs
47241675Suqsstruct	tbl_phrase {
48241675Suqs	const char	*name;
49241675Suqs	int		 key;
50241675Suqs	enum tbl_ident	 ident;
51241675Suqs};
52241675Suqs
53241675Suqs/* Handle Commonwealth/American spellings. */
54241675Suqs#define	KEY_MAXKEYS	 14
55241675Suqs
56241675Suqs/* Maximum length of key name string. */
57241675Suqs#define	KEY_MAXNAME	 13
58241675Suqs
59241675Suqs/* Maximum length of key number size. */
60241675Suqs#define	KEY_MAXNUMSZ	 10
61241675Suqs
62241675Suqsstatic	const struct tbl_phrase keys[KEY_MAXKEYS] = {
63241675Suqs	{ "center",	 TBL_OPT_CENTRE,	KEY_CENTRE},
64241675Suqs	{ "centre",	 TBL_OPT_CENTRE,	KEY_CENTRE},
65241675Suqs	{ "delim",	 0,	       		KEY_DELIM},
66241675Suqs	{ "expand",	 TBL_OPT_EXPAND,	KEY_EXPAND},
67241675Suqs	{ "box",	 TBL_OPT_BOX,   	KEY_BOX},
68241675Suqs	{ "doublebox",	 TBL_OPT_DBOX,  	KEY_DBOX},
69241675Suqs	{ "allbox",	 TBL_OPT_ALLBOX,	KEY_ALLBOX},
70241675Suqs	{ "frame",	 TBL_OPT_BOX,		KEY_FRAME},
71241675Suqs	{ "doubleframe", TBL_OPT_DBOX,		KEY_DFRAME},
72241675Suqs	{ "tab",	 0,			KEY_TAB},
73241675Suqs	{ "linesize",	 0,			KEY_LINESIZE},
74241675Suqs	{ "nokeep",	 TBL_OPT_NOKEEP,	KEY_NOKEEP},
75241675Suqs	{ "decimalpoint", 0,			KEY_DPOINT},
76241675Suqs	{ "nospaces",	 TBL_OPT_NOSPACE,	KEY_NOSPACE},
77241675Suqs};
78241675Suqs
79241675Suqsstatic	int		 arg(struct tbl_node *, int,
80241675Suqs				const char *, int *, enum tbl_ident);
81241675Suqsstatic	void		 opt(struct tbl_node *, int,
82241675Suqs				const char *, int *);
83241675Suqs
84241675Suqsstatic int
85241675Suqsarg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
86241675Suqs{
87241675Suqs	int		 i;
88241675Suqs	char		 buf[KEY_MAXNUMSZ];
89241675Suqs
90241675Suqs	while (isspace((unsigned char)p[*pos]))
91241675Suqs		(*pos)++;
92241675Suqs
93241675Suqs	/* Arguments always begin with a parenthesis. */
94241675Suqs
95241675Suqs	if ('(' != p[*pos]) {
96241675Suqs		mandoc_msg(MANDOCERR_TBL, tbl->parse,
97241675Suqs				ln, *pos, NULL);
98241675Suqs		return(0);
99241675Suqs	}
100241675Suqs
101241675Suqs	(*pos)++;
102241675Suqs
103241675Suqs	/*
104241675Suqs	 * The arguments can be ANY value, so we can't just stop at the
105241675Suqs	 * next close parenthesis (the argument can be a closed
106241675Suqs	 * parenthesis itself).
107241675Suqs	 */
108241675Suqs
109241675Suqs	switch (key) {
110241675Suqs	case (KEY_DELIM):
111241675Suqs		if ('\0' == p[(*pos)++]) {
112241675Suqs			mandoc_msg(MANDOCERR_TBL, tbl->parse,
113241675Suqs					ln, *pos - 1, NULL);
114241675Suqs			return(0);
115241675Suqs		}
116241675Suqs
117241675Suqs		if ('\0' == p[(*pos)++]) {
118241675Suqs			mandoc_msg(MANDOCERR_TBL, tbl->parse,
119241675Suqs					ln, *pos - 1, NULL);
120241675Suqs			return(0);
121241675Suqs		}
122241675Suqs		break;
123241675Suqs	case (KEY_TAB):
124241675Suqs		if ('\0' != (tbl->opts.tab = p[(*pos)++]))
125241675Suqs			break;
126241675Suqs
127241675Suqs		mandoc_msg(MANDOCERR_TBL, tbl->parse,
128241675Suqs				ln, *pos - 1, NULL);
129241675Suqs		return(0);
130241675Suqs	case (KEY_LINESIZE):
131241675Suqs		for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) {
132241675Suqs			buf[i] = p[*pos];
133241675Suqs			if ( ! isdigit((unsigned char)buf[i]))
134241675Suqs				break;
135241675Suqs		}
136241675Suqs
137241675Suqs		if (i < KEY_MAXNUMSZ) {
138241675Suqs			buf[i] = '\0';
139241675Suqs			tbl->opts.linesize = atoi(buf);
140241675Suqs			break;
141241675Suqs		}
142241675Suqs
143241675Suqs		mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
144241675Suqs		return(0);
145241675Suqs	case (KEY_DPOINT):
146241675Suqs		if ('\0' != (tbl->opts.decimal = p[(*pos)++]))
147241675Suqs			break;
148241675Suqs
149241675Suqs		mandoc_msg(MANDOCERR_TBL, tbl->parse,
150241675Suqs				ln, *pos - 1, NULL);
151241675Suqs		return(0);
152241675Suqs	default:
153241675Suqs		abort();
154241675Suqs		/* NOTREACHED */
155241675Suqs	}
156241675Suqs
157241675Suqs	/* End with a close parenthesis. */
158241675Suqs
159241675Suqs	if (')' == p[(*pos)++])
160241675Suqs		return(1);
161241675Suqs
162241675Suqs	mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL);
163241675Suqs	return(0);
164241675Suqs}
165241675Suqs
166241675Suqsstatic void
167241675Suqsopt(struct tbl_node *tbl, int ln, const char *p, int *pos)
168241675Suqs{
169241675Suqs	int		 i, sv;
170241675Suqs	char		 buf[KEY_MAXNAME];
171241675Suqs
172241675Suqs	/*
173241675Suqs	 * Parse individual options from the stream as surrounded by
174241675Suqs	 * this goto.  Each pass through the routine parses out a single
175241675Suqs	 * option and registers it.  Option arguments are processed in
176241675Suqs	 * the arg() function.
177241675Suqs	 */
178241675Suqs
179241675Suqsagain:	/*
180241675Suqs	 * EBNF describing this section:
181241675Suqs	 *
182241675Suqs	 * options	::= option_list [:space:]* [;][\n]
183241675Suqs	 * option_list	::= option option_tail
184241675Suqs	 * option_tail	::= [:space:]+ option_list |
185241675Suqs	 * 		::= epsilon
186241675Suqs	 * option	::= [:alpha:]+ args
187241675Suqs	 * args		::= [:space:]* [(] [:alpha:]+ [)]
188241675Suqs	 */
189241675Suqs
190241675Suqs	while (isspace((unsigned char)p[*pos]))
191241675Suqs		(*pos)++;
192241675Suqs
193241675Suqs	/* Safe exit point. */
194241675Suqs
195241675Suqs	if (';' == p[*pos])
196241675Suqs		return;
197241675Suqs
198241675Suqs	/* Copy up to first non-alpha character. */
199241675Suqs
200241675Suqs	for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) {
201241675Suqs		buf[i] = (char)tolower((unsigned char)p[*pos]);
202241675Suqs		if ( ! isalpha((unsigned char)buf[i]))
203241675Suqs			break;
204241675Suqs	}
205241675Suqs
206241675Suqs	/* Exit if buffer is empty (or overrun). */
207241675Suqs
208241675Suqs	if (KEY_MAXNAME == i || 0 == i) {
209241675Suqs		mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
210241675Suqs		return;
211241675Suqs	}
212241675Suqs
213241675Suqs	buf[i] = '\0';
214241675Suqs
215241675Suqs	while (isspace((unsigned char)p[*pos]))
216241675Suqs		(*pos)++;
217241675Suqs
218241675Suqs	/*
219241675Suqs	 * Look through all of the available keys to find one that
220241675Suqs	 * matches the input.  FIXME: hashtable this.
221241675Suqs	 */
222241675Suqs
223241675Suqs	for (i = 0; i < KEY_MAXKEYS; i++) {
224241675Suqs		if (strcmp(buf, keys[i].name))
225241675Suqs			continue;
226241675Suqs
227241675Suqs		/*
228241675Suqs		 * Note: this is more difficult to recover from, as we
229241675Suqs		 * can be anywhere in the option sequence and it's
230241675Suqs		 * harder to jump to the next.  Meanwhile, just bail out
231241675Suqs		 * of the sequence altogether.
232241675Suqs		 */
233241675Suqs
234241675Suqs		if (keys[i].key)
235241675Suqs			tbl->opts.opts |= keys[i].key;
236241675Suqs		else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
237241675Suqs			return;
238241675Suqs
239241675Suqs		break;
240241675Suqs	}
241241675Suqs
242241675Suqs	/*
243241675Suqs	 * Allow us to recover from bad options by continuing to another
244241675Suqs	 * parse sequence.
245241675Suqs	 */
246241675Suqs
247241675Suqs	if (KEY_MAXKEYS == i)
248241675Suqs		mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL);
249241675Suqs
250241675Suqs	goto again;
251241675Suqs	/* NOTREACHED */
252241675Suqs}
253241675Suqs
254241675Suqsint
255241675Suqstbl_option(struct tbl_node *tbl, int ln, const char *p)
256241675Suqs{
257241675Suqs	int		 pos;
258241675Suqs
259241675Suqs	/*
260241675Suqs	 * Table options are always on just one line, so automatically
261241675Suqs	 * switch into the next input mode here.
262241675Suqs	 */
263241675Suqs	tbl->part = TBL_PART_LAYOUT;
264241675Suqs
265241675Suqs	pos = 0;
266241675Suqs	opt(tbl, ln, p, &pos);
267241675Suqs
268241675Suqs	/* Always succeed. */
269241675Suqs	return(1);
270241675Suqs}
271