Annotation of mandoc/tbl_term.c, Revision 1.21
1.21 ! schwarze 1: /* $Id: tbl_term.c,v 1.20 2011/07/17 15:43:00 kristaps Exp $ */
1.1 kristaps 2: /*
1.20 kristaps 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.19 schwarze 4: * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 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: #ifdef HAVE_CONFIG_H
19: #include "config.h"
20: #endif
21:
22: #include <assert.h>
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26:
27: #include "mandoc.h"
28: #include "out.h"
29: #include "term.h"
30:
1.11 kristaps 31: static size_t term_tbl_len(size_t, void *);
32: static size_t term_tbl_strlen(const char *, void *);
1.12 kristaps 33: static void tbl_char(struct termp *, char, size_t);
1.11 kristaps 34: static void tbl_data(struct termp *, const struct tbl *,
35: const struct tbl_dat *,
36: const struct roffcol *);
1.21 ! schwarze 37: static size_t tbl_rulewidth(struct termp *, const struct tbl_head *);
! 38: static void tbl_hframe(struct termp *, const struct tbl_span *, int);
1.11 kristaps 39: static void tbl_literal(struct termp *, const struct tbl_dat *,
40: const struct roffcol *);
41: static void tbl_number(struct termp *, const struct tbl *,
42: const struct tbl_dat *,
43: const struct roffcol *);
44: static void tbl_hrule(struct termp *, const struct tbl_span *);
45: static void tbl_vrule(struct termp *, const struct tbl_head *);
46:
47:
48: static size_t
49: term_tbl_strlen(const char *p, void *arg)
50: {
51:
52: return(term_strlen((const struct termp *)arg, p));
53: }
54:
55: static size_t
56: term_tbl_len(size_t sz, void *arg)
57: {
58:
59: return(term_len((const struct termp *)arg, sz));
60: }
1.1 kristaps 61:
62: void
63: term_tbl(struct termp *tp, const struct tbl_span *sp)
64: {
1.11 kristaps 65: const struct tbl_head *hp;
66: const struct tbl_dat *dp;
67: struct roffcol *col;
1.16 kristaps 68: int spans;
1.12 kristaps 69: size_t rmargin, maxrmargin;
1.11 kristaps 70:
71: rmargin = tp->rmargin;
72: maxrmargin = tp->maxrmargin;
73:
74: tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
1.1 kristaps 75:
1.4 kristaps 76: /* Inhibit printing of spaces: we do padding ourselves. */
77:
78: tp->flags |= TERMP_NONOSPACE;
79: tp->flags |= TERMP_NOSPACE;
80:
81: /*
1.11 kristaps 82: * The first time we're invoked for a given table block,
83: * calculate the table widths and decimal positions.
1.4 kristaps 84: */
85:
1.2 kristaps 86: if (TBL_SPAN_FIRST & sp->flags) {
1.11 kristaps 87: term_flushln(tp);
88:
89: tp->tbl.len = term_tbl_len;
90: tp->tbl.slen = term_tbl_strlen;
91: tp->tbl.arg = tp;
1.4 kristaps 92:
1.11 kristaps 93: tblcalc(&tp->tbl, sp);
1.2 kristaps 94: }
1.1 kristaps 95:
1.4 kristaps 96: /* Horizontal frame at the start of boxed tables. */
97:
1.21 ! schwarze 98: if (TBL_SPAN_FIRST & sp->flags) {
! 99: if (TBL_OPT_DBOX & sp->tbl->opts)
! 100: tbl_hframe(tp, sp, 1);
! 101: if (TBL_OPT_DBOX & sp->tbl->opts ||
! 102: TBL_OPT_BOX & sp->tbl->opts)
! 103: tbl_hframe(tp, sp, 0);
! 104: }
1.1 kristaps 105:
1.4 kristaps 106: /* Vertical frame at the start of each row. */
1.1 kristaps 107:
1.21 ! schwarze 108: if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
! 109: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
! 110: TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
1.1 kristaps 111:
1.4 kristaps 112: /*
113: * Now print the actual data itself depending on the span type.
114: * Spanner spans get a horizontal rule; data spanners have their
115: * data printed by matching data to header.
116: */
117:
1.1 kristaps 118: switch (sp->pos) {
119: case (TBL_SPAN_HORIZ):
120: /* FALLTHROUGH */
121: case (TBL_SPAN_DHORIZ):
122: tbl_hrule(tp, sp);
123: break;
1.4 kristaps 124: case (TBL_SPAN_DATA):
125: /* Iterate over template headers. */
126: dp = sp->first;
1.16 kristaps 127: spans = 0;
1.4 kristaps 128: for (hp = sp->head; hp; hp = hp->next) {
1.16 kristaps 129: /*
130: * If the current data header is invoked during
131: * a spanner ("spans" > 0), don't emit anything
132: * at all.
133: */
1.4 kristaps 134: switch (hp->pos) {
135: case (TBL_HEAD_VERT):
136: /* FALLTHROUGH */
137: case (TBL_HEAD_DVERT):
1.16 kristaps 138: if (spans <= 0)
139: tbl_vrule(tp, hp);
1.4 kristaps 140: continue;
141: case (TBL_HEAD_DATA):
142: break;
143: }
1.11 kristaps 144:
1.16 kristaps 145: if (--spans >= 0)
146: continue;
147:
1.21 ! schwarze 148: /*
! 149: * All cells get a leading blank, except the
! 150: * first one and those after double rulers.
! 151: */
! 152:
! 153: if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
! 154: tbl_char(tp, ASCII_NBRSP, 1);
! 155:
1.11 kristaps 156: col = &tp->tbl.cols[hp->ident];
157: tbl_data(tp, sp->tbl, dp, col);
1.1 kristaps 158:
1.21 ! schwarze 159: /* No trailing blanks. */
! 160:
! 161: if (NULL == hp->next)
! 162: break;
! 163:
! 164: /*
! 165: * Add another blank between cells,
! 166: * or two when there is no vertical ruler.
! 167: */
! 168:
! 169: tbl_char(tp, ASCII_NBRSP,
! 170: TBL_HEAD_VERT == hp->next->pos ||
! 171: TBL_HEAD_DVERT == hp->next->pos ? 1 : 2);
! 172:
1.16 kristaps 173: /*
174: * Go to the next data cell and assign the
175: * number of subsequent spans, if applicable.
176: */
177:
178: if (dp) {
179: spans = dp->spans;
1.1 kristaps 180: dp = dp->next;
1.16 kristaps 181: }
1.1 kristaps 182: }
1.4 kristaps 183: break;
1.1 kristaps 184: }
185:
1.21 ! schwarze 186: /* Vertical frame at the end of each row. */
! 187:
! 188: if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
! 189: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
! 190: TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
1.1 kristaps 191: term_flushln(tp);
192:
1.4 kristaps 193: /*
194: * If we're the last row, clean up after ourselves: clear the
195: * existing table configuration and set it to NULL.
196: */
197:
1.2 kristaps 198: if (TBL_SPAN_LAST & sp->flags) {
1.21 ! schwarze 199: if (TBL_OPT_DBOX & sp->tbl->opts ||
! 200: TBL_OPT_BOX & sp->tbl->opts)
! 201: tbl_hframe(tp, sp, 0);
! 202: if (TBL_OPT_DBOX & sp->tbl->opts)
! 203: tbl_hframe(tp, sp, 1);
1.11 kristaps 204: assert(tp->tbl.cols);
205: free(tp->tbl.cols);
206: tp->tbl.cols = NULL;
1.2 kristaps 207: }
1.1 kristaps 208:
209: tp->flags &= ~TERMP_NONOSPACE;
1.11 kristaps 210: tp->rmargin = rmargin;
211: tp->maxrmargin = maxrmargin;
1.1 kristaps 212:
213: }
214:
1.21 ! schwarze 215: /*
! 216: * Horizontal rules extend across the entire table.
! 217: * Calculate the width by iterating over columns.
! 218: */
! 219: static size_t
! 220: tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
! 221: {
! 222: size_t width;
! 223:
! 224: width = tp->tbl.cols[hp->ident].width;
! 225: if (TBL_HEAD_DATA == hp->pos) {
! 226: /* Account for leading blanks. */
! 227: if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
! 228: width++;
! 229: /* Account for trailing blanks. */
! 230: width++;
! 231: if (hp->next &&
! 232: TBL_HEAD_VERT != hp->next->pos &&
! 233: TBL_HEAD_DVERT != hp->next->pos)
! 234: width++;
! 235: }
! 236: return(width);
! 237: }
! 238:
! 239: /*
! 240: * Rules inside the table can be single or double
! 241: * and have crossings with vertical rules marked with pluses.
! 242: */
1.1 kristaps 243: static void
244: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
245: {
246: const struct tbl_head *hp;
247: char c;
248:
249: c = '-';
250: if (TBL_SPAN_DHORIZ == sp->pos)
251: c = '=';
252:
1.21 ! schwarze 253: for (hp = sp->head; hp; hp = hp->next)
! 254: tbl_char(tp,
! 255: TBL_HEAD_DATA == hp->pos ? c : '+',
! 256: tbl_rulewidth(tp, hp));
1.1 kristaps 257: }
258:
1.21 ! schwarze 259: /*
! 260: * Rules above and below the table are always single
! 261: * and have an additional plus at the beginning and end.
! 262: * For double frames, this function is called twice,
! 263: * and the outer one does not have crossings.
! 264: */
1.1 kristaps 265: static void
1.21 ! schwarze 266: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1 kristaps 267: {
268: const struct tbl_head *hp;
269:
270: term_word(tp, "+");
1.21 ! schwarze 271: for (hp = sp->head; hp; hp = hp->next)
! 272: tbl_char(tp,
! 273: outer || TBL_HEAD_DATA == hp->pos ? '-' : '+',
! 274: tbl_rulewidth(tp, hp));
1.1 kristaps 275: term_word(tp, "+");
276: term_flushln(tp);
277: }
278:
279: static void
280: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 281: const struct tbl_dat *dp,
1.11 kristaps 282: const struct roffcol *col)
1.1 kristaps 283: {
284:
285: if (NULL == dp) {
1.11 kristaps 286: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 287: return;
288: }
1.16 kristaps 289: assert(dp->layout);
1.1 kristaps 290:
291: switch (dp->pos) {
1.10 kristaps 292: case (TBL_DATA_NONE):
1.11 kristaps 293: tbl_char(tp, ASCII_NBRSP, col->width);
1.10 kristaps 294: return;
1.1 kristaps 295: case (TBL_DATA_HORIZ):
296: /* FALLTHROUGH */
1.7 kristaps 297: case (TBL_DATA_NHORIZ):
1.11 kristaps 298: tbl_char(tp, '-', col->width);
1.7 kristaps 299: return;
300: case (TBL_DATA_NDHORIZ):
301: /* FALLTHROUGH */
1.1 kristaps 302: case (TBL_DATA_DHORIZ):
1.11 kristaps 303: tbl_char(tp, '=', col->width);
1.1 kristaps 304: return;
305: default:
306: break;
307: }
308:
1.16 kristaps 309: switch (dp->layout->pos) {
1.1 kristaps 310: case (TBL_CELL_HORIZ):
1.11 kristaps 311: tbl_char(tp, '-', col->width);
1.7 kristaps 312: break;
1.1 kristaps 313: case (TBL_CELL_DHORIZ):
1.11 kristaps 314: tbl_char(tp, '=', col->width);
1.1 kristaps 315: break;
316: case (TBL_CELL_LONG):
317: /* FALLTHROUGH */
318: case (TBL_CELL_CENTRE):
319: /* FALLTHROUGH */
320: case (TBL_CELL_LEFT):
321: /* FALLTHROUGH */
322: case (TBL_CELL_RIGHT):
1.11 kristaps 323: tbl_literal(tp, dp, col);
1.1 kristaps 324: break;
325: case (TBL_CELL_NUMBER):
1.11 kristaps 326: tbl_number(tp, tbl, dp, col);
1.18 kristaps 327: break;
328: case (TBL_CELL_DOWN):
329: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 330: break;
331: default:
332: abort();
333: /* NOTREACHED */
334: }
335: }
1.11 kristaps 336:
1.1 kristaps 337: static void
1.11 kristaps 338: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 kristaps 339: {
340:
341: switch (hp->pos) {
342: case (TBL_HEAD_VERT):
343: term_word(tp, "|");
344: break;
345: case (TBL_HEAD_DVERT):
346: term_word(tp, "||");
347: break;
348: default:
349: break;
350: }
351: }
352:
353: static void
1.12 kristaps 354: tbl_char(struct termp *tp, char c, size_t len)
1.1 kristaps 355: {
1.12 kristaps 356: size_t i, sz;
357: char cp[2];
358:
359: cp[0] = c;
360: cp[1] = '\0';
1.1 kristaps 361:
1.3 kristaps 362: sz = term_strlen(tp, cp);
363:
364: for (i = 0; i < len; i += sz)
1.1 kristaps 365: term_word(tp, cp);
366: }
367:
368: static void
1.11 kristaps 369: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
370: const struct roffcol *col)
1.1 kristaps 371: {
1.21 ! schwarze 372: size_t len, padl, padr;
1.1 kristaps 373:
1.17 kristaps 374: assert(dp->string);
1.21 ! schwarze 375: len = term_strlen(tp, dp->string);
! 376: padr = col->width > len ? col->width - len : 0;
! 377: padl = 0;
1.1 kristaps 378:
1.16 kristaps 379: switch (dp->layout->pos) {
1.1 kristaps 380: case (TBL_CELL_LONG):
1.21 ! schwarze 381: padl = term_len(tp, 1);
! 382: padr = padr > padl ? padr - padl : 0;
1.1 kristaps 383: break;
384: case (TBL_CELL_CENTRE):
1.21 ! schwarze 385: if (2 > padr)
1.19 schwarze 386: break;
1.21 ! schwarze 387: padl = padr / 2;
1.19 schwarze 388: padr -= padl;
1.1 kristaps 389: break;
390: case (TBL_CELL_RIGHT):
1.21 ! schwarze 391: padl = padr;
! 392: padr = 0;
1.1 kristaps 393: break;
394: default:
395: break;
396: }
397:
398: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 399: term_word(tp, dp->string);
1.21 ! schwarze 400: tbl_char(tp, ASCII_NBRSP, padr);
1.1 kristaps 401: }
402:
403: static void
1.11 kristaps 404: tbl_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 405: const struct tbl_dat *dp,
1.11 kristaps 406: const struct roffcol *col)
1.1 kristaps 407: {
1.11 kristaps 408: char *cp;
409: char buf[2];
410: size_t sz, psz, ssz, d, padl;
411: int i;
1.1 kristaps 412:
413: /*
414: * See calc_data_number(). Left-pad by taking the offset of our
415: * and the maximum decimal; right-pad by the remaining amount.
416: */
417:
1.17 kristaps 418: assert(dp->string);
1.2 kristaps 419:
1.17 kristaps 420: sz = term_strlen(tp, dp->string);
1.2 kristaps 421:
1.11 kristaps 422: buf[0] = tbl->decimal;
423: buf[1] = '\0';
1.2 kristaps 424:
1.11 kristaps 425: psz = term_strlen(tp, buf);
1.10 kristaps 426:
1.17 kristaps 427: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 kristaps 428: buf[1] = '\0';
1.17 kristaps 429: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
430: buf[0] = dp->string[i];
1.5 kristaps 431: ssz += term_strlen(tp, buf);
432: }
433: d = ssz + psz;
434: } else
435: d = sz + psz;
1.2 kristaps 436:
1.11 kristaps 437: padl = col->decimal - d;
1.2 kristaps 438:
1.11 kristaps 439: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 440: term_word(tp, dp->string);
1.21 ! schwarze 441: if (col->width > sz + padl)
! 442: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2 kristaps 443: }
444:
CVSweb