Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.30
1.30 ! schwarze 1: /* $OpenBSD: tbl_term.c,v 1.29 2015/09/26 00:53:15 schwarze Exp $ */
1.1 schwarze 2: /*
1.9 schwarze 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.21 schwarze 4: * Copyright (c) 2011, 2012, 2014, 2015 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: */
1.17 schwarze 18: #include <sys/types.h>
19:
1.1 schwarze 20: #include <assert.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
1.5 schwarze 25: #include "mandoc.h"
1.2 schwarze 26: #include "out.h"
27: #include "term.h"
1.1 schwarze 28:
1.6 schwarze 29: static size_t term_tbl_len(size_t, void *);
30: static size_t term_tbl_strlen(const char *, void *);
31: static void tbl_char(struct termp *, char, size_t);
1.14 schwarze 32: static void tbl_data(struct termp *, const struct tbl_opts *,
1.16 schwarze 33: const struct tbl_dat *,
1.6 schwarze 34: const struct roffcol *);
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 *);
1.21 schwarze 40: static void tbl_hrule(struct termp *, const struct tbl_span *, int);
1.17 schwarze 41: static void tbl_word(struct termp *, const struct tbl_dat *);
1.1 schwarze 42:
1.6 schwarze 43:
44: static size_t
45: term_tbl_strlen(const char *p, void *arg)
46: {
47:
1.30 ! schwarze 48: return term_strlen((const struct termp *)arg, p);
1.6 schwarze 49: }
50:
51: static size_t
52: term_tbl_len(size_t sz, void *arg)
53: {
54:
1.30 ! schwarze 55: return term_len((const struct termp *)arg, sz);
1.6 schwarze 56: }
1.5 schwarze 57:
58: void
59: term_tbl(struct termp *tp, const struct tbl_span *sp)
60: {
1.21 schwarze 61: const struct tbl_cell *cp;
1.6 schwarze 62: const struct tbl_dat *dp;
1.22 schwarze 63: static size_t offset;
64: size_t rmargin, maxrmargin, tsz;
1.24 schwarze 65: int ic, horiz, spans, vert;
1.27 schwarze 66:
1.6 schwarze 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.25 schwarze 82: if (tp->tbl.cols == NULL) {
1.6 schwarze 83: tp->tbl.len = term_tbl_len;
84: tp->tbl.slen = term_tbl_strlen;
85: tp->tbl.arg = tp;
1.5 schwarze 86:
1.18 schwarze 87: tblcalc(&tp->tbl, sp, rmargin - tp->offset);
1.2 schwarze 88:
1.22 schwarze 89: /* Center the table as a whole. */
90:
91: offset = tp->offset;
92: if (sp->opts->opts & TBL_OPT_CENTRE) {
93: tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
94: ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
1.24 schwarze 95: for (ic = 0; ic < sp->opts->cols; ic++)
96: tsz += tp->tbl.cols[ic].width + 3;
1.22 schwarze 97: tsz -= 3;
98: if (offset + tsz > rmargin)
99: tsz -= 1;
100: tp->offset = (offset + rmargin > tsz) ?
101: (offset + rmargin - tsz) / 2 : 0;
102: }
103:
1.21 schwarze 104: /* Horizontal frame at the start of boxed tables. */
1.1 schwarze 105:
1.21 schwarze 106: if (sp->opts->opts & TBL_OPT_DBOX)
107: tbl_hrule(tp, sp, 2);
108: if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
109: tbl_hrule(tp, sp, 1);
1.10 schwarze 110: }
1.5 schwarze 111:
112: /* Vertical frame at the start of each row. */
113:
1.21 schwarze 114: horiz = sp->pos == TBL_SPAN_HORIZ || sp->pos == TBL_SPAN_DHORIZ;
115:
116: if (sp->layout->vert ||
117: (sp->prev != NULL && sp->prev->layout->vert) ||
118: sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
119: term_word(tp, horiz ? "+" : "|");
120: else if (sp->opts->lvert)
121: tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
1.1 schwarze 122:
123: /*
1.5 schwarze 124: * Now print the actual data itself depending on the span type.
1.24 schwarze 125: * Match data cells to column numbers.
1.1 schwarze 126: */
127:
1.21 schwarze 128: if (sp->pos == TBL_SPAN_DATA) {
129: cp = sp->layout->first;
1.5 schwarze 130: dp = sp->first;
1.7 schwarze 131: spans = 0;
1.24 schwarze 132: for (ic = 0; ic < sp->opts->cols; ic++) {
1.11 schwarze 133:
1.16 schwarze 134: /*
1.21 schwarze 135: * Remeber whether we need a vertical bar
136: * after this cell.
1.7 schwarze 137: */
1.6 schwarze 138:
1.21 schwarze 139: vert = cp == NULL ? 0 : cp->vert;
1.10 schwarze 140:
1.21 schwarze 141: /*
142: * Print the data and advance to the next cell.
143: */
1.10 schwarze 144:
1.21 schwarze 145: if (spans == 0) {
1.24 schwarze 146: tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic);
1.21 schwarze 147: if (dp != NULL) {
148: spans = dp->spans;
149: dp = dp->next;
150: }
151: } else
152: spans--;
153: if (cp != NULL)
154: cp = cp->next;
1.5 schwarze 155:
1.16 schwarze 156: /*
1.21 schwarze 157: * Separate columns, except in the middle
158: * of spans and after the last cell.
1.7 schwarze 159: */
160:
1.24 schwarze 161: if (ic + 1 == sp->opts->cols || spans)
1.21 schwarze 162: continue;
163:
164: tbl_char(tp, ASCII_NBRSP, 1);
165: if (vert > 0)
166: tbl_char(tp, '|', vert);
1.24 schwarze 167: if (vert < 2)
1.21 schwarze 168: tbl_char(tp, ASCII_NBRSP, 2 - vert);
1.1 schwarze 169: }
1.21 schwarze 170: } else if (horiz)
171: tbl_hrule(tp, sp, 0);
1.1 schwarze 172:
1.10 schwarze 173: /* Vertical frame at the end of each row. */
174:
1.21 schwarze 175: if (sp->layout->last->vert ||
176: (sp->prev != NULL && sp->prev->layout->last->vert) ||
177: (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
178: term_word(tp, horiz ? "+" : " |");
179: else if (sp->opts->rvert)
180: tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
1.5 schwarze 181: term_flushln(tp);
1.1 schwarze 182:
1.5 schwarze 183: /*
184: * If we're the last row, clean up after ourselves: clear the
185: * existing table configuration and set it to NULL.
186: */
1.1 schwarze 187:
1.25 schwarze 188: if (sp->next == NULL) {
1.21 schwarze 189: if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
190: tbl_hrule(tp, sp, 1);
1.13 schwarze 191: tp->skipvsp = 1;
192: }
1.21 schwarze 193: if (sp->opts->opts & TBL_OPT_DBOX) {
194: tbl_hrule(tp, sp, 2);
1.13 schwarze 195: tp->skipvsp = 2;
196: }
1.6 schwarze 197: assert(tp->tbl.cols);
198: free(tp->tbl.cols);
199: tp->tbl.cols = NULL;
1.22 schwarze 200: tp->offset = offset;
1.1 schwarze 201: }
202:
1.5 schwarze 203: tp->flags &= ~TERMP_NONOSPACE;
1.6 schwarze 204: tp->rmargin = rmargin;
205: tp->maxrmargin = maxrmargin;
1.1 schwarze 206: }
207:
1.10 schwarze 208: /*
1.21 schwarze 209: * Kinds of horizontal rulers:
210: * 0: inside the table (single or double line with crossings)
211: * 1: inner frame (single line with crossings and ends)
212: * 2: outer frame (single line without crossings with ends)
1.10 schwarze 213: */
1.1 schwarze 214: static void
1.21 schwarze 215: tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
1.1 schwarze 216: {
1.21 schwarze 217: const struct tbl_cell *c1, *c2;
218: int vert;
219: char line, cross;
220:
221: line = (kind == 0 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
222: cross = (kind < 2) ? '+' : '-';
223:
224: if (kind)
225: term_word(tp, "+");
226: c1 = sp->layout->first;
227: c2 = sp->prev == NULL ? NULL : sp->prev->layout->first;
228: if (c2 == c1)
229: c2 = NULL;
230: for (;;) {
1.24 schwarze 231: tbl_char(tp, line, tp->tbl.cols[c1->col].width + 1);
1.21 schwarze 232: vert = c1->vert;
233: if ((c1 = c1->next) == NULL)
234: break;
235: if (c2 != NULL) {
236: if (vert < c2->vert)
237: vert = c2->vert;
238: c2 = c2->next;
239: }
240: if (vert)
241: tbl_char(tp, cross, vert);
242: if (vert < 2)
243: tbl_char(tp, line, 2 - vert);
1.11 schwarze 244: }
1.21 schwarze 245: if (kind) {
246: term_word(tp, "+");
247: term_flushln(tp);
1.11 schwarze 248: }
1.1 schwarze 249: }
250:
251: static void
1.14 schwarze 252: tbl_data(struct termp *tp, const struct tbl_opts *opts,
1.16 schwarze 253: const struct tbl_dat *dp,
254: const struct roffcol *col)
1.1 schwarze 255: {
256:
1.23 schwarze 257: if (dp == NULL) {
1.6 schwarze 258: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 schwarze 259: return;
1.5 schwarze 260: }
1.1 schwarze 261:
1.5 schwarze 262: switch (dp->pos) {
1.16 schwarze 263: case TBL_DATA_NONE:
1.6 schwarze 264: tbl_char(tp, ASCII_NBRSP, col->width);
1.5 schwarze 265: return;
1.16 schwarze 266: case TBL_DATA_HORIZ:
1.5 schwarze 267: /* FALLTHROUGH */
1.16 schwarze 268: case TBL_DATA_NHORIZ:
1.6 schwarze 269: tbl_char(tp, '-', col->width);
1.5 schwarze 270: return;
1.16 schwarze 271: case TBL_DATA_NDHORIZ:
1.1 schwarze 272: /* FALLTHROUGH */
1.16 schwarze 273: case TBL_DATA_DHORIZ:
1.6 schwarze 274: tbl_char(tp, '=', col->width);
1.5 schwarze 275: return;
1.1 schwarze 276: default:
277: break;
278: }
1.16 schwarze 279:
1.7 schwarze 280: switch (dp->layout->pos) {
1.16 schwarze 281: case TBL_CELL_HORIZ:
1.6 schwarze 282: tbl_char(tp, '-', col->width);
1.5 schwarze 283: break;
1.16 schwarze 284: case TBL_CELL_DHORIZ:
1.6 schwarze 285: tbl_char(tp, '=', col->width);
1.1 schwarze 286: break;
1.16 schwarze 287: case TBL_CELL_LONG:
1.1 schwarze 288: /* FALLTHROUGH */
1.16 schwarze 289: case TBL_CELL_CENTRE:
1.1 schwarze 290: /* FALLTHROUGH */
1.16 schwarze 291: case TBL_CELL_LEFT:
1.1 schwarze 292: /* FALLTHROUGH */
1.16 schwarze 293: case TBL_CELL_RIGHT:
1.6 schwarze 294: tbl_literal(tp, dp, col);
1.1 schwarze 295: break;
1.16 schwarze 296: case TBL_CELL_NUMBER:
1.14 schwarze 297: tbl_number(tp, opts, dp, col);
1.4 schwarze 298: break;
1.16 schwarze 299: case TBL_CELL_DOWN:
1.7 schwarze 300: tbl_char(tp, ASCII_NBRSP, col->width);
301: break;
1.1 schwarze 302: default:
303: abort();
304: }
305: }
306:
307: static void
1.6 schwarze 308: tbl_char(struct termp *tp, char c, size_t len)
1.5 schwarze 309: {
1.6 schwarze 310: size_t i, sz;
1.5 schwarze 311: char cp[2];
1.1 schwarze 312:
1.5 schwarze 313: cp[0] = c;
314: cp[1] = '\0';
1.1 schwarze 315:
1.5 schwarze 316: sz = term_strlen(tp, cp);
1.1 schwarze 317:
1.5 schwarze 318: for (i = 0; i < len; i += sz)
319: term_word(tp, cp);
1.1 schwarze 320: }
321:
322: static void
1.16 schwarze 323: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
1.6 schwarze 324: const struct roffcol *col)
1.1 schwarze 325: {
1.24 schwarze 326: size_t len, padl, padr, width;
327: int ic, spans;
1.1 schwarze 328:
1.7 schwarze 329: assert(dp->string);
1.10 schwarze 330: len = term_strlen(tp, dp->string);
1.12 schwarze 331: width = col->width;
1.24 schwarze 332: ic = dp->layout->col;
333: spans = dp->spans;
334: while (spans--)
335: width += tp->tbl.cols[++ic].width + 3;
1.12 schwarze 336:
337: padr = width > len ? width - len : 0;
1.10 schwarze 338: padl = 0;
1.5 schwarze 339:
1.7 schwarze 340: switch (dp->layout->pos) {
1.16 schwarze 341: case TBL_CELL_LONG:
1.10 schwarze 342: padl = term_len(tp, 1);
343: padr = padr > padl ? padr - padl : 0;
1.1 schwarze 344: break;
1.16 schwarze 345: case TBL_CELL_CENTRE:
1.10 schwarze 346: if (2 > padr)
1.8 schwarze 347: break;
1.10 schwarze 348: padl = padr / 2;
1.8 schwarze 349: padr -= padl;
1.1 schwarze 350: break;
1.16 schwarze 351: case TBL_CELL_RIGHT:
1.10 schwarze 352: padl = padr;
353: padr = 0;
1.1 schwarze 354: break;
355: default:
356: break;
357: }
358:
1.5 schwarze 359: tbl_char(tp, ASCII_NBRSP, padl);
1.17 schwarze 360: tbl_word(tp, dp);
1.10 schwarze 361: tbl_char(tp, ASCII_NBRSP, padr);
1.1 schwarze 362: }
363:
1.5 schwarze 364: static void
1.14 schwarze 365: tbl_number(struct termp *tp, const struct tbl_opts *opts,
1.5 schwarze 366: const struct tbl_dat *dp,
1.6 schwarze 367: const struct roffcol *col)
1.5 schwarze 368: {
1.6 schwarze 369: char *cp;
370: char buf[2];
371: size_t sz, psz, ssz, d, padl;
372: int i;
1.5 schwarze 373:
374: /*
375: * See calc_data_number(). Left-pad by taking the offset of our
376: * and the maximum decimal; right-pad by the remaining amount.
377: */
378:
1.7 schwarze 379: assert(dp->string);
1.5 schwarze 380:
1.7 schwarze 381: sz = term_strlen(tp, dp->string);
1.5 schwarze 382:
1.14 schwarze 383: buf[0] = opts->decimal;
1.6 schwarze 384: buf[1] = '\0';
1.5 schwarze 385:
1.6 schwarze 386: psz = term_strlen(tp, buf);
1.5 schwarze 387:
1.23 schwarze 388: if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
1.7 schwarze 389: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
390: buf[0] = dp->string[i];
1.5 schwarze 391: ssz += term_strlen(tp, buf);
392: }
393: d = ssz + psz;
394: } else
395: d = sz + psz;
396:
1.20 schwarze 397: if (col->decimal > d && col->width > sz) {
398: padl = col->decimal - d;
399: if (padl + sz > col->width)
400: padl = col->width - sz;
401: tbl_char(tp, ASCII_NBRSP, padl);
402: } else
403: padl = 0;
1.17 schwarze 404: tbl_word(tp, dp);
1.10 schwarze 405: if (col->width > sz + padl)
406: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.5 schwarze 407: }
1.1 schwarze 408:
1.17 schwarze 409: static void
410: tbl_word(struct termp *tp, const struct tbl_dat *dp)
411: {
1.26 schwarze 412: int prev_font;
1.17 schwarze 413:
1.26 schwarze 414: prev_font = tp->fonti;
1.17 schwarze 415: if (dp->layout->flags & TBL_CELL_BOLD)
416: term_fontpush(tp, TERMFONT_BOLD);
417: else if (dp->layout->flags & TBL_CELL_ITALIC)
418: term_fontpush(tp, TERMFONT_UNDER);
419:
420: term_word(tp, dp->string);
421:
422: term_fontpopq(tp, prev_font);
423: }