Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.11
1.11 ! schwarze 1: /* $Id: tbl_term.c,v 1.10 2011/09/20 23:05:46 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.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 ||
170: TBL_OPT_BOX & sp->tbl->opts)
171: tbl_hframe(tp, sp, 0);
172: if (TBL_OPT_DBOX & sp->tbl->opts)
173: tbl_hframe(tp, sp, 1);
1.6 schwarze 174: assert(tp->tbl.cols);
175: free(tp->tbl.cols);
176: tp->tbl.cols = NULL;
1.1 schwarze 177: }
178:
1.5 schwarze 179: tp->flags &= ~TERMP_NONOSPACE;
1.6 schwarze 180: tp->rmargin = rmargin;
181: tp->maxrmargin = maxrmargin;
1.1 schwarze 182:
183: }
184:
1.10 schwarze 185: /*
186: * Horizontal rules extend across the entire table.
187: * Calculate the width by iterating over columns.
188: */
189: static size_t
190: tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
191: {
192: size_t width;
193:
194: width = tp->tbl.cols[hp->ident].width;
1.11 ! schwarze 195:
! 196: /* Account for leading blanks. */
! 197: if (hp->prev)
! 198: width += 2 - hp->vert;
! 199:
! 200: /* Account for trailing blank. */
! 201: width++;
! 202:
1.10 schwarze 203: return(width);
204: }
205:
206: /*
207: * Rules inside the table can be single or double
208: * and have crossings with vertical rules marked with pluses.
209: */
1.1 schwarze 210: static void
1.5 schwarze 211: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
1.1 schwarze 212: {
1.5 schwarze 213: const struct tbl_head *hp;
214: char c;
1.1 schwarze 215:
216: c = '-';
1.5 schwarze 217: if (TBL_SPAN_DHORIZ == sp->pos)
1.1 schwarze 218: c = '=';
219:
1.11 ! schwarze 220: for (hp = sp->head; hp; hp = hp->next) {
! 221: if (hp->prev && hp->vert)
! 222: tbl_char(tp, '+', hp->vert);
! 223: tbl_char(tp, c, tbl_rulewidth(tp, hp));
! 224: }
1.1 schwarze 225: }
226:
1.10 schwarze 227: /*
228: * Rules above and below the table are always single
229: * and have an additional plus at the beginning and end.
230: * For double frames, this function is called twice,
231: * and the outer one does not have crossings.
232: */
1.1 schwarze 233: static void
1.10 schwarze 234: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1 schwarze 235: {
1.5 schwarze 236: const struct tbl_head *hp;
1.1 schwarze 237:
1.5 schwarze 238: term_word(tp, "+");
1.11 ! schwarze 239: for (hp = sp->head; hp; hp = hp->next) {
! 240: if (hp->prev && hp->vert)
! 241: tbl_char(tp, (outer ? '-' : '+'), hp->vert);
! 242: tbl_char(tp, '-', tbl_rulewidth(tp, hp));
! 243: }
1.5 schwarze 244: term_word(tp, "+");
245: term_flushln(tp);
1.1 schwarze 246: }
247:
248: static void
1.5 schwarze 249: tbl_data(struct termp *tp, const struct tbl *tbl,
250: const struct tbl_dat *dp,
1.6 schwarze 251: const struct roffcol *col)
1.1 schwarze 252: {
253:
1.5 schwarze 254: if (NULL == dp) {
1.6 schwarze 255: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 schwarze 256: return;
1.5 schwarze 257: }
1.7 schwarze 258: assert(dp->layout);
1.1 schwarze 259:
1.5 schwarze 260: switch (dp->pos) {
261: case (TBL_DATA_NONE):
1.6 schwarze 262: tbl_char(tp, ASCII_NBRSP, col->width);
1.5 schwarze 263: return;
264: case (TBL_DATA_HORIZ):
265: /* FALLTHROUGH */
266: case (TBL_DATA_NHORIZ):
1.6 schwarze 267: tbl_char(tp, '-', col->width);
1.5 schwarze 268: return;
269: case (TBL_DATA_NDHORIZ):
1.1 schwarze 270: /* FALLTHROUGH */
1.5 schwarze 271: case (TBL_DATA_DHORIZ):
1.6 schwarze 272: tbl_char(tp, '=', col->width);
1.5 schwarze 273: return;
1.1 schwarze 274: default:
275: break;
276: }
1.5 schwarze 277:
1.7 schwarze 278: switch (dp->layout->pos) {
1.1 schwarze 279: case (TBL_CELL_HORIZ):
1.6 schwarze 280: tbl_char(tp, '-', col->width);
1.5 schwarze 281: break;
1.1 schwarze 282: case (TBL_CELL_DHORIZ):
1.6 schwarze 283: tbl_char(tp, '=', col->width);
1.1 schwarze 284: break;
285: case (TBL_CELL_LONG):
286: /* FALLTHROUGH */
287: case (TBL_CELL_CENTRE):
288: /* FALLTHROUGH */
289: case (TBL_CELL_LEFT):
290: /* FALLTHROUGH */
291: case (TBL_CELL_RIGHT):
1.6 schwarze 292: tbl_literal(tp, dp, col);
1.1 schwarze 293: break;
294: case (TBL_CELL_NUMBER):
1.6 schwarze 295: tbl_number(tp, tbl, dp, col);
1.4 schwarze 296: break;
1.7 schwarze 297: case (TBL_CELL_DOWN):
298: tbl_char(tp, ASCII_NBRSP, col->width);
299: break;
1.1 schwarze 300: default:
301: abort();
302: /* NOTREACHED */
303: }
304: }
1.6 schwarze 305:
1.1 schwarze 306: static void
1.6 schwarze 307: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 schwarze 308: {
309:
1.11 ! schwarze 310: tbl_char(tp, ASCII_NBRSP, 1);
! 311: if (0 < hp->vert)
! 312: tbl_char(tp, '|', hp->vert);
! 313: if (2 > hp->vert)
! 314: tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
1.1 schwarze 315: }
316:
317: static void
1.6 schwarze 318: tbl_char(struct termp *tp, char c, size_t len)
1.5 schwarze 319: {
1.6 schwarze 320: size_t i, sz;
1.5 schwarze 321: char cp[2];
1.1 schwarze 322:
1.5 schwarze 323: cp[0] = c;
324: cp[1] = '\0';
1.1 schwarze 325:
1.5 schwarze 326: sz = term_strlen(tp, cp);
1.1 schwarze 327:
1.5 schwarze 328: for (i = 0; i < len; i += sz)
329: term_word(tp, cp);
1.1 schwarze 330: }
331:
332: static void
1.6 schwarze 333: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
334: const struct roffcol *col)
1.1 schwarze 335: {
1.10 schwarze 336: size_t len, padl, padr;
1.1 schwarze 337:
1.7 schwarze 338: assert(dp->string);
1.10 schwarze 339: len = term_strlen(tp, dp->string);
340: padr = col->width > len ? col->width - len : 0;
341: padl = 0;
1.5 schwarze 342:
1.7 schwarze 343: switch (dp->layout->pos) {
1.1 schwarze 344: case (TBL_CELL_LONG):
1.10 schwarze 345: padl = term_len(tp, 1);
346: padr = padr > padl ? padr - padl : 0;
1.1 schwarze 347: break;
348: case (TBL_CELL_CENTRE):
1.10 schwarze 349: if (2 > padr)
1.8 schwarze 350: break;
1.10 schwarze 351: padl = padr / 2;
1.8 schwarze 352: padr -= padl;
1.1 schwarze 353: break;
354: case (TBL_CELL_RIGHT):
1.10 schwarze 355: padl = padr;
356: padr = 0;
1.1 schwarze 357: break;
358: default:
359: break;
360: }
361:
1.5 schwarze 362: tbl_char(tp, ASCII_NBRSP, padl);
1.7 schwarze 363: term_word(tp, dp->string);
1.10 schwarze 364: tbl_char(tp, ASCII_NBRSP, padr);
1.1 schwarze 365: }
366:
1.5 schwarze 367: static void
1.6 schwarze 368: tbl_number(struct termp *tp, const struct tbl *tbl,
1.5 schwarze 369: const struct tbl_dat *dp,
1.6 schwarze 370: const struct roffcol *col)
1.5 schwarze 371: {
1.6 schwarze 372: char *cp;
373: char buf[2];
374: size_t sz, psz, ssz, d, padl;
375: int i;
1.5 schwarze 376:
377: /*
378: * See calc_data_number(). Left-pad by taking the offset of our
379: * and the maximum decimal; right-pad by the remaining amount.
380: */
381:
1.7 schwarze 382: assert(dp->string);
1.5 schwarze 383:
1.7 schwarze 384: sz = term_strlen(tp, dp->string);
1.5 schwarze 385:
1.6 schwarze 386: buf[0] = tbl->decimal;
387: buf[1] = '\0';
1.5 schwarze 388:
1.6 schwarze 389: psz = term_strlen(tp, buf);
1.5 schwarze 390:
1.7 schwarze 391: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 schwarze 392: buf[1] = '\0';
1.7 schwarze 393: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
394: buf[0] = dp->string[i];
1.5 schwarze 395: ssz += term_strlen(tp, buf);
396: }
397: d = ssz + psz;
398: } else
399: d = sz + psz;
400:
1.6 schwarze 401: padl = col->decimal - d;
1.5 schwarze 402:
1.6 schwarze 403: tbl_char(tp, ASCII_NBRSP, padl);
1.7 schwarze 404: term_word(tp, dp->string);
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: