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