]> git.cameronkatri.com Git - mandoc.git/blob - tbl_term.c
Remove TODO for tbl.
[mandoc.git] / tbl_term.c
1 /* $Id: tbl_term.c,v 1.6 2011/01/03 16:04:41 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "mandoc.h"
27 #include "out.h"
28 #include "term.h"
29
30 /* FIXME: `n' modifier doesn't always do the right thing. */
31 /* FIXME: `n' modifier doesn't use the cell-spacing buffer. */
32
33 static inline void tbl_char(struct termp *, char, int);
34 static void tbl_hframe(struct termp *,
35 const struct tbl_span *);
36 static void tbl_data_number(struct termp *,
37 const struct tbl *,
38 const struct tbl_dat *,
39 const struct termp_tbl *);
40 static void tbl_data_literal(struct termp *,
41 const struct tbl_dat *,
42 const struct termp_tbl *);
43 static void tbl_data_spanner(struct termp *,
44 const struct tbl_dat *,
45 const struct termp_tbl *);
46 static void tbl_data(struct termp *, const struct tbl *,
47 const struct tbl_dat *,
48 const struct termp_tbl *);
49 static void tbl_spanner(struct termp *,
50 const struct tbl_head *);
51 static void tbl_hrule(struct termp *,
52 const struct tbl_span *);
53 static void tbl_vframe(struct termp *,
54 const struct tbl *);
55 static void tbl_calc(struct termp *,
56 const struct tbl_span *);
57 static void tbl_calc_data(struct termp *,
58 const struct tbl *,
59 const struct tbl_dat *,
60 struct termp_tbl *);
61 static void tbl_calc_data_literal(struct termp *,
62 const struct tbl_dat *,
63 struct termp_tbl *);
64 static void tbl_calc_data_number(struct termp *,
65 const struct tbl *,
66 const struct tbl_dat *,
67 struct termp_tbl *);
68
69 void
70 term_tbl(struct termp *tp, const struct tbl_span *sp)
71 {
72 const struct tbl_head *hp;
73 const struct tbl_dat *dp;
74
75 /* Inhibit printing of spaces: we do padding ourselves. */
76
77 tp->flags |= TERMP_NONOSPACE;
78 tp->flags |= TERMP_NOSPACE;
79
80 /*
81 * The first time we're invoked for a given table block, create
82 * the termp_tbl structure. This contains the column
83 * configuration for the entire table, e.g., table-wide column
84 * width, decimal point, etc.
85 */
86
87 if (TBL_SPAN_FIRST & sp->flags) {
88 assert(NULL == tp->tbl);
89 tp->tbl = calloc
90 (sp->tbl->cols, sizeof(struct termp_tbl));
91 if (NULL == tp->tbl) {
92 perror(NULL);
93 exit(EXIT_FAILURE);
94 }
95 tbl_calc(tp, sp);
96
97 /* Flush out any preceding data. */
98 term_flushln(tp);
99 }
100
101 /* Horizontal frame at the start of boxed tables. */
102
103 if (TBL_SPAN_FIRST & sp->flags)
104 tbl_hframe(tp, sp);
105
106 /* Vertical frame at the start of each row. */
107
108 tbl_vframe(tp, sp->tbl);
109
110 /*
111 * Now print the actual data itself depending on the span type.
112 * Spanner spans get a horizontal rule; data spanners have their
113 * data printed by matching data to header.
114 */
115
116 switch (sp->pos) {
117 case (TBL_SPAN_HORIZ):
118 /* FALLTHROUGH */
119 case (TBL_SPAN_DHORIZ):
120 tbl_hrule(tp, sp);
121 break;
122 case (TBL_SPAN_DATA):
123 /* Iterate over template headers. */
124 dp = sp->first;
125 for (hp = sp->head; hp; hp = hp->next) {
126 switch (hp->pos) {
127 case (TBL_HEAD_VERT):
128 /* FALLTHROUGH */
129 case (TBL_HEAD_DVERT):
130 tbl_spanner(tp, hp);
131 continue;
132 case (TBL_HEAD_DATA):
133 break;
134 }
135 tbl_data(tp, sp->tbl, dp,
136 &tp->tbl[hp->ident]);
137
138 /* Go to the next data cell. */
139 if (dp)
140 dp = dp->next;
141 }
142 break;
143 }
144
145 tbl_vframe(tp, sp->tbl);
146 term_flushln(tp);
147
148 /*
149 * If we're the last row, clean up after ourselves: clear the
150 * existing table configuration and set it to NULL.
151 */
152
153 if (TBL_SPAN_LAST & sp->flags) {
154 tbl_hframe(tp, sp);
155 assert(tp->tbl);
156 free(tp->tbl);
157 tp->tbl = NULL;
158 }
159
160 tp->flags &= ~TERMP_NONOSPACE;
161
162 }
163
164 static void
165 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
166 {
167 const struct tbl_head *hp;
168 char c;
169 int width;
170
171 /*
172 * An hrule extends across the entire table and is demarked by a
173 * standalone `_' or whatnot in lieu of a table row. Spanning
174 * headers are marked by a `+', as are table boundaries.
175 */
176
177 c = '-';
178 if (TBL_SPAN_DHORIZ == sp->pos)
179 c = '=';
180
181 /* FIXME: don't use `+' between data and a spanner! */
182
183 for (hp = sp->head; hp; hp = hp->next) {
184 width = tp->tbl[hp->ident].width;
185 switch (hp->pos) {
186 case (TBL_HEAD_DATA):
187 tbl_char(tp, c, width);
188 break;
189 case (TBL_HEAD_DVERT):
190 tbl_char(tp, '+', width);
191 /* FALLTHROUGH */
192 case (TBL_HEAD_VERT):
193 tbl_char(tp, '+', width);
194 break;
195 default:
196 abort();
197 /* NOTREACHED */
198 }
199 }
200 }
201
202 static void
203 tbl_hframe(struct termp *tp, const struct tbl_span *sp)
204 {
205 const struct tbl_head *hp;
206 int width;
207
208 if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
209 TBL_OPT_DBOX & sp->tbl->opts))
210 return;
211
212 tp->flags |= TERMP_NONOSPACE;
213 tp->flags |= TERMP_NOSPACE;
214
215 /*
216 * Print out the horizontal part of a frame or double frame. A
217 * double frame has an unbroken `-' outer line the width of the
218 * table, bordered by `+'. The frame (or inner frame, in the
219 * case of the double frame) is a `-' bordered by `+' and broken
220 * by `+' whenever a span is encountered.
221 */
222
223 if (TBL_OPT_DBOX & sp->tbl->opts) {
224 term_word(tp, "+");
225 for (hp = sp->head; hp; hp = hp->next) {
226 width = tp->tbl[hp->ident].width;
227 tbl_char(tp, '-', width);
228 }
229 term_word(tp, "+");
230 term_flushln(tp);
231 }
232
233 term_word(tp, "+");
234 for (hp = sp->head; hp; hp = hp->next) {
235 width = tp->tbl[hp->ident].width;
236 switch (hp->pos) {
237 case (TBL_HEAD_DATA):
238 tbl_char(tp, '-', width);
239 break;
240 default:
241 tbl_char(tp, '+', width);
242 break;
243 }
244 }
245 term_word(tp, "+");
246 term_flushln(tp);
247 }
248
249 static void
250 tbl_data(struct termp *tp, const struct tbl *tbl,
251 const struct tbl_dat *dp,
252 const struct termp_tbl *tbp)
253 {
254 enum tbl_cellt pos;
255
256 if (NULL == dp) {
257 tbl_char(tp, ASCII_NBRSP, tbp->width);
258 return;
259 }
260
261 switch (dp->pos) {
262 case (TBL_DATA_HORIZ):
263 /* FALLTHROUGH */
264 case (TBL_DATA_DHORIZ):
265 tbl_data_spanner(tp, dp, tbp);
266 return;
267 default:
268 break;
269 }
270
271 pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
272
273 switch (pos) {
274 case (TBL_CELL_HORIZ):
275 /* FALLTHROUGH */
276 case (TBL_CELL_DHORIZ):
277 /* FIXME: THIS IS WRONG. */
278 tbl_data_spanner(tp, dp, tbp);
279 break;
280 case (TBL_CELL_LONG):
281 /* FALLTHROUGH */
282 case (TBL_CELL_CENTRE):
283 /* FALLTHROUGH */
284 case (TBL_CELL_LEFT):
285 /* FALLTHROUGH */
286 case (TBL_CELL_RIGHT):
287 tbl_data_literal(tp, dp, tbp);
288 break;
289 case (TBL_CELL_NUMBER):
290 tbl_data_number(tp, tbl, dp, tbp);
291 break;
292 default:
293 abort();
294 /* NOTREACHED */
295 }
296 }
297 static void
298 tbl_spanner(struct termp *tp, const struct tbl_head *hp)
299 {
300
301 switch (hp->pos) {
302 case (TBL_HEAD_VERT):
303 term_word(tp, "|");
304 break;
305 case (TBL_HEAD_DVERT):
306 term_word(tp, "||");
307 break;
308 default:
309 break;
310 }
311 }
312
313 static void
314 tbl_vframe(struct termp *tp, const struct tbl *tbl)
315 {
316 /* Always just a single vertical line. */
317
318 if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
319 term_word(tp, "|");
320 }
321
322
323 static inline void
324 tbl_char(struct termp *tp, char c, int len)
325 {
326 int i, sz;
327 char cp[2];
328
329 cp[0] = c;
330 cp[1] = '\0';
331
332 sz = term_strlen(tp, cp);
333
334 for (i = 0; i < len; i += sz)
335 term_word(tp, cp);
336 }
337
338 static void
339 tbl_data_spanner(struct termp *tp,
340 const struct tbl_dat *dp,
341 const struct termp_tbl *tblp)
342 {
343
344 switch (dp->pos) {
345 case (TBL_DATA_HORIZ):
346 case (TBL_DATA_NHORIZ):
347 tbl_char(tp, '-', tblp->width);
348 break;
349 case (TBL_DATA_DHORIZ):
350 case (TBL_DATA_NDHORIZ):
351 tbl_char(tp, '=', tblp->width);
352 break;
353 default:
354 break;
355 }
356 }
357
358 static void
359 tbl_data_literal(struct termp *tp,
360 const struct tbl_dat *dp,
361 const struct termp_tbl *tblp)
362 {
363 int padl, padr, ssz;
364 enum tbl_cellt pos;
365
366 padl = padr = 0;
367
368 pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
369 ssz = term_len(tp, 1);
370
371 switch (pos) {
372 case (TBL_CELL_LONG):
373 padl = ssz;
374 padr = tblp->width - term_strlen(tp, dp->string) - ssz;
375 break;
376 case (TBL_CELL_CENTRE):
377 padl = tblp->width - term_strlen(tp, dp->string);
378 if (padl % 2)
379 padr++;
380 padl /= 2;
381 padr += padl;
382 break;
383 case (TBL_CELL_RIGHT):
384 padl = tblp->width - term_strlen(tp, dp->string);
385 break;
386 default:
387 padr = tblp->width - term_strlen(tp, dp->string);
388 break;
389 }
390
391 tbl_char(tp, ASCII_NBRSP, padl);
392 term_word(tp, dp->string);
393 tbl_char(tp, ASCII_NBRSP, padr);
394 }
395
396 static void
397 tbl_data_number(struct termp *tp, const struct tbl *tbl,
398 const struct tbl_dat *dp,
399 const struct termp_tbl *tblp)
400 {
401 char *decp, buf[2];
402 int d, padl, sz, psz, ssz, i;
403
404 /*
405 * See calc_data_number(). Left-pad by taking the offset of our
406 * and the maximum decimal; right-pad by the remaining amount.
407 */
408
409 sz = term_strlen(tp, dp->string);
410 psz = term_strlen(tp, ".");
411
412 if (NULL != (decp = strchr(dp->string, tbl->decimal))) {
413 buf[1] = '\0';
414 for (ssz = i = 0; decp != &dp->string[i]; i++) {
415 buf[0] = dp->string[i];
416 ssz += term_strlen(tp, buf);
417 }
418 d = ssz + psz;
419 } else
420 d = sz + psz;
421
422 assert(d <= tblp->decimal);
423 assert(sz - d <= tblp->width - tblp->decimal);
424
425 padl = tblp->decimal - d + term_len(tp, 1);
426 assert(tblp->width - sz - padl);
427
428 tbl_char(tp, ASCII_NBRSP, padl);
429 term_word(tp, dp->string);
430 tbl_char(tp, ASCII_NBRSP, tblp->width - sz - padl);
431 }
432
433 static void
434 tbl_calc(struct termp *tp, const struct tbl_span *sp)
435 {
436 const struct tbl_dat *dp;
437 const struct tbl_head *hp;
438 struct termp_tbl *p;
439
440 /* Calculate width as the max of column cells' widths. */
441
442 hp = sp->head;
443
444 for ( ; sp; sp = sp->next) {
445 switch (sp->pos) {
446 case (TBL_DATA_HORIZ):
447 /* FALLTHROUGH */
448 case (TBL_DATA_DHORIZ):
449 continue;
450 default:
451 break;
452 }
453 for (dp = sp->first; dp; dp = dp->next) {
454 if (NULL == dp->layout)
455 continue;
456 p = &tp->tbl[dp->layout->head->ident];
457 tbl_calc_data(tp, sp->tbl, dp, p);
458 }
459 }
460
461 /* Calculate width as the simple spanner value. */
462
463 for ( ; hp; hp = hp->next)
464 switch (hp->pos) {
465 case (TBL_HEAD_VERT):
466 tp->tbl[hp->ident].width = term_len(tp, 1);
467 break;
468 case (TBL_HEAD_DVERT):
469 tp->tbl[hp->ident].width = term_len(tp, 2);
470 break;
471 default:
472 break;
473 }
474 }
475
476 static void
477 tbl_calc_data(struct termp *tp, const struct tbl *tbl,
478 const struct tbl_dat *dp, struct termp_tbl *tblp)
479 {
480
481 /* Branch down into data sub-types. */
482
483 switch (dp->layout->pos) {
484 case (TBL_CELL_HORIZ):
485 /* FALLTHROUGH */
486 case (TBL_CELL_DHORIZ):
487 tblp->width = 1;
488 break;
489 case (TBL_CELL_LONG):
490 /* FALLTHROUGH */
491 case (TBL_CELL_CENTRE):
492 /* FALLTHROUGH */
493 case (TBL_CELL_LEFT):
494 /* FALLTHROUGH */
495 case (TBL_CELL_RIGHT):
496 tbl_calc_data_literal(tp, dp, tblp);
497 break;
498 case (TBL_CELL_NUMBER):
499 tbl_calc_data_number(tp, tbl, dp, tblp);
500 break;
501 default:
502 abort();
503 /* NOTREACHED */
504 }
505 }
506
507 static void
508 tbl_calc_data_number(struct termp *tp, const struct tbl *tbl,
509 const struct tbl_dat *dp, struct termp_tbl *tblp)
510 {
511 int sz, d, psz, i, ssz;
512 char *cp, buf[2];
513
514 /*
515 * First calculate number width and decimal place (last + 1 for
516 * no-decimal numbers). If the stored decimal is subsequent
517 * ours, make our size longer by that difference
518 * (right-"shifting"); similarly, if ours is subsequent the
519 * stored, then extend the stored size by the difference.
520 * Finally, re-assign the stored values.
521 */
522
523 /* TODO: use spacing modifier. */
524
525 assert(dp->string);
526 sz = term_strlen(tp, dp->string);
527 psz = term_strlen(tp, ".");
528
529 if (NULL != (cp = strchr(dp->string, tbl->decimal))) {
530 buf[1] = '\0';
531 for (ssz = i = 0; cp != &dp->string[i]; i++) {
532 buf[0] = dp->string[i];
533 ssz += term_strlen(tp, buf);
534 }
535 d = ssz + psz;
536 } else
537 d = sz + psz;
538
539 sz += term_len(tp, 2);
540
541 if (tblp->decimal > d) {
542 sz += tblp->decimal - d;
543 d = tblp->decimal;
544 } else
545 tblp->width += d - tblp->decimal;
546
547 if (sz > tblp->width)
548 tblp->width = sz;
549 if (d > tblp->decimal)
550 tblp->decimal = d;
551 }
552
553 static void
554 tbl_calc_data_literal(struct termp *tp,
555 const struct tbl_dat *dp,
556 struct termp_tbl *tblp)
557 {
558 int sz, bufsz;
559
560 /*
561 * Calculate our width and use the spacing, with a minimum
562 * spacing dictated by position (centre, e.g,. gets a space on
563 * either side, while right/left get a single adjacent space).
564 */
565
566 assert(dp->string);
567 sz = term_strlen(tp, dp->string);
568
569 switch (dp->layout->pos) {
570 case (TBL_CELL_LONG):
571 /* FALLTHROUGH */
572 case (TBL_CELL_CENTRE):
573 bufsz = 2;
574 break;
575 default:
576 bufsz = 1;
577 break;
578 }
579
580 if (dp->layout->spacing)
581 bufsz = bufsz > dp->layout->spacing ?
582 bufsz : dp->layout->spacing;
583
584 sz += bufsz;
585 if (tblp->width < sz)
586 tblp->width = sz;
587 }