static void tbl_literal(struct termp *tp, const struct tbl_dat *dp, const struct roffcol *col) { size_t len, padl, padr; assert(dp->string); len = term_strlen(tp, dp->string); padr = col->width > len ? col->width - len : 0; padl = 0; switch (dp->layout->pos) { case (TBL_CELL_LONG): padl = term_len(tp, 1); padr = padr > padl ? padr - padl : 0; break; case (TBL_CELL_CENTRE): if (2 > padr) break; padl = padr / 2; padr -= padl; break; case (TBL_CELL_RIGHT): padl = padr; padr = 0; break; default: break; } tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); tbl_char(tp, ASCII_NBRSP, padr); }
static void tbl_vrule(struct termp *tp, const struct tbl_head *hp) { 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); }
/* * 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, int outer) { const struct tbl_head *hp; term_word(tp, "+"); for (hp = sp->head; hp; hp = hp->next) { if (hp->prev && hp->vert) tbl_char(tp, (outer ? '-' : '+'), hp->vert); tbl_char(tp, '-', tbl_rulewidth(tp, hp)); } term_word(tp, "+"); term_flushln(tp); }
/* * 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; c = '-'; if (TBL_SPAN_DHORIZ == sp->pos) c = '='; for (hp = sp->head; hp; hp = hp->next) { if (hp->prev && hp->vert) tbl_char(tp, '+', hp->vert); tbl_char(tp, c, tbl_rulewidth(tp, hp)); } }
static void tbl_number(struct termp *tp, const struct tbl *tbl, const struct tbl_dat *dp, const struct roffcol *col) { char *cp; char buf[2]; const char *str; 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. */ str = dp && dp->string ? dp->string : ""; sz = term_strlen(tp, str); buf[0] = tbl->decimal; buf[1] = '\0'; psz = term_strlen(tp, buf); if (NULL != (cp = strchr(str, tbl->decimal))) { buf[1] = '\0'; for (ssz = 0, i = 0; cp != &str[i]; i++) { buf[0] = str[i]; ssz += term_strlen(tp, buf); } d = ssz + psz; } else d = sz + psz; sz += term_len(tp, 2); d += term_len(tp, 1); padl = col->decimal - d; tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, str); tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); }
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) { 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_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; assert(dp->string); len = term_strlen(tp, dp->string); hp = dp->layout->head->next; width = col->width; for (spans = dp->spans; spans--; hp = hp->next) width += tp->tbl.cols[hp->ident].width + 3; padr = width > len ? width - len : 0; padl = 0; switch (dp->layout->pos) { case TBL_CELL_LONG: padl = term_len(tp, 1); padr = padr > padl ? padr - padl : 0; break; case TBL_CELL_CENTRE: if (2 > padr) break; padl = padr / 2; padr -= padl; break; case TBL_CELL_RIGHT: padl = padr; padr = 0; break; default: break; } tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); tbl_char(tp, ASCII_NBRSP, padr); }
static void tbl_data(struct termp *tp, const struct tbl_opts *opts, const struct tbl_dat *dp, const struct roffcol *col) { if (NULL == dp) { tbl_char(tp, ASCII_NBRSP, col->width); return; } assert(dp->layout); switch (dp->pos) { case TBL_DATA_NONE: tbl_char(tp, ASCII_NBRSP, col->width); return; case TBL_DATA_HORIZ: /* FALLTHROUGH */ 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; } switch (dp->layout->pos) { case TBL_CELL_HORIZ: tbl_char(tp, '-', col->width); break; 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: tbl_literal(tp, dp, col); break; 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_number(struct termp *tp, const struct tbl_opts *opts, const struct tbl_dat *dp, const struct roffcol *col) { 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); buf[0] = opts->decimal; buf[1] = '\0'; psz = term_strlen(tp, buf); if (NULL != (cp = strrchr(dp->string, opts->decimal))) { buf[1] = '\0'; for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { buf[0] = dp->string[i]; ssz += term_strlen(tp, buf); } d = ssz + psz; } else d = sz + psz; padl = col->decimal - d; tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); if (col->width > sz + padl) tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); }
/* * 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, int outer) { const struct tbl_head *hp; term_word(tp, "+"); for (hp = sp->head; hp; hp = hp->next) tbl_char(tp, outer || TBL_HEAD_DATA == hp->pos ? '-' : '+', tbl_rulewidth(tp, hp)); term_word(tp, "+"); term_flushln(tp); }
static void tbl_literal(struct termp *tp, const struct tbl_dat *dp, const struct roffcol *col) { size_t padl, padr, ssz; enum tbl_cellt pos; const char *str; padl = padr = 0; pos = dp && dp->layout ? dp->layout->pos : TBL_CELL_LEFT; str = dp && dp->string ? dp->string : ""; ssz = term_len(tp, 1); switch (pos) { case (TBL_CELL_LONG): padl = ssz; padr = col->width - term_strlen(tp, str) - ssz; break; case (TBL_CELL_CENTRE): padl = col->width - term_strlen(tp, str); if (padl % 2) padr++; padl /= 2; padr += padl; break; case (TBL_CELL_RIGHT): padl = col->width - term_strlen(tp, str); break; default: padr = col->width - term_strlen(tp, str); break; } tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, str); tbl_char(tp, ASCII_NBRSP, padr); }
static void tbl_hrule(struct termp *tp, const struct tbl_span *sp) { const struct tbl_head *hp; char c; size_t 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.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 */ } } }
/* * 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; c = '-'; if (TBL_SPAN_DHORIZ == sp->pos) c = '='; for (hp = sp->head; hp; hp = hp->next) tbl_char(tp, TBL_HEAD_DATA == hp->pos ? c : '+', tbl_rulewidth(tp, hp)); }
static void tbl_data(struct termp *tp, const struct tbl *tbl, const struct tbl_dat *dp, const struct roffcol *col) { enum tbl_cellt pos; if (NULL == dp) { tbl_char(tp, ASCII_NBRSP, col->width); return; } switch (dp->pos) { case (TBL_DATA_NONE): tbl_char(tp, ASCII_NBRSP, col->width); return; case (TBL_DATA_HORIZ): /* FALLTHROUGH */ 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 && dp->layout ? dp->layout->pos : TBL_CELL_LEFT; switch (pos) { case (TBL_CELL_HORIZ): tbl_char(tp, '-', col->width); break; 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): tbl_literal(tp, dp, col); break; case (TBL_CELL_NUMBER): tbl_number(tp, tbl, dp, col); break; default: abort(); /* NOTREACHED */ } }
void term_tbl(struct termp *tp, const struct tbl_span *sp) { 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, * calculate the table widths and decimal positions. */ if (TBL_SPAN_FIRST & sp->flags) { 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) { if (TBL_OPT_DBOX & sp->tbl->opts) tbl_hframe(tp, sp, 1); if (TBL_OPT_DBOX & sp->tbl->opts || TBL_OPT_BOX & sp->tbl->opts) tbl_hframe(tp, sp, 0); } /* Vertical frame at the start of each row. */ if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts) term_word(tp, TBL_SPAN_HORIZ == sp->pos || TBL_SPAN_DHORIZ == sp->pos ? "+" : "|"); /* * 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. */ switch (sp->pos) { case (TBL_SPAN_HORIZ): /* FALLTHROUGH */ case (TBL_SPAN_DHORIZ): tbl_hrule(tp, sp); break; case (TBL_SPAN_DATA): /* Iterate over template headers. */ 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); continue; case (TBL_HEAD_DATA): break; } if (--spans >= 0) continue; /* * All cells get a leading blank, except the * first one and those after double rulers. */ if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos) tbl_char(tp, ASCII_NBRSP, 1); col = &tp->tbl.cols[hp->ident]; tbl_data(tp, sp->tbl, dp, col); /* No trailing blanks. */ if (NULL == hp->next) break; /* * Add another blank between cells, * or two when there is no vertical ruler. */ tbl_char(tp, ASCII_NBRSP, TBL_HEAD_VERT == hp->next->pos || TBL_HEAD_DVERT == hp->next->pos ? 1 : 2); /* * Go to the next data cell and assign the * number of subsequent spans, if applicable. */ if (dp) { spans = dp->spans; dp = dp->next; } } break; } /* Vertical frame at the end of each row. */ if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts) term_word(tp, TBL_SPAN_HORIZ == sp->pos || TBL_SPAN_DHORIZ == sp->pos ? "+" : " |"); term_flushln(tp); /* * 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) { if (TBL_OPT_DBOX & sp->tbl->opts || TBL_OPT_BOX & sp->tbl->opts) tbl_hframe(tp, sp, 0); if (TBL_OPT_DBOX & sp->tbl->opts) tbl_hframe(tp, sp, 1); assert(tp->tbl.cols); free(tp->tbl.cols); tp->tbl.cols = NULL; } tp->flags &= ~TERMP_NONOSPACE; tp->rmargin = rmargin; tp->maxrmargin = maxrmargin; }