Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.13
1.13 ! schwarze 1: /* $Id: tbl_term.c,v 1.12 2012/05/26 20:53:17 schwarze Exp $ */
1.1 schwarze 2: /*
1.9 schwarze 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.12 schwarze 4: * Copyright (c) 2011, 2012 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.11 schwarze 125:
1.7 schwarze 126: /*
127: * If the current data header is invoked during
128: * a spanner ("spans" > 0), don't emit anything
129: * at all.
130: */
1.6 schwarze 131:
1.7 schwarze 132: if (--spans >= 0)
133: continue;
134:
1.11 schwarze 135: /* Separate columns. */
1.10 schwarze 136:
1.11 schwarze 137: if (NULL != hp->prev)
138: tbl_vrule(tp, hp);
1.10 schwarze 139:
1.6 schwarze 140: col = &tp->tbl.cols[hp->ident];
141: tbl_data(tp, sp->tbl, dp, col);
1.5 schwarze 142:
1.7 schwarze 143: /*
144: * Go to the next data cell and assign the
145: * number of subsequent spans, if applicable.
146: */
147:
148: if (dp) {
149: spans = dp->spans;
1.5 schwarze 150: dp = dp->next;
1.7 schwarze 151: }
1.1 schwarze 152: }
1.5 schwarze 153: break;
1.1 schwarze 154: }
155:
1.10 schwarze 156: /* Vertical frame at the end of each row. */
157:
158: if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
159: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
160: TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
1.5 schwarze 161: term_flushln(tp);
1.1 schwarze 162:
1.5 schwarze 163: /*
164: * If we're the last row, clean up after ourselves: clear the
165: * existing table configuration and set it to NULL.
166: */
1.1 schwarze 167:
1.5 schwarze 168: if (TBL_SPAN_LAST & sp->flags) {
1.10 schwarze 169: if (TBL_OPT_DBOX & sp->tbl->opts ||
1.13 ! schwarze 170: TBL_OPT_BOX & sp->tbl->opts) {
1.10 schwarze 171: tbl_hframe(tp, sp, 0);
1.13 ! schwarze 172: tp->skipvsp = 1;
! 173: }
! 174: if (TBL_OPT_DBOX & sp->tbl->opts) {
1.10 schwarze 175: tbl_hframe(tp, sp, 1);
1.13 ! schwarze 176: tp->skipvsp = 2;
! 177: }
1.6 schwarze 178: assert(tp->tbl.cols);
179: free(tp->tbl.cols);
180: tp->tbl.cols = NULL;
1.1 schwarze 181: }
182:
1.5 schwarze 183: tp->flags &= ~TERMP_NONOSPACE;
1.6 schwarze 184: tp->rmargin = rmargin;
185: tp->maxrmargin = maxrmargin;
1.1 schwarze 186:
187: }
188:
1.10 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.11 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.10 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 schwarze 214: static void
1.5 schwarze 215: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
1.1 schwarze 216: {
1.5 schwarze 217: const struct tbl_head *hp;
218: char c;
1.1 schwarze 219:
220: c = '-';
1.5 schwarze 221: if (TBL_SPAN_DHORIZ == sp->pos)
1.1 schwarze 222: c = '=';
223:
1.11 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 schwarze 229: }
230:
1.10 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 schwarze 237: static void
1.10 schwarze 238: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1 schwarze 239: {
1.5 schwarze 240: const struct tbl_head *hp;
1.1 schwarze 241:
1.5 schwarze 242: term_word(tp, "+");
1.11 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.5 schwarze 248: term_word(tp, "+");
249: term_flushln(tp);
1.1 schwarze 250: }
251:
252: static void
1.5 schwarze 253: tbl_data(struct termp *tp, const struct tbl *tbl,
254: const struct tbl_dat *dp,
1.6 schwarze 255: const struct roffcol *col)
1.1 schwarze 256: {
257:
1.5 schwarze 258: if (NULL == dp) {
1.6 schwarze 259: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 schwarze 260: return;
1.5 schwarze 261: }
1.7 schwarze 262: assert(dp->layout);
1.1 schwarze 263:
1.5 schwarze 264: switch (dp->pos) {
265: case (TBL_DATA_NONE):
1.6 schwarze 266: tbl_char(tp, ASCII_NBRSP, col->width);
1.5 schwarze 267: return;
268: case (TBL_DATA_HORIZ):
269: /* FALLTHROUGH */
270: case (TBL_DATA_NHORIZ):
1.6 schwarze 271: tbl_char(tp, '-', col->width);
1.5 schwarze 272: return;
273: case (TBL_DATA_NDHORIZ):
1.1 schwarze 274: /* FALLTHROUGH */
1.5 schwarze 275: case (TBL_DATA_DHORIZ):
1.6 schwarze 276: tbl_char(tp, '=', col->width);
1.5 schwarze 277: return;
1.1 schwarze 278: default:
279: break;
280: }
1.5 schwarze 281:
1.7 schwarze 282: switch (dp->layout->pos) {
1.1 schwarze 283: case (TBL_CELL_HORIZ):
1.6 schwarze 284: tbl_char(tp, '-', col->width);
1.5 schwarze 285: break;
1.1 schwarze 286: case (TBL_CELL_DHORIZ):
1.6 schwarze 287: tbl_char(tp, '=', col->width);
1.1 schwarze 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.6 schwarze 296: tbl_literal(tp, dp, col);
1.1 schwarze 297: break;
298: case (TBL_CELL_NUMBER):
1.6 schwarze 299: tbl_number(tp, tbl, dp, col);
1.4 schwarze 300: break;
1.7 schwarze 301: case (TBL_CELL_DOWN):
302: tbl_char(tp, ASCII_NBRSP, col->width);
303: break;
1.1 schwarze 304: default:
305: abort();
306: /* NOTREACHED */
307: }
308: }
1.6 schwarze 309:
1.1 schwarze 310: static void
1.6 schwarze 311: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 schwarze 312: {
313:
1.11 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 schwarze 319: }
320:
321: static void
1.6 schwarze 322: tbl_char(struct termp *tp, char c, size_t len)
1.5 schwarze 323: {
1.6 schwarze 324: size_t i, sz;
1.5 schwarze 325: char cp[2];
1.1 schwarze 326:
1.5 schwarze 327: cp[0] = c;
328: cp[1] = '\0';
1.1 schwarze 329:
1.5 schwarze 330: sz = term_strlen(tp, cp);
1.1 schwarze 331:
1.5 schwarze 332: for (i = 0; i < len; i += sz)
333: term_word(tp, cp);
1.1 schwarze 334: }
335:
336: static void
1.6 schwarze 337: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
338: const struct roffcol *col)
1.1 schwarze 339: {
1.12 schwarze 340: struct tbl_head *hp;
341: size_t width, len, padl, padr;
342: int spans;
1.1 schwarze 343:
1.7 schwarze 344: assert(dp->string);
1.10 schwarze 345: len = term_strlen(tp, dp->string);
1.12 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.10 schwarze 353: padl = 0;
1.5 schwarze 354:
1.7 schwarze 355: switch (dp->layout->pos) {
1.1 schwarze 356: case (TBL_CELL_LONG):
1.10 schwarze 357: padl = term_len(tp, 1);
358: padr = padr > padl ? padr - padl : 0;
1.1 schwarze 359: break;
360: case (TBL_CELL_CENTRE):
1.10 schwarze 361: if (2 > padr)
1.8 schwarze 362: break;
1.10 schwarze 363: padl = padr / 2;
1.8 schwarze 364: padr -= padl;
1.1 schwarze 365: break;
366: case (TBL_CELL_RIGHT):
1.10 schwarze 367: padl = padr;
368: padr = 0;
1.1 schwarze 369: break;
370: default:
371: break;
372: }
373:
1.5 schwarze 374: tbl_char(tp, ASCII_NBRSP, padl);
1.7 schwarze 375: term_word(tp, dp->string);
1.10 schwarze 376: tbl_char(tp, ASCII_NBRSP, padr);
1.1 schwarze 377: }
378:
1.5 schwarze 379: static void
1.6 schwarze 380: tbl_number(struct termp *tp, const struct tbl *tbl,
1.5 schwarze 381: const struct tbl_dat *dp,
1.6 schwarze 382: const struct roffcol *col)
1.5 schwarze 383: {
1.6 schwarze 384: char *cp;
385: char buf[2];
386: size_t sz, psz, ssz, d, padl;
387: int i;
1.5 schwarze 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.7 schwarze 394: assert(dp->string);
1.5 schwarze 395:
1.7 schwarze 396: sz = term_strlen(tp, dp->string);
1.5 schwarze 397:
1.6 schwarze 398: buf[0] = tbl->decimal;
399: buf[1] = '\0';
1.5 schwarze 400:
1.6 schwarze 401: psz = term_strlen(tp, buf);
1.5 schwarze 402:
1.7 schwarze 403: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 schwarze 404: buf[1] = '\0';
1.7 schwarze 405: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
406: buf[0] = dp->string[i];
1.5 schwarze 407: ssz += term_strlen(tp, buf);
408: }
409: d = ssz + psz;
410: } else
411: d = sz + psz;
412:
1.6 schwarze 413: padl = col->decimal - d;
1.5 schwarze 414:
1.6 schwarze 415: tbl_char(tp, ASCII_NBRSP, padl);
1.7 schwarze 416: term_word(tp, dp->string);
1.10 schwarze 417: if (col->width > sz + padl)
418: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.5 schwarze 419: }
1.1 schwarze 420: