Annotation of mandoc/tbl_term.c, Revision 1.23
1.23 ! schwarze 1: /* $Id: tbl_term.c,v 1.22 2012/05/27 17:54:54 schwarze Exp $ */
1.1 kristaps 2: /*
1.20 kristaps 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.23 ! schwarze 4: * Copyright (c) 2011, 2012 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.22 schwarze 129:
1.16 kristaps 130: /*
131: * If the current data header is invoked during
132: * a spanner ("spans" > 0), don't emit anything
133: * at all.
134: */
1.11 kristaps 135:
1.16 kristaps 136: if (--spans >= 0)
137: continue;
138:
1.22 schwarze 139: /* Separate columns. */
1.21 schwarze 140:
1.22 schwarze 141: if (NULL != hp->prev)
142: tbl_vrule(tp, hp);
1.21 schwarze 143:
1.11 kristaps 144: col = &tp->tbl.cols[hp->ident];
145: tbl_data(tp, sp->tbl, dp, col);
1.1 kristaps 146:
1.16 kristaps 147: /*
148: * Go to the next data cell and assign the
149: * number of subsequent spans, if applicable.
150: */
151:
152: if (dp) {
153: spans = dp->spans;
1.1 kristaps 154: dp = dp->next;
1.16 kristaps 155: }
1.1 kristaps 156: }
1.4 kristaps 157: break;
1.1 kristaps 158: }
159:
1.21 schwarze 160: /* Vertical frame at the end of each row. */
161:
162: if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
163: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
164: TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
1.1 kristaps 165: term_flushln(tp);
166:
1.4 kristaps 167: /*
168: * If we're the last row, clean up after ourselves: clear the
169: * existing table configuration and set it to NULL.
170: */
171:
1.2 kristaps 172: if (TBL_SPAN_LAST & sp->flags) {
1.21 schwarze 173: if (TBL_OPT_DBOX & sp->tbl->opts ||
174: TBL_OPT_BOX & sp->tbl->opts)
175: tbl_hframe(tp, sp, 0);
176: if (TBL_OPT_DBOX & sp->tbl->opts)
177: tbl_hframe(tp, sp, 1);
1.11 kristaps 178: assert(tp->tbl.cols);
179: free(tp->tbl.cols);
180: tp->tbl.cols = NULL;
1.2 kristaps 181: }
1.1 kristaps 182:
183: tp->flags &= ~TERMP_NONOSPACE;
1.11 kristaps 184: tp->rmargin = rmargin;
185: tp->maxrmargin = maxrmargin;
1.1 kristaps 186:
187: }
188:
1.21 schwarze 189: /*
190: * Horizontal rules extend across the entire table.
191: * Calculate the width by iterating over columns.
192: */
193: static size_t
194: tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
195: {
196: size_t width;
197:
198: width = tp->tbl.cols[hp->ident].width;
1.22 schwarze 199:
200: /* Account for leading blanks. */
201: if (hp->prev)
202: width += 2 - hp->vert;
203:
204: /* Account for trailing blank. */
205: width++;
206:
1.21 schwarze 207: return(width);
208: }
209:
210: /*
211: * Rules inside the table can be single or double
212: * and have crossings with vertical rules marked with pluses.
213: */
1.1 kristaps 214: static void
215: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
216: {
217: const struct tbl_head *hp;
218: char c;
219:
220: c = '-';
221: if (TBL_SPAN_DHORIZ == sp->pos)
222: c = '=';
223:
1.22 schwarze 224: for (hp = sp->head; hp; hp = hp->next) {
225: if (hp->prev && hp->vert)
226: tbl_char(tp, '+', hp->vert);
227: tbl_char(tp, c, tbl_rulewidth(tp, hp));
228: }
1.1 kristaps 229: }
230:
1.21 schwarze 231: /*
232: * Rules above and below the table are always single
233: * and have an additional plus at the beginning and end.
234: * For double frames, this function is called twice,
235: * and the outer one does not have crossings.
236: */
1.1 kristaps 237: static void
1.21 schwarze 238: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1 kristaps 239: {
240: const struct tbl_head *hp;
241:
242: term_word(tp, "+");
1.22 schwarze 243: for (hp = sp->head; hp; hp = hp->next) {
244: if (hp->prev && hp->vert)
245: tbl_char(tp, (outer ? '-' : '+'), hp->vert);
246: tbl_char(tp, '-', tbl_rulewidth(tp, hp));
247: }
1.1 kristaps 248: term_word(tp, "+");
249: term_flushln(tp);
250: }
251:
252: static void
253: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 254: const struct tbl_dat *dp,
1.11 kristaps 255: const struct roffcol *col)
1.1 kristaps 256: {
257:
258: if (NULL == dp) {
1.11 kristaps 259: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 260: return;
261: }
1.16 kristaps 262: assert(dp->layout);
1.1 kristaps 263:
264: switch (dp->pos) {
1.10 kristaps 265: case (TBL_DATA_NONE):
1.11 kristaps 266: tbl_char(tp, ASCII_NBRSP, col->width);
1.10 kristaps 267: return;
1.1 kristaps 268: case (TBL_DATA_HORIZ):
269: /* FALLTHROUGH */
1.7 kristaps 270: case (TBL_DATA_NHORIZ):
1.11 kristaps 271: tbl_char(tp, '-', col->width);
1.7 kristaps 272: return;
273: case (TBL_DATA_NDHORIZ):
274: /* FALLTHROUGH */
1.1 kristaps 275: case (TBL_DATA_DHORIZ):
1.11 kristaps 276: tbl_char(tp, '=', col->width);
1.1 kristaps 277: return;
278: default:
279: break;
280: }
281:
1.16 kristaps 282: switch (dp->layout->pos) {
1.1 kristaps 283: case (TBL_CELL_HORIZ):
1.11 kristaps 284: tbl_char(tp, '-', col->width);
1.7 kristaps 285: break;
1.1 kristaps 286: case (TBL_CELL_DHORIZ):
1.11 kristaps 287: tbl_char(tp, '=', col->width);
1.1 kristaps 288: break;
289: case (TBL_CELL_LONG):
290: /* FALLTHROUGH */
291: case (TBL_CELL_CENTRE):
292: /* FALLTHROUGH */
293: case (TBL_CELL_LEFT):
294: /* FALLTHROUGH */
295: case (TBL_CELL_RIGHT):
1.11 kristaps 296: tbl_literal(tp, dp, col);
1.1 kristaps 297: break;
298: case (TBL_CELL_NUMBER):
1.11 kristaps 299: tbl_number(tp, tbl, dp, col);
1.18 kristaps 300: break;
301: case (TBL_CELL_DOWN):
302: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 303: break;
304: default:
305: abort();
306: /* NOTREACHED */
307: }
308: }
1.11 kristaps 309:
1.1 kristaps 310: static void
1.11 kristaps 311: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 kristaps 312: {
313:
1.22 schwarze 314: tbl_char(tp, ASCII_NBRSP, 1);
315: if (0 < hp->vert)
316: tbl_char(tp, '|', hp->vert);
317: if (2 > hp->vert)
318: tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
1.1 kristaps 319: }
320:
321: static void
1.12 kristaps 322: tbl_char(struct termp *tp, char c, size_t len)
1.1 kristaps 323: {
1.12 kristaps 324: size_t i, sz;
325: char cp[2];
326:
327: cp[0] = c;
328: cp[1] = '\0';
1.1 kristaps 329:
1.3 kristaps 330: sz = term_strlen(tp, cp);
331:
332: for (i = 0; i < len; i += sz)
1.1 kristaps 333: term_word(tp, cp);
334: }
335:
336: static void
1.11 kristaps 337: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
338: const struct roffcol *col)
1.1 kristaps 339: {
1.23 ! schwarze 340: struct tbl_head *hp;
! 341: size_t width, len, padl, padr;
! 342: int spans;
1.1 kristaps 343:
1.17 kristaps 344: assert(dp->string);
1.21 schwarze 345: len = term_strlen(tp, dp->string);
1.23 ! schwarze 346:
! 347: hp = dp->layout->head->next;
! 348: width = col->width;
! 349: for (spans = dp->spans; spans--; hp = hp->next)
! 350: width += tp->tbl.cols[hp->ident].width + 3;
! 351:
! 352: padr = width > len ? width - len : 0;
1.21 schwarze 353: padl = 0;
1.1 kristaps 354:
1.16 kristaps 355: switch (dp->layout->pos) {
1.1 kristaps 356: case (TBL_CELL_LONG):
1.21 schwarze 357: padl = term_len(tp, 1);
358: padr = padr > padl ? padr - padl : 0;
1.1 kristaps 359: break;
360: case (TBL_CELL_CENTRE):
1.21 schwarze 361: if (2 > padr)
1.19 schwarze 362: break;
1.21 schwarze 363: padl = padr / 2;
1.19 schwarze 364: padr -= padl;
1.1 kristaps 365: break;
366: case (TBL_CELL_RIGHT):
1.21 schwarze 367: padl = padr;
368: padr = 0;
1.1 kristaps 369: break;
370: default:
371: break;
372: }
373:
374: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 375: term_word(tp, dp->string);
1.21 schwarze 376: tbl_char(tp, ASCII_NBRSP, padr);
1.1 kristaps 377: }
378:
379: static void
1.11 kristaps 380: tbl_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 381: const struct tbl_dat *dp,
1.11 kristaps 382: const struct roffcol *col)
1.1 kristaps 383: {
1.11 kristaps 384: char *cp;
385: char buf[2];
386: size_t sz, psz, ssz, d, padl;
387: int i;
1.1 kristaps 388:
389: /*
390: * See calc_data_number(). Left-pad by taking the offset of our
391: * and the maximum decimal; right-pad by the remaining amount.
392: */
393:
1.17 kristaps 394: assert(dp->string);
1.2 kristaps 395:
1.17 kristaps 396: sz = term_strlen(tp, dp->string);
1.2 kristaps 397:
1.11 kristaps 398: buf[0] = tbl->decimal;
399: buf[1] = '\0';
1.2 kristaps 400:
1.11 kristaps 401: psz = term_strlen(tp, buf);
1.10 kristaps 402:
1.17 kristaps 403: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 kristaps 404: buf[1] = '\0';
1.17 kristaps 405: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
406: buf[0] = dp->string[i];
1.5 kristaps 407: ssz += term_strlen(tp, buf);
408: }
409: d = ssz + psz;
410: } else
411: d = sz + psz;
1.2 kristaps 412:
1.11 kristaps 413: padl = col->decimal - d;
1.2 kristaps 414:
1.11 kristaps 415: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 416: term_word(tp, dp->string);
1.21 schwarze 417: if (col->width > sz + padl)
418: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2 kristaps 419: }
420:
CVSweb