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