]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/tbl_layout.c
Update llvm/clang to r240225.
[FreeBSD/FreeBSD.git] / contrib / mdocml / tbl_layout.c
1 /*      $Id: tbl_layout.c,v 1.38 2015/02/10 11:03:13 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2012, 2014, 2015 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 static  const struct tbl_phrase keys[] = {
38         { 'c',           TBL_CELL_CENTRE },
39         { 'r',           TBL_CELL_RIGHT },
40         { 'l',           TBL_CELL_LEFT },
41         { 'n',           TBL_CELL_NUMBER },
42         { 's',           TBL_CELL_SPAN },
43         { 'a',           TBL_CELL_LONG },
44         { '^',           TBL_CELL_DOWN },
45         { '-',           TBL_CELL_HORIZ },
46         { '_',           TBL_CELL_HORIZ },
47         { '=',           TBL_CELL_DHORIZ }
48 };
49
50 #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
51
52 static  void             mods(struct tbl_node *, struct tbl_cell *,
53                                 int, const char *, int *);
54 static  void             cell(struct tbl_node *, struct tbl_row *,
55                                 int, const char *, int *);
56 static  struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
57                                 enum tbl_cellt);
58
59
60 static void
61 mods(struct tbl_node *tbl, struct tbl_cell *cp,
62                 int ln, const char *p, int *pos)
63 {
64         char            *endptr;
65
66 mod:
67         while (p[*pos] == ' ' || p[*pos] == '\t')
68                 (*pos)++;
69
70         /* Row delimiters and cell specifiers end modifier lists. */
71
72         if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
73                 return;
74
75         /* Throw away parenthesised expression. */
76
77         if ('(' == p[*pos]) {
78                 (*pos)++;
79                 while (p[*pos] && ')' != p[*pos])
80                         (*pos)++;
81                 if (')' == p[*pos]) {
82                         (*pos)++;
83                         goto mod;
84                 }
85                 mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
86                     ln, *pos, NULL);
87                 return;
88         }
89
90         /* Parse numerical spacing from modifier string. */
91
92         if (isdigit((unsigned char)p[*pos])) {
93                 cp->spacing = strtoull(p + *pos, &endptr, 10);
94                 *pos = endptr - p;
95                 goto mod;
96         }
97
98         switch (tolower((unsigned char)p[(*pos)++])) {
99         case 'b':
100                 cp->flags |= TBL_CELL_BOLD;
101                 goto mod;
102         case 'd':
103                 cp->flags |= TBL_CELL_BALIGN;
104                 goto mod;
105         case 'e':
106                 cp->flags |= TBL_CELL_EQUAL;
107                 goto mod;
108         case 'f':
109                 break;
110         case 'i':
111                 cp->flags |= TBL_CELL_ITALIC;
112                 goto mod;
113         case 'm':
114                 mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
115                     ln, *pos, "m");
116                 goto mod;
117         case 'p':
118                 /* FALLTHROUGH */
119         case 'v':
120                 if (p[*pos] == '-' || p[*pos] == '+')
121                         (*pos)++;
122                 while (isdigit((unsigned char)p[*pos]))
123                         (*pos)++;
124                 goto mod;
125         case 't':
126                 cp->flags |= TBL_CELL_TALIGN;
127                 goto mod;
128         case 'u':
129                 cp->flags |= TBL_CELL_UP;
130                 goto mod;
131         case 'w':  /* XXX for now, ignore minimal column width */
132                 goto mod;
133         case 'x':
134                 cp->flags |= TBL_CELL_WMAX;
135                 goto mod;
136         case 'z':
137                 cp->flags |= TBL_CELL_WIGN;
138                 goto mod;
139         case '|':
140                 if (cp->vert < 2)
141                         cp->vert++;
142                 else
143                         mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
144                             tbl->parse, ln, *pos - 1, NULL);
145                 goto mod;
146         default:
147                 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
148                     ln, *pos - 1, "%c", p[*pos - 1]);
149                 goto mod;
150         }
151
152         /* Ignore parenthised font names for now. */
153
154         if (p[*pos] == '(')
155                 goto mod;
156
157         /* Support only one-character font-names for now. */
158
159         if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
160                 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
161                     ln, *pos, "TS %s", p + *pos - 1);
162                 if (p[*pos] != '\0')
163                         (*pos)++;
164                 if (p[*pos] != '\0')
165                         (*pos)++;
166                 goto mod;
167         }
168
169         switch (p[(*pos)++]) {
170         case '3':
171                 /* FALLTHROUGH */
172         case 'B':
173                 cp->flags |= TBL_CELL_BOLD;
174                 goto mod;
175         case '2':
176                 /* FALLTHROUGH */
177         case 'I':
178                 cp->flags |= TBL_CELL_ITALIC;
179                 goto mod;
180         case '1':
181                 /* FALLTHROUGH */
182         case 'R':
183                 goto mod;
184         default:
185                 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
186                     ln, *pos - 1, "TS f%c", p[*pos - 1]);
187                 goto mod;
188         }
189 }
190
191 static void
192 cell(struct tbl_node *tbl, struct tbl_row *rp,
193                 int ln, const char *p, int *pos)
194 {
195         int              i;
196         enum tbl_cellt   c;
197
198         /* Handle leading vertical lines */
199
200         while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
201                 if (p[*pos] == '|') {
202                         if (rp->vert < 2)
203                                 rp->vert++;
204                         else
205                                 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
206                                     tbl->parse, ln, *pos, NULL);
207                 }
208                 (*pos)++;
209         }
210
211 again:
212         while (p[*pos] == ' ' || p[*pos] == '\t')
213                 (*pos)++;
214
215         if (p[*pos] == '.' || p[*pos] == '\0')
216                 return;
217
218         /* Parse the column position (`c', `l', `r', ...). */
219
220         for (i = 0; i < KEYS_MAX; i++)
221                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
222                         break;
223
224         if (i == KEYS_MAX) {
225                 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
226                     ln, *pos, "%c", p[*pos]);
227                 (*pos)++;
228                 goto again;
229         }
230         c = keys[i].key;
231
232         /* Special cases of spanners. */
233
234         if (c == TBL_CELL_SPAN) {
235                 if (rp->last == NULL)
236                         mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
237                             tbl->parse, ln, *pos, NULL);
238                 else if (rp->last->pos == TBL_CELL_HORIZ ||
239                     rp->last->pos == TBL_CELL_DHORIZ)
240                         c = rp->last->pos;
241         } else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
242                 mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
243                     tbl->parse, ln, *pos, NULL);
244
245         (*pos)++;
246
247         /* Allocate cell then parse its modifiers. */
248
249         mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
250 }
251
252 void
253 tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
254 {
255         struct tbl_row  *rp;
256
257         rp = NULL;
258         for (;;) {
259                 /* Skip whitespace before and after each cell. */
260
261                 while (p[pos] == ' ' || p[pos] == '\t')
262                         pos++;
263
264                 switch (p[pos]) {
265                 case ',':  /* Next row on this input line. */
266                         pos++;
267                         rp = NULL;
268                         continue;
269                 case '\0':  /* Next row on next input line. */
270                         return;
271                 case '.':  /* End of layout. */
272                         pos++;
273                         tbl->part = TBL_PART_DATA;
274
275                         /*
276                          * When the layout is completely empty,
277                          * default to one left-justified column.
278                          */
279
280                         if (tbl->first_row == NULL) {
281                                 tbl->first_row = tbl->last_row =
282                                     mandoc_calloc(1, sizeof(*rp));
283                         }
284                         if (tbl->first_row->first == NULL) {
285                                 mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
286                                     tbl->parse, ln, pos, NULL);
287                                 cell_alloc(tbl, tbl->first_row,
288                                     TBL_CELL_LEFT);
289                                 return;
290                         }
291
292                         /*
293                          * Search for the widest line
294                          * along the left and right margins.
295                          */
296
297                         for (rp = tbl->first_row; rp; rp = rp->next) {
298                                 if (tbl->opts.lvert < rp->vert)
299                                         tbl->opts.lvert = rp->vert;
300                                 if (rp->last != NULL &&
301                                     rp->last->col + 1 == tbl->opts.cols &&
302                                     tbl->opts.rvert < rp->last->vert)
303                                         tbl->opts.rvert = rp->last->vert;
304
305                                 /* If the last line is empty, drop it. */
306
307                                 if (rp->next != NULL &&
308                                     rp->next->first == NULL) {
309                                         free(rp->next);
310                                         rp->next = NULL;
311                                 }
312                         }
313                         return;
314                 default:  /* Cell. */
315                         break;
316                 }
317
318                 /*
319                  * If the last line had at least one cell,
320                  * start a new one; otherwise, continue it.
321                  */
322
323                 if (rp == NULL) {
324                         if (tbl->last_row == NULL ||
325                             tbl->last_row->first != NULL) {
326                                 rp = mandoc_calloc(1, sizeof(*rp));
327                                 if (tbl->last_row)
328                                         tbl->last_row->next = rp;
329                                 else
330                                         tbl->first_row = rp;
331                                 tbl->last_row = rp;
332                         } else
333                                 rp = tbl->last_row;
334                 }
335                 cell(tbl, rp, ln, p, &pos);
336         }
337 }
338
339 static struct tbl_cell *
340 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
341 {
342         struct tbl_cell *p, *pp;
343
344         p = mandoc_calloc(1, sizeof(*p));
345         p->pos = pos;
346
347         if ((pp = rp->last) != NULL) {
348                 pp->next = p;
349                 p->col = pp->col + 1;
350         } else
351                 rp->first = p;
352         rp->last = p;
353
354         if (tbl->opts.cols <= p->col)
355                 tbl->opts.cols = p->col + 1;
356
357         return(p);
358 }