Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.45
1.45 ! schwarze 1: /* $OpenBSD: tbl_term.c,v 1.44 2017/07/08 13:43:09 schwarze Exp $ */
1.1 schwarze 2: /*
1.9 schwarze 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.32 schwarze 4: * Copyright (c) 2011,2012,2014,2015,2017 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;
461: char line, cross;
462:
1.41 schwarze 463: line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
464: cross = (kind < 3) ? '+' : '-';
1.21 schwarze 465:
466: if (kind)
467: term_word(tp, "+");
1.41 schwarze 468: cp = sp->layout->first;
469: cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first;
470: if (cpp == cp)
471: cpp = NULL;
472: cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first;
473: if (cpn == cp)
474: cpn = NULL;
1.21 schwarze 475: for (;;) {
1.43 schwarze 476: col = tp->tbl.cols + cp->col;
477: tbl_char(tp, line, col->width + col->spacing / 2);
1.41 schwarze 478: vert = cp->vert;
479: if ((cp = cp->next) == NULL)
1.21 schwarze 480: break;
1.41 schwarze 481: if (cpp != NULL) {
482: if (vert < cpp->vert)
483: vert = cpp->vert;
484: cpp = cpp->next;
485: }
486: if (cpn != NULL) {
487: if (vert < cpn->vert)
488: vert = cpn->vert;
489: cpn = cpn->next;
1.21 schwarze 490: }
1.37 schwarze 491: if (sp->opts->opts & TBL_OPT_ALLBOX && !vert)
492: vert = 1;
1.43 schwarze 493: if (col->spacing)
494: tbl_char(tp, vert ? cross : line, 1);
495: if (col->spacing > 2)
496: tbl_char(tp, vert > 1 ? cross : line, 1);
497: if (col->spacing > 4)
498: tbl_char(tp, line, (col->spacing - 3) / 2);
1.11 schwarze 499: }
1.21 schwarze 500: if (kind) {
501: term_word(tp, "+");
502: term_flushln(tp);
1.11 schwarze 503: }
1.1 schwarze 504: }
505:
506: static void
1.14 schwarze 507: tbl_data(struct termp *tp, const struct tbl_opts *opts,
1.41 schwarze 508: const struct tbl_cell *cp, const struct tbl_dat *dp,
509: const struct roffcol *col)
1.1 schwarze 510: {
1.41 schwarze 511: switch (cp->pos) {
512: case TBL_CELL_HORIZ:
513: tbl_char(tp, '-', col->width);
514: return;
515: case TBL_CELL_DHORIZ:
516: tbl_char(tp, '=', col->width);
517: return;
518: default:
519: break;
520: }
1.1 schwarze 521:
1.44 schwarze 522: if (dp == NULL)
1.1 schwarze 523: return;
524:
1.5 schwarze 525: switch (dp->pos) {
1.16 schwarze 526: case TBL_DATA_NONE:
1.5 schwarze 527: return;
1.16 schwarze 528: case TBL_DATA_HORIZ:
529: case TBL_DATA_NHORIZ:
1.6 schwarze 530: tbl_char(tp, '-', col->width);
1.5 schwarze 531: return;
1.16 schwarze 532: case TBL_DATA_NDHORIZ:
533: case TBL_DATA_DHORIZ:
1.6 schwarze 534: tbl_char(tp, '=', col->width);
1.5 schwarze 535: return;
1.1 schwarze 536: default:
537: break;
538: }
1.16 schwarze 539:
1.41 schwarze 540: switch (cp->pos) {
1.16 schwarze 541: case TBL_CELL_LONG:
542: case TBL_CELL_CENTRE:
543: case TBL_CELL_LEFT:
544: case TBL_CELL_RIGHT:
1.6 schwarze 545: tbl_literal(tp, dp, col);
1.1 schwarze 546: break;
1.16 schwarze 547: case TBL_CELL_NUMBER:
1.14 schwarze 548: tbl_number(tp, opts, dp, col);
1.4 schwarze 549: break;
1.16 schwarze 550: case TBL_CELL_DOWN:
1.44 schwarze 551: case TBL_CELL_SPAN:
1.7 schwarze 552: break;
1.1 schwarze 553: default:
554: abort();
555: }
556: }
557:
558: static void
1.6 schwarze 559: tbl_char(struct termp *tp, char c, size_t len)
1.5 schwarze 560: {
1.6 schwarze 561: size_t i, sz;
1.5 schwarze 562: char cp[2];
1.1 schwarze 563:
1.5 schwarze 564: cp[0] = c;
565: cp[1] = '\0';
1.1 schwarze 566:
1.5 schwarze 567: sz = term_strlen(tp, cp);
1.1 schwarze 568:
1.5 schwarze 569: for (i = 0; i < len; i += sz)
570: term_word(tp, cp);
1.1 schwarze 571: }
572:
573: static void
1.16 schwarze 574: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
1.6 schwarze 575: const struct roffcol *col)
1.1 schwarze 576: {
1.24 schwarze 577: size_t len, padl, padr, width;
578: int ic, spans;
1.1 schwarze 579:
1.7 schwarze 580: assert(dp->string);
1.10 schwarze 581: len = term_strlen(tp, dp->string);
1.12 schwarze 582: width = col->width;
1.24 schwarze 583: ic = dp->layout->col;
584: spans = dp->spans;
585: while (spans--)
586: width += tp->tbl.cols[++ic].width + 3;
1.12 schwarze 587:
588: padr = width > len ? width - len : 0;
1.10 schwarze 589: padl = 0;
1.5 schwarze 590:
1.7 schwarze 591: switch (dp->layout->pos) {
1.16 schwarze 592: case TBL_CELL_LONG:
1.10 schwarze 593: padl = term_len(tp, 1);
594: padr = padr > padl ? padr - padl : 0;
1.1 schwarze 595: break;
1.16 schwarze 596: case TBL_CELL_CENTRE:
1.10 schwarze 597: if (2 > padr)
1.8 schwarze 598: break;
1.10 schwarze 599: padl = padr / 2;
1.8 schwarze 600: padr -= padl;
1.1 schwarze 601: break;
1.16 schwarze 602: case TBL_CELL_RIGHT:
1.10 schwarze 603: padl = padr;
604: padr = 0;
1.1 schwarze 605: break;
606: default:
607: break;
608: }
609:
1.5 schwarze 610: tbl_char(tp, ASCII_NBRSP, padl);
1.17 schwarze 611: tbl_word(tp, dp);
1.10 schwarze 612: tbl_char(tp, ASCII_NBRSP, padr);
1.1 schwarze 613: }
614:
1.5 schwarze 615: static void
1.14 schwarze 616: tbl_number(struct termp *tp, const struct tbl_opts *opts,
1.5 schwarze 617: const struct tbl_dat *dp,
1.6 schwarze 618: const struct roffcol *col)
1.5 schwarze 619: {
1.6 schwarze 620: char *cp;
621: char buf[2];
622: size_t sz, psz, ssz, d, padl;
623: int i;
1.5 schwarze 624:
625: /*
626: * See calc_data_number(). Left-pad by taking the offset of our
627: * and the maximum decimal; right-pad by the remaining amount.
628: */
629:
1.7 schwarze 630: assert(dp->string);
1.5 schwarze 631:
1.7 schwarze 632: sz = term_strlen(tp, dp->string);
1.5 schwarze 633:
1.14 schwarze 634: buf[0] = opts->decimal;
1.6 schwarze 635: buf[1] = '\0';
1.5 schwarze 636:
1.6 schwarze 637: psz = term_strlen(tp, buf);
1.5 schwarze 638:
1.23 schwarze 639: if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
1.7 schwarze 640: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
641: buf[0] = dp->string[i];
1.5 schwarze 642: ssz += term_strlen(tp, buf);
643: }
644: d = ssz + psz;
645: } else
646: d = sz + psz;
647:
1.20 schwarze 648: if (col->decimal > d && col->width > sz) {
649: padl = col->decimal - d;
650: if (padl + sz > col->width)
651: padl = col->width - sz;
652: tbl_char(tp, ASCII_NBRSP, padl);
653: } else
654: padl = 0;
1.17 schwarze 655: tbl_word(tp, dp);
1.10 schwarze 656: if (col->width > sz + padl)
657: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.5 schwarze 658: }
1.1 schwarze 659:
1.17 schwarze 660: static void
661: tbl_word(struct termp *tp, const struct tbl_dat *dp)
662: {
1.26 schwarze 663: int prev_font;
1.17 schwarze 664:
1.26 schwarze 665: prev_font = tp->fonti;
1.17 schwarze 666: if (dp->layout->flags & TBL_CELL_BOLD)
667: term_fontpush(tp, TERMFONT_BOLD);
668: else if (dp->layout->flags & TBL_CELL_ITALIC)
669: term_fontpush(tp, TERMFONT_UNDER);
670:
671: term_word(tp, dp->string);
672:
673: term_fontpopq(tp, prev_font);
674: }