]> git.cameronkatri.com Git - mandoc.git/blob - tbl_layout.c
Further apropos(1) speed optimization was trickier than anticipated.
[mandoc.git] / tbl_layout.c
1 /* $Id: tbl_layout.c,v 1.25 2014/03/28 23:26:25 schwarze Exp $ */
2 /*
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "mandoc.h"
28 #include "mandoc_aux.h"
29 #include "libmandoc.h"
30 #include "libroff.h"
31
32 struct tbl_phrase {
33 char name;
34 enum tbl_cellt key;
35 };
36
37 /*
38 * FIXME: we can make this parse a lot nicer by, when an error is
39 * encountered in a layout key, bailing to the next key (i.e. to the
40 * next whitespace then continuing).
41 */
42
43 #define KEYS_MAX 11
44
45 static const struct tbl_phrase keys[KEYS_MAX] = {
46 { 'c', TBL_CELL_CENTRE },
47 { 'r', TBL_CELL_RIGHT },
48 { 'l', TBL_CELL_LEFT },
49 { 'n', TBL_CELL_NUMBER },
50 { 's', TBL_CELL_SPAN },
51 { 'a', TBL_CELL_LONG },
52 { '^', TBL_CELL_DOWN },
53 { '-', TBL_CELL_HORIZ },
54 { '_', TBL_CELL_HORIZ },
55 { '=', TBL_CELL_DHORIZ }
56 };
57
58 static int mods(struct tbl_node *, struct tbl_cell *,
59 int, const char *, int *);
60 static int cell(struct tbl_node *, struct tbl_row *,
61 int, const char *, int *);
62 static void row(struct tbl_node *, int, const char *, int *);
63 static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
64 enum tbl_cellt, int vert);
65
66 static int
67 mods(struct tbl_node *tbl, struct tbl_cell *cp,
68 int ln, const char *p, int *pos)
69 {
70 char buf[5];
71 int i;
72
73 /* Not all types accept modifiers. */
74
75 switch (cp->pos) {
76 case (TBL_CELL_DOWN):
77 /* FALLTHROUGH */
78 case (TBL_CELL_HORIZ):
79 /* FALLTHROUGH */
80 case (TBL_CELL_DHORIZ):
81 return(1);
82 default:
83 break;
84 }
85
86 mod:
87 /*
88 * XXX: since, at least for now, modifiers are non-conflicting
89 * (are separable by value, regardless of position), we let
90 * modifiers come in any order. The existing tbl doesn't let
91 * this happen.
92 */
93 switch (p[*pos]) {
94 case ('\0'):
95 /* FALLTHROUGH */
96 case (' '):
97 /* FALLTHROUGH */
98 case ('\t'):
99 /* FALLTHROUGH */
100 case (','):
101 /* FALLTHROUGH */
102 case ('.'):
103 /* FALLTHROUGH */
104 case ('|'):
105 return(1);
106 default:
107 break;
108 }
109
110 /* Throw away parenthesised expression. */
111
112 if ('(' == p[*pos]) {
113 (*pos)++;
114 while (p[*pos] && ')' != p[*pos])
115 (*pos)++;
116 if (')' == p[*pos]) {
117 (*pos)++;
118 goto mod;
119 }
120 mandoc_msg(MANDOCERR_TBLLAYOUT,
121 tbl->parse, ln, *pos, NULL);
122 return(0);
123 }
124
125 /* Parse numerical spacing from modifier string. */
126
127 if (isdigit((unsigned char)p[*pos])) {
128 for (i = 0; i < 4; i++) {
129 if ( ! isdigit((unsigned char)p[*pos + i]))
130 break;
131 buf[i] = p[*pos + i];
132 }
133 buf[i] = '\0';
134
135 /* No greater than 4 digits. */
136
137 if (4 == i) {
138 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
139 ln, *pos, NULL);
140 return(0);
141 }
142
143 *pos += i;
144 cp->spacing = (size_t)atoi(buf);
145
146 goto mod;
147 /* NOTREACHED */
148 }
149
150 /* TODO: GNU has many more extensions. */
151
152 switch (tolower((unsigned char)p[(*pos)++])) {
153 case ('z'):
154 cp->flags |= TBL_CELL_WIGN;
155 goto mod;
156 case ('u'):
157 cp->flags |= TBL_CELL_UP;
158 goto mod;
159 case ('e'):
160 cp->flags |= TBL_CELL_EQUAL;
161 goto mod;
162 case ('t'):
163 cp->flags |= TBL_CELL_TALIGN;
164 goto mod;
165 case ('d'):
166 cp->flags |= TBL_CELL_BALIGN;
167 goto mod;
168 case ('w'): /* XXX for now, ignore minimal column width */
169 goto mod;
170 case ('f'):
171 break;
172 case ('r'):
173 /* FALLTHROUGH */
174 case ('b'):
175 /* FALLTHROUGH */
176 case ('i'):
177 (*pos)--;
178 break;
179 default:
180 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
181 ln, *pos - 1, NULL);
182 return(0);
183 }
184
185 switch (tolower((unsigned char)p[(*pos)++])) {
186 case ('3'):
187 /* FALLTHROUGH */
188 case ('b'):
189 cp->flags |= TBL_CELL_BOLD;
190 goto mod;
191 case ('2'):
192 /* FALLTHROUGH */
193 case ('i'):
194 cp->flags |= TBL_CELL_ITALIC;
195 goto mod;
196 case ('1'):
197 /* FALLTHROUGH */
198 case ('r'):
199 goto mod;
200 default:
201 break;
202 }
203
204 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
205 ln, *pos - 1, NULL);
206 return(0);
207 }
208
209 static int
210 cell(struct tbl_node *tbl, struct tbl_row *rp,
211 int ln, const char *p, int *pos)
212 {
213 int vert, i;
214 enum tbl_cellt c;
215
216 /* Handle vertical lines. */
217
218 for (vert = 0; '|' == p[*pos]; ++*pos)
219 vert++;
220 while (' ' == p[*pos])
221 (*pos)++;
222
223 /* Handle trailing vertical lines */
224
225 if ('.' == p[*pos] || '\0' == p[*pos]) {
226 rp->vert = vert;
227 return(1);
228 }
229
230 /* Parse the column position (`c', `l', `r', ...). */
231
232 for (i = 0; i < KEYS_MAX; i++)
233 if (tolower((unsigned char)p[*pos]) == keys[i].name)
234 break;
235
236 if (KEYS_MAX == i) {
237 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
238 ln, *pos, NULL);
239 return(0);
240 }
241
242 c = keys[i].key;
243
244 /*
245 * If a span cell is found first, raise a warning and abort the
246 * parse. If a span cell is found and the last layout element
247 * isn't a "normal" layout, bail.
248 *
249 * FIXME: recover from this somehow?
250 */
251
252 if (TBL_CELL_SPAN == c) {
253 if (NULL == rp->first) {
254 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
255 ln, *pos, NULL);
256 return(0);
257 } else if (rp->last)
258 switch (rp->last->pos) {
259 case (TBL_CELL_HORIZ):
260 case (TBL_CELL_DHORIZ):
261 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
262 ln, *pos, NULL);
263 return(0);
264 default:
265 break;
266 }
267 }
268
269 /*
270 * If a vertical spanner is found, we may not be in the first
271 * row.
272 */
273
274 if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
275 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
276 return(0);
277 }
278
279 (*pos)++;
280
281 /* Disallow adjacent spacers. */
282
283 if (vert > 2) {
284 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
285 return(0);
286 }
287
288 /* Allocate cell then parse its modifiers. */
289
290 return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
291 }
292
293
294 static void
295 row(struct tbl_node *tbl, int ln, const char *p, int *pos)
296 {
297 struct tbl_row *rp;
298
299 row: /*
300 * EBNF describing this section:
301 *
302 * row ::= row_list [:space:]* [.]?[\n]
303 * row_list ::= [:space:]* row_elem row_tail
304 * row_tail ::= [:space:]*[,] row_list |
305 * epsilon
306 * row_elem ::= [\t\ ]*[:alpha:]+
307 */
308
309 rp = mandoc_calloc(1, sizeof(struct tbl_row));
310 if (tbl->last_row)
311 tbl->last_row->next = rp;
312 else
313 tbl->first_row = rp;
314 tbl->last_row = rp;
315
316 cell:
317 while (isspace((unsigned char)p[*pos]))
318 (*pos)++;
319
320 /* Safely exit layout context. */
321
322 if ('.' == p[*pos]) {
323 tbl->part = TBL_PART_DATA;
324 if (NULL == tbl->first_row)
325 mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse,
326 ln, *pos, NULL);
327 (*pos)++;
328 return;
329 }
330
331 /* End (and possibly restart) a row. */
332
333 if (',' == p[*pos]) {
334 (*pos)++;
335 goto row;
336 } else if ('\0' == p[*pos])
337 return;
338
339 if ( ! cell(tbl, rp, ln, p, pos))
340 return;
341
342 goto cell;
343 /* NOTREACHED */
344 }
345
346 int
347 tbl_layout(struct tbl_node *tbl, int ln, const char *p)
348 {
349 int pos;
350
351 pos = 0;
352 row(tbl, ln, p, &pos);
353
354 /* Always succeed. */
355 return(1);
356 }
357
358 static struct tbl_cell *
359 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
360 int vert)
361 {
362 struct tbl_cell *p, *pp;
363 struct tbl_head *h, *hp;
364
365 p = mandoc_calloc(1, sizeof(struct tbl_cell));
366
367 if (NULL != (pp = rp->last)) {
368 pp->next = p;
369 h = pp->head->next;
370 } else {
371 rp->first = p;
372 h = tbl->first_head;
373 }
374 rp->last = p;
375
376 p->pos = pos;
377 p->vert = vert;
378
379 /* Re-use header. */
380
381 if (h) {
382 p->head = h;
383 return(p);
384 }
385
386 hp = mandoc_calloc(1, sizeof(struct tbl_head));
387 hp->ident = tbl->opts.cols++;
388 hp->vert = vert;
389
390 if (tbl->last_head) {
391 hp->prev = tbl->last_head;
392 tbl->last_head->next = hp;
393 } else
394 tbl->first_head = hp;
395 tbl->last_head = hp;
396
397 p->head = hp;
398 return(p);
399 }