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