Annotation of mandoc/tbl_term.c, Revision 1.3
1.3 ! kristaps 1: /* $Id: tbl_term.c,v 1.2 2011/01/03 13:59:21 kristaps Exp $ */
1.1 kristaps 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 *,
1.2 kristaps 38: const struct tbl_dat *,
39: const struct termp_tbl *);
1.1 kristaps 40: static void tbl_data_literal(struct termp *,
1.2 kristaps 41: const struct tbl_dat *,
42: const struct termp_tbl *);
1.1 kristaps 43: static void tbl_data_spanner(struct termp *,
1.2 kristaps 44: const struct tbl_dat *,
45: const struct termp_tbl *);
1.1 kristaps 46: static void tbl_data(struct termp *, const struct tbl *,
1.2 kristaps 47: const struct tbl_dat *,
48: const struct termp_tbl *);
1.1 kristaps 49: static void tbl_spanner(struct termp *,
50: const struct tbl_head *);
51: static void tbl_hrule(struct termp *,
52: const struct tbl_span *);
1.2 kristaps 53: static void tbl_vframe(struct termp *,
54: const struct tbl *);
1.3 ! kristaps 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 *,
1.2 kristaps 60: struct termp_tbl *);
1.3 ! kristaps 61: static void tbl_calc_data_literal(struct termp *,
1.2 kristaps 62: const struct tbl_dat *,
63: struct termp_tbl *);
1.3 ! kristaps 64: static void tbl_calc_data_number(struct termp *,
! 65: const struct tbl *,
1.2 kristaps 66: const struct tbl_dat *,
67: struct termp_tbl *);
1.1 kristaps 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:
1.2 kristaps 75: if (TBL_SPAN_FIRST & sp->flags) {
76: assert(NULL == tp->tbl);
77: tp->tbl = calloc
78: (sp->tbl->cols, sizeof(struct termp_tbl));
79: if (NULL == tp->tbl) {
80: perror(NULL);
81: exit(EXIT_FAILURE);
82: }
1.3 ! kristaps 83: tbl_calc(tp, sp);
1.1 kristaps 84: term_flushln(tp);
1.2 kristaps 85: }
1.1 kristaps 86:
87: if (TBL_SPAN_FIRST & sp->flags)
88: tbl_hframe(tp, sp);
89:
90: tp->flags |= TERMP_NONOSPACE;
91: tp->flags |= TERMP_NOSPACE;
92:
93: tbl_vframe(tp, sp->tbl);
94:
95: switch (sp->pos) {
96: case (TBL_SPAN_HORIZ):
97: /* FALLTHROUGH */
98: case (TBL_SPAN_DHORIZ):
99: tbl_hrule(tp, sp);
100: tbl_vframe(tp, sp->tbl);
101: term_newln(tp);
1.2 kristaps 102: goto end;
1.1 kristaps 103: default:
104: break;
105: }
106:
107: dp = sp->first;
108: for (hp = sp->head; hp; hp = hp->next) {
109: switch (hp->pos) {
110: case (TBL_HEAD_VERT):
111: /* FALLTHROUGH */
112: case (TBL_HEAD_DVERT):
113: tbl_spanner(tp, hp);
114: break;
115: case (TBL_HEAD_DATA):
1.2 kristaps 116: tbl_data(tp, sp->tbl, dp, &tp->tbl[hp->ident]);
1.1 kristaps 117: if (dp)
118: dp = dp->next;
119: break;
120: default:
121: abort();
122: /* NOTREACHED */
123: }
124: }
125:
126: tbl_vframe(tp, sp->tbl);
127: term_flushln(tp);
128:
1.2 kristaps 129: end:
130: if (TBL_SPAN_LAST & sp->flags) {
1.1 kristaps 131: tbl_hframe(tp, sp);
1.2 kristaps 132: assert(tp->tbl);
133: free(tp->tbl);
134: tp->tbl = NULL;
135: }
1.1 kristaps 136:
137: tp->flags &= ~TERMP_NONOSPACE;
138:
139: }
140:
141: static void
142: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
143: {
144: const struct tbl_head *hp;
145: char c;
1.2 kristaps 146: int width;
1.1 kristaps 147:
148: /*
149: * An hrule extends across the entire table and is demarked by a
150: * standalone `_' or whatnot in lieu of a table row. Spanning
151: * headers are marked by a `+', as are table boundaries.
152: */
153:
154: c = '-';
155: if (TBL_SPAN_DHORIZ == sp->pos)
156: c = '=';
157:
158: /* FIXME: don't use `+' between data and a spanner! */
159:
160: for (hp = sp->head; hp; hp = hp->next) {
1.2 kristaps 161: width = tp->tbl[hp->ident].width;
1.1 kristaps 162: switch (hp->pos) {
163: case (TBL_HEAD_DATA):
1.2 kristaps 164: tbl_char(tp, c, width);
1.1 kristaps 165: break;
166: case (TBL_HEAD_DVERT):
1.2 kristaps 167: tbl_char(tp, '+', width);
1.1 kristaps 168: /* FALLTHROUGH */
169: case (TBL_HEAD_VERT):
1.2 kristaps 170: tbl_char(tp, '+', width);
1.1 kristaps 171: break;
172: default:
173: abort();
174: /* NOTREACHED */
175: }
176: }
177: }
178:
179: static void
180: tbl_hframe(struct termp *tp, const struct tbl_span *sp)
181: {
182: const struct tbl_head *hp;
1.2 kristaps 183: int width;
1.1 kristaps 184:
185: if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
186: TBL_OPT_DBOX & sp->tbl->opts))
187: return;
188:
189: tp->flags |= TERMP_NONOSPACE;
190: tp->flags |= TERMP_NOSPACE;
191:
192: /*
193: * Print out the horizontal part of a frame or double frame. A
194: * double frame has an unbroken `-' outer line the width of the
195: * table, bordered by `+'. The frame (or inner frame, in the
196: * case of the double frame) is a `-' bordered by `+' and broken
197: * by `+' whenever a span is encountered.
198: */
199:
200: if (TBL_OPT_DBOX & sp->tbl->opts) {
201: term_word(tp, "+");
1.2 kristaps 202: for (hp = sp->head; hp; hp = hp->next) {
203: width = tp->tbl[hp->ident].width;
204: tbl_char(tp, '-', width);
205: }
1.1 kristaps 206: term_word(tp, "+");
207: term_flushln(tp);
208: }
209:
210: term_word(tp, "+");
211: for (hp = sp->head; hp; hp = hp->next) {
1.2 kristaps 212: width = tp->tbl[hp->ident].width;
1.1 kristaps 213: switch (hp->pos) {
214: case (TBL_HEAD_DATA):
1.2 kristaps 215: tbl_char(tp, '-', width);
1.1 kristaps 216: break;
217: default:
1.2 kristaps 218: tbl_char(tp, '+', width);
1.1 kristaps 219: break;
220: }
221: }
222: term_word(tp, "+");
223: term_flushln(tp);
224: }
225:
226: static void
227: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 228: const struct tbl_dat *dp,
229: const struct termp_tbl *tbp)
1.1 kristaps 230: {
231: enum tbl_cellt pos;
232:
233: if (NULL == dp) {
1.2 kristaps 234: tbl_char(tp, ASCII_NBRSP, tbp->width);
1.1 kristaps 235: return;
236: }
237:
238: switch (dp->pos) {
239: case (TBL_DATA_HORIZ):
240: /* FALLTHROUGH */
241: case (TBL_DATA_DHORIZ):
1.2 kristaps 242: tbl_data_spanner(tp, dp, tbp);
1.1 kristaps 243: return;
244: default:
245: break;
246: }
247:
248: pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
249:
250: switch (pos) {
251: case (TBL_CELL_HORIZ):
252: /* FALLTHROUGH */
253: case (TBL_CELL_DHORIZ):
1.2 kristaps 254: tbl_data_spanner(tp, dp, tbp);
1.1 kristaps 255: break;
256: case (TBL_CELL_LONG):
257: /* FALLTHROUGH */
258: case (TBL_CELL_CENTRE):
259: /* FALLTHROUGH */
260: case (TBL_CELL_LEFT):
261: /* FALLTHROUGH */
262: case (TBL_CELL_RIGHT):
1.2 kristaps 263: tbl_data_literal(tp, dp, tbp);
1.1 kristaps 264: break;
265: case (TBL_CELL_NUMBER):
1.2 kristaps 266: tbl_data_number(tp, tbl, dp, tbp);
1.1 kristaps 267: break;
268: default:
269: abort();
270: /* NOTREACHED */
271: }
272: }
273: static void
274: tbl_spanner(struct termp *tp, const struct tbl_head *hp)
275: {
276:
277: switch (hp->pos) {
278: case (TBL_HEAD_VERT):
279: term_word(tp, "|");
280: break;
281: case (TBL_HEAD_DVERT):
282: term_word(tp, "||");
283: break;
284: default:
285: break;
286: }
287: }
288:
289: static void
290: tbl_vframe(struct termp *tp, const struct tbl *tbl)
291: {
292: /* Always just a single vertical line. */
293:
294: if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
295: term_word(tp, "|");
296: }
297:
298:
299: static inline void
300: tbl_char(struct termp *tp, char c, int len)
301: {
1.3 ! kristaps 302: int i, sz;
1.1 kristaps 303: char cp[2];
304:
305: cp[0] = c;
306: cp[1] = '\0';
307:
1.3 ! kristaps 308: sz = term_strlen(tp, cp);
! 309:
! 310: for (i = 0; i < len; i += sz)
1.1 kristaps 311: term_word(tp, cp);
312: }
313:
314: static void
1.2 kristaps 315: tbl_data_spanner(struct termp *tp,
316: const struct tbl_dat *dp,
317: const struct termp_tbl *tblp)
1.1 kristaps 318: {
319:
320: switch (dp->pos) {
321: case (TBL_DATA_HORIZ):
322: case (TBL_DATA_NHORIZ):
1.2 kristaps 323: tbl_char(tp, '-', tblp->width);
1.1 kristaps 324: break;
325: case (TBL_DATA_DHORIZ):
326: case (TBL_DATA_NDHORIZ):
1.2 kristaps 327: tbl_char(tp, '=', tblp->width);
1.1 kristaps 328: break;
329: default:
330: break;
331: }
332: }
333:
334: static void
1.2 kristaps 335: tbl_data_literal(struct termp *tp,
336: const struct tbl_dat *dp,
337: const struct termp_tbl *tblp)
1.1 kristaps 338: {
1.3 ! kristaps 339: int padl, padr, ssz;
1.1 kristaps 340: enum tbl_cellt pos;
341:
342: padl = padr = 0;
343:
344: pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
1.3 ! kristaps 345: ssz = term_len(tp, 1);
1.1 kristaps 346:
347: switch (pos) {
348: case (TBL_CELL_LONG):
1.3 ! kristaps 349: padl = ssz;
! 350: padr = tblp->width - term_strlen(tp, dp->string) - ssz;
1.1 kristaps 351: break;
352: case (TBL_CELL_CENTRE):
1.3 ! kristaps 353: padl = tblp->width - term_strlen(tp, dp->string);
1.1 kristaps 354: if (padl % 2)
355: padr++;
356: padl /= 2;
357: padr += padl;
358: break;
359: case (TBL_CELL_RIGHT):
1.3 ! kristaps 360: padl = tblp->width - term_strlen(tp, dp->string);
1.1 kristaps 361: break;
362: default:
1.3 ! kristaps 363: padr = tblp->width - term_strlen(tp, dp->string);
1.1 kristaps 364: break;
365: }
366:
367: tbl_char(tp, ASCII_NBRSP, padl);
368: term_word(tp, dp->string);
369: tbl_char(tp, ASCII_NBRSP, padr);
370: }
371:
372: static void
373: tbl_data_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 374: const struct tbl_dat *dp,
375: const struct termp_tbl *tblp)
1.1 kristaps 376: {
1.3 ! kristaps 377: char *decp;
1.1 kristaps 378: int d, padl, sz;
379:
380: /*
381: * See calc_data_number(). Left-pad by taking the offset of our
382: * and the maximum decimal; right-pad by the remaining amount.
383: */
384:
385: sz = (int)strlen(dp->string);
386:
1.3 ! kristaps 387: if (NULL == (decp = strchr(dp->string, tbl->decimal))) {
1.1 kristaps 388: d = sz + 1;
389: } else {
390: d = (int)(decp - dp->string) + 1;
391: }
392:
1.2 kristaps 393: assert(d <= tblp->decimal);
394: assert(sz - d <= tblp->width - tblp->decimal);
1.1 kristaps 395:
1.2 kristaps 396: padl = tblp->decimal - d + 1;
397: assert(tblp->width - sz - padl);
1.1 kristaps 398:
399: tbl_char(tp, ASCII_NBRSP, padl);
400: term_word(tp, dp->string);
1.2 kristaps 401: tbl_char(tp, ASCII_NBRSP, tblp->width - sz - padl);
402: }
403:
404: static void
1.3 ! kristaps 405: tbl_calc(struct termp *tp, const struct tbl_span *sp)
1.2 kristaps 406: {
407: const struct tbl_dat *dp;
408: const struct tbl_head *hp;
409: struct termp_tbl *p;
410:
411: /* Calculate width as the max of column cells' widths. */
412:
413: hp = sp->head;
414:
415: for ( ; sp; sp = sp->next) {
416: switch (sp->pos) {
417: case (TBL_DATA_HORIZ):
418: /* FALLTHROUGH */
419: case (TBL_DATA_DHORIZ):
420: continue;
421: default:
422: break;
423: }
424: for (dp = sp->first; dp; dp = dp->next) {
425: if (NULL == dp->layout)
426: continue;
1.3 ! kristaps 427: p = &tp->tbl[dp->layout->head->ident];
! 428: tbl_calc_data(tp, sp->tbl, dp, p);
1.2 kristaps 429: }
430: }
431:
432: /* Calculate width as the simple spanner value. */
433:
434: for ( ; hp; hp = hp->next)
435: switch (hp->pos) {
436: case (TBL_HEAD_VERT):
1.3 ! kristaps 437: tp->tbl[hp->ident].width = term_len(tp, 1);
1.2 kristaps 438: break;
439: case (TBL_HEAD_DVERT):
1.3 ! kristaps 440: tp->tbl[hp->ident].width = term_len(tp, 2);
1.2 kristaps 441: break;
442: default:
443: break;
444: }
445: }
446:
447: static void
1.3 ! kristaps 448: tbl_calc_data(struct termp *tp, const struct tbl *tbl,
! 449: const struct tbl_dat *dp, struct termp_tbl *tblp)
1.2 kristaps 450: {
451:
452: /* Branch down into data sub-types. */
453:
454: switch (dp->layout->pos) {
455: case (TBL_CELL_HORIZ):
456: /* FALLTHROUGH */
457: case (TBL_CELL_DHORIZ):
458: tblp->width = 1;
459: break;
460: case (TBL_CELL_LONG):
461: /* FALLTHROUGH */
462: case (TBL_CELL_CENTRE):
463: /* FALLTHROUGH */
464: case (TBL_CELL_LEFT):
465: /* FALLTHROUGH */
466: case (TBL_CELL_RIGHT):
1.3 ! kristaps 467: tbl_calc_data_literal(tp, dp, tblp);
1.2 kristaps 468: break;
469: case (TBL_CELL_NUMBER):
1.3 ! kristaps 470: tbl_calc_data_number(tp, tbl, dp, tblp);
1.2 kristaps 471: break;
472: default:
473: abort();
474: /* NOTREACHED */
475: }
476: }
477:
478: static void
1.3 ! kristaps 479: tbl_calc_data_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 480: const struct tbl_dat *dp, struct termp_tbl *tblp)
481: {
482: int sz, d;
1.3 ! kristaps 483: char *cp;
1.2 kristaps 484:
485: /*
486: * First calculate number width and decimal place (last + 1 for
487: * no-decimal numbers). If the stored decimal is subsequent
488: * ours, make our size longer by that difference
489: * (right-"shifting"); similarly, if ours is subsequent the
490: * stored, then extend the stored size by the difference.
491: * Finally, re-assign the stored values.
492: */
493:
494: /* TODO: use spacing modifier. */
495:
496: assert(dp->string);
497: sz = (int)strlen(dp->string);
498:
1.3 ! kristaps 499: if (NULL == (cp = strchr(dp->string, tbl->decimal)))
1.2 kristaps 500: d = sz + 1;
501: else
502: d = (int)(cp - dp->string) + 1;
503:
504: sz += 2;
505:
506: if (tblp->decimal > d) {
507: sz += tblp->decimal - d;
508: d = tblp->decimal;
509: } else
510: tblp->width += d - tblp->decimal;
511:
512: if (sz > tblp->width)
513: tblp->width = sz;
514: if (d > tblp->decimal)
515: tblp->decimal = d;
516: }
517:
518: static void
1.3 ! kristaps 519: tbl_calc_data_literal(struct termp *tp,
! 520: const struct tbl_dat *dp,
! 521: struct termp_tbl *tblp)
1.2 kristaps 522: {
523: int sz, bufsz;
524:
525: /*
526: * Calculate our width and use the spacing, with a minimum
527: * spacing dictated by position (centre, e.g,. gets a space on
528: * either side, while right/left get a single adjacent space).
529: */
530:
531: assert(dp->string);
1.3 ! kristaps 532: sz = term_strlen(tp, dp->string);
1.2 kristaps 533:
534: switch (dp->layout->pos) {
535: case (TBL_CELL_LONG):
536: /* FALLTHROUGH */
537: case (TBL_CELL_CENTRE):
538: bufsz = 2;
539: break;
540: default:
541: bufsz = 1;
542: break;
543: }
544:
545: if (dp->layout->spacing)
546: bufsz = bufsz > dp->layout->spacing ?
547: bufsz : dp->layout->spacing;
548:
549: sz += bufsz;
550: if (tblp->width < sz)
551: tblp->width = sz;
1.1 kristaps 552: }
CVSweb