1324581Sbapt/* $Id: tbl_term.c,v 1.57 2017/07/31 16:14:10 schwarze Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4322249Sbapt * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#include "config.h" 19241675Suqs 20275432Sbapt#include <sys/types.h> 21275432Sbapt 22241675Suqs#include <assert.h> 23241675Suqs#include <stdio.h> 24241675Suqs#include <stdlib.h> 25241675Suqs#include <string.h> 26241675Suqs 27241675Suqs#include "mandoc.h" 28241675Suqs#include "out.h" 29241675Suqs#include "term.h" 30241675Suqs 31322249Sbapt#define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \ 32322249Sbapt (cp)->pos == TBL_CELL_DHORIZ) 33322249Sbapt 34241675Suqsstatic size_t term_tbl_len(size_t, void *); 35241675Suqsstatic size_t term_tbl_strlen(const char *, void *); 36322249Sbaptstatic size_t term_tbl_sulen(const struct roffsu *, void *); 37241675Suqsstatic void tbl_char(struct termp *, char, size_t); 38261344Suqsstatic void tbl_data(struct termp *, const struct tbl_opts *, 39322249Sbapt const struct tbl_cell *, 40274880Sbapt const struct tbl_dat *, 41241675Suqs const struct roffcol *); 42274880Sbaptstatic void tbl_literal(struct termp *, const struct tbl_dat *, 43241675Suqs const struct roffcol *); 44274880Sbaptstatic void tbl_number(struct termp *, const struct tbl_opts *, 45274880Sbapt const struct tbl_dat *, 46241675Suqs const struct roffcol *); 47279527Sbaptstatic void tbl_hrule(struct termp *, const struct tbl_span *, int); 48275432Sbaptstatic void tbl_word(struct termp *, const struct tbl_dat *); 49241675Suqs 50241675Suqs 51241675Suqsstatic size_t 52322249Sbaptterm_tbl_sulen(const struct roffsu *su, void *arg) 53322249Sbapt{ 54324581Sbapt int i; 55324581Sbapt 56324581Sbapt i = term_hen((const struct termp *)arg, su); 57324581Sbapt return i > 0 ? i : 0; 58322249Sbapt} 59322249Sbapt 60322249Sbaptstatic size_t 61241675Suqsterm_tbl_strlen(const char *p, void *arg) 62241675Suqs{ 63294113Sbapt return term_strlen((const struct termp *)arg, p); 64241675Suqs} 65241675Suqs 66241675Suqsstatic size_t 67241675Suqsterm_tbl_len(size_t sz, void *arg) 68241675Suqs{ 69294113Sbapt return term_len((const struct termp *)arg, sz); 70241675Suqs} 71241675Suqs 72241675Suqsvoid 73241675Suqsterm_tbl(struct termp *tp, const struct tbl_span *sp) 74241675Suqs{ 75322249Sbapt const struct tbl_cell *cp, *cpn, *cpp; 76241675Suqs const struct tbl_dat *dp; 77279527Sbapt static size_t offset; 78322249Sbapt size_t coloff, tsz; 79322249Sbapt int ic, horiz, spans, vert, more; 80322249Sbapt char fc; 81241675Suqs 82241675Suqs /* Inhibit printing of spaces: we do padding ourselves. */ 83241675Suqs 84322249Sbapt tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE; 85241675Suqs 86241675Suqs /* 87241675Suqs * The first time we're invoked for a given table block, 88241675Suqs * calculate the table widths and decimal positions. 89241675Suqs */ 90241675Suqs 91279527Sbapt if (tp->tbl.cols == NULL) { 92241675Suqs tp->tbl.len = term_tbl_len; 93241675Suqs tp->tbl.slen = term_tbl_strlen; 94322249Sbapt tp->tbl.sulen = term_tbl_sulen; 95241675Suqs tp->tbl.arg = tp; 96241675Suqs 97322249Sbapt tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); 98241675Suqs 99322249Sbapt /* Tables leak .ta settings to subsequent text. */ 100322249Sbapt 101322249Sbapt term_tab_set(tp, NULL); 102322249Sbapt coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || 103322249Sbapt sp->opts->lvert; 104322249Sbapt for (ic = 0; ic < sp->opts->cols; ic++) { 105322249Sbapt coloff += tp->tbl.cols[ic].width; 106322249Sbapt term_tab_iset(coloff); 107322249Sbapt coloff += tp->tbl.cols[ic].spacing; 108322249Sbapt } 109322249Sbapt 110279527Sbapt /* Center the table as a whole. */ 111241675Suqs 112322249Sbapt offset = tp->tcol->offset; 113279527Sbapt if (sp->opts->opts & TBL_OPT_CENTRE) { 114279527Sbapt tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) 115279527Sbapt ? 2 : !!sp->opts->lvert + !!sp->opts->rvert; 116322249Sbapt for (ic = 0; ic + 1 < sp->opts->cols; ic++) 117322249Sbapt tsz += tp->tbl.cols[ic].width + 118322249Sbapt tp->tbl.cols[ic].spacing; 119322249Sbapt if (sp->opts->cols) 120322249Sbapt tsz += tp->tbl.cols[sp->opts->cols - 1].width; 121322249Sbapt if (offset + tsz > tp->tcol->rmargin) 122279527Sbapt tsz -= 1; 123322249Sbapt tp->tcol->offset = offset + tp->tcol->rmargin > tsz ? 124322249Sbapt (offset + tp->tcol->rmargin - tsz) / 2 : 0; 125279527Sbapt } 126279527Sbapt 127279527Sbapt /* Horizontal frame at the start of boxed tables. */ 128279527Sbapt 129279527Sbapt if (sp->opts->opts & TBL_OPT_DBOX) 130322249Sbapt tbl_hrule(tp, sp, 3); 131322249Sbapt if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) 132279527Sbapt tbl_hrule(tp, sp, 2); 133241675Suqs } 134241675Suqs 135322249Sbapt /* Set up the columns. */ 136241675Suqs 137322249Sbapt tp->flags |= TERMP_MULTICOL; 138322249Sbapt horiz = 0; 139322249Sbapt switch (sp->pos) { 140322249Sbapt case TBL_SPAN_HORIZ: 141322249Sbapt case TBL_SPAN_DHORIZ: 142322249Sbapt horiz = 1; 143322249Sbapt term_setcol(tp, 1); 144322249Sbapt break; 145322249Sbapt case TBL_SPAN_DATA: 146322249Sbapt term_setcol(tp, sp->opts->cols + 2); 147322249Sbapt coloff = tp->tcol->offset; 148241675Suqs 149322249Sbapt /* Set up a column for a left vertical frame. */ 150279527Sbapt 151322249Sbapt if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || 152322249Sbapt sp->opts->lvert) 153322249Sbapt coloff++; 154322249Sbapt tp->tcol->rmargin = coloff; 155241675Suqs 156322249Sbapt /* Set up the data columns. */ 157322249Sbapt 158241675Suqs dp = sp->first; 159241675Suqs spans = 0; 160279527Sbapt for (ic = 0; ic < sp->opts->cols; ic++) { 161322249Sbapt if (spans == 0) { 162322249Sbapt tp->tcol++; 163322249Sbapt tp->tcol->offset = coloff; 164322249Sbapt } 165322249Sbapt coloff += tp->tbl.cols[ic].width; 166322249Sbapt tp->tcol->rmargin = coloff; 167322249Sbapt if (ic + 1 < sp->opts->cols) 168322249Sbapt coloff += tp->tbl.cols[ic].spacing; 169322249Sbapt if (spans) { 170322249Sbapt spans--; 171322249Sbapt continue; 172322249Sbapt } 173322249Sbapt if (dp == NULL) 174322249Sbapt continue; 175322249Sbapt spans = dp->spans; 176322249Sbapt if (ic || sp->layout->first->pos != TBL_CELL_SPAN) 177322249Sbapt dp = dp->next; 178322249Sbapt } 179261344Suqs 180322249Sbapt /* Set up a column for a right vertical frame. */ 181241675Suqs 182322249Sbapt tp->tcol++; 183322249Sbapt tp->tcol->offset = coloff + 1; 184322249Sbapt tp->tcol->rmargin = tp->maxrmargin; 185241675Suqs 186322249Sbapt /* Spans may have reduced the number of columns. */ 187241675Suqs 188322249Sbapt tp->lasttcol = tp->tcol - tp->tcols; 189322249Sbapt 190322249Sbapt /* Fill the buffers for all data columns. */ 191322249Sbapt 192322249Sbapt tp->tcol = tp->tcols; 193322249Sbapt cp = cpn = sp->layout->first; 194322249Sbapt dp = sp->first; 195322249Sbapt spans = 0; 196322249Sbapt for (ic = 0; ic < sp->opts->cols; ic++) { 197322249Sbapt if (cpn != NULL) { 198322249Sbapt cp = cpn; 199322249Sbapt cpn = cpn->next; 200322249Sbapt } 201322249Sbapt if (spans) { 202322249Sbapt spans--; 203322249Sbapt continue; 204322249Sbapt } 205322249Sbapt tp->tcol++; 206322249Sbapt tp->col = 0; 207322249Sbapt tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); 208322249Sbapt if (dp == NULL) 209322249Sbapt continue; 210322249Sbapt spans = dp->spans; 211322249Sbapt if (cp->pos != TBL_CELL_SPAN) 212322249Sbapt dp = dp->next; 213322249Sbapt } 214322249Sbapt break; 215322249Sbapt } 216322249Sbapt 217322249Sbapt do { 218322249Sbapt /* Print the vertical frame at the start of each row. */ 219322249Sbapt 220322249Sbapt tp->tcol = tp->tcols; 221322249Sbapt fc = '\0'; 222322249Sbapt if (sp->layout->vert || 223322249Sbapt (sp->next != NULL && sp->next->layout->vert && 224322249Sbapt sp->next->pos == TBL_SPAN_DATA) || 225322249Sbapt (sp->prev != NULL && sp->prev->layout->vert && 226322249Sbapt (horiz || (IS_HORIZ(sp->layout->first) && 227322249Sbapt !IS_HORIZ(sp->prev->layout->first)))) || 228322249Sbapt sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)) 229322249Sbapt fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|'; 230322249Sbapt else if (horiz && sp->opts->lvert) 231322249Sbapt fc = '-'; 232322249Sbapt if (fc != '\0') { 233322249Sbapt (*tp->advance)(tp, tp->tcols->offset); 234322249Sbapt (*tp->letter)(tp, fc); 235322249Sbapt tp->viscol = tp->tcol->offset + 1; 236322249Sbapt } 237322249Sbapt 238322249Sbapt /* Print the data cells. */ 239322249Sbapt 240322249Sbapt more = 0; 241322249Sbapt if (horiz) { 242322249Sbapt tbl_hrule(tp, sp, 0); 243322249Sbapt term_flushln(tp); 244322249Sbapt } else { 245322249Sbapt cp = sp->layout->first; 246322249Sbapt cpn = sp->next == NULL ? NULL : 247322249Sbapt sp->next->layout->first; 248322249Sbapt cpp = sp->prev == NULL ? NULL : 249322249Sbapt sp->prev->layout->first; 250322249Sbapt dp = sp->first; 251322249Sbapt spans = 0; 252322249Sbapt for (ic = 0; ic < sp->opts->cols; ic++) { 253322249Sbapt 254322249Sbapt /* 255322249Sbapt * Figure out whether to print a 256322249Sbapt * vertical line after this cell 257322249Sbapt * and advance to next layout cell. 258322249Sbapt */ 259322249Sbapt 260322249Sbapt if (cp != NULL) { 261322249Sbapt vert = cp->vert; 262322249Sbapt switch (cp->pos) { 263322249Sbapt case TBL_CELL_HORIZ: 264322249Sbapt fc = '-'; 265322249Sbapt break; 266322249Sbapt case TBL_CELL_DHORIZ: 267322249Sbapt fc = '='; 268322249Sbapt break; 269322249Sbapt default: 270322249Sbapt fc = ' '; 271322249Sbapt break; 272322249Sbapt } 273322249Sbapt } else { 274322249Sbapt vert = 0; 275322249Sbapt fc = ' '; 276322249Sbapt } 277322249Sbapt if (cpp != NULL) { 278322249Sbapt if (vert == 0 && 279322249Sbapt cp != NULL && 280322249Sbapt ((IS_HORIZ(cp) && 281322249Sbapt !IS_HORIZ(cpp)) || 282322249Sbapt (cp->next != NULL && 283322249Sbapt cpp->next != NULL && 284322249Sbapt IS_HORIZ(cp->next) && 285322249Sbapt !IS_HORIZ(cpp->next)))) 286322249Sbapt vert = cpp->vert; 287322249Sbapt cpp = cpp->next; 288322249Sbapt } 289322249Sbapt if (vert == 0 && 290322249Sbapt sp->opts->opts & TBL_OPT_ALLBOX) 291322249Sbapt vert = 1; 292322249Sbapt if (cpn != NULL) { 293322249Sbapt if (vert == 0) 294322249Sbapt vert = cpn->vert; 295322249Sbapt cpn = cpn->next; 296322249Sbapt } 297322249Sbapt if (cp != NULL) 298322249Sbapt cp = cp->next; 299322249Sbapt 300322249Sbapt /* 301322249Sbapt * Skip later cells in a span, 302322249Sbapt * figure out whether to start a span, 303322249Sbapt * and advance to next data cell. 304322249Sbapt */ 305322249Sbapt 306322249Sbapt if (spans) { 307322249Sbapt spans--; 308322249Sbapt continue; 309322249Sbapt } 310279527Sbapt if (dp != NULL) { 311279527Sbapt spans = dp->spans; 312322249Sbapt if (ic || sp->layout->first->pos 313322249Sbapt != TBL_CELL_SPAN) 314322249Sbapt dp = dp->next; 315279527Sbapt } 316241675Suqs 317322249Sbapt /* 318322249Sbapt * Print one line of text in the cell 319322249Sbapt * and remember whether there is more. 320322249Sbapt */ 321241675Suqs 322322249Sbapt tp->tcol++; 323322249Sbapt if (tp->tcol->col < tp->tcol->lastcol) 324322249Sbapt term_flushln(tp); 325322249Sbapt if (tp->tcol->col < tp->tcol->lastcol) 326322249Sbapt more = 1; 327279527Sbapt 328322249Sbapt /* 329322249Sbapt * Vertical frames between data cells, 330322249Sbapt * but not after the last column. 331322249Sbapt */ 332322249Sbapt 333322249Sbapt if (fc == ' ' && ((vert == 0 && 334322249Sbapt (cp == NULL || !IS_HORIZ(cp))) || 335322249Sbapt tp->tcol + 1 == tp->tcols + tp->lasttcol)) 336322249Sbapt continue; 337322249Sbapt 338322249Sbapt if (tp->viscol < tp->tcol->rmargin) { 339322249Sbapt (*tp->advance)(tp, tp->tcol->rmargin 340322249Sbapt - tp->viscol); 341322249Sbapt tp->viscol = tp->tcol->rmargin; 342322249Sbapt } 343322249Sbapt while (tp->viscol < tp->tcol->rmargin + 344322249Sbapt tp->tbl.cols[ic].spacing / 2) { 345322249Sbapt (*tp->letter)(tp, fc); 346322249Sbapt tp->viscol++; 347322249Sbapt } 348322249Sbapt 349322249Sbapt if (tp->tcol + 1 == tp->tcols + tp->lasttcol) 350322249Sbapt continue; 351322249Sbapt 352322249Sbapt if (fc == ' ' && cp != NULL) { 353322249Sbapt switch (cp->pos) { 354322249Sbapt case TBL_CELL_HORIZ: 355322249Sbapt fc = '-'; 356322249Sbapt break; 357322249Sbapt case TBL_CELL_DHORIZ: 358322249Sbapt fc = '='; 359322249Sbapt break; 360322249Sbapt default: 361322249Sbapt break; 362322249Sbapt } 363322249Sbapt } 364322249Sbapt if (tp->tbl.cols[ic].spacing) { 365322249Sbapt (*tp->letter)(tp, fc == ' ' ? '|' : 366322249Sbapt vert ? '+' : fc); 367322249Sbapt tp->viscol++; 368322249Sbapt } 369322249Sbapt 370322249Sbapt if (fc != ' ') { 371322249Sbapt if (cp != NULL && 372322249Sbapt cp->pos == TBL_CELL_HORIZ) 373322249Sbapt fc = '-'; 374322249Sbapt else if (cp != NULL && 375322249Sbapt cp->pos == TBL_CELL_DHORIZ) 376322249Sbapt fc = '='; 377322249Sbapt else 378322249Sbapt fc = ' '; 379322249Sbapt } 380322249Sbapt if (tp->tbl.cols[ic].spacing > 2 && 381322249Sbapt (vert > 1 || fc != ' ')) { 382322249Sbapt (*tp->letter)(tp, fc == ' ' ? '|' : 383322249Sbapt vert > 1 ? '+' : fc); 384322249Sbapt tp->viscol++; 385322249Sbapt } 386322249Sbapt } 387241675Suqs } 388241675Suqs 389322249Sbapt /* Print the vertical frame at the end of each row. */ 390241675Suqs 391322249Sbapt fc = '\0'; 392322249Sbapt if ((sp->layout->last->vert && 393322249Sbapt sp->layout->last->col + 1 == sp->opts->cols) || 394322249Sbapt (sp->next != NULL && 395322249Sbapt sp->next->layout->last->vert && 396322249Sbapt sp->next->layout->last->col + 1 == sp->opts->cols) || 397322249Sbapt (sp->prev != NULL && 398322249Sbapt sp->prev->layout->last->vert && 399322249Sbapt sp->prev->layout->last->col + 1 == sp->opts->cols && 400322249Sbapt (horiz || (IS_HORIZ(sp->layout->last) && 401322249Sbapt !IS_HORIZ(sp->prev->layout->last)))) || 402322249Sbapt (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))) 403322249Sbapt fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|'; 404322249Sbapt else if (horiz && sp->opts->rvert) 405322249Sbapt fc = '-'; 406322249Sbapt if (fc != '\0') { 407322249Sbapt if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 || 408322249Sbapt sp->layout->last->col + 1 < sp->opts->cols)) { 409322249Sbapt tp->tcol++; 410322249Sbapt (*tp->advance)(tp, 411322249Sbapt tp->tcol->offset > tp->viscol ? 412322249Sbapt tp->tcol->offset - tp->viscol : 1); 413322249Sbapt } 414322249Sbapt (*tp->letter)(tp, fc); 415322249Sbapt } 416322249Sbapt (*tp->endline)(tp); 417322249Sbapt tp->viscol = 0; 418322249Sbapt } while (more); 419241675Suqs 420241675Suqs /* 421322249Sbapt * Clean up after this row. If it is the last line 422322249Sbapt * of the table, print the box line and clean up 423322249Sbapt * column data; otherwise, print the allbox line. 424241675Suqs */ 425241675Suqs 426322249Sbapt term_setcol(tp, 1); 427322249Sbapt tp->flags &= ~TERMP_MULTICOL; 428322249Sbapt tp->tcol->rmargin = tp->maxrmargin; 429279527Sbapt if (sp->next == NULL) { 430279527Sbapt if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { 431322249Sbapt tbl_hrule(tp, sp, 2); 432261344Suqs tp->skipvsp = 1; 433261344Suqs } 434279527Sbapt if (sp->opts->opts & TBL_OPT_DBOX) { 435322249Sbapt tbl_hrule(tp, sp, 3); 436261344Suqs tp->skipvsp = 2; 437261344Suqs } 438241675Suqs assert(tp->tbl.cols); 439241675Suqs free(tp->tbl.cols); 440241675Suqs tp->tbl.cols = NULL; 441322249Sbapt tp->tcol->offset = offset; 442322249Sbapt } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX && 443322249Sbapt (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA || 444322249Sbapt sp->next->next != NULL)) 445322249Sbapt tbl_hrule(tp, sp, 1); 446241675Suqs 447241675Suqs tp->flags &= ~TERMP_NONOSPACE; 448241675Suqs} 449241675Suqs 450241675Suqs/* 451279527Sbapt * Kinds of horizontal rulers: 452279527Sbapt * 0: inside the table (single or double line with crossings) 453322249Sbapt * 1: inside the table (single or double line with crossings and ends) 454322249Sbapt * 2: inner frame (single line with crossings and ends) 455322249Sbapt * 3: outer frame (single line without crossings with ends) 456241675Suqs */ 457241675Suqsstatic void 458279527Sbapttbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind) 459241675Suqs{ 460322249Sbapt const struct tbl_cell *cp, *cpn, *cpp; 461322249Sbapt const struct roffcol *col; 462279527Sbapt int vert; 463279527Sbapt char line, cross; 464241675Suqs 465322249Sbapt line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-'; 466322249Sbapt cross = (kind < 3) ? '+' : '-'; 467241675Suqs 468279527Sbapt if (kind) 469279527Sbapt term_word(tp, "+"); 470322249Sbapt cp = sp->layout->first; 471322249Sbapt cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first; 472322249Sbapt if (cpp == cp) 473322249Sbapt cpp = NULL; 474322249Sbapt cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first; 475322249Sbapt if (cpn == cp) 476322249Sbapt cpn = NULL; 477279527Sbapt for (;;) { 478322249Sbapt col = tp->tbl.cols + cp->col; 479322249Sbapt tbl_char(tp, line, col->width + col->spacing / 2); 480322249Sbapt vert = cp->vert; 481322249Sbapt if ((cp = cp->next) == NULL) 482279527Sbapt break; 483322249Sbapt if (cpp != NULL) { 484322249Sbapt if (vert < cpp->vert) 485322249Sbapt vert = cpp->vert; 486322249Sbapt cpp = cpp->next; 487279527Sbapt } 488322249Sbapt if (cpn != NULL) { 489322249Sbapt if (vert < cpn->vert) 490322249Sbapt vert = cpn->vert; 491322249Sbapt cpn = cpn->next; 492322249Sbapt } 493322249Sbapt if (sp->opts->opts & TBL_OPT_ALLBOX && !vert) 494322249Sbapt vert = 1; 495322249Sbapt if (col->spacing) 496322249Sbapt tbl_char(tp, vert ? cross : line, 1); 497322249Sbapt if (col->spacing > 2) 498322249Sbapt tbl_char(tp, vert > 1 ? cross : line, 1); 499322249Sbapt if (col->spacing > 4) 500322249Sbapt tbl_char(tp, line, (col->spacing - 3) / 2); 501261344Suqs } 502279527Sbapt if (kind) { 503279527Sbapt term_word(tp, "+"); 504279527Sbapt term_flushln(tp); 505261344Suqs } 506241675Suqs} 507241675Suqs 508241675Suqsstatic void 509261344Suqstbl_data(struct termp *tp, const struct tbl_opts *opts, 510322249Sbapt const struct tbl_cell *cp, const struct tbl_dat *dp, 511322249Sbapt const struct roffcol *col) 512241675Suqs{ 513322249Sbapt switch (cp->pos) { 514322249Sbapt case TBL_CELL_HORIZ: 515322249Sbapt tbl_char(tp, '-', col->width); 516241675Suqs return; 517322249Sbapt case TBL_CELL_DHORIZ: 518322249Sbapt tbl_char(tp, '=', col->width); 519322249Sbapt return; 520322249Sbapt default: 521322249Sbapt break; 522241675Suqs } 523241675Suqs 524322249Sbapt if (dp == NULL) 525322249Sbapt return; 526322249Sbapt 527241675Suqs switch (dp->pos) { 528274880Sbapt case TBL_DATA_NONE: 529241675Suqs return; 530274880Sbapt case TBL_DATA_HORIZ: 531274880Sbapt case TBL_DATA_NHORIZ: 532241675Suqs tbl_char(tp, '-', col->width); 533241675Suqs return; 534274880Sbapt case TBL_DATA_NDHORIZ: 535274880Sbapt case TBL_DATA_DHORIZ: 536241675Suqs tbl_char(tp, '=', col->width); 537241675Suqs return; 538241675Suqs default: 539241675Suqs break; 540241675Suqs } 541274880Sbapt 542322249Sbapt switch (cp->pos) { 543274880Sbapt case TBL_CELL_LONG: 544274880Sbapt case TBL_CELL_CENTRE: 545274880Sbapt case TBL_CELL_LEFT: 546274880Sbapt case TBL_CELL_RIGHT: 547241675Suqs tbl_literal(tp, dp, col); 548241675Suqs break; 549274880Sbapt case TBL_CELL_NUMBER: 550261344Suqs tbl_number(tp, opts, dp, col); 551241675Suqs break; 552274880Sbapt case TBL_CELL_DOWN: 553322249Sbapt case TBL_CELL_SPAN: 554241675Suqs break; 555241675Suqs default: 556241675Suqs abort(); 557241675Suqs } 558241675Suqs} 559241675Suqs 560241675Suqsstatic void 561241675Suqstbl_char(struct termp *tp, char c, size_t len) 562241675Suqs{ 563241675Suqs size_t i, sz; 564241675Suqs char cp[2]; 565241675Suqs 566241675Suqs cp[0] = c; 567241675Suqs cp[1] = '\0'; 568241675Suqs 569241675Suqs sz = term_strlen(tp, cp); 570241675Suqs 571241675Suqs for (i = 0; i < len; i += sz) 572241675Suqs term_word(tp, cp); 573241675Suqs} 574241675Suqs 575241675Suqsstatic void 576274880Sbapttbl_literal(struct termp *tp, const struct tbl_dat *dp, 577241675Suqs const struct roffcol *col) 578241675Suqs{ 579279527Sbapt size_t len, padl, padr, width; 580279527Sbapt int ic, spans; 581241675Suqs 582241675Suqs assert(dp->string); 583241675Suqs len = term_strlen(tp, dp->string); 584261344Suqs width = col->width; 585279527Sbapt ic = dp->layout->col; 586279527Sbapt spans = dp->spans; 587279527Sbapt while (spans--) 588279527Sbapt width += tp->tbl.cols[++ic].width + 3; 589261344Suqs 590261344Suqs padr = width > len ? width - len : 0; 591241675Suqs padl = 0; 592241675Suqs 593241675Suqs switch (dp->layout->pos) { 594274880Sbapt case TBL_CELL_LONG: 595241675Suqs padl = term_len(tp, 1); 596241675Suqs padr = padr > padl ? padr - padl : 0; 597241675Suqs break; 598274880Sbapt case TBL_CELL_CENTRE: 599241675Suqs if (2 > padr) 600241675Suqs break; 601241675Suqs padl = padr / 2; 602241675Suqs padr -= padl; 603241675Suqs break; 604274880Sbapt case TBL_CELL_RIGHT: 605241675Suqs padl = padr; 606241675Suqs padr = 0; 607241675Suqs break; 608241675Suqs default: 609241675Suqs break; 610241675Suqs } 611241675Suqs 612241675Suqs tbl_char(tp, ASCII_NBRSP, padl); 613275432Sbapt tbl_word(tp, dp); 614241675Suqs tbl_char(tp, ASCII_NBRSP, padr); 615241675Suqs} 616241675Suqs 617241675Suqsstatic void 618261344Suqstbl_number(struct termp *tp, const struct tbl_opts *opts, 619241675Suqs const struct tbl_dat *dp, 620241675Suqs const struct roffcol *col) 621241675Suqs{ 622241675Suqs char *cp; 623241675Suqs char buf[2]; 624241675Suqs size_t sz, psz, ssz, d, padl; 625241675Suqs int i; 626241675Suqs 627241675Suqs /* 628241675Suqs * See calc_data_number(). Left-pad by taking the offset of our 629241675Suqs * and the maximum decimal; right-pad by the remaining amount. 630241675Suqs */ 631241675Suqs 632241675Suqs assert(dp->string); 633241675Suqs 634241675Suqs sz = term_strlen(tp, dp->string); 635241675Suqs 636261344Suqs buf[0] = opts->decimal; 637241675Suqs buf[1] = '\0'; 638241675Suqs 639241675Suqs psz = term_strlen(tp, buf); 640241675Suqs 641279527Sbapt if ((cp = strrchr(dp->string, opts->decimal)) != NULL) { 642241675Suqs for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { 643241675Suqs buf[0] = dp->string[i]; 644241675Suqs ssz += term_strlen(tp, buf); 645241675Suqs } 646241675Suqs d = ssz + psz; 647241675Suqs } else 648241675Suqs d = sz + psz; 649241675Suqs 650279527Sbapt if (col->decimal > d && col->width > sz) { 651279527Sbapt padl = col->decimal - d; 652279527Sbapt if (padl + sz > col->width) 653279527Sbapt padl = col->width - sz; 654279527Sbapt tbl_char(tp, ASCII_NBRSP, padl); 655279527Sbapt } else 656279527Sbapt padl = 0; 657275432Sbapt tbl_word(tp, dp); 658241675Suqs if (col->width > sz + padl) 659241675Suqs tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); 660241675Suqs} 661241675Suqs 662275432Sbaptstatic void 663275432Sbapttbl_word(struct termp *tp, const struct tbl_dat *dp) 664275432Sbapt{ 665279527Sbapt int prev_font; 666275432Sbapt 667279527Sbapt prev_font = tp->fonti; 668275432Sbapt if (dp->layout->flags & TBL_CELL_BOLD) 669275432Sbapt term_fontpush(tp, TERMFONT_BOLD); 670275432Sbapt else if (dp->layout->flags & TBL_CELL_ITALIC) 671275432Sbapt term_fontpush(tp, TERMFONT_UNDER); 672275432Sbapt 673275432Sbapt term_word(tp, dp->string); 674275432Sbapt 675275432Sbapt term_fontpopq(tp, prev_font); 676275432Sbapt} 677