Annotation of mandoc/tbl_term.c, Revision 1.19
1.19 ! schwarze 1: /* $Id: tbl_term.c,v 1.18 2011/01/11 14:12:01 kristaps Exp $ */
1.1 kristaps 2: /*
1.19 ! schwarze 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@kth.se>
! 4: * Copyright (c) 2011 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 *);
37: static void tbl_hframe(struct termp *, const struct tbl_span *);
38: static void tbl_literal(struct termp *, const struct tbl_dat *,
39: const struct roffcol *);
40: static void tbl_number(struct termp *, const struct tbl *,
41: const struct tbl_dat *,
42: const struct roffcol *);
43: static void tbl_hrule(struct termp *, const struct tbl_span *);
44: static void tbl_vframe(struct termp *, const struct tbl *);
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.1 kristaps 98: if (TBL_SPAN_FIRST & sp->flags)
99: tbl_hframe(tp, sp);
100:
1.4 kristaps 101: /* Vertical frame at the start of each row. */
1.1 kristaps 102:
103: tbl_vframe(tp, sp->tbl);
104:
1.4 kristaps 105: /*
106: * Now print the actual data itself depending on the span type.
107: * Spanner spans get a horizontal rule; data spanners have their
108: * data printed by matching data to header.
109: */
110:
1.1 kristaps 111: switch (sp->pos) {
112: case (TBL_SPAN_HORIZ):
113: /* FALLTHROUGH */
114: case (TBL_SPAN_DHORIZ):
115: tbl_hrule(tp, sp);
116: break;
1.4 kristaps 117: case (TBL_SPAN_DATA):
118: /* Iterate over template headers. */
119: dp = sp->first;
1.16 kristaps 120: spans = 0;
1.4 kristaps 121: for (hp = sp->head; hp; hp = hp->next) {
1.16 kristaps 122: /*
123: * If the current data header is invoked during
124: * a spanner ("spans" > 0), don't emit anything
125: * at all.
126: */
1.4 kristaps 127: switch (hp->pos) {
128: case (TBL_HEAD_VERT):
129: /* FALLTHROUGH */
130: case (TBL_HEAD_DVERT):
1.16 kristaps 131: if (spans <= 0)
132: tbl_vrule(tp, hp);
1.4 kristaps 133: continue;
134: case (TBL_HEAD_DATA):
135: break;
136: }
1.11 kristaps 137:
1.16 kristaps 138: if (--spans >= 0)
139: continue;
140:
1.11 kristaps 141: col = &tp->tbl.cols[hp->ident];
142: tbl_data(tp, sp->tbl, dp, col);
1.1 kristaps 143:
1.16 kristaps 144: /*
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.1 kristaps 151: dp = dp->next;
1.16 kristaps 152: }
1.1 kristaps 153: }
1.4 kristaps 154: break;
1.1 kristaps 155: }
156:
157: tbl_vframe(tp, sp->tbl);
158: term_flushln(tp);
159:
1.4 kristaps 160: /*
161: * If we're the last row, clean up after ourselves: clear the
162: * existing table configuration and set it to NULL.
163: */
164:
1.2 kristaps 165: if (TBL_SPAN_LAST & sp->flags) {
1.1 kristaps 166: tbl_hframe(tp, sp);
1.11 kristaps 167: assert(tp->tbl.cols);
168: free(tp->tbl.cols);
169: tp->tbl.cols = NULL;
1.2 kristaps 170: }
1.1 kristaps 171:
172: tp->flags &= ~TERMP_NONOSPACE;
1.11 kristaps 173: tp->rmargin = rmargin;
174: tp->maxrmargin = maxrmargin;
1.1 kristaps 175:
176: }
177:
178: static void
179: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
180: {
181: const struct tbl_head *hp;
182: char c;
1.12 kristaps 183: size_t width;
1.1 kristaps 184:
185: /*
186: * An hrule extends across the entire table and is demarked by a
187: * standalone `_' or whatnot in lieu of a table row. Spanning
188: * headers are marked by a `+', as are table boundaries.
189: */
190:
191: c = '-';
192: if (TBL_SPAN_DHORIZ == sp->pos)
193: c = '=';
194:
195: /* FIXME: don't use `+' between data and a spanner! */
196:
197: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 198: width = tp->tbl.cols[hp->ident].width;
1.1 kristaps 199: switch (hp->pos) {
200: case (TBL_HEAD_DATA):
1.19 ! schwarze 201: if (hp->next)
! 202: width += 2;
1.2 kristaps 203: tbl_char(tp, c, width);
1.1 kristaps 204: break;
205: case (TBL_HEAD_DVERT):
1.2 kristaps 206: tbl_char(tp, '+', width);
1.1 kristaps 207: /* FALLTHROUGH */
208: case (TBL_HEAD_VERT):
1.2 kristaps 209: tbl_char(tp, '+', width);
1.1 kristaps 210: break;
211: default:
212: abort();
213: /* NOTREACHED */
214: }
215: }
216: }
217:
218: static void
219: tbl_hframe(struct termp *tp, const struct tbl_span *sp)
220: {
221: const struct tbl_head *hp;
1.12 kristaps 222: size_t width;
1.1 kristaps 223:
224: if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
225: TBL_OPT_DBOX & sp->tbl->opts))
226: return;
227:
228: /*
229: * Print out the horizontal part of a frame or double frame. A
230: * double frame has an unbroken `-' outer line the width of the
231: * table, bordered by `+'. The frame (or inner frame, in the
232: * case of the double frame) is a `-' bordered by `+' and broken
233: * by `+' whenever a span is encountered.
234: */
235:
236: if (TBL_OPT_DBOX & sp->tbl->opts) {
237: term_word(tp, "+");
1.2 kristaps 238: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 239: width = tp->tbl.cols[hp->ident].width;
1.2 kristaps 240: tbl_char(tp, '-', width);
241: }
1.1 kristaps 242: term_word(tp, "+");
243: term_flushln(tp);
244: }
245:
246: term_word(tp, "+");
247: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 248: width = tp->tbl.cols[hp->ident].width;
1.1 kristaps 249: switch (hp->pos) {
250: case (TBL_HEAD_DATA):
1.2 kristaps 251: tbl_char(tp, '-', width);
1.1 kristaps 252: break;
253: default:
1.2 kristaps 254: tbl_char(tp, '+', width);
1.1 kristaps 255: break;
256: }
257: }
258: term_word(tp, "+");
259: term_flushln(tp);
260: }
261:
262: static void
263: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 264: const struct tbl_dat *dp,
1.11 kristaps 265: const struct roffcol *col)
1.1 kristaps 266: {
267:
268: if (NULL == dp) {
1.11 kristaps 269: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 270: return;
271: }
1.16 kristaps 272: assert(dp->layout);
1.1 kristaps 273:
274: switch (dp->pos) {
1.10 kristaps 275: case (TBL_DATA_NONE):
1.11 kristaps 276: tbl_char(tp, ASCII_NBRSP, col->width);
1.10 kristaps 277: return;
1.1 kristaps 278: case (TBL_DATA_HORIZ):
279: /* FALLTHROUGH */
1.7 kristaps 280: case (TBL_DATA_NHORIZ):
1.11 kristaps 281: tbl_char(tp, '-', col->width);
1.7 kristaps 282: return;
283: case (TBL_DATA_NDHORIZ):
284: /* FALLTHROUGH */
1.1 kristaps 285: case (TBL_DATA_DHORIZ):
1.11 kristaps 286: tbl_char(tp, '=', col->width);
1.1 kristaps 287: return;
288: default:
289: break;
290: }
291:
1.16 kristaps 292: switch (dp->layout->pos) {
1.1 kristaps 293: case (TBL_CELL_HORIZ):
1.11 kristaps 294: tbl_char(tp, '-', col->width);
1.7 kristaps 295: break;
1.1 kristaps 296: case (TBL_CELL_DHORIZ):
1.11 kristaps 297: tbl_char(tp, '=', col->width);
1.1 kristaps 298: break;
299: case (TBL_CELL_LONG):
300: /* FALLTHROUGH */
301: case (TBL_CELL_CENTRE):
302: /* FALLTHROUGH */
303: case (TBL_CELL_LEFT):
304: /* FALLTHROUGH */
305: case (TBL_CELL_RIGHT):
1.11 kristaps 306: tbl_literal(tp, dp, col);
1.1 kristaps 307: break;
308: case (TBL_CELL_NUMBER):
1.11 kristaps 309: tbl_number(tp, tbl, dp, col);
1.18 kristaps 310: break;
311: case (TBL_CELL_DOWN):
312: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 313: break;
314: default:
315: abort();
316: /* NOTREACHED */
317: }
318: }
1.11 kristaps 319:
1.1 kristaps 320: static void
1.11 kristaps 321: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 kristaps 322: {
323:
324: switch (hp->pos) {
325: case (TBL_HEAD_VERT):
326: term_word(tp, "|");
327: break;
328: case (TBL_HEAD_DVERT):
329: term_word(tp, "||");
330: break;
331: default:
332: break;
333: }
334: }
335:
336: static void
337: tbl_vframe(struct termp *tp, const struct tbl *tbl)
338: {
339:
340: if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
341: term_word(tp, "|");
342: }
343:
1.11 kristaps 344: static void
1.12 kristaps 345: tbl_char(struct termp *tp, char c, size_t len)
1.1 kristaps 346: {
1.12 kristaps 347: size_t i, sz;
348: char cp[2];
349:
350: cp[0] = c;
351: cp[1] = '\0';
1.1 kristaps 352:
1.3 kristaps 353: sz = term_strlen(tp, cp);
354:
355: for (i = 0; i < len; i += sz)
1.1 kristaps 356: term_word(tp, cp);
357: }
358:
359: static void
1.11 kristaps 360: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
361: const struct roffcol *col)
1.1 kristaps 362: {
1.12 kristaps 363: size_t padl, padr, ssz;
1.1 kristaps 364:
365: padl = padr = 0;
366:
1.17 kristaps 367: assert(dp->string);
1.13 kristaps 368:
1.3 kristaps 369: ssz = term_len(tp, 1);
1.1 kristaps 370:
1.16 kristaps 371: switch (dp->layout->pos) {
1.1 kristaps 372: case (TBL_CELL_LONG):
1.3 kristaps 373: padl = ssz;
1.17 kristaps 374: padr = col->width - term_strlen(tp, dp->string) - ssz;
1.1 kristaps 375: break;
376: case (TBL_CELL_CENTRE):
1.19 ! schwarze 377: padr = col->width - term_strlen(tp, dp->string);
! 378: if (3 > padr)
! 379: break;
! 380: padl = (padr - 1) / 2;
! 381: padr -= padl;
1.1 kristaps 382: break;
383: case (TBL_CELL_RIGHT):
1.17 kristaps 384: padl = col->width - term_strlen(tp, dp->string);
1.1 kristaps 385: break;
386: default:
1.17 kristaps 387: padr = col->width - term_strlen(tp, dp->string);
1.1 kristaps 388: break;
389: }
390:
391: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 392: term_word(tp, dp->string);
1.19 ! schwarze 393: tbl_char(tp, ASCII_NBRSP, padr + 2);
1.1 kristaps 394: }
395:
396: static void
1.11 kristaps 397: tbl_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 398: const struct tbl_dat *dp,
1.11 kristaps 399: const struct roffcol *col)
1.1 kristaps 400: {
1.11 kristaps 401: char *cp;
402: char buf[2];
403: size_t sz, psz, ssz, d, padl;
404: int i;
1.1 kristaps 405:
406: /*
407: * See calc_data_number(). Left-pad by taking the offset of our
408: * and the maximum decimal; right-pad by the remaining amount.
409: */
410:
1.17 kristaps 411: assert(dp->string);
1.2 kristaps 412:
1.17 kristaps 413: sz = term_strlen(tp, dp->string);
1.2 kristaps 414:
1.11 kristaps 415: buf[0] = tbl->decimal;
416: buf[1] = '\0';
1.2 kristaps 417:
1.11 kristaps 418: psz = term_strlen(tp, buf);
1.10 kristaps 419:
1.17 kristaps 420: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 kristaps 421: buf[1] = '\0';
1.17 kristaps 422: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
423: buf[0] = dp->string[i];
1.5 kristaps 424: ssz += term_strlen(tp, buf);
425: }
426: d = ssz + psz;
427: } else
428: d = sz + psz;
1.2 kristaps 429:
1.5 kristaps 430: sz += term_len(tp, 2);
1.11 kristaps 431: d += term_len(tp, 1);
1.2 kristaps 432:
1.11 kristaps 433: padl = col->decimal - d;
1.2 kristaps 434:
1.11 kristaps 435: tbl_char(tp, ASCII_NBRSP, padl);
1.17 kristaps 436: term_word(tp, dp->string);
1.11 kristaps 437: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2 kristaps 438: }
439:
CVSweb