=================================================================== RCS file: /cvs/mandoc/tbl_term.c,v retrieving revision 1.6 retrieving revision 1.29 diff -u -p -r1.6 -r1.29 --- mandoc/tbl_term.c 2011/01/03 16:04:41 1.6 +++ mandoc/tbl_term.c 2014/10/13 23:31:46 1.29 @@ -1,6 +1,7 @@ -/* $Id: tbl_term.c,v 1.6 2011/01/03 16:04:41 kristaps Exp $ */ +/* $Id: tbl_term.c,v 1.29 2014/10/13 23:31:46 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2011 Kristaps Dzonsons + * Copyright (c) 2011, 2012, 2014 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 @@ -27,85 +28,88 @@ #include "out.h" #include "term.h" -/* FIXME: `n' modifier doesn't always do the right thing. */ -/* FIXME: `n' modifier doesn't use the cell-spacing buffer. */ +static size_t term_tbl_len(size_t, void *); +static size_t term_tbl_strlen(const char *, void *); +static void tbl_char(struct termp *, char, size_t); +static void tbl_data(struct termp *, const struct tbl_opts *, + const struct tbl_dat *, + const struct roffcol *); +static size_t tbl_rulewidth(struct termp *, const struct tbl_head *); +static void tbl_hframe(struct termp *, const struct tbl_span *, int); +static void tbl_literal(struct termp *, const struct tbl_dat *, + const struct roffcol *); +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_vrule(struct termp *, const struct tbl_head *); +static void tbl_word(struct termp *, const struct tbl_dat *); -static inline void tbl_char(struct termp *, char, int); -static void tbl_hframe(struct termp *, - const struct tbl_span *); -static void tbl_data_number(struct termp *, - const struct tbl *, - const struct tbl_dat *, - const struct termp_tbl *); -static void tbl_data_literal(struct termp *, - const struct tbl_dat *, - const struct termp_tbl *); -static void tbl_data_spanner(struct termp *, - const struct tbl_dat *, - const struct termp_tbl *); -static void tbl_data(struct termp *, const struct tbl *, - const struct tbl_dat *, - const struct termp_tbl *); -static void tbl_spanner(struct termp *, - const struct tbl_head *); -static void tbl_hrule(struct termp *, - const struct tbl_span *); -static void tbl_vframe(struct termp *, - const struct tbl *); -static void tbl_calc(struct termp *, - const struct tbl_span *); -static void tbl_calc_data(struct termp *, - const struct tbl *, - const struct tbl_dat *, - struct termp_tbl *); -static void tbl_calc_data_literal(struct termp *, - const struct tbl_dat *, - struct termp_tbl *); -static void tbl_calc_data_number(struct termp *, - const struct tbl *, - const struct tbl_dat *, - struct termp_tbl *); +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)); +} + void term_tbl(struct termp *tp, const struct tbl_span *sp) { - const struct tbl_head *hp; - const struct tbl_dat *dp; + const struct tbl_head *hp; + const struct tbl_dat *dp; + struct roffcol *col; + int spans; + size_t rmargin, maxrmargin; + 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; /* - * The first time we're invoked for a given table block, create - * the termp_tbl structure. This contains the column - * configuration for the entire table, e.g., table-wide column - * width, decimal point, etc. + * The first time we're invoked for a given table block, + * calculate the table widths and decimal positions. */ if (TBL_SPAN_FIRST & sp->flags) { - assert(NULL == tp->tbl); - tp->tbl = calloc - (sp->tbl->cols, sizeof(struct termp_tbl)); - if (NULL == tp->tbl) { - perror(NULL); - exit(EXIT_FAILURE); - } - tbl_calc(tp, sp); - - /* Flush out any preceding data. */ term_flushln(tp); + + tp->tbl.len = term_tbl_len; + tp->tbl.slen = term_tbl_strlen; + tp->tbl.arg = tp; + + tblcalc(&tp->tbl, sp); } /* Horizontal frame at the start of boxed tables. */ - if (TBL_SPAN_FIRST & sp->flags) - tbl_hframe(tp, sp); + if (TBL_SPAN_FIRST & sp->flags) { + if (TBL_OPT_DBOX & sp->opts->opts) + tbl_hframe(tp, sp, 1); + if (TBL_OPT_DBOX & sp->opts->opts || + TBL_OPT_BOX & sp->opts->opts) + tbl_hframe(tp, sp, 0); + } /* Vertical frame at the start of each row. */ - tbl_vframe(tp, sp->tbl); + if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts || + sp->head->vert) + term_word(tp, TBL_SPAN_HORIZ == sp->pos || + TBL_SPAN_DHORIZ == sp->pos ? "+" : "|"); /* * Now print the actual data itself depending on the span type. @@ -114,35 +118,53 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) */ switch (sp->pos) { - case (TBL_SPAN_HORIZ): + case TBL_SPAN_HORIZ: /* FALLTHROUGH */ - case (TBL_SPAN_DHORIZ): + case TBL_SPAN_DHORIZ: tbl_hrule(tp, sp); break; - case (TBL_SPAN_DATA): + case TBL_SPAN_DATA: /* Iterate over template headers. */ dp = sp->first; + spans = 0; for (hp = sp->head; hp; hp = hp->next) { - switch (hp->pos) { - case (TBL_HEAD_VERT): - /* FALLTHROUGH */ - case (TBL_HEAD_DVERT): - tbl_spanner(tp, hp); + + /* + * If the current data header is invoked during + * a spanner ("spans" > 0), don't emit anything + * at all. + */ + + if (--spans >= 0) continue; - case (TBL_HEAD_DATA): - break; - } - tbl_data(tp, sp->tbl, dp, - &tp->tbl[hp->ident]); - /* Go to the next data cell. */ - if (dp) + /* Separate columns. */ + + if (NULL != hp->prev) + tbl_vrule(tp, hp); + + col = &tp->tbl.cols[hp->ident]; + tbl_data(tp, sp->opts, dp, col); + + /* + * Go to the next data cell and assign the + * number of subsequent spans, if applicable. + */ + + if (dp) { + spans = dp->spans; dp = dp->next; + } } break; } - tbl_vframe(tp, sp->tbl); + /* Vertical frame at the end of each row. */ + + if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts || + sp->layout->vert) + term_word(tp, TBL_SPAN_HORIZ == sp->pos || + TBL_SPAN_DHORIZ == sp->pos ? "+" : " |"); term_flushln(tp); /* @@ -151,179 +173,162 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) */ if (TBL_SPAN_LAST & sp->flags) { - tbl_hframe(tp, sp); - assert(tp->tbl); - free(tp->tbl); - tp->tbl = NULL; + if (TBL_OPT_DBOX & sp->opts->opts || + TBL_OPT_BOX & sp->opts->opts) { + tbl_hframe(tp, sp, 0); + tp->skipvsp = 1; + } + if (TBL_OPT_DBOX & sp->opts->opts) { + tbl_hframe(tp, sp, 1); + tp->skipvsp = 2; + } + assert(tp->tbl.cols); + free(tp->tbl.cols); + tp->tbl.cols = NULL; } tp->flags &= ~TERMP_NONOSPACE; + tp->rmargin = rmargin; + tp->maxrmargin = maxrmargin; } +/* + * Horizontal rules extend across the entire table. + * Calculate the width by iterating over columns. + */ +static size_t +tbl_rulewidth(struct termp *tp, const struct tbl_head *hp) +{ + size_t width; + + width = tp->tbl.cols[hp->ident].width; + + /* Account for leading blanks. */ + if (hp->prev) + width += 2 - hp->vert; + + /* Account for trailing blank. */ + width++; + + return(width); +} + +/* + * Rules inside the table can be single or double + * and have crossings with vertical rules marked with pluses. + */ static void tbl_hrule(struct termp *tp, const struct tbl_span *sp) { const struct tbl_head *hp; char c; - int width; - /* - * 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. - */ - 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[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 (hp->prev && hp->vert) + tbl_char(tp, '+', hp->vert); + tbl_char(tp, c, tbl_rulewidth(tp, hp)); } } +/* + * Rules above and below the table are always single + * and have an additional plus at the beginning and end. + * For double frames, this function is called twice, + * and the outer one does not have crossings. + */ static void -tbl_hframe(struct termp *tp, const struct tbl_span *sp) +tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer) { const struct tbl_head *hp; - int width; - if ( ! (TBL_OPT_BOX & sp->tbl->opts || - TBL_OPT_DBOX & sp->tbl->opts)) - return; - - tp->flags |= TERMP_NONOSPACE; - tp->flags |= TERMP_NOSPACE; - - /* - * 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) { - term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl[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[hp->ident].width; - switch (hp->pos) { - case (TBL_HEAD_DATA): - tbl_char(tp, '-', width); - break; - default: - tbl_char(tp, '+', width); - break; - } + if (hp->prev && hp->vert) + tbl_char(tp, (outer ? '-' : '+'), hp->vert); + tbl_char(tp, '-', tbl_rulewidth(tp, hp)); } term_word(tp, "+"); term_flushln(tp); } static void -tbl_data(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, - const struct termp_tbl *tbp) +tbl_data(struct termp *tp, const struct tbl_opts *opts, + const struct tbl_dat *dp, + const struct roffcol *col) { - enum tbl_cellt pos; if (NULL == dp) { - tbl_char(tp, ASCII_NBRSP, tbp->width); + tbl_char(tp, ASCII_NBRSP, col->width); return; } + assert(dp->layout); switch (dp->pos) { - case (TBL_DATA_HORIZ): + case TBL_DATA_NONE: + tbl_char(tp, ASCII_NBRSP, col->width); + return; + case TBL_DATA_HORIZ: /* FALLTHROUGH */ - case (TBL_DATA_DHORIZ): - tbl_data_spanner(tp, dp, tbp); + case TBL_DATA_NHORIZ: + tbl_char(tp, '-', col->width); return; + case TBL_DATA_NDHORIZ: + /* FALLTHROUGH */ + case TBL_DATA_DHORIZ: + tbl_char(tp, '=', col->width); + return; default: break; } - - pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT; - switch (pos) { - case (TBL_CELL_HORIZ): - /* FALLTHROUGH */ - case (TBL_CELL_DHORIZ): - /* FIXME: THIS IS WRONG. */ - tbl_data_spanner(tp, dp, tbp); + switch (dp->layout->pos) { + case TBL_CELL_HORIZ: + tbl_char(tp, '-', col->width); break; - case (TBL_CELL_LONG): + case TBL_CELL_DHORIZ: + tbl_char(tp, '=', col->width); + break; + case TBL_CELL_LONG: /* FALLTHROUGH */ - case (TBL_CELL_CENTRE): + case TBL_CELL_CENTRE: /* FALLTHROUGH */ - case (TBL_CELL_LEFT): + case TBL_CELL_LEFT: /* FALLTHROUGH */ - case (TBL_CELL_RIGHT): - tbl_data_literal(tp, dp, tbp); + case TBL_CELL_RIGHT: + tbl_literal(tp, dp, col); break; - case (TBL_CELL_NUMBER): - tbl_data_number(tp, tbl, dp, tbp); + 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_spanner(struct termp *tp, const struct tbl_head *hp) +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; - } + tbl_char(tp, ASCII_NBRSP, 1); + if (0 < hp->vert) + tbl_char(tp, '|', hp->vert); + if (2 > hp->vert) + tbl_char(tp, ASCII_NBRSP, 2 - hp->vert); } static void -tbl_vframe(struct termp *tp, const struct tbl *tbl) +tbl_char(struct termp *tp, char c, size_t len) { - /* Always just a single vertical line. */ - - if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts) - term_word(tp, "|"); -} - - -static inline void -tbl_char(struct termp *tp, char c, int len) -{ - int i, sz; + size_t i, sz; char cp[2]; cp[0] = c; @@ -336,199 +341,75 @@ tbl_char(struct termp *tp, char c, int len) } static void -tbl_data_spanner(struct termp *tp, - const struct tbl_dat *dp, - const struct termp_tbl *tblp) +tbl_literal(struct termp *tp, const struct tbl_dat *dp, + const struct roffcol *col) { + struct tbl_head *hp; + size_t width, len, padl, padr; + int spans; - switch (dp->pos) { - case (TBL_DATA_HORIZ): - case (TBL_DATA_NHORIZ): - tbl_char(tp, '-', tblp->width); - break; - case (TBL_DATA_DHORIZ): - case (TBL_DATA_NDHORIZ): - tbl_char(tp, '=', tblp->width); - break; - default: - break; - } -} + assert(dp->string); + len = term_strlen(tp, dp->string); -static void -tbl_data_literal(struct termp *tp, - const struct tbl_dat *dp, - const struct termp_tbl *tblp) -{ - int padl, padr, ssz; - enum tbl_cellt pos; + hp = dp->layout->head->next; + width = col->width; + for (spans = dp->spans; spans--; hp = hp->next) + width += tp->tbl.cols[hp->ident].width + 3; - padl = padr = 0; + padr = width > len ? width - len : 0; + padl = 0; - pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT; - ssz = term_len(tp, 1); - - switch (pos) { - case (TBL_CELL_LONG): - padl = ssz; - padr = tblp->width - term_strlen(tp, dp->string) - ssz; + switch (dp->layout->pos) { + case TBL_CELL_LONG: + padl = term_len(tp, 1); + padr = padr > padl ? padr - padl : 0; break; - case (TBL_CELL_CENTRE): - padl = tblp->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 = tblp->width - term_strlen(tp, dp->string); + case TBL_CELL_RIGHT: + padl = padr; + padr = 0; break; default: - padr = tblp->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_data_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 termp_tbl *tblp) + const struct roffcol *col) { - char *decp, buf[2]; - int d, padl, sz, psz, ssz, i; + char *cp; + char buf[2]; + size_t sz, psz, ssz, d, padl; + int i; /* * See calc_data_number(). Left-pad by taking the offset of our * and the maximum decimal; right-pad by the remaining amount. */ + assert(dp->string); + sz = term_strlen(tp, dp->string); - psz = term_strlen(tp, "."); - if (NULL != (decp = strchr(dp->string, tbl->decimal))) { - buf[1] = '\0'; - for (ssz = i = 0; decp != &dp->string[i]; i++) { - buf[0] = dp->string[i]; - ssz += term_strlen(tp, buf); - } - d = ssz + psz; - } else - d = sz + psz; + buf[0] = opts->decimal; + buf[1] = '\0'; - assert(d <= tblp->decimal); - assert(sz - d <= tblp->width - tblp->decimal); + psz = term_strlen(tp, buf); - padl = tblp->decimal - d + term_len(tp, 1); - assert(tblp->width - sz - padl); - - tbl_char(tp, ASCII_NBRSP, padl); - term_word(tp, dp->string); - tbl_char(tp, ASCII_NBRSP, tblp->width - sz - padl); -} - -static void -tbl_calc(struct termp *tp, const struct tbl_span *sp) -{ - const struct tbl_dat *dp; - const struct tbl_head *hp; - struct termp_tbl *p; - - /* Calculate width as the max of column cells' widths. */ - - hp = sp->head; - - for ( ; sp; sp = sp->next) { - switch (sp->pos) { - case (TBL_DATA_HORIZ): - /* FALLTHROUGH */ - case (TBL_DATA_DHORIZ): - continue; - default: - break; - } - for (dp = sp->first; dp; dp = dp->next) { - if (NULL == dp->layout) - continue; - p = &tp->tbl[dp->layout->head->ident]; - tbl_calc_data(tp, sp->tbl, dp, p); - } - } - - /* Calculate width as the simple spanner value. */ - - for ( ; hp; hp = hp->next) - switch (hp->pos) { - case (TBL_HEAD_VERT): - tp->tbl[hp->ident].width = term_len(tp, 1); - break; - case (TBL_HEAD_DVERT): - tp->tbl[hp->ident].width = term_len(tp, 2); - break; - default: - break; - } -} - -static void -tbl_calc_data(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, struct termp_tbl *tblp) -{ - - /* Branch down into data sub-types. */ - - switch (dp->layout->pos) { - case (TBL_CELL_HORIZ): - /* FALLTHROUGH */ - case (TBL_CELL_DHORIZ): - tblp->width = 1; - break; - case (TBL_CELL_LONG): - /* FALLTHROUGH */ - case (TBL_CELL_CENTRE): - /* FALLTHROUGH */ - case (TBL_CELL_LEFT): - /* FALLTHROUGH */ - case (TBL_CELL_RIGHT): - tbl_calc_data_literal(tp, dp, tblp); - break; - case (TBL_CELL_NUMBER): - tbl_calc_data_number(tp, tbl, dp, tblp); - break; - default: - abort(); - /* NOTREACHED */ - } -} - -static void -tbl_calc_data_number(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, struct termp_tbl *tblp) -{ - int sz, d, psz, i, ssz; - char *cp, buf[2]; - - /* - * First calculate number width and decimal place (last + 1 for - * no-decimal numbers). If the stored decimal is subsequent - * ours, make our size longer by that difference - * (right-"shifting"); similarly, if ours is subsequent the - * stored, then extend the stored size by the difference. - * Finally, re-assign the stored values. - */ - - /* TODO: use spacing modifier. */ - - assert(dp->string); - sz = term_strlen(tp, dp->string); - psz = term_strlen(tp, "."); - - if (NULL != (cp = strchr(dp->string, tbl->decimal))) { + if (NULL != (cp = strrchr(dp->string, opts->decimal))) { buf[1] = '\0'; - for (ssz = i = 0; cp != &dp->string[i]; i++) { + for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { buf[0] = dp->string[i]; ssz += term_strlen(tp, buf); } @@ -536,52 +417,26 @@ tbl_calc_data_number(struct termp *tp, const struct tb } else d = sz + psz; - sz += term_len(tp, 2); + padl = col->decimal - d; - if (tblp->decimal > d) { - sz += tblp->decimal - d; - d = tblp->decimal; - } else - tblp->width += d - tblp->decimal; - - if (sz > tblp->width) - tblp->width = sz; - if (d > tblp->decimal) - tblp->decimal = d; + tbl_char(tp, ASCII_NBRSP, padl); + tbl_word(tp, dp); + if (col->width > sz + padl) + tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); } static void -tbl_calc_data_literal(struct termp *tp, - const struct tbl_dat *dp, - struct termp_tbl *tblp) +tbl_word(struct termp *tp, const struct tbl_dat *dp) { - int sz, bufsz; + const void *prev_font; - /* - * Calculate our width and use the spacing, with a minimum - * spacing dictated by position (centre, e.g,. gets a space on - * either side, while right/left get a single adjacent space). - */ + prev_font = term_fontq(tp); + 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); - assert(dp->string); - sz = term_strlen(tp, dp->string); + term_word(tp, dp->string); - switch (dp->layout->pos) { - case (TBL_CELL_LONG): - /* FALLTHROUGH */ - case (TBL_CELL_CENTRE): - bufsz = 2; - break; - default: - bufsz = 1; - break; - } - - if (dp->layout->spacing) - bufsz = bufsz > dp->layout->spacing ? - bufsz : dp->layout->spacing; - - sz += bufsz; - if (tblp->width < sz) - tblp->width = sz; + term_fontpopq(tp, prev_font); }