Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.47
1.47 ! schwarze 1: /* $OpenBSD: tbl_term.c,v 1.46 2018/08/18 16:44:52 schwarze Exp $ */
1.1 schwarze 2: /*
1.9 schwarze 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.46 schwarze 4: * Copyright (c) 2011-2018 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.41 schwarze 29: #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \
30: (cp)->pos == TBL_CELL_DHORIZ)
31:
1.6 schwarze 32: static size_t term_tbl_len(size_t, void *);
33: static size_t term_tbl_strlen(const char *, void *);
1.34 schwarze 34: static size_t term_tbl_sulen(const struct roffsu *, void *);
1.6 schwarze 35: static void tbl_char(struct termp *, char, size_t);
1.14 schwarze 36: static void tbl_data(struct termp *, const struct tbl_opts *,
1.41 schwarze 37: const struct tbl_cell *,
1.16 schwarze 38: const struct tbl_dat *,
1.6 schwarze 39: const struct roffcol *);
1.16 schwarze 40: static void tbl_literal(struct termp *, const struct tbl_dat *,
1.6 schwarze 41: const struct roffcol *);
1.16 schwarze 42: static void tbl_number(struct termp *, const struct tbl_opts *,
43: const struct tbl_dat *,
1.6 schwarze 44: const struct roffcol *);
1.21 schwarze 45: static void tbl_hrule(struct termp *, const struct tbl_span *, int);
1.17 schwarze 46: static void tbl_word(struct termp *, const struct tbl_dat *);
1.1 schwarze 47:
1.6 schwarze 48:
49: static size_t
1.34 schwarze 50: term_tbl_sulen(const struct roffsu *su, void *arg)
51: {
1.45 schwarze 52: int i;
53:
54: i = term_hen((const struct termp *)arg, su);
55: return i > 0 ? i : 0;
1.34 schwarze 56: }
57:
58: static size_t
1.6 schwarze 59: term_tbl_strlen(const char *p, void *arg)
60: {
1.30 schwarze 61: return term_strlen((const struct termp *)arg, p);
1.6 schwarze 62: }
63:
64: static size_t
65: term_tbl_len(size_t sz, void *arg)
66: {
1.30 schwarze 67: return term_len((const struct termp *)arg, sz);
1.6 schwarze 68: }
1.5 schwarze 69:
70: void
71: term_tbl(struct termp *tp, const struct tbl_span *sp)
72: {
1.41 schwarze 73: const struct tbl_cell *cp, *cpn, *cpp;
1.6 schwarze 74: const struct tbl_dat *dp;
1.22 schwarze 75: static size_t offset;
1.35 schwarze 76: size_t coloff, tsz;
77: int ic, horiz, spans, vert, more;
78: char fc;
1.27 schwarze 79:
1.5 schwarze 80: /* Inhibit printing of spaces: we do padding ourselves. */
81:
1.35 schwarze 82: tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
1.1 schwarze 83:
84: /*
1.6 schwarze 85: * The first time we're invoked for a given table block,
86: * calculate the table widths and decimal positions.
1.1 schwarze 87: */
88:
1.25 schwarze 89: if (tp->tbl.cols == NULL) {
1.6 schwarze 90: tp->tbl.len = term_tbl_len;
91: tp->tbl.slen = term_tbl_strlen;
1.34 schwarze 92: tp->tbl.sulen = term_tbl_sulen;
1.6 schwarze 93: tp->tbl.arg = tp;
1.5 schwarze 94:
1.36 schwarze 95: tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
1.42 schwarze 96:
97: /* Tables leak .ta settings to subsequent text. */
98:
99: term_tab_set(tp, NULL);
100: coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
101: sp->opts->lvert;
102: for (ic = 0; ic < sp->opts->cols; ic++) {
103: coloff += tp->tbl.cols[ic].width;
104: term_tab_iset(coloff);
1.43 schwarze 105: coloff += tp->tbl.cols[ic].spacing;
1.42 schwarze 106: }
1.2 schwarze 107:
1.22 schwarze 108: /* Center the table as a whole. */
109:
1.33 schwarze 110: offset = tp->tcol->offset;
1.22 schwarze 111: if (sp->opts->opts & TBL_OPT_CENTRE) {
112: tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
113: ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
1.43 schwarze 114: for (ic = 0; ic + 1 < sp->opts->cols; ic++)
115: tsz += tp->tbl.cols[ic].width +
116: tp->tbl.cols[ic].spacing;
117: if (sp->opts->cols)
118: tsz += tp->tbl.cols[sp->opts->cols - 1].width;
1.33 schwarze 119: if (offset + tsz > tp->tcol->rmargin)
1.22 schwarze 120: tsz -= 1;
1.33 schwarze 121: tp->tcol->offset = offset + tp->tcol->rmargin > tsz ?
122: (offset + tp->tcol->rmargin - tsz) / 2 : 0;
1.22 schwarze 123: }
124:
1.21 schwarze 125: /* Horizontal frame at the start of boxed tables. */
1.1 schwarze 126:
1.21 schwarze 127: if (sp->opts->opts & TBL_OPT_DBOX)
1.41 schwarze 128: tbl_hrule(tp, sp, 3);
129: if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
1.21 schwarze 130: tbl_hrule(tp, sp, 2);
1.10 schwarze 131: }
1.5 schwarze 132:
1.35 schwarze 133: /* Set up the columns. */
1.5 schwarze 134:
1.35 schwarze 135: tp->flags |= TERMP_MULTICOL;
136: horiz = 0;
137: switch (sp->pos) {
138: case TBL_SPAN_HORIZ:
139: case TBL_SPAN_DHORIZ:
140: horiz = 1;
141: term_setcol(tp, 1);
142: break;
143: case TBL_SPAN_DATA:
144: term_setcol(tp, sp->opts->cols + 2);
145: coloff = tp->tcol->offset;
146:
147: /* Set up a column for a left vertical frame. */
148:
149: if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
150: sp->opts->lvert)
151: coloff++;
152: tp->tcol->rmargin = coloff;
1.21 schwarze 153:
1.35 schwarze 154: /* Set up the data columns. */
1.1 schwarze 155:
1.35 schwarze 156: dp = sp->first;
157: spans = 0;
158: for (ic = 0; ic < sp->opts->cols; ic++) {
159: if (spans == 0) {
160: tp->tcol++;
161: tp->tcol->offset = coloff;
162: }
163: coloff += tp->tbl.cols[ic].width;
164: tp->tcol->rmargin = coloff;
165: if (ic + 1 < sp->opts->cols)
1.43 schwarze 166: coloff += tp->tbl.cols[ic].spacing;
1.35 schwarze 167: if (spans) {
168: spans--;
169: continue;
170: }
171: if (dp == NULL)
172: continue;
173: spans = dp->spans;
1.44 schwarze 174: if (ic || sp->layout->first->pos != TBL_CELL_SPAN)
175: dp = dp->next;
1.35 schwarze 176: }
177:
178: /* Set up a column for a right vertical frame. */
179:
180: tp->tcol++;
1.43 schwarze 181: tp->tcol->offset = coloff + 1;
1.41 schwarze 182: tp->tcol->rmargin = tp->maxrmargin;
1.35 schwarze 183:
184: /* Spans may have reduced the number of columns. */
185:
186: tp->lasttcol = tp->tcol - tp->tcols;
187:
188: /* Fill the buffers for all data columns. */
1.1 schwarze 189:
1.35 schwarze 190: tp->tcol = tp->tcols;
1.41 schwarze 191: cp = cpn = sp->layout->first;
1.5 schwarze 192: dp = sp->first;
1.7 schwarze 193: spans = 0;
1.24 schwarze 194: for (ic = 0; ic < sp->opts->cols; ic++) {
1.41 schwarze 195: if (cpn != NULL) {
196: cp = cpn;
197: cpn = cpn->next;
198: }
1.35 schwarze 199: if (spans) {
200: spans--;
201: continue;
202: }
203: tp->tcol++;
204: tp->col = 0;
1.41 schwarze 205: tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
1.35 schwarze 206: if (dp == NULL)
207: continue;
208: spans = dp->spans;
1.44 schwarze 209: if (cp->pos != TBL_CELL_SPAN)
210: dp = dp->next;
1.35 schwarze 211: }
212: break;
213: }
214:
215: do {
216: /* Print the vertical frame at the start of each row. */
217:
218: tp->tcol = tp->tcols;
219: fc = '\0';
220: if (sp->layout->vert ||
1.41 schwarze 221: (sp->next != NULL && sp->next->layout->vert &&
222: sp->next->pos == TBL_SPAN_DATA) ||
223: (sp->prev != NULL && sp->prev->layout->vert &&
224: (horiz || (IS_HORIZ(sp->layout->first) &&
225: !IS_HORIZ(sp->prev->layout->first)))) ||
1.35 schwarze 226: sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
1.41 schwarze 227: fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|';
1.35 schwarze 228: else if (horiz && sp->opts->lvert)
229: fc = '-';
230: if (fc != '\0') {
231: (*tp->advance)(tp, tp->tcols->offset);
232: (*tp->letter)(tp, fc);
233: tp->viscol = tp->tcol->offset + 1;
234: }
1.11 schwarze 235:
1.35 schwarze 236: /* Print the data cells. */
1.10 schwarze 237:
1.35 schwarze 238: more = 0;
239: if (horiz) {
240: tbl_hrule(tp, sp, 0);
241: term_flushln(tp);
242: } else {
243: cp = sp->layout->first;
1.41 schwarze 244: cpn = sp->next == NULL ? NULL :
245: sp->next->layout->first;
246: cpp = sp->prev == NULL ? NULL :
247: sp->prev->layout->first;
1.35 schwarze 248: dp = sp->first;
249: spans = 0;
250: for (ic = 0; ic < sp->opts->cols; ic++) {
251:
1.41 schwarze 252: /*
253: * Figure out whether to print a
254: * vertical line after this cell
255: * and advance to next layout cell.
256: */
1.35 schwarze 257:
258: if (cp != NULL) {
259: vert = cp->vert;
1.41 schwarze 260: switch (cp->pos) {
261: case TBL_CELL_HORIZ:
262: fc = '-';
263: break;
264: case TBL_CELL_DHORIZ:
265: fc = '=';
266: break;
267: default:
268: fc = ' ';
269: break;
270: }
271: } else {
272: vert = 0;
273: fc = ' ';
274: }
275: if (cpp != NULL) {
276: if (vert == 0 &&
277: cp != NULL &&
278: ((IS_HORIZ(cp) &&
279: !IS_HORIZ(cpp)) ||
280: (cp->next != NULL &&
281: cpp->next != NULL &&
282: IS_HORIZ(cp->next) &&
283: !IS_HORIZ(cpp->next))))
284: vert = cpp->vert;
285: cpp = cpp->next;
286: }
287: if (vert == 0 &&
288: sp->opts->opts & TBL_OPT_ALLBOX)
289: vert = 1;
290: if (cpn != NULL) {
291: if (vert == 0)
292: vert = cpn->vert;
293: cpn = cpn->next;
294: }
295: if (cp != NULL)
1.35 schwarze 296: cp = cp->next;
1.39 schwarze 297:
1.41 schwarze 298: /*
299: * Skip later cells in a span,
300: * figure out whether to start a span,
301: * and advance to next data cell.
302: */
1.39 schwarze 303:
304: if (spans) {
305: spans--;
306: continue;
307: }
308: if (dp != NULL) {
309: spans = dp->spans;
1.44 schwarze 310: if (ic || sp->layout->first->pos
311: != TBL_CELL_SPAN)
312: dp = dp->next;
1.39 schwarze 313: }
314:
1.41 schwarze 315: /*
316: * Print one line of text in the cell
317: * and remember whether there is more.
318: */
1.39 schwarze 319:
320: tp->tcol++;
321: if (tp->tcol->col < tp->tcol->lastcol)
322: term_flushln(tp);
323: if (tp->tcol->col < tp->tcol->lastcol)
324: more = 1;
325:
326: /*
327: * Vertical frames between data cells,
328: * but not after the last column.
329: */
330:
1.41 schwarze 331: if (fc == ' ' && ((vert == 0 &&
332: (cp == NULL || !IS_HORIZ(cp))) ||
333: tp->tcol + 1 == tp->tcols + tp->lasttcol))
334: continue;
335:
1.43 schwarze 336: if (tp->viscol < tp->tcol->rmargin) {
1.41 schwarze 337: (*tp->advance)(tp, tp->tcol->rmargin
338: - tp->viscol);
339: tp->viscol = tp->tcol->rmargin;
340: }
1.43 schwarze 341: while (tp->viscol < tp->tcol->rmargin +
342: tp->tbl.cols[ic].spacing / 2) {
1.41 schwarze 343: (*tp->letter)(tp, fc);
344: tp->viscol++;
345: }
346:
1.39 schwarze 347: if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
348: continue;
1.35 schwarze 349:
1.41 schwarze 350: if (fc == ' ' && cp != NULL) {
351: switch (cp->pos) {
352: case TBL_CELL_HORIZ:
353: fc = '-';
354: break;
355: case TBL_CELL_DHORIZ:
356: fc = '=';
357: break;
358: default:
359: break;
360: }
361: }
1.43 schwarze 362: if (tp->tbl.cols[ic].spacing) {
363: (*tp->letter)(tp, fc == ' ' ? '|' :
364: vert ? '+' : fc);
365: tp->viscol++;
366: }
1.41 schwarze 367:
368: if (fc != ' ') {
369: if (cp != NULL &&
370: cp->pos == TBL_CELL_HORIZ)
371: fc = '-';
372: else if (cp != NULL &&
373: cp->pos == TBL_CELL_DHORIZ)
374: fc = '=';
375: else
376: fc = ' ';
1.35 schwarze 377: }
1.43 schwarze 378: if (tp->tbl.cols[ic].spacing > 2 &&
379: (vert > 1 || fc != ' ')) {
1.41 schwarze 380: (*tp->letter)(tp, fc == ' ' ? '|' :
381: vert > 1 ? '+' : fc);
1.35 schwarze 382: tp->viscol++;
1.21 schwarze 383: }
1.35 schwarze 384: }
385: }
1.5 schwarze 386:
1.35 schwarze 387: /* Print the vertical frame at the end of each row. */
1.7 schwarze 388:
1.35 schwarze 389: fc = '\0';
1.41 schwarze 390: if ((sp->layout->last->vert &&
391: sp->layout->last->col + 1 == sp->opts->cols) ||
392: (sp->next != NULL &&
393: sp->next->layout->last->vert &&
394: sp->next->layout->last->col + 1 == sp->opts->cols) ||
395: (sp->prev != NULL &&
396: sp->prev->layout->last->vert &&
397: sp->prev->layout->last->col + 1 == sp->opts->cols &&
398: (horiz || (IS_HORIZ(sp->layout->last) &&
399: !IS_HORIZ(sp->prev->layout->last)))) ||
1.35 schwarze 400: (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
1.41 schwarze 401: fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|';
1.35 schwarze 402: else if (horiz && sp->opts->rvert)
403: fc = '-';
404: if (fc != '\0') {
1.41 schwarze 405: if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
406: sp->layout->last->col + 1 < sp->opts->cols)) {
1.35 schwarze 407: tp->tcol++;
408: (*tp->advance)(tp,
409: tp->tcol->offset > tp->viscol ?
410: tp->tcol->offset - tp->viscol : 1);
411: }
412: (*tp->letter)(tp, fc);
413: }
414: (*tp->endline)(tp);
415: tp->viscol = 0;
416: } while (more);
1.1 schwarze 417:
1.5 schwarze 418: /*
1.41 schwarze 419: * Clean up after this row. If it is the last line
420: * of the table, print the box line and clean up
421: * column data; otherwise, print the allbox line.
1.5 schwarze 422: */
1.1 schwarze 423:
1.35 schwarze 424: term_setcol(tp, 1);
425: tp->flags &= ~TERMP_MULTICOL;
426: tp->tcol->rmargin = tp->maxrmargin;
1.25 schwarze 427: if (sp->next == NULL) {
1.21 schwarze 428: if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
1.41 schwarze 429: tbl_hrule(tp, sp, 2);
1.13 schwarze 430: tp->skipvsp = 1;
431: }
1.21 schwarze 432: if (sp->opts->opts & TBL_OPT_DBOX) {
1.41 schwarze 433: tbl_hrule(tp, sp, 3);
1.13 schwarze 434: tp->skipvsp = 2;
435: }
1.6 schwarze 436: assert(tp->tbl.cols);
437: free(tp->tbl.cols);
438: tp->tbl.cols = NULL;
1.33 schwarze 439: tp->tcol->offset = offset;
1.38 schwarze 440: } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
441: (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
442: sp->next->next != NULL))
1.37 schwarze 443: tbl_hrule(tp, sp, 1);
444:
1.35 schwarze 445: tp->flags &= ~TERMP_NONOSPACE;
1.1 schwarze 446: }
447:
1.10 schwarze 448: /*
1.21 schwarze 449: * Kinds of horizontal rulers:
450: * 0: inside the table (single or double line with crossings)
1.41 schwarze 451: * 1: inside the table (single or double line with crossings and ends)
452: * 2: inner frame (single line with crossings and ends)
453: * 3: outer frame (single line without crossings with ends)
1.10 schwarze 454: */
1.1 schwarze 455: static void
1.21 schwarze 456: tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
1.1 schwarze 457: {
1.41 schwarze 458: const struct tbl_cell *cp, *cpn, *cpp;
1.43 schwarze 459: const struct roffcol *col;
1.21 schwarze 460: int vert;
1.47 ! schwarze 461: char cross, line, stdcross, stdline;
1.21 schwarze 462:
1.47 ! schwarze 463: stdline = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
! 464: stdcross = (kind < 3) ? '+' : '-';
1.21 schwarze 465:
1.41 schwarze 466: cp = sp->layout->first;
467: cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first;
468: if (cpp == cp)
469: cpp = NULL;
470: cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first;
471: if (cpn == cp)
472: cpn = NULL;
1.47 ! schwarze 473: if (kind)
! 474: term_word(tp,
! 475: cpn == NULL || cpn->pos != TBL_CELL_DOWN ? "+" : "|");
1.21 schwarze 476: for (;;) {
1.43 schwarze 477: col = tp->tbl.cols + cp->col;
1.47 ! schwarze 478: if (cpn == NULL || cpn->pos != TBL_CELL_DOWN) {
! 479: line = stdline;
! 480: cross = stdcross;
! 481: } else {
! 482: line = ' ';
! 483: cross = (kind < 3) ? '|' : ' ';
! 484: }
1.43 schwarze 485: tbl_char(tp, line, col->width + col->spacing / 2);
1.41 schwarze 486: vert = cp->vert;
487: if ((cp = cp->next) == NULL)
1.21 schwarze 488: break;
1.41 schwarze 489: if (cpp != NULL) {
490: if (vert < cpp->vert)
491: vert = cpp->vert;
492: cpp = cpp->next;
493: }
494: if (cpn != NULL) {
495: if (vert < cpn->vert)
496: vert = cpn->vert;
497: cpn = cpn->next;
1.21 schwarze 498: }
1.47 ! schwarze 499: if (cpn == NULL || cpn->pos != TBL_CELL_DOWN) {
! 500: line = stdline;
! 501: cross = stdcross;
! 502: } else
! 503: line = ' ';
1.37 schwarze 504: if (sp->opts->opts & TBL_OPT_ALLBOX && !vert)
505: vert = 1;
1.43 schwarze 506: if (col->spacing)
507: tbl_char(tp, vert ? cross : line, 1);
508: if (col->spacing > 2)
509: tbl_char(tp, vert > 1 ? cross : line, 1);
510: if (col->spacing > 4)
511: tbl_char(tp, line, (col->spacing - 3) / 2);
1.11 schwarze 512: }
1.21 schwarze 513: if (kind) {
1.47 ! schwarze 514: term_word(tp,
! 515: cpn == NULL || cpn->pos != TBL_CELL_DOWN ? "+" : "|");
1.21 schwarze 516: term_flushln(tp);
1.11 schwarze 517: }
1.1 schwarze 518: }
519:
520: static void
1.14 schwarze 521: tbl_data(struct termp *tp, const struct tbl_opts *opts,
1.41 schwarze 522: const struct tbl_cell *cp, const struct tbl_dat *dp,
523: const struct roffcol *col)
1.1 schwarze 524: {
1.41 schwarze 525: switch (cp->pos) {
526: case TBL_CELL_HORIZ:
527: tbl_char(tp, '-', col->width);
528: return;
529: case TBL_CELL_DHORIZ:
530: tbl_char(tp, '=', col->width);
531: return;
532: default:
533: break;
534: }
1.1 schwarze 535:
1.44 schwarze 536: if (dp == NULL)
1.1 schwarze 537: return;
538:
1.5 schwarze 539: switch (dp->pos) {
1.16 schwarze 540: case TBL_DATA_NONE:
1.5 schwarze 541: return;
1.16 schwarze 542: case TBL_DATA_HORIZ:
543: case TBL_DATA_NHORIZ:
1.6 schwarze 544: tbl_char(tp, '-', col->width);
1.5 schwarze 545: return;
1.16 schwarze 546: case TBL_DATA_NDHORIZ:
547: case TBL_DATA_DHORIZ:
1.6 schwarze 548: tbl_char(tp, '=', col->width);
1.5 schwarze 549: return;
1.1 schwarze 550: default:
551: break;
552: }
1.16 schwarze 553:
1.41 schwarze 554: switch (cp->pos) {
1.16 schwarze 555: case TBL_CELL_LONG:
556: case TBL_CELL_CENTRE:
557: case TBL_CELL_LEFT:
558: case TBL_CELL_RIGHT:
1.6 schwarze 559: tbl_literal(tp, dp, col);
1.1 schwarze 560: break;
1.16 schwarze 561: case TBL_CELL_NUMBER:
1.14 schwarze 562: tbl_number(tp, opts, dp, col);
1.4 schwarze 563: break;
1.16 schwarze 564: case TBL_CELL_DOWN:
1.44 schwarze 565: case TBL_CELL_SPAN:
1.7 schwarze 566: break;
1.1 schwarze 567: default:
568: abort();
569: }
570: }
571:
572: static void
1.6 schwarze 573: tbl_char(struct termp *tp, char c, size_t len)
1.5 schwarze 574: {
1.6 schwarze 575: size_t i, sz;
1.5 schwarze 576: char cp[2];
1.1 schwarze 577:
1.5 schwarze 578: cp[0] = c;
579: cp[1] = '\0';
1.1 schwarze 580:
1.5 schwarze 581: sz = term_strlen(tp, cp);
1.1 schwarze 582:
1.5 schwarze 583: for (i = 0; i < len; i += sz)
584: term_word(tp, cp);
1.1 schwarze 585: }
586:
587: static void
1.16 schwarze 588: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
1.6 schwarze 589: const struct roffcol *col)
1.1 schwarze 590: {
1.24 schwarze 591: size_t len, padl, padr, width;
592: int ic, spans;
1.1 schwarze 593:
1.7 schwarze 594: assert(dp->string);
1.10 schwarze 595: len = term_strlen(tp, dp->string);
1.12 schwarze 596: width = col->width;
1.24 schwarze 597: ic = dp->layout->col;
598: spans = dp->spans;
599: while (spans--)
600: width += tp->tbl.cols[++ic].width + 3;
1.12 schwarze 601:
602: padr = width > len ? width - len : 0;
1.10 schwarze 603: padl = 0;
1.5 schwarze 604:
1.7 schwarze 605: switch (dp->layout->pos) {
1.16 schwarze 606: case TBL_CELL_LONG:
1.10 schwarze 607: padl = term_len(tp, 1);
608: padr = padr > padl ? padr - padl : 0;
1.1 schwarze 609: break;
1.16 schwarze 610: case TBL_CELL_CENTRE:
1.10 schwarze 611: if (2 > padr)
1.8 schwarze 612: break;
1.10 schwarze 613: padl = padr / 2;
1.8 schwarze 614: padr -= padl;
1.1 schwarze 615: break;
1.16 schwarze 616: case TBL_CELL_RIGHT:
1.10 schwarze 617: padl = padr;
618: padr = 0;
1.1 schwarze 619: break;
620: default:
621: break;
622: }
623:
1.5 schwarze 624: tbl_char(tp, ASCII_NBRSP, padl);
1.17 schwarze 625: tbl_word(tp, dp);
1.10 schwarze 626: tbl_char(tp, ASCII_NBRSP, padr);
1.1 schwarze 627: }
628:
1.5 schwarze 629: static void
1.14 schwarze 630: tbl_number(struct termp *tp, const struct tbl_opts *opts,
1.5 schwarze 631: const struct tbl_dat *dp,
1.6 schwarze 632: const struct roffcol *col)
1.5 schwarze 633: {
1.6 schwarze 634: char *cp;
635: char buf[2];
1.46 schwarze 636: size_t sz, ssz, d, padl;
1.6 schwarze 637: int i;
1.5 schwarze 638:
639: /*
640: * See calc_data_number(). Left-pad by taking the offset of our
641: * and the maximum decimal; right-pad by the remaining amount.
642: */
643:
1.7 schwarze 644: assert(dp->string);
1.5 schwarze 645:
1.7 schwarze 646: sz = term_strlen(tp, dp->string);
1.5 schwarze 647:
1.14 schwarze 648: buf[0] = opts->decimal;
1.6 schwarze 649: buf[1] = '\0';
1.5 schwarze 650:
1.23 schwarze 651: if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
1.7 schwarze 652: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
653: buf[0] = dp->string[i];
1.5 schwarze 654: ssz += term_strlen(tp, buf);
655: }
1.46 schwarze 656: d = ssz;
1.5 schwarze 657: } else
1.46 schwarze 658: d = sz;
1.5 schwarze 659:
1.20 schwarze 660: if (col->decimal > d && col->width > sz) {
661: padl = col->decimal - d;
662: if (padl + sz > col->width)
663: padl = col->width - sz;
664: tbl_char(tp, ASCII_NBRSP, padl);
665: } else
666: padl = 0;
1.17 schwarze 667: tbl_word(tp, dp);
1.10 schwarze 668: if (col->width > sz + padl)
669: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.5 schwarze 670: }
1.1 schwarze 671:
1.17 schwarze 672: static void
673: tbl_word(struct termp *tp, const struct tbl_dat *dp)
674: {
1.26 schwarze 675: int prev_font;
1.17 schwarze 676:
1.26 schwarze 677: prev_font = tp->fonti;
1.17 schwarze 678: if (dp->layout->flags & TBL_CELL_BOLD)
679: term_fontpush(tp, TERMFONT_BOLD);
680: else if (dp->layout->flags & TBL_CELL_ITALIC)
681: term_fontpush(tp, TERMFONT_UNDER);
682:
683: term_word(tp, dp->string);
684:
685: term_fontpopq(tp, prev_font);
686: }