]> git.cameronkatri.com Git - mandoc.git/blob - tbl_layout.c
eaf72769840520daf3ac3f26b6cd5acdd5e5190b
[mandoc.git] / tbl_layout.c
1 /* $Id: tbl_layout.c,v 1.32 2015/01/26 18:42:30 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, int vert);
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 /* FALLTHROUGH */
101 case 'i':
102 /* FALLTHROUGH */
103 case 'r':
104 (*pos)--;
105 break;
106 case 'd':
107 cp->flags |= TBL_CELL_BALIGN;
108 goto mod;
109 case 'e':
110 cp->flags |= TBL_CELL_EQUAL;
111 goto mod;
112 case 'f':
113 break;
114 case 'm':
115 mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
116 ln, *pos, "m");
117 goto mod;
118 case 'p':
119 /* FALLTHROUGH */
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': /* XXX for now, ignore minimal column width */
133 goto mod;
134 case 'x':
135 cp->flags |= TBL_CELL_WMAX;
136 goto mod;
137 case 'z':
138 cp->flags |= TBL_CELL_WIGN;
139 goto mod;
140 default:
141 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
142 ln, *pos - 1, "%c", p[*pos - 1]);
143 goto mod;
144 }
145
146 switch (tolower((unsigned char)p[(*pos)++])) {
147 case '3':
148 /* FALLTHROUGH */
149 case 'b':
150 cp->flags |= TBL_CELL_BOLD;
151 goto mod;
152 case '2':
153 /* FALLTHROUGH */
154 case 'i':
155 cp->flags |= TBL_CELL_ITALIC;
156 goto mod;
157 case '1':
158 /* FALLTHROUGH */
159 case 'r':
160 goto mod;
161 default:
162 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
163 ln, *pos - 1, "TS f%c", p[*pos - 1]);
164 goto mod;
165 }
166 }
167
168 static void
169 cell(struct tbl_node *tbl, struct tbl_row *rp,
170 int ln, const char *p, int *pos)
171 {
172 int vert, i;
173 enum tbl_cellt c;
174
175 /* Handle vertical lines. */
176
177 vert = 0;
178 again:
179 while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
180 if (p[*pos] == '|') {
181 if (vert < 2)
182 vert++;
183 else
184 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
185 tbl->parse, ln, *pos, NULL);
186 }
187 (*pos)++;
188 }
189
190 /* Handle trailing vertical lines */
191
192 if ('.' == p[*pos] || '\0' == p[*pos]) {
193 rp->vert = vert;
194 return;
195 }
196
197 /* Parse the column position (`c', `l', `r', ...). */
198
199 for (i = 0; i < KEYS_MAX; i++)
200 if (tolower((unsigned char)p[*pos]) == keys[i].name)
201 break;
202
203 if (i == KEYS_MAX) {
204 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
205 ln, *pos, "%c", p[*pos]);
206 (*pos)++;
207 goto again;
208 }
209 c = keys[i].key;
210
211 /* Special cases of spanners. */
212
213 if (c == TBL_CELL_SPAN) {
214 if (rp->last == NULL)
215 mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
216 tbl->parse, ln, *pos, NULL);
217 else if (rp->last->pos == TBL_CELL_HORIZ ||
218 rp->last->pos == TBL_CELL_DHORIZ)
219 c = rp->last->pos;
220 } else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
221 mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
222 tbl->parse, ln, *pos, NULL);
223
224 (*pos)++;
225
226 /* Allocate cell then parse its modifiers. */
227
228 mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos);
229 }
230
231 void
232 tbl_layout(struct tbl_node *tbl, int ln, const char *p)
233 {
234 struct tbl_row *rp;
235 int pos;
236
237 pos = 0;
238 rp = NULL;
239
240 for (;;) {
241 /* Skip whitespace before and after each cell. */
242
243 while (p[pos] == ' ' || p[pos] == '\t')
244 pos++;
245
246 switch (p[pos]) {
247 case ',': /* Next row on this input line. */
248 pos++;
249 rp = NULL;
250 continue;
251 case '\0': /* Next row on next input line. */
252 return;
253 case '.': /* End of layout. */
254 pos++;
255 tbl->part = TBL_PART_DATA;
256 if (tbl->first_row != NULL)
257 return;
258 mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
259 tbl->parse, ln, pos, NULL);
260 rp = mandoc_calloc(1, sizeof(*rp));
261 cell_alloc(tbl, rp, TBL_CELL_LEFT, 0);
262 tbl->first_row = tbl->last_row = rp;
263 return;
264 default: /* Cell. */
265 break;
266 }
267
268 if (rp == NULL) { /* First cell on this line. */
269 rp = mandoc_calloc(1, sizeof(*rp));
270 if (tbl->last_row)
271 tbl->last_row->next = rp;
272 else
273 tbl->first_row = rp;
274 tbl->last_row = rp;
275 }
276 cell(tbl, rp, ln, p, &pos);
277 }
278 }
279
280 static struct tbl_cell *
281 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
282 int vert)
283 {
284 struct tbl_cell *p, *pp;
285 struct tbl_head *h, *hp;
286
287 p = mandoc_calloc(1, sizeof(struct tbl_cell));
288
289 if (NULL != (pp = rp->last)) {
290 pp->next = p;
291 h = pp->head->next;
292 } else {
293 rp->first = p;
294 h = tbl->first_head;
295 }
296 rp->last = p;
297
298 p->pos = pos;
299 p->vert = vert;
300
301 /* Re-use header. */
302
303 if (h) {
304 p->head = h;
305 return(p);
306 }
307
308 hp = mandoc_calloc(1, sizeof(struct tbl_head));
309 hp->ident = tbl->opts.cols++;
310 hp->vert = vert;
311
312 if (tbl->last_head) {
313 hp->prev = tbl->last_head;
314 tbl->last_head->next = hp;
315 } else
316 tbl->first_head = hp;
317 tbl->last_head = hp;
318
319 p->head = hp;
320 return(p);
321 }