=================================================================== RCS file: /cvs/mandoc/tbl_term.c,v retrieving revision 1.17 retrieving revision 1.49 diff -u -p -r1.17 -r1.49 --- mandoc/tbl_term.c 2011/01/10 14:56:06 1.17 +++ mandoc/tbl_term.c 2017/06/12 20:45:18 1.49 @@ -1,6 +1,7 @@ -/* $Id: tbl_term.c,v 1.17 2011/01/10 14:56:06 kristaps Exp $ */ +/* $Id: tbl_term.c,v 1.49 2017/06/12 20:45:18 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2011 Kristaps Dzonsons + * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,10 +15,10 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include @@ -29,313 +30,381 @@ static size_t term_tbl_len(size_t, void *); static size_t term_tbl_strlen(const char *, void *); +static size_t term_tbl_sulen(const struct roffsu *, void *); static void tbl_char(struct termp *, char, size_t); -static void tbl_data(struct termp *, const struct tbl *, - const struct tbl_dat *, +static void tbl_data(struct termp *, const struct tbl_opts *, + const struct tbl_dat *, const struct roffcol *); -static void tbl_hframe(struct termp *, const struct tbl_span *); -static void tbl_literal(struct termp *, const struct tbl_dat *, +static void tbl_literal(struct termp *, const struct tbl_dat *, const struct roffcol *); -static void tbl_number(struct termp *, const struct tbl *, - const struct tbl_dat *, +static void tbl_number(struct termp *, const struct tbl_opts *, + const struct tbl_dat *, const struct roffcol *); -static void tbl_hrule(struct termp *, const struct tbl_span *); -static void tbl_vframe(struct termp *, const struct tbl *); -static void tbl_vrule(struct termp *, const struct tbl_head *); +static void tbl_hrule(struct termp *, const struct tbl_span *, int); +static void tbl_word(struct termp *, const struct tbl_dat *); static size_t -term_tbl_strlen(const char *p, void *arg) +term_tbl_sulen(const struct roffsu *su, void *arg) { + return term_hspan((const struct termp *)arg, su) / 24; +} - return(term_strlen((const struct termp *)arg, p)); +static size_t +term_tbl_strlen(const char *p, void *arg) +{ + return term_strlen((const struct termp *)arg, p); } static size_t term_tbl_len(size_t sz, void *arg) { - - return(term_len((const struct termp *)arg, sz)); + return term_len((const struct termp *)arg, sz); } void term_tbl(struct termp *tp, const struct tbl_span *sp) { - const struct tbl_head *hp; + const struct tbl_cell *cp; const struct tbl_dat *dp; - struct roffcol *col; - int spans; - size_t rmargin, maxrmargin; + static size_t offset; + size_t coloff, tsz; + int ic, horiz, spans, vert, more; + char fc; - rmargin = tp->rmargin; - maxrmargin = tp->maxrmargin; - - tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN; - /* Inhibit printing of spaces: we do padding ourselves. */ - tp->flags |= TERMP_NONOSPACE; - tp->flags |= TERMP_NOSPACE; + tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE; /* * The first time we're invoked for a given table block, * calculate the table widths and decimal positions. */ - if (TBL_SPAN_FIRST & sp->flags) { - term_flushln(tp); - + if (tp->tbl.cols == NULL) { tp->tbl.len = term_tbl_len; tp->tbl.slen = term_tbl_strlen; + tp->tbl.sulen = term_tbl_sulen; tp->tbl.arg = tp; - tblcalc(&tp->tbl, sp); - } + tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); - /* Horizontal frame at the start of boxed tables. */ + /* Center the table as a whole. */ - if (TBL_SPAN_FIRST & sp->flags) - tbl_hframe(tp, sp); + offset = tp->tcol->offset; + if (sp->opts->opts & TBL_OPT_CENTRE) { + tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) + ? 2 : !!sp->opts->lvert + !!sp->opts->rvert; + for (ic = 0; ic < sp->opts->cols; ic++) + tsz += tp->tbl.cols[ic].width + 3; + tsz -= 3; + if (offset + tsz > tp->tcol->rmargin) + tsz -= 1; + tp->tcol->offset = offset + tp->tcol->rmargin > tsz ? + (offset + tp->tcol->rmargin - tsz) / 2 : 0; + } - /* Vertical frame at the start of each row. */ + /* Horizontal frame at the start of boxed tables. */ - tbl_vframe(tp, sp->tbl); + if (sp->opts->opts & TBL_OPT_DBOX) + tbl_hrule(tp, sp, 2); + if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) + tbl_hrule(tp, sp, 1); + } - /* - * Now print the actual data itself depending on the span type. - * Spanner spans get a horizontal rule; data spanners have their - * data printed by matching data to header. - */ + /* Set up the columns. */ + tp->flags |= TERMP_MULTICOL; + horiz = 0; switch (sp->pos) { - case (TBL_SPAN_HORIZ): - /* FALLTHROUGH */ - case (TBL_SPAN_DHORIZ): - tbl_hrule(tp, sp); + case TBL_SPAN_HORIZ: + case TBL_SPAN_DHORIZ: + horiz = 1; + term_setcol(tp, 1); break; - case (TBL_SPAN_DATA): - /* Iterate over template headers. */ + case TBL_SPAN_DATA: + term_setcol(tp, sp->opts->cols + 2); + coloff = tp->tcol->offset; + + /* Set up a column for a left vertical frame. */ + + if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || + sp->opts->lvert) + coloff++; + tp->tcol->rmargin = coloff; + + /* Set up the data columns. */ + dp = sp->first; spans = 0; - for (hp = sp->head; hp; hp = hp->next) { - /* - * If the current data header is invoked during - * a spanner ("spans" > 0), don't emit anything - * at all. - */ - switch (hp->pos) { - case (TBL_HEAD_VERT): - /* FALLTHROUGH */ - case (TBL_HEAD_DVERT): - if (spans <= 0) - tbl_vrule(tp, hp); + for (ic = 0; ic < sp->opts->cols; ic++) { + if (spans == 0) { + tp->tcol++; + tp->tcol->offset = coloff; + } + coloff += tp->tbl.cols[ic].width; + tp->tcol->rmargin = coloff; + coloff++; + if (ic + 1 < sp->opts->cols) + coloff += 2; + if (spans) { + spans--; continue; - case (TBL_HEAD_DATA): - break; } - - if (--spans >= 0) + if (dp == NULL) continue; + spans = dp->spans; + dp = dp->next; + } - col = &tp->tbl.cols[hp->ident]; - tbl_data(tp, sp->tbl, dp, col); + /* Set up a column for a right vertical frame. */ - /* - * Go to the next data cell and assign the - * number of subsequent spans, if applicable. - */ + tp->tcol++; + tp->tcol->offset = coloff; + if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || + sp->opts->rvert) + coloff++; + tp->tcol->rmargin = coloff; - if (dp) { - spans = dp->spans; - dp = dp->next; + /* Spans may have reduced the number of columns. */ + + tp->lasttcol = tp->tcol - tp->tcols; + + /* Fill the buffers for all data columns. */ + + tp->tcol = tp->tcols; + dp = sp->first; + spans = 0; + for (ic = 0; ic < sp->opts->cols; ic++) { + if (spans) { + spans--; + continue; } + tp->tcol++; + tp->col = 0; + tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic); + if (dp == NULL) + continue; + spans = dp->spans; + dp = dp->next; } break; } - tbl_vframe(tp, sp->tbl); - term_flushln(tp); + do { + /* Print the vertical frame at the start of each row. */ + tp->tcol = tp->tcols; + fc = '\0'; + if (sp->layout->vert || + (sp->prev != NULL && sp->prev->layout->vert) || + sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)) + fc = horiz ? '+' : '|'; + else if (horiz && sp->opts->lvert) + fc = '-'; + if (fc != '\0') { + (*tp->advance)(tp, tp->tcols->offset); + (*tp->letter)(tp, fc); + tp->viscol = tp->tcol->offset + 1; + } + + /* Print the data cells. */ + + more = 0; + if (horiz) { + tbl_hrule(tp, sp, 0); + term_flushln(tp); + } else { + cp = sp->layout->first; + dp = sp->first; + spans = 0; + for (ic = 0; ic < sp->opts->cols; ic++) { + if (spans == 0) { + tp->tcol++; + if (dp != NULL) { + spans = dp->spans; + dp = dp->next; + } + if (tp->tcol->col < tp->tcol->lastcol) + term_flushln(tp); + if (tp->tcol->col < tp->tcol->lastcol) + more = 1; + if (tp->tcol + 1 == + tp->tcols + tp->lasttcol) + continue; + } else + spans--; + + /* Vertical frames between data cells. */ + + if (cp != NULL) { + vert = cp->vert; + cp = cp->next; + } else + vert = 0; + if (sp->opts->opts & TBL_OPT_ALLBOX) + vert = 1; + if (vert == 0) + continue; + + if (tp->tcol->rmargin + 1 > tp->viscol) { + (*tp->advance)(tp, tp->tcol->rmargin + + 1 - tp->viscol); + tp->viscol = tp->tcol->rmargin + 1; + } + while (vert--) { + (*tp->letter)(tp, '|'); + tp->viscol++; + } + } + } + + /* Print the vertical frame at the end of each row. */ + + fc = '\0'; + if (sp->layout->last->vert || + (sp->prev != NULL && sp->prev->layout->last->vert) || + (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))) + fc = horiz ? '+' : '|'; + else if (horiz && sp->opts->rvert) + fc = '-'; + if (fc != '\0') { + if (horiz == 0) { + tp->tcol++; + (*tp->advance)(tp, + tp->tcol->offset > tp->viscol ? + tp->tcol->offset - tp->viscol : 1); + } + (*tp->letter)(tp, fc); + } + (*tp->endline)(tp); + tp->viscol = 0; + } while (more); + /* * If we're the last row, clean up after ourselves: clear the * existing table configuration and set it to NULL. */ - if (TBL_SPAN_LAST & sp->flags) { - tbl_hframe(tp, sp); + term_setcol(tp, 1); + tp->flags &= ~TERMP_MULTICOL; + tp->tcol->rmargin = tp->maxrmargin; + if (sp->next == NULL) { + if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { + tbl_hrule(tp, sp, 1); + tp->skipvsp = 1; + } + if (sp->opts->opts & TBL_OPT_DBOX) { + tbl_hrule(tp, sp, 2); + tp->skipvsp = 2; + } assert(tp->tbl.cols); free(tp->tbl.cols); tp->tbl.cols = NULL; - } + tp->tcol->offset = offset; + } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX) + tbl_hrule(tp, sp, 1); tp->flags &= ~TERMP_NONOSPACE; - tp->rmargin = rmargin; - tp->maxrmargin = maxrmargin; - } +/* + * Kinds of horizontal rulers: + * 0: inside the table (single or double line with crossings) + * 1: inner frame (single line with crossings and ends) + * 2: outer frame (single line without crossings with ends) + */ static void -tbl_hrule(struct termp *tp, const struct tbl_span *sp) +tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind) { - const struct tbl_head *hp; - char c; - size_t width; + const struct tbl_cell *c1, *c2; + int vert; + char line, cross; - /* - * An hrule extends across the entire table and is demarked by a - * standalone `_' or whatnot in lieu of a table row. Spanning - * headers are marked by a `+', as are table boundaries. - */ + line = (kind == 0 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-'; + cross = (kind < 2) ? '+' : '-'; - c = '-'; - if (TBL_SPAN_DHORIZ == sp->pos) - c = '='; - - /* FIXME: don't use `+' between data and a spanner! */ - - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - switch (hp->pos) { - case (TBL_HEAD_DATA): - tbl_char(tp, c, width); - break; - case (TBL_HEAD_DVERT): - tbl_char(tp, '+', width); - /* FALLTHROUGH */ - case (TBL_HEAD_VERT): - tbl_char(tp, '+', width); - break; - default: - abort(); - /* NOTREACHED */ + if (kind) + term_word(tp, "+"); + c1 = sp->layout->first; + c2 = sp->prev == NULL ? NULL : sp->prev->layout->first; + if (c2 == c1) + c2 = NULL; + for (;;) { + tbl_char(tp, line, tp->tbl.cols[c1->col].width + 1); + vert = c1->vert; + if ((c1 = c1->next) == NULL) + break; + if (c2 != NULL) { + if (vert < c2->vert) + vert = c2->vert; + c2 = c2->next; } + if (sp->opts->opts & TBL_OPT_ALLBOX && !vert) + vert = 1; + if (vert) + tbl_char(tp, cross, vert); + if (vert < 2) + tbl_char(tp, line, 2 - vert); } -} - -static void -tbl_hframe(struct termp *tp, const struct tbl_span *sp) -{ - const struct tbl_head *hp; - size_t width; - - if ( ! (TBL_OPT_BOX & sp->tbl->opts || - TBL_OPT_DBOX & sp->tbl->opts)) - return; - - /* - * Print out the horizontal part of a frame or double frame. A - * double frame has an unbroken `-' outer line the width of the - * table, bordered by `+'. The frame (or inner frame, in the - * case of the double frame) is a `-' bordered by `+' and broken - * by `+' whenever a span is encountered. - */ - - if (TBL_OPT_DBOX & sp->tbl->opts) { + if (kind) { term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - tbl_char(tp, '-', width); - } - term_word(tp, "+"); term_flushln(tp); } - - term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - switch (hp->pos) { - case (TBL_HEAD_DATA): - tbl_char(tp, '-', width); - break; - default: - tbl_char(tp, '+', width); - break; - } - } - term_word(tp, "+"); - term_flushln(tp); } static void -tbl_data(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, - const struct roffcol *col) +tbl_data(struct termp *tp, const struct tbl_opts *opts, + const struct tbl_dat *dp, + const struct roffcol *col) { - if (NULL == dp) { + if (dp == NULL) { tbl_char(tp, ASCII_NBRSP, col->width); return; } - assert(dp->layout); switch (dp->pos) { - case (TBL_DATA_NONE): + case TBL_DATA_NONE: tbl_char(tp, ASCII_NBRSP, col->width); return; - case (TBL_DATA_HORIZ): - /* FALLTHROUGH */ - case (TBL_DATA_NHORIZ): + case TBL_DATA_HORIZ: + case TBL_DATA_NHORIZ: tbl_char(tp, '-', col->width); return; - case (TBL_DATA_NDHORIZ): - /* FALLTHROUGH */ - case (TBL_DATA_DHORIZ): + case TBL_DATA_NDHORIZ: + case TBL_DATA_DHORIZ: tbl_char(tp, '=', col->width); return; default: break; } - + switch (dp->layout->pos) { - case (TBL_CELL_HORIZ): + case TBL_CELL_HORIZ: tbl_char(tp, '-', col->width); break; - case (TBL_CELL_DHORIZ): + case TBL_CELL_DHORIZ: tbl_char(tp, '=', col->width); break; - case (TBL_CELL_LONG): - /* FALLTHROUGH */ - case (TBL_CELL_CENTRE): - /* FALLTHROUGH */ - case (TBL_CELL_LEFT): - /* FALLTHROUGH */ - case (TBL_CELL_RIGHT): + case TBL_CELL_LONG: + case TBL_CELL_CENTRE: + case TBL_CELL_LEFT: + case TBL_CELL_RIGHT: tbl_literal(tp, dp, col); break; - case (TBL_CELL_NUMBER): - tbl_number(tp, tbl, dp, col); + case TBL_CELL_NUMBER: + tbl_number(tp, opts, dp, col); break; + case TBL_CELL_DOWN: + tbl_char(tp, ASCII_NBRSP, col->width); + break; default: abort(); - /* NOTREACHED */ } } static void -tbl_vrule(struct termp *tp, const struct tbl_head *hp) -{ - - switch (hp->pos) { - case (TBL_HEAD_VERT): - term_word(tp, "|"); - break; - case (TBL_HEAD_DVERT): - term_word(tp, "||"); - break; - default: - break; - } -} - -static void -tbl_vframe(struct termp *tp, const struct tbl *tbl) -{ - - if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts) - term_word(tp, "|"); -} - -static void tbl_char(struct termp *tp, char c, size_t len) { size_t i, sz; @@ -351,44 +420,49 @@ tbl_char(struct termp *tp, char c, size_t len) } static void -tbl_literal(struct termp *tp, const struct tbl_dat *dp, +tbl_literal(struct termp *tp, const struct tbl_dat *dp, const struct roffcol *col) { - size_t padl, padr, ssz; + size_t len, padl, padr, width; + int ic, spans; - padl = padr = 0; - assert(dp->string); + len = term_strlen(tp, dp->string); + width = col->width; + ic = dp->layout->col; + spans = dp->spans; + while (spans--) + width += tp->tbl.cols[++ic].width + 3; - ssz = term_len(tp, 1); + padr = width > len ? width - len : 0; + padl = 0; switch (dp->layout->pos) { - case (TBL_CELL_LONG): - padl = ssz; - padr = col->width - term_strlen(tp, dp->string) - ssz; + case TBL_CELL_LONG: + padl = term_len(tp, 1); + padr = padr > padl ? padr - padl : 0; break; - case (TBL_CELL_CENTRE): - padl = col->width - term_strlen(tp, dp->string); - if (padl % 2) - padr++; - padl /= 2; - padr += padl; + case TBL_CELL_CENTRE: + if (2 > padr) + break; + padl = padr / 2; + padr -= padl; break; - case (TBL_CELL_RIGHT): - padl = col->width - term_strlen(tp, dp->string); + case TBL_CELL_RIGHT: + padl = padr; + padr = 0; break; default: - padr = col->width - term_strlen(tp, dp->string); break; } tbl_char(tp, ASCII_NBRSP, padl); - term_word(tp, dp->string); + tbl_word(tp, dp); tbl_char(tp, ASCII_NBRSP, padr); } static void -tbl_number(struct termp *tp, const struct tbl *tbl, +tbl_number(struct termp *tp, const struct tbl_opts *opts, const struct tbl_dat *dp, const struct roffcol *col) { @@ -406,13 +480,12 @@ tbl_number(struct termp *tp, const struct tbl *tbl, sz = term_strlen(tp, dp->string); - buf[0] = tbl->decimal; + buf[0] = opts->decimal; buf[1] = '\0'; psz = term_strlen(tp, buf); - if (NULL != (cp = strrchr(dp->string, tbl->decimal))) { - buf[1] = '\0'; + if ((cp = strrchr(dp->string, opts->decimal)) != NULL) { for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { buf[0] = dp->string[i]; ssz += term_strlen(tp, buf); @@ -421,13 +494,30 @@ tbl_number(struct termp *tp, const struct tbl *tbl, } else d = sz + psz; - sz += term_len(tp, 2); - d += term_len(tp, 1); + if (col->decimal > d && col->width > sz) { + padl = col->decimal - d; + if (padl + sz > col->width) + padl = col->width - sz; + tbl_char(tp, ASCII_NBRSP, padl); + } else + padl = 0; + tbl_word(tp, dp); + if (col->width > sz + padl) + tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); +} - padl = col->decimal - d; +static void +tbl_word(struct termp *tp, const struct tbl_dat *dp) +{ + int prev_font; - tbl_char(tp, ASCII_NBRSP, padl); + prev_font = tp->fonti; + if (dp->layout->flags & TBL_CELL_BOLD) + term_fontpush(tp, TERMFONT_BOLD); + else if (dp->layout->flags & TBL_CELL_ITALIC) + term_fontpush(tp, TERMFONT_UNDER); + term_word(tp, dp->string); - tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); -} + term_fontpopq(tp, prev_font); +}