]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/tbl_layout.c
Copy elftoolchain readelf from vendor branch
[FreeBSD/FreeBSD.git] / contrib / mdocml / tbl_layout.c
1 /*      $Id: tbl_layout.c,v 1.30 2014/11/25 21:41:47 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 #include "config.h"
19
20 #include <sys/types.h>
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  struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
63                                 enum tbl_cellt, int vert);
64
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, tbl->parse,
121                     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,
139                             tbl->parse, 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 'x':
171                 cp->flags |= TBL_CELL_WMAX;
172                 goto mod;
173         case 'f':
174                 break;
175         case 'r':
176                 /* FALLTHROUGH */
177         case 'b':
178                 /* FALLTHROUGH */
179         case 'i':
180                 (*pos)--;
181                 break;
182         default:
183                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
184                     ln, *pos - 1, NULL);
185                 return(0);
186         }
187
188         switch (tolower((unsigned char)p[(*pos)++])) {
189         case '3':
190                 /* FALLTHROUGH */
191         case 'b':
192                 cp->flags |= TBL_CELL_BOLD;
193                 goto mod;
194         case '2':
195                 /* FALLTHROUGH */
196         case 'i':
197                 cp->flags |= TBL_CELL_ITALIC;
198                 goto mod;
199         case '1':
200                 /* FALLTHROUGH */
201         case 'r':
202                 goto mod;
203         default:
204                 break;
205         }
206         if (isalnum((unsigned char)p[*pos - 1])) {
207                 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
208                     ln, *pos - 1, "TS f%c", p[*pos - 1]);
209                 goto mod;
210         }
211
212         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
213             ln, *pos - 1, NULL);
214         return(0);
215 }
216
217 static int
218 cell(struct tbl_node *tbl, struct tbl_row *rp,
219                 int ln, const char *p, int *pos)
220 {
221         int              vert, i;
222         enum tbl_cellt   c;
223
224         /* Handle vertical lines. */
225
226         for (vert = 0; '|' == p[*pos]; ++*pos)
227                 vert++;
228         while (' ' == p[*pos])
229                 (*pos)++;
230
231         /* Handle trailing vertical lines */
232
233         if ('.' == p[*pos] || '\0' == p[*pos]) {
234                 rp->vert = vert;
235                 return(1);
236         }
237
238         /* Parse the column position (`c', `l', `r', ...). */
239
240         for (i = 0; i < KEYS_MAX; i++)
241                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
242                         break;
243
244         if (KEYS_MAX == i) {
245                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
246                     ln, *pos, NULL);
247                 return(0);
248         }
249
250         c = keys[i].key;
251
252         /*
253          * If a span cell is found first, raise a warning and abort the
254          * parse.  If a span cell is found and the last layout element
255          * isn't a "normal" layout, bail.
256          *
257          * FIXME: recover from this somehow?
258          */
259
260         if (TBL_CELL_SPAN == c) {
261                 if (NULL == rp->first) {
262                         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
263                             ln, *pos, NULL);
264                         return(0);
265                 } else if (rp->last)
266                         switch (rp->last->pos) {
267                         case TBL_CELL_HORIZ:
268                                 /* FALLTHROUGH */
269                         case TBL_CELL_DHORIZ:
270                                 mandoc_msg(MANDOCERR_TBLLAYOUT,
271                                     tbl->parse, ln, *pos, NULL);
272                                 return(0);
273                         default:
274                                 break;
275                         }
276         }
277
278         /*
279          * If a vertical spanner is found, we may not be in the first
280          * row.
281          */
282
283         if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
284                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
285                 return(0);
286         }
287
288         (*pos)++;
289
290         /* Disallow adjacent spacers. */
291
292         if (vert > 2) {
293                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
294                 return(0);
295         }
296
297         /* Allocate cell then parse its modifiers. */
298
299         return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
300 }
301
302 int
303 tbl_layout(struct tbl_node *tbl, int ln, const char *p)
304 {
305         struct tbl_row  *rp;
306         int              pos;
307
308         pos = 0;
309         rp = NULL;
310
311         for (;;) {
312                 /* Skip whitespace before and after each cell. */
313
314                 while (isspace((unsigned char)p[pos]))
315                         pos++;
316
317                 switch (p[pos]) {
318                 case ',':  /* Next row on this input line. */
319                         pos++;
320                         rp = NULL;
321                         continue;
322                 case '\0':  /* Next row on next input line. */
323                         return(1);
324                 case '.':  /* End of layout. */
325                         pos++;
326                         tbl->part = TBL_PART_DATA;
327                         if (tbl->first_row != NULL)
328                                 return(1);
329                         mandoc_msg(MANDOCERR_TBLNOLAYOUT,
330                             tbl->parse, ln, pos, NULL);
331                         rp = mandoc_calloc(1, sizeof(*rp));
332                         cell_alloc(tbl, rp, TBL_CELL_LEFT, 0);
333                         tbl->first_row = tbl->last_row = rp;
334                         return(1);
335                 default:  /* Cell. */
336                         break;
337                 }
338
339                 if (rp == NULL) {  /* First cell on this line. */
340                         rp = mandoc_calloc(1, sizeof(*rp));
341                         if (tbl->last_row)
342                                 tbl->last_row->next = rp;
343                         else
344                                 tbl->first_row = rp;
345                         tbl->last_row = rp;
346                 }
347                 if ( ! cell(tbl, rp, ln, p, &pos))
348                         return(1);
349         }
350 }
351
352 static struct tbl_cell *
353 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
354                 int vert)
355 {
356         struct tbl_cell *p, *pp;
357         struct tbl_head *h, *hp;
358
359         p = mandoc_calloc(1, sizeof(struct tbl_cell));
360
361         if (NULL != (pp = rp->last)) {
362                 pp->next = p;
363                 h = pp->head->next;
364         } else {
365                 rp->first = p;
366                 h = tbl->first_head;
367         }
368         rp->last = p;
369
370         p->pos = pos;
371         p->vert = vert;
372
373         /* Re-use header. */
374
375         if (h) {
376                 p->head = h;
377                 return(p);
378         }
379
380         hp = mandoc_calloc(1, sizeof(struct tbl_head));
381         hp->ident = tbl->opts.cols++;
382         hp->vert = vert;
383
384         if (tbl->last_head) {
385                 hp->prev = tbl->last_head;
386                 tbl->last_head->next = hp;
387         } else
388                 tbl->first_head = hp;
389         tbl->last_head = hp;
390
391         p->head = hp;
392         return(p);
393 }