Merge branch 'vendor/FILE'
[dragonfly.git] / contrib / mdocml / 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 }