=================================================================== RCS file: /cvs/mandoc/tbl_term.c,v retrieving revision 1.1 retrieving revision 1.4 diff -u -p -r1.1 -r1.4 --- mandoc/tbl_term.c 2011/01/02 12:21:07 1.1 +++ mandoc/tbl_term.c 2011/01/03 14:57:04 1.4 @@ -1,4 +1,4 @@ -/* $Id: tbl_term.c,v 1.1 2011/01/02 12:21:07 kristaps Exp $ */ +/* $Id: tbl_term.c,v 1.4 2011/01/03 14:57:04 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -35,18 +35,36 @@ static void tbl_hframe(struct termp *, const struct tbl_span *); static void tbl_data_number(struct termp *, const struct tbl *, - const struct tbl_dat *, int); + const struct tbl_dat *, + const struct termp_tbl *); static void tbl_data_literal(struct termp *, - const struct tbl_dat *, int); + const struct tbl_dat *, + const struct termp_tbl *); static void tbl_data_spanner(struct termp *, - const struct tbl_dat *, int); + const struct tbl_dat *, + const struct termp_tbl *); static void tbl_data(struct termp *, const struct tbl *, - const struct tbl_dat *, int); + 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_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 *); void term_tbl(struct termp *tp, const struct tbl_span *sp) @@ -54,54 +72,90 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) const struct tbl_head *hp; const struct tbl_dat *dp; - if (TBL_SPAN_FIRST & sp->flags) + /* 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. + */ + + 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); + } + /* Horizontal frame at the start of boxed tables. */ + if (TBL_SPAN_FIRST & sp->flags) tbl_hframe(tp, sp); - tp->flags |= TERMP_NONOSPACE; - tp->flags |= TERMP_NOSPACE; + /* Vertical frame at the start of each row. */ tbl_vframe(tp, sp->tbl); + /* + * 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); - tbl_vframe(tp, sp->tbl); - term_newln(tp); - tp->flags &= ~TERMP_NONOSPACE; - return; - default: break; - } + case (TBL_SPAN_DATA): + /* Iterate over template headers. */ + dp = sp->first; + for (hp = sp->head; hp; hp = hp->next) { + switch (hp->pos) { + case (TBL_HEAD_VERT): + /* FALLTHROUGH */ + case (TBL_HEAD_DVERT): + tbl_spanner(tp, hp); + continue; + case (TBL_HEAD_DATA): + break; + } + tbl_data(tp, sp->tbl, dp, + &tp->tbl[hp->ident]); - dp = sp->first; - for (hp = sp->head; hp; hp = hp->next) { - switch (hp->pos) { - case (TBL_HEAD_VERT): - /* FALLTHROUGH */ - case (TBL_HEAD_DVERT): - tbl_spanner(tp, hp); - break; - case (TBL_HEAD_DATA): - tbl_data(tp, sp->tbl, dp, hp->width); + /* Go to the next data cell. */ if (dp) dp = dp->next; - break; - default: - abort(); - /* NOTREACHED */ } + break; } tbl_vframe(tp, sp->tbl); term_flushln(tp); - if (TBL_SPAN_LAST & sp->flags) + /* + * 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); + assert(tp->tbl); + free(tp->tbl); + tp->tbl = NULL; + } tp->flags &= ~TERMP_NONOSPACE; @@ -112,6 +166,7 @@ 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 @@ -126,15 +181,16 @@ tbl_hrule(struct termp *tp, const struct tbl_span *sp) /* 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, hp->width); + tbl_char(tp, c, width); break; case (TBL_HEAD_DVERT): - tbl_char(tp, '+', hp->width); + tbl_char(tp, '+', width); /* FALLTHROUGH */ case (TBL_HEAD_VERT): - tbl_char(tp, '+', hp->width); + tbl_char(tp, '+', width); break; default: abort(); @@ -147,6 +203,7 @@ static void tbl_hframe(struct termp *tp, const struct tbl_span *sp) { const struct tbl_head *hp; + int width; if ( ! (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)) @@ -165,20 +222,23 @@ tbl_hframe(struct termp *tp, const struct tbl_span *sp if (TBL_OPT_DBOX & sp->tbl->opts) { term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) - tbl_char(tp, '-', hp->width); + 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, '-', hp->width); + tbl_char(tp, '-', width); break; default: - tbl_char(tp, '+', hp->width); + tbl_char(tp, '+', width); break; } } @@ -188,12 +248,13 @@ tbl_hframe(struct termp *tp, const struct tbl_span *sp static void tbl_data(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, int width) + const struct tbl_dat *dp, + const struct termp_tbl *tbp) { enum tbl_cellt pos; if (NULL == dp) { - tbl_char(tp, ASCII_NBRSP, width); + tbl_char(tp, ASCII_NBRSP, tbp->width); return; } @@ -201,7 +262,7 @@ tbl_data(struct termp *tp, const struct tbl *tbl, case (TBL_DATA_HORIZ): /* FALLTHROUGH */ case (TBL_DATA_DHORIZ): - tbl_data_spanner(tp, dp, width); + tbl_data_spanner(tp, dp, tbp); return; default: break; @@ -213,7 +274,7 @@ tbl_data(struct termp *tp, const struct tbl *tbl, case (TBL_CELL_HORIZ): /* FALLTHROUGH */ case (TBL_CELL_DHORIZ): - tbl_data_spanner(tp, dp, width); + tbl_data_spanner(tp, dp, tbp); break; case (TBL_CELL_LONG): /* FALLTHROUGH */ @@ -222,10 +283,10 @@ tbl_data(struct termp *tp, const struct tbl *tbl, case (TBL_CELL_LEFT): /* FALLTHROUGH */ case (TBL_CELL_RIGHT): - tbl_data_literal(tp, dp, width); + tbl_data_literal(tp, dp, tbp); break; case (TBL_CELL_NUMBER): - tbl_data_number(tp, tbl, dp, width); + tbl_data_number(tp, tbl, dp, tbp); break; default: abort(); @@ -261,28 +322,32 @@ tbl_vframe(struct termp *tp, const struct tbl *tbl) static inline void tbl_char(struct termp *tp, char c, int len) { - int i; + int i, sz; char cp[2]; cp[0] = c; cp[1] = '\0'; - for (i = 0; i < len; i++) + sz = term_strlen(tp, cp); + + for (i = 0; i < len; i += sz) term_word(tp, cp); } static void -tbl_data_spanner(struct termp *tp, const struct tbl_dat *dp, int width) +tbl_data_spanner(struct termp *tp, + const struct tbl_dat *dp, + const struct termp_tbl *tblp) { switch (dp->pos) { case (TBL_DATA_HORIZ): case (TBL_DATA_NHORIZ): - tbl_char(tp, '-', width); + tbl_char(tp, '-', tblp->width); break; case (TBL_DATA_DHORIZ): case (TBL_DATA_NDHORIZ): - tbl_char(tp, '=', width); + tbl_char(tp, '=', tblp->width); break; default: break; @@ -290,32 +355,35 @@ tbl_data_spanner(struct termp *tp, const struct tbl_da } static void -tbl_data_literal(struct termp *tp, const struct tbl_dat *dp, int width) +tbl_data_literal(struct termp *tp, + const struct tbl_dat *dp, + const struct termp_tbl *tblp) { - int padl, padr; + int padl, padr, ssz; enum tbl_cellt pos; padl = padr = 0; pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT; + ssz = term_len(tp, 1); switch (pos) { case (TBL_CELL_LONG): - padl = 1; - padr = width - (int)strlen(dp->string) - 1; + padl = ssz; + padr = tblp->width - term_strlen(tp, dp->string) - ssz; break; case (TBL_CELL_CENTRE): - padl = width - (int)strlen(dp->string); + padl = tblp->width - term_strlen(tp, dp->string); if (padl % 2) padr++; padl /= 2; padr += padl; break; case (TBL_CELL_RIGHT): - padl = width - (int)strlen(dp->string); + padl = tblp->width - term_strlen(tp, dp->string); break; default: - padr = width - (int)strlen(dp->string); + padr = tblp->width - term_strlen(tp, dp->string); break; } @@ -326,9 +394,10 @@ tbl_data_literal(struct termp *tp, const struct tbl_da static void tbl_data_number(struct termp *tp, const struct tbl *tbl, - const struct tbl_dat *dp, int width) + const struct tbl_dat *dp, + const struct termp_tbl *tblp) { - char *decp, pnt; + char *decp; int d, padl, sz; /* @@ -337,22 +406,170 @@ tbl_data_number(struct termp *tp, const struct tbl *tb */ sz = (int)strlen(dp->string); - pnt = tbl->decimal; - if (NULL == (decp = strchr(dp->string, pnt))) { + if (NULL == (decp = strchr(dp->string, tbl->decimal))) { d = sz + 1; } else { d = (int)(decp - dp->string) + 1; } - assert(d <= dp->layout->head->decimal); - assert(sz - d <= dp->layout->head->width - - dp->layout->head->decimal); + assert(d <= tblp->decimal); + assert(sz - d <= tblp->width - tblp->decimal); - padl = dp->layout->head->decimal - d + 1; - assert(width - sz - padl); + padl = tblp->decimal - d + 1; + assert(tblp->width - sz - padl); tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); - tbl_char(tp, ASCII_NBRSP, width - sz - padl); + 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; + char *cp; + + /* + * 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 = (int)strlen(dp->string); + + if (NULL == (cp = strchr(dp->string, tbl->decimal))) + d = sz + 1; + else + d = (int)(cp - dp->string) + 1; + + sz += 2; + + 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; +} + +static void +tbl_calc_data_literal(struct termp *tp, + const struct tbl_dat *dp, + struct termp_tbl *tblp) +{ + int sz, bufsz; + + /* + * 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). + */ + + assert(dp->string); + sz = term_strlen(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; }