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