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