Annotation of mandoc/tbl_term.c, Revision 1.17
1.17 ! kristaps 1: /* $Id: tbl_term.c,v 1.16 2011/01/10 14:40:30 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #ifdef HAVE_CONFIG_H
18: #include "config.h"
19: #endif
20:
21: #include <assert.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25:
26: #include "mandoc.h"
27: #include "out.h"
28: #include "term.h"
29:
1.11 kristaps 30: static size_t term_tbl_len(size_t, void *);
31: static size_t term_tbl_strlen(const char *, void *);
1.12 kristaps 32: static void tbl_char(struct termp *, char, size_t);
1.11 kristaps 33: static void tbl_data(struct termp *, const struct tbl *,
34: const struct tbl_dat *,
35: const struct roffcol *);
36: static void tbl_hframe(struct termp *, const struct tbl_span *);
37: static void tbl_literal(struct termp *, const struct tbl_dat *,
38: const struct roffcol *);
39: static void tbl_number(struct termp *, const struct tbl *,
40: const struct tbl_dat *,
41: const struct roffcol *);
42: static void tbl_hrule(struct termp *, const struct tbl_span *);
43: static void tbl_vframe(struct termp *, const struct tbl *);
44: static void tbl_vrule(struct termp *, const struct tbl_head *);
45:
46:
47: static size_t
48: term_tbl_strlen(const char *p, void *arg)
49: {
50:
51: return(term_strlen((const struct termp *)arg, p));
52: }
53:
54: static size_t
55: term_tbl_len(size_t sz, void *arg)
56: {
57:
58: return(term_len((const struct termp *)arg, sz));
59: }
1.1 kristaps 60:
61: void
62: term_tbl(struct termp *tp, const struct tbl_span *sp)
63: {
1.11 kristaps 64: const struct tbl_head *hp;
65: const struct tbl_dat *dp;
66: struct roffcol *col;
1.16 kristaps 67: int spans;
1.12 kristaps 68: size_t rmargin, maxrmargin;
1.11 kristaps 69:
70: rmargin = tp->rmargin;
71: maxrmargin = tp->maxrmargin;
72:
73: tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
1.1 kristaps 74:
1.4 kristaps 75: /* Inhibit printing of spaces: we do padding ourselves. */
76:
77: tp->flags |= TERMP_NONOSPACE;
78: tp->flags |= TERMP_NOSPACE;
79:
80: /*
1.11 kristaps 81: * The first time we're invoked for a given table block,
82: * calculate the table widths and decimal positions.
1.4 kristaps 83: */
84:
1.2 kristaps 85: if (TBL_SPAN_FIRST & sp->flags) {
1.11 kristaps 86: term_flushln(tp);
87:
88: tp->tbl.len = term_tbl_len;
89: tp->tbl.slen = term_tbl_strlen;
90: tp->tbl.arg = tp;
1.4 kristaps 91:
1.11 kristaps 92: tblcalc(&tp->tbl, sp);
1.2 kristaps 93: }
1.1 kristaps 94:
1.4 kristaps 95: /* Horizontal frame at the start of boxed tables. */
96:
1.1 kristaps 97: if (TBL_SPAN_FIRST & sp->flags)
98: tbl_hframe(tp, sp);
99:
1.4 kristaps 100: /* Vertical frame at the start of each row. */
1.1 kristaps 101:
102: tbl_vframe(tp, sp->tbl);
103:
1.4 kristaps 104: /*
105: * Now print the actual data itself depending on the span type.
106: * Spanner spans get a horizontal rule; data spanners have their
107: * data printed by matching data to header.
108: */
109:
1.1 kristaps 110: switch (sp->pos) {
111: case (TBL_SPAN_HORIZ):
112: /* FALLTHROUGH */
113: case (TBL_SPAN_DHORIZ):
114: tbl_hrule(tp, sp);
115: break;
1.4 kristaps 116: case (TBL_SPAN_DATA):
117: /* Iterate over template headers. */
118: dp = sp->first;
1.16 kristaps 119: spans = 0;
1.4 kristaps 120: for (hp = sp->head; hp; hp = hp->next) {
1.16 kristaps 121: /*
122: * If the current data header is invoked during
123: * a spanner ("spans" > 0), don't emit anything
124: * at all.
125: */
1.4 kristaps 126: switch (hp->pos) {
127: case (TBL_HEAD_VERT):
128: /* FALLTHROUGH */
129: case (TBL_HEAD_DVERT):
1.16 kristaps 130: if (spans <= 0)
131: tbl_vrule(tp, hp);
1.4 kristaps 132: continue;
133: case (TBL_HEAD_DATA):
134: break;
135: }
1.11 kristaps 136:
1.16 kristaps 137: if (--spans >= 0)
138: continue;
139:
1.11 kristaps 140: col = &tp->tbl.cols[hp->ident];
141: tbl_data(tp, sp->tbl, dp, col);
1.1 kristaps 142:
1.16 kristaps 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.1 kristaps 150: dp = dp->next;
1.16 kristaps 151: }
1.1 kristaps 152: }
1.4 kristaps 153: break;
1.1 kristaps 154: }
155:
156: tbl_vframe(tp, sp->tbl);
157: term_flushln(tp);
158:
1.4 kristaps 159: /*
160: * If we're the last row, clean up after ourselves: clear the
161: * existing table configuration and set it to NULL.
162: */
163:
1.2 kristaps 164: if (TBL_SPAN_LAST & sp->flags) {
1.1 kristaps 165: tbl_hframe(tp, sp);
1.11 kristaps 166: assert(tp->tbl.cols);
167: free(tp->tbl.cols);
168: tp->tbl.cols = NULL;
1.2 kristaps 169: }
1.1 kristaps 170:
171: tp->flags &= ~TERMP_NONOSPACE;
1.11 kristaps 172: tp->rmargin = rmargin;
173: tp->maxrmargin = maxrmargin;
1.1 kristaps 174:
175: }
176:
177: static void
178: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
179: {
180: const struct tbl_head *hp;
181: char c;
1.12 kristaps 182: size_t width;
1.1 kristaps 183:
184: /*
185: * An hrule extends across the entire table and is demarked by a
186: * standalone `_' or whatnot in lieu of a table row. Spanning
187: * headers are marked by a `+', as are table boundaries.
188: */
189:
190: c = '-';
191: if (TBL_SPAN_DHORIZ == sp->pos)
192: c = '=';
193:
194: /* FIXME: don't use `+' between data and a spanner! */
195:
196: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 197: width = tp->tbl.cols[hp->ident].width;
1.1 kristaps 198: switch (hp->pos) {
199: case (TBL_HEAD_DATA):
1.2 kristaps 200: tbl_char(tp, c, width);
1.1 kristaps 201: break;
202: case (TBL_HEAD_DVERT):
1.2 kristaps 203: tbl_char(tp, '+', width);
1.1 kristaps 204: /* FALLTHROUGH */
205: case (TBL_HEAD_VERT):
1.2 kristaps 206: tbl_char(tp, '+', width);
1.1 kristaps 207: break;
208: default:
209: abort();
210: /* NOTREACHED */
211: }
212: }
213: }
214:
215: static void
216: tbl_hframe(struct termp *tp, const struct tbl_span *sp)
217: {
218: const struct tbl_head *hp;
1.12 kristaps 219: size_t width;
1.1 kristaps 220:
221: if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
222: TBL_OPT_DBOX & sp->tbl->opts))
223: return;
224:
225: /*
226: * Print out the horizontal part of a frame or double frame. A
227: * double frame has an unbroken `-' outer line the width of the
228: * table, bordered by `+'. The frame (or inner frame, in the
229: * case of the double frame) is a `-' bordered by `+' and broken
230: * by `+' whenever a span is encountered.
231: */
232:
233: if (TBL_OPT_DBOX & sp->tbl->opts) {
234: term_word(tp, "+");
1.2 kristaps 235: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 236: width = tp->tbl.cols[hp->ident].width;
1.2 kristaps 237: tbl_char(tp, '-', width);
238: }
1.1 kristaps 239: term_word(tp, "+");
240: term_flushln(tp);
241: }
242:
243: term_word(tp, "+");
244: for (hp = sp->head; hp; hp = hp->next) {
1.11 kristaps 245: width = tp->tbl.cols[hp->ident].width;
1.1 kristaps 246: switch (hp->pos) {
247: case (TBL_HEAD_DATA):
1.2 kristaps 248: tbl_char(tp, '-', width);
1.1 kristaps 249: break;
250: default:
1.2 kristaps 251: tbl_char(tp, '+', width);
1.1 kristaps 252: break;
253: }
254: }
255: term_word(tp, "+");
256: term_flushln(tp);
257: }
258:
259: static void
260: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 261: const struct tbl_dat *dp,
1.11 kristaps 262: const struct roffcol *col)
1.1 kristaps 263: {
264:
265: if (NULL == dp) {
1.11 kristaps 266: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 267: return;
268: }
1.16 kristaps 269: assert(dp->layout);
1.1 kristaps 270:
271: switch (dp->pos) {
1.10 kristaps 272: case (TBL_DATA_NONE):
1.11 kristaps 273: tbl_char(tp, ASCII_NBRSP, col->width);
1.10 kristaps 274: return;
1.1 kristaps 275: case (TBL_DATA_HORIZ):
276: /* FALLTHROUGH */
1.7 kristaps 277: case (TBL_DATA_NHORIZ):
1.11 kristaps 278: tbl_char(tp, '-', col->width);
1.7 kristaps 279: return;
280: case (TBL_DATA_NDHORIZ):
281: /* FALLTHROUGH */
1.1 kristaps 282: case (TBL_DATA_DHORIZ):
1.11 kristaps 283: tbl_char(tp, '=', col->width);
1.1 kristaps 284: return;
285: default:
286: break;
287: }
288:
1.16 kristaps 289: switch (dp->layout->pos) {
1.1 kristaps 290: case (TBL_CELL_HORIZ):
1.11 kristaps 291: tbl_char(tp, '-', col->width);
1.7 kristaps 292: break;
1.1 kristaps 293: case (TBL_CELL_DHORIZ):
1.11 kristaps 294: tbl_char(tp, '=', col->width);
1.1 kristaps 295: break;
296: case (TBL_CELL_LONG):
297: /* FALLTHROUGH */
298: case (TBL_CELL_CENTRE):
299: /* FALLTHROUGH */
300: case (TBL_CELL_LEFT):
301: /* FALLTHROUGH */
302: case (TBL_CELL_RIGHT):
1.11 kristaps 303: tbl_literal(tp, dp, col);
1.1 kristaps 304: break;
305: case (TBL_CELL_NUMBER):
1.11 kristaps 306: tbl_number(tp, tbl, dp, col);
1.1 kristaps 307: break;
308: default:
309: abort();
310: /* NOTREACHED */
311: }
312: }
1.11 kristaps 313:
1.1 kristaps 314: static void
1.11 kristaps 315: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 kristaps 316: {
317:
318: switch (hp->pos) {
319: case (TBL_HEAD_VERT):
320: term_word(tp, "|");
321: break;
322: case (TBL_HEAD_DVERT):
323: term_word(tp, "||");
324: break;
325: default:
326: break;
327: }
328: }
329:
330: static void
331: tbl_vframe(struct termp *tp, const struct tbl *tbl)
332: {
333:
334: if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
335: term_word(tp, "|");
336: }
337:
1.11 kristaps 338: static void
1.12 kristaps 339: tbl_char(struct termp *tp, char c, size_t len)
1.1 kristaps 340: {
1.12 kristaps 341: size_t i, sz;
342: char cp[2];
343:
344: cp[0] = c;
345: cp[1] = '\0';
1.1 kristaps 346:
1.3 kristaps 347: sz = term_strlen(tp, cp);
348:
349: for (i = 0; i < len; i += sz)
1.1 kristaps 350: term_word(tp, cp);
351: }
352:
353: static void
1.11 kristaps 354: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
355: const struct roffcol *col)
1.1 kristaps 356: {
1.12 kristaps 357: size_t padl, padr, ssz;
1.1 kristaps 358:
359: padl = padr = 0;
360:
1.17 ! kristaps 361: assert(dp->string);
1.13 kristaps 362:
1.3 kristaps 363: ssz = term_len(tp, 1);
1.1 kristaps 364:
1.16 kristaps 365: switch (dp->layout->pos) {
1.1 kristaps 366: case (TBL_CELL_LONG):
1.3 kristaps 367: padl = ssz;
1.17 ! kristaps 368: padr = col->width - term_strlen(tp, dp->string) - ssz;
1.1 kristaps 369: break;
370: case (TBL_CELL_CENTRE):
1.17 ! kristaps 371: padl = col->width - term_strlen(tp, dp->string);
1.1 kristaps 372: if (padl % 2)
373: padr++;
374: padl /= 2;
375: padr += padl;
376: break;
377: case (TBL_CELL_RIGHT):
1.17 ! kristaps 378: padl = col->width - term_strlen(tp, dp->string);
1.1 kristaps 379: break;
380: default:
1.17 ! kristaps 381: padr = col->width - term_strlen(tp, dp->string);
1.1 kristaps 382: break;
383: }
384:
385: tbl_char(tp, ASCII_NBRSP, padl);
1.17 ! kristaps 386: term_word(tp, dp->string);
1.1 kristaps 387: tbl_char(tp, ASCII_NBRSP, padr);
388: }
389:
390: static void
1.11 kristaps 391: tbl_number(struct termp *tp, const struct tbl *tbl,
1.2 kristaps 392: const struct tbl_dat *dp,
1.11 kristaps 393: const struct roffcol *col)
1.1 kristaps 394: {
1.11 kristaps 395: char *cp;
396: char buf[2];
397: size_t sz, psz, ssz, d, padl;
398: int i;
1.1 kristaps 399:
400: /*
401: * See calc_data_number(). Left-pad by taking the offset of our
402: * and the maximum decimal; right-pad by the remaining amount.
403: */
404:
1.17 ! kristaps 405: assert(dp->string);
1.2 kristaps 406:
1.17 ! kristaps 407: sz = term_strlen(tp, dp->string);
1.2 kristaps 408:
1.11 kristaps 409: buf[0] = tbl->decimal;
410: buf[1] = '\0';
1.2 kristaps 411:
1.11 kristaps 412: psz = term_strlen(tp, buf);
1.10 kristaps 413:
1.17 ! kristaps 414: if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5 kristaps 415: buf[1] = '\0';
1.17 ! kristaps 416: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
! 417: buf[0] = dp->string[i];
1.5 kristaps 418: ssz += term_strlen(tp, buf);
419: }
420: d = ssz + psz;
421: } else
422: d = sz + psz;
1.2 kristaps 423:
1.5 kristaps 424: sz += term_len(tp, 2);
1.11 kristaps 425: d += term_len(tp, 1);
1.2 kristaps 426:
1.11 kristaps 427: padl = col->decimal - d;
1.2 kristaps 428:
1.11 kristaps 429: tbl_char(tp, ASCII_NBRSP, padl);
1.17 ! kristaps 430: term_word(tp, dp->string);
1.11 kristaps 431: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2 kristaps 432: }
433:
CVSweb