1*c1c95addSBrooks Davis /* $Id: tbl_term.c,v 1.79 2022/08/28 10:58:31 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin *
661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin *
1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin
2061d06d6bSBaptiste Daroussin #include <sys/types.h>
2161d06d6bSBaptiste Daroussin
2261d06d6bSBaptiste Daroussin #include <assert.h>
237295610fSBaptiste Daroussin #include <ctype.h>
2461d06d6bSBaptiste Daroussin #include <stdio.h>
2561d06d6bSBaptiste Daroussin #include <stdlib.h>
2661d06d6bSBaptiste Daroussin #include <string.h>
2761d06d6bSBaptiste Daroussin
28*c1c95addSBrooks Davis #if DEBUG_MEMORY
29*c1c95addSBrooks Davis #include "mandoc_dbg.h"
30*c1c95addSBrooks Davis #endif
3161d06d6bSBaptiste Daroussin #include "mandoc.h"
327295610fSBaptiste Daroussin #include "tbl.h"
3361d06d6bSBaptiste Daroussin #include "out.h"
3461d06d6bSBaptiste Daroussin #include "term.h"
3561d06d6bSBaptiste Daroussin
3661d06d6bSBaptiste Daroussin #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \
3761d06d6bSBaptiste Daroussin (cp)->pos == TBL_CELL_DHORIZ)
3861d06d6bSBaptiste Daroussin
397295610fSBaptiste Daroussin
4061d06d6bSBaptiste Daroussin static size_t term_tbl_len(size_t, void *);
4161d06d6bSBaptiste Daroussin static size_t term_tbl_strlen(const char *, void *);
4261d06d6bSBaptiste Daroussin static size_t term_tbl_sulen(const struct roffsu *, void *);
4361d06d6bSBaptiste Daroussin static void tbl_data(struct termp *, const struct tbl_opts *,
4461d06d6bSBaptiste Daroussin const struct tbl_cell *,
4561d06d6bSBaptiste Daroussin const struct tbl_dat *,
4661d06d6bSBaptiste Daroussin const struct roffcol *);
477295610fSBaptiste Daroussin static void tbl_direct_border(struct termp *, int, size_t);
487295610fSBaptiste Daroussin static void tbl_fill_border(struct termp *, int, size_t);
497295610fSBaptiste Daroussin static void tbl_fill_char(struct termp *, char, size_t);
507295610fSBaptiste Daroussin static void tbl_fill_string(struct termp *, const char *, size_t);
517295610fSBaptiste Daroussin static void tbl_hrule(struct termp *, const struct tbl_span *,
5245a5aec3SBaptiste Daroussin const struct tbl_span *, const struct tbl_span *,
5345a5aec3SBaptiste Daroussin int);
5461d06d6bSBaptiste Daroussin static void tbl_literal(struct termp *, const struct tbl_dat *,
5561d06d6bSBaptiste Daroussin const struct roffcol *);
5661d06d6bSBaptiste Daroussin static void tbl_number(struct termp *, const struct tbl_opts *,
5761d06d6bSBaptiste Daroussin const struct tbl_dat *,
5861d06d6bSBaptiste Daroussin const struct roffcol *);
5961d06d6bSBaptiste Daroussin static void tbl_word(struct termp *, const struct tbl_dat *);
6061d06d6bSBaptiste Daroussin
6161d06d6bSBaptiste Daroussin
627295610fSBaptiste Daroussin /*
637295610fSBaptiste Daroussin * The following border-character tables are indexed
647295610fSBaptiste Daroussin * by ternary (3-based) numbers, as opposed to binary or decimal.
657295610fSBaptiste Daroussin * Each ternary digit describes the line width in one direction:
667295610fSBaptiste Daroussin * 0 means no line, 1 single or light line, 2 double or heavy line.
677295610fSBaptiste Daroussin */
687295610fSBaptiste Daroussin
697295610fSBaptiste Daroussin /* Positional values of the four directions. */
707295610fSBaptiste Daroussin #define BRIGHT 1
717295610fSBaptiste Daroussin #define BDOWN 3
727295610fSBaptiste Daroussin #define BLEFT (3 * 3)
737295610fSBaptiste Daroussin #define BUP (3 * 3 * 3)
747295610fSBaptiste Daroussin #define BHORIZ (BLEFT + BRIGHT)
757295610fSBaptiste Daroussin
767295610fSBaptiste Daroussin /* Code points to use for each combination of widths. */
777295610fSBaptiste Daroussin static const int borders_utf8[81] = {
787295610fSBaptiste Daroussin 0x0020, 0x2576, 0x257a, /* 000 right */
797295610fSBaptiste Daroussin 0x2577, 0x250c, 0x250d, /* 001 down */
807295610fSBaptiste Daroussin 0x257b, 0x250e, 0x250f, /* 002 */
817295610fSBaptiste Daroussin 0x2574, 0x2500, 0x257c, /* 010 left */
827295610fSBaptiste Daroussin 0x2510, 0x252c, 0x252e, /* 011 left down */
837295610fSBaptiste Daroussin 0x2512, 0x2530, 0x2532, /* 012 */
847295610fSBaptiste Daroussin 0x2578, 0x257e, 0x2501, /* 020 left */
857295610fSBaptiste Daroussin 0x2511, 0x252d, 0x252f, /* 021 left down */
867295610fSBaptiste Daroussin 0x2513, 0x2531, 0x2533, /* 022 */
877295610fSBaptiste Daroussin 0x2575, 0x2514, 0x2515, /* 100 up */
887295610fSBaptiste Daroussin 0x2502, 0x251c, 0x251d, /* 101 up down */
897295610fSBaptiste Daroussin 0x257d, 0x251f, 0x2522, /* 102 */
907295610fSBaptiste Daroussin 0x2518, 0x2534, 0x2536, /* 110 up left */
917295610fSBaptiste Daroussin 0x2524, 0x253c, 0x253e, /* 111 all */
927295610fSBaptiste Daroussin 0x2527, 0x2541, 0x2546, /* 112 */
937295610fSBaptiste Daroussin 0x2519, 0x2535, 0x2537, /* 120 up left */
947295610fSBaptiste Daroussin 0x2525, 0x253d, 0x253f, /* 121 all */
957295610fSBaptiste Daroussin 0x252a, 0x2545, 0x2548, /* 122 */
967295610fSBaptiste Daroussin 0x2579, 0x2516, 0x2517, /* 200 up */
977295610fSBaptiste Daroussin 0x257f, 0x251e, 0x2521, /* 201 up down */
987295610fSBaptiste Daroussin 0x2503, 0x2520, 0x2523, /* 202 */
997295610fSBaptiste Daroussin 0x251a, 0x2538, 0x253a, /* 210 up left */
1007295610fSBaptiste Daroussin 0x2526, 0x2540, 0x2544, /* 211 all */
1017295610fSBaptiste Daroussin 0x2528, 0x2542, 0x254a, /* 212 */
1027295610fSBaptiste Daroussin 0x251b, 0x2539, 0x253b, /* 220 up left */
1037295610fSBaptiste Daroussin 0x2529, 0x2543, 0x2547, /* 221 all */
1047295610fSBaptiste Daroussin 0x252b, 0x2549, 0x254b, /* 222 */
1057295610fSBaptiste Daroussin };
1067295610fSBaptiste Daroussin
1077295610fSBaptiste Daroussin /* ASCII approximations for these code points, compatible with groff. */
1087295610fSBaptiste Daroussin static const int borders_ascii[81] = {
1097295610fSBaptiste Daroussin ' ', '-', '=', /* 000 right */
1107295610fSBaptiste Daroussin '|', '+', '+', /* 001 down */
1117295610fSBaptiste Daroussin '|', '+', '+', /* 002 */
1127295610fSBaptiste Daroussin '-', '-', '=', /* 010 left */
1137295610fSBaptiste Daroussin '+', '+', '+', /* 011 left down */
1147295610fSBaptiste Daroussin '+', '+', '+', /* 012 */
1157295610fSBaptiste Daroussin '=', '=', '=', /* 020 left */
1167295610fSBaptiste Daroussin '+', '+', '+', /* 021 left down */
1177295610fSBaptiste Daroussin '+', '+', '+', /* 022 */
1187295610fSBaptiste Daroussin '|', '+', '+', /* 100 up */
1197295610fSBaptiste Daroussin '|', '+', '+', /* 101 up down */
1207295610fSBaptiste Daroussin '|', '+', '+', /* 102 */
1217295610fSBaptiste Daroussin '+', '+', '+', /* 110 up left */
1227295610fSBaptiste Daroussin '+', '+', '+', /* 111 all */
1237295610fSBaptiste Daroussin '+', '+', '+', /* 112 */
1247295610fSBaptiste Daroussin '+', '+', '+', /* 120 up left */
1257295610fSBaptiste Daroussin '+', '+', '+', /* 121 all */
1267295610fSBaptiste Daroussin '+', '+', '+', /* 122 */
1277295610fSBaptiste Daroussin '|', '+', '+', /* 200 up */
1287295610fSBaptiste Daroussin '|', '+', '+', /* 201 up down */
1297295610fSBaptiste Daroussin '|', '+', '+', /* 202 */
1307295610fSBaptiste Daroussin '+', '+', '+', /* 210 up left */
1317295610fSBaptiste Daroussin '+', '+', '+', /* 211 all */
1327295610fSBaptiste Daroussin '+', '+', '+', /* 212 */
1337295610fSBaptiste Daroussin '+', '+', '+', /* 220 up left */
1347295610fSBaptiste Daroussin '+', '+', '+', /* 221 all */
1357295610fSBaptiste Daroussin '+', '+', '+', /* 222 */
1367295610fSBaptiste Daroussin };
1377295610fSBaptiste Daroussin
1387295610fSBaptiste Daroussin /* Either of the above according to the selected output encoding. */
1397295610fSBaptiste Daroussin static const int *borders_locale;
1407295610fSBaptiste Daroussin
1417295610fSBaptiste Daroussin
14261d06d6bSBaptiste Daroussin static size_t
term_tbl_sulen(const struct roffsu * su,void * arg)14361d06d6bSBaptiste Daroussin term_tbl_sulen(const struct roffsu *su, void *arg)
14461d06d6bSBaptiste Daroussin {
14561d06d6bSBaptiste Daroussin int i;
14661d06d6bSBaptiste Daroussin
14761d06d6bSBaptiste Daroussin i = term_hen((const struct termp *)arg, su);
14861d06d6bSBaptiste Daroussin return i > 0 ? i : 0;
14961d06d6bSBaptiste Daroussin }
15061d06d6bSBaptiste Daroussin
15161d06d6bSBaptiste Daroussin static size_t
term_tbl_strlen(const char * p,void * arg)15261d06d6bSBaptiste Daroussin term_tbl_strlen(const char *p, void *arg)
15361d06d6bSBaptiste Daroussin {
15461d06d6bSBaptiste Daroussin return term_strlen((const struct termp *)arg, p);
15561d06d6bSBaptiste Daroussin }
15661d06d6bSBaptiste Daroussin
15761d06d6bSBaptiste Daroussin static size_t
term_tbl_len(size_t sz,void * arg)15861d06d6bSBaptiste Daroussin term_tbl_len(size_t sz, void *arg)
15961d06d6bSBaptiste Daroussin {
16061d06d6bSBaptiste Daroussin return term_len((const struct termp *)arg, sz);
16161d06d6bSBaptiste Daroussin }
16261d06d6bSBaptiste Daroussin
1637295610fSBaptiste Daroussin
16461d06d6bSBaptiste Daroussin void
term_tbl(struct termp * tp,const struct tbl_span * sp)16561d06d6bSBaptiste Daroussin term_tbl(struct termp *tp, const struct tbl_span *sp)
16661d06d6bSBaptiste Daroussin {
1677295610fSBaptiste Daroussin const struct tbl_cell *cp, *cpn, *cpp, *cps;
16861d06d6bSBaptiste Daroussin const struct tbl_dat *dp;
16961d06d6bSBaptiste Daroussin static size_t offset;
1707295610fSBaptiste Daroussin size_t save_offset;
17161d06d6bSBaptiste Daroussin size_t coloff, tsz;
1727295610fSBaptiste Daroussin int hspans, ic, more;
1737295610fSBaptiste Daroussin int dvert, fc, horiz, lhori, rhori, uvert;
17461d06d6bSBaptiste Daroussin
17561d06d6bSBaptiste Daroussin /* Inhibit printing of spaces: we do padding ourselves. */
17661d06d6bSBaptiste Daroussin
17761d06d6bSBaptiste Daroussin tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
1787295610fSBaptiste Daroussin save_offset = tp->tcol->offset;
17961d06d6bSBaptiste Daroussin
18061d06d6bSBaptiste Daroussin /*
18161d06d6bSBaptiste Daroussin * The first time we're invoked for a given table block,
18261d06d6bSBaptiste Daroussin * calculate the table widths and decimal positions.
18361d06d6bSBaptiste Daroussin */
18461d06d6bSBaptiste Daroussin
18561d06d6bSBaptiste Daroussin if (tp->tbl.cols == NULL) {
1867295610fSBaptiste Daroussin borders_locale = tp->enc == TERMENC_UTF8 ?
1877295610fSBaptiste Daroussin borders_utf8 : borders_ascii;
1887295610fSBaptiste Daroussin
18961d06d6bSBaptiste Daroussin tp->tbl.len = term_tbl_len;
19061d06d6bSBaptiste Daroussin tp->tbl.slen = term_tbl_strlen;
19161d06d6bSBaptiste Daroussin tp->tbl.sulen = term_tbl_sulen;
19261d06d6bSBaptiste Daroussin tp->tbl.arg = tp;
19361d06d6bSBaptiste Daroussin
19461d06d6bSBaptiste Daroussin tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
19561d06d6bSBaptiste Daroussin
19661d06d6bSBaptiste Daroussin /* Center the table as a whole. */
19761d06d6bSBaptiste Daroussin
19861d06d6bSBaptiste Daroussin offset = tp->tcol->offset;
19961d06d6bSBaptiste Daroussin if (sp->opts->opts & TBL_OPT_CENTRE) {
20061d06d6bSBaptiste Daroussin tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
20161d06d6bSBaptiste Daroussin ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
20261d06d6bSBaptiste Daroussin for (ic = 0; ic + 1 < sp->opts->cols; ic++)
20361d06d6bSBaptiste Daroussin tsz += tp->tbl.cols[ic].width +
20461d06d6bSBaptiste Daroussin tp->tbl.cols[ic].spacing;
20561d06d6bSBaptiste Daroussin if (sp->opts->cols)
20661d06d6bSBaptiste Daroussin tsz += tp->tbl.cols[sp->opts->cols - 1].width;
20761d06d6bSBaptiste Daroussin if (offset + tsz > tp->tcol->rmargin)
20861d06d6bSBaptiste Daroussin tsz -= 1;
2097295610fSBaptiste Daroussin offset = offset + tp->tcol->rmargin > tsz ?
21061d06d6bSBaptiste Daroussin (offset + tp->tcol->rmargin - tsz) / 2 : 0;
2117295610fSBaptiste Daroussin tp->tcol->offset = offset;
21261d06d6bSBaptiste Daroussin }
21361d06d6bSBaptiste Daroussin
21461d06d6bSBaptiste Daroussin /* Horizontal frame at the start of boxed tables. */
21561d06d6bSBaptiste Daroussin
2167295610fSBaptiste Daroussin if (tp->enc == TERMENC_ASCII &&
2177295610fSBaptiste Daroussin sp->opts->opts & TBL_OPT_DBOX)
21845a5aec3SBaptiste Daroussin tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX);
21961d06d6bSBaptiste Daroussin if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
22045a5aec3SBaptiste Daroussin tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX);
22161d06d6bSBaptiste Daroussin }
22261d06d6bSBaptiste Daroussin
22361d06d6bSBaptiste Daroussin /* Set up the columns. */
22461d06d6bSBaptiste Daroussin
22561d06d6bSBaptiste Daroussin tp->flags |= TERMP_MULTICOL;
2267295610fSBaptiste Daroussin tp->tcol->offset = offset;
22761d06d6bSBaptiste Daroussin horiz = 0;
22861d06d6bSBaptiste Daroussin switch (sp->pos) {
22961d06d6bSBaptiste Daroussin case TBL_SPAN_HORIZ:
23061d06d6bSBaptiste Daroussin case TBL_SPAN_DHORIZ:
23161d06d6bSBaptiste Daroussin horiz = 1;
23261d06d6bSBaptiste Daroussin term_setcol(tp, 1);
23361d06d6bSBaptiste Daroussin break;
23461d06d6bSBaptiste Daroussin case TBL_SPAN_DATA:
23561d06d6bSBaptiste Daroussin term_setcol(tp, sp->opts->cols + 2);
23661d06d6bSBaptiste Daroussin coloff = tp->tcol->offset;
23761d06d6bSBaptiste Daroussin
23861d06d6bSBaptiste Daroussin /* Set up a column for a left vertical frame. */
23961d06d6bSBaptiste Daroussin
24061d06d6bSBaptiste Daroussin if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
24161d06d6bSBaptiste Daroussin sp->opts->lvert)
24261d06d6bSBaptiste Daroussin coloff++;
24361d06d6bSBaptiste Daroussin tp->tcol->rmargin = coloff;
24461d06d6bSBaptiste Daroussin
24561d06d6bSBaptiste Daroussin /* Set up the data columns. */
24661d06d6bSBaptiste Daroussin
24761d06d6bSBaptiste Daroussin dp = sp->first;
2487295610fSBaptiste Daroussin hspans = 0;
24961d06d6bSBaptiste Daroussin for (ic = 0; ic < sp->opts->cols; ic++) {
2507295610fSBaptiste Daroussin if (hspans == 0) {
25161d06d6bSBaptiste Daroussin tp->tcol++;
25261d06d6bSBaptiste Daroussin tp->tcol->offset = coloff;
25361d06d6bSBaptiste Daroussin }
25461d06d6bSBaptiste Daroussin coloff += tp->tbl.cols[ic].width;
25561d06d6bSBaptiste Daroussin tp->tcol->rmargin = coloff;
25661d06d6bSBaptiste Daroussin if (ic + 1 < sp->opts->cols)
25761d06d6bSBaptiste Daroussin coloff += tp->tbl.cols[ic].spacing;
2587295610fSBaptiste Daroussin if (hspans) {
2597295610fSBaptiste Daroussin hspans--;
26061d06d6bSBaptiste Daroussin continue;
26161d06d6bSBaptiste Daroussin }
2626d38604fSBaptiste Daroussin if (dp != NULL &&
2636d38604fSBaptiste Daroussin (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
2647295610fSBaptiste Daroussin hspans = dp->hspans;
26561d06d6bSBaptiste Daroussin dp = dp->next;
26661d06d6bSBaptiste Daroussin }
2676d38604fSBaptiste Daroussin }
26861d06d6bSBaptiste Daroussin
26961d06d6bSBaptiste Daroussin /* Set up a column for a right vertical frame. */
27061d06d6bSBaptiste Daroussin
27161d06d6bSBaptiste Daroussin tp->tcol++;
27261d06d6bSBaptiste Daroussin tp->tcol->offset = coloff + 1;
27361d06d6bSBaptiste Daroussin tp->tcol->rmargin = tp->maxrmargin;
27461d06d6bSBaptiste Daroussin
27561d06d6bSBaptiste Daroussin /* Spans may have reduced the number of columns. */
27661d06d6bSBaptiste Daroussin
27761d06d6bSBaptiste Daroussin tp->lasttcol = tp->tcol - tp->tcols;
27861d06d6bSBaptiste Daroussin
27961d06d6bSBaptiste Daroussin /* Fill the buffers for all data columns. */
28061d06d6bSBaptiste Daroussin
28161d06d6bSBaptiste Daroussin tp->tcol = tp->tcols;
28261d06d6bSBaptiste Daroussin cp = cpn = sp->layout->first;
28361d06d6bSBaptiste Daroussin dp = sp->first;
2847295610fSBaptiste Daroussin hspans = 0;
28561d06d6bSBaptiste Daroussin for (ic = 0; ic < sp->opts->cols; ic++) {
28661d06d6bSBaptiste Daroussin if (cpn != NULL) {
28761d06d6bSBaptiste Daroussin cp = cpn;
28861d06d6bSBaptiste Daroussin cpn = cpn->next;
28961d06d6bSBaptiste Daroussin }
2907295610fSBaptiste Daroussin if (hspans) {
2917295610fSBaptiste Daroussin hspans--;
29261d06d6bSBaptiste Daroussin continue;
29361d06d6bSBaptiste Daroussin }
29461d06d6bSBaptiste Daroussin tp->tcol++;
29561d06d6bSBaptiste Daroussin tp->col = 0;
296*c1c95addSBrooks Davis tp->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE);
29761d06d6bSBaptiste Daroussin tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
2986d38604fSBaptiste Daroussin if (dp != NULL &&
2996d38604fSBaptiste Daroussin (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
3007295610fSBaptiste Daroussin hspans = dp->hspans;
30161d06d6bSBaptiste Daroussin dp = dp->next;
30261d06d6bSBaptiste Daroussin }
3036d38604fSBaptiste Daroussin }
30461d06d6bSBaptiste Daroussin break;
30561d06d6bSBaptiste Daroussin }
30661d06d6bSBaptiste Daroussin
30761d06d6bSBaptiste Daroussin do {
30861d06d6bSBaptiste Daroussin /* Print the vertical frame at the start of each row. */
30961d06d6bSBaptiste Daroussin
31061d06d6bSBaptiste Daroussin tp->tcol = tp->tcols;
3117295610fSBaptiste Daroussin uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
3127295610fSBaptiste Daroussin sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
3137295610fSBaptiste Daroussin if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert)
3147295610fSBaptiste Daroussin uvert = dvert = sp->layout->vert;
3157295610fSBaptiste Daroussin if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA &&
3167295610fSBaptiste Daroussin dvert < sp->next->layout->vert)
3177295610fSBaptiste Daroussin dvert = sp->next->layout->vert;
3187295610fSBaptiste Daroussin if (sp->prev != NULL && uvert < sp->prev->layout->vert &&
31961d06d6bSBaptiste Daroussin (horiz || (IS_HORIZ(sp->layout->first) &&
3207295610fSBaptiste Daroussin !IS_HORIZ(sp->prev->layout->first))))
3217295610fSBaptiste Daroussin uvert = sp->prev->layout->vert;
3227295610fSBaptiste Daroussin rhori = sp->pos == TBL_SPAN_DHORIZ ||
3237295610fSBaptiste Daroussin (sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) ||
3247295610fSBaptiste Daroussin sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 :
3257295610fSBaptiste Daroussin sp->pos == TBL_SPAN_HORIZ ||
3267295610fSBaptiste Daroussin (sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) ||
3277295610fSBaptiste Daroussin sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0;
3287295610fSBaptiste Daroussin fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
3297295610fSBaptiste Daroussin if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
33061d06d6bSBaptiste Daroussin (*tp->advance)(tp, tp->tcols->offset);
3317295610fSBaptiste Daroussin tp->viscol = tp->tcol->offset;
3327295610fSBaptiste Daroussin tbl_direct_border(tp, fc, 1);
33361d06d6bSBaptiste Daroussin }
33461d06d6bSBaptiste Daroussin
33561d06d6bSBaptiste Daroussin /* Print the data cells. */
33661d06d6bSBaptiste Daroussin
33761d06d6bSBaptiste Daroussin more = 0;
3387295610fSBaptiste Daroussin if (horiz)
33945a5aec3SBaptiste Daroussin tbl_hrule(tp, sp->prev, sp, sp->next, 0);
3407295610fSBaptiste Daroussin else {
34161d06d6bSBaptiste Daroussin cp = sp->layout->first;
34261d06d6bSBaptiste Daroussin cpn = sp->next == NULL ? NULL :
34361d06d6bSBaptiste Daroussin sp->next->layout->first;
34461d06d6bSBaptiste Daroussin cpp = sp->prev == NULL ? NULL :
34561d06d6bSBaptiste Daroussin sp->prev->layout->first;
34661d06d6bSBaptiste Daroussin dp = sp->first;
3477295610fSBaptiste Daroussin hspans = 0;
34861d06d6bSBaptiste Daroussin for (ic = 0; ic < sp->opts->cols; ic++) {
34961d06d6bSBaptiste Daroussin
35061d06d6bSBaptiste Daroussin /*
35161d06d6bSBaptiste Daroussin * Figure out whether to print a
35261d06d6bSBaptiste Daroussin * vertical line after this cell
35361d06d6bSBaptiste Daroussin * and advance to next layout cell.
35461d06d6bSBaptiste Daroussin */
35561d06d6bSBaptiste Daroussin
3567295610fSBaptiste Daroussin uvert = dvert = fc = 0;
35761d06d6bSBaptiste Daroussin if (cp != NULL) {
3587295610fSBaptiste Daroussin cps = cp;
3597295610fSBaptiste Daroussin while (cps->next != NULL &&
3607295610fSBaptiste Daroussin cps->next->pos == TBL_CELL_SPAN)
3617295610fSBaptiste Daroussin cps = cps->next;
3627295610fSBaptiste Daroussin if (sp->pos == TBL_SPAN_DATA)
3637295610fSBaptiste Daroussin uvert = dvert = cps->vert;
36461d06d6bSBaptiste Daroussin switch (cp->pos) {
36561d06d6bSBaptiste Daroussin case TBL_CELL_HORIZ:
3667295610fSBaptiste Daroussin fc = BHORIZ;
36761d06d6bSBaptiste Daroussin break;
36861d06d6bSBaptiste Daroussin case TBL_CELL_DHORIZ:
3697295610fSBaptiste Daroussin fc = BHORIZ * 2;
37061d06d6bSBaptiste Daroussin break;
37161d06d6bSBaptiste Daroussin default:
37261d06d6bSBaptiste Daroussin break;
37361d06d6bSBaptiste Daroussin }
37461d06d6bSBaptiste Daroussin }
37561d06d6bSBaptiste Daroussin if (cpp != NULL) {
3767295610fSBaptiste Daroussin if (uvert < cpp->vert &&
37761d06d6bSBaptiste Daroussin cp != NULL &&
37861d06d6bSBaptiste Daroussin ((IS_HORIZ(cp) &&
37961d06d6bSBaptiste Daroussin !IS_HORIZ(cpp)) ||
38061d06d6bSBaptiste Daroussin (cp->next != NULL &&
38161d06d6bSBaptiste Daroussin cpp->next != NULL &&
38261d06d6bSBaptiste Daroussin IS_HORIZ(cp->next) &&
38361d06d6bSBaptiste Daroussin !IS_HORIZ(cpp->next))))
3847295610fSBaptiste Daroussin uvert = cpp->vert;
38561d06d6bSBaptiste Daroussin cpp = cpp->next;
38661d06d6bSBaptiste Daroussin }
3877295610fSBaptiste Daroussin if (sp->opts->opts & TBL_OPT_ALLBOX) {
3887295610fSBaptiste Daroussin if (uvert == 0)
3897295610fSBaptiste Daroussin uvert = 1;
3907295610fSBaptiste Daroussin if (dvert == 0)
3917295610fSBaptiste Daroussin dvert = 1;
3927295610fSBaptiste Daroussin }
39361d06d6bSBaptiste Daroussin if (cpn != NULL) {
3947295610fSBaptiste Daroussin if (dvert == 0 ||
3957295610fSBaptiste Daroussin (dvert < cpn->vert &&
3967295610fSBaptiste Daroussin tp->enc == TERMENC_UTF8))
3977295610fSBaptiste Daroussin dvert = cpn->vert;
39861d06d6bSBaptiste Daroussin cpn = cpn->next;
39961d06d6bSBaptiste Daroussin }
4007295610fSBaptiste Daroussin
4017295610fSBaptiste Daroussin lhori = (cp != NULL &&
4027295610fSBaptiste Daroussin cp->pos == TBL_CELL_DHORIZ) ||
4037295610fSBaptiste Daroussin (dp != NULL &&
4047295610fSBaptiste Daroussin dp->pos == TBL_DATA_DHORIZ) ? 2 :
4057295610fSBaptiste Daroussin (cp != NULL &&
4067295610fSBaptiste Daroussin cp->pos == TBL_CELL_HORIZ) ||
4077295610fSBaptiste Daroussin (dp != NULL &&
4087295610fSBaptiste Daroussin dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
40961d06d6bSBaptiste Daroussin
41061d06d6bSBaptiste Daroussin /*
41161d06d6bSBaptiste Daroussin * Skip later cells in a span,
41261d06d6bSBaptiste Daroussin * figure out whether to start a span,
41361d06d6bSBaptiste Daroussin * and advance to next data cell.
41461d06d6bSBaptiste Daroussin */
41561d06d6bSBaptiste Daroussin
4167295610fSBaptiste Daroussin if (hspans) {
4177295610fSBaptiste Daroussin hspans--;
4187295610fSBaptiste Daroussin cp = cp->next;
41961d06d6bSBaptiste Daroussin continue;
42061d06d6bSBaptiste Daroussin }
4216d38604fSBaptiste Daroussin if (dp != NULL && (ic ||
4226d38604fSBaptiste Daroussin sp->layout->first->pos != TBL_CELL_SPAN)) {
4237295610fSBaptiste Daroussin hspans = dp->hspans;
42461d06d6bSBaptiste Daroussin dp = dp->next;
42561d06d6bSBaptiste Daroussin }
42661d06d6bSBaptiste Daroussin
42761d06d6bSBaptiste Daroussin /*
42861d06d6bSBaptiste Daroussin * Print one line of text in the cell
42961d06d6bSBaptiste Daroussin * and remember whether there is more.
43061d06d6bSBaptiste Daroussin */
43161d06d6bSBaptiste Daroussin
43261d06d6bSBaptiste Daroussin tp->tcol++;
43361d06d6bSBaptiste Daroussin if (tp->tcol->col < tp->tcol->lastcol)
43461d06d6bSBaptiste Daroussin term_flushln(tp);
43561d06d6bSBaptiste Daroussin if (tp->tcol->col < tp->tcol->lastcol)
43661d06d6bSBaptiste Daroussin more = 1;
43761d06d6bSBaptiste Daroussin
43861d06d6bSBaptiste Daroussin /*
43961d06d6bSBaptiste Daroussin * Vertical frames between data cells,
44061d06d6bSBaptiste Daroussin * but not after the last column.
44161d06d6bSBaptiste Daroussin */
44261d06d6bSBaptiste Daroussin
4437295610fSBaptiste Daroussin if (fc == 0 &&
4447295610fSBaptiste Daroussin ((uvert == 0 && dvert == 0 &&
4457295610fSBaptiste Daroussin cp != NULL && (cp->next == NULL ||
4467295610fSBaptiste Daroussin !IS_HORIZ(cp->next))) ||
4477295610fSBaptiste Daroussin tp->tcol + 1 ==
4487295610fSBaptiste Daroussin tp->tcols + tp->lasttcol)) {
4497295610fSBaptiste Daroussin if (cp != NULL)
4507295610fSBaptiste Daroussin cp = cp->next;
45161d06d6bSBaptiste Daroussin continue;
4527295610fSBaptiste Daroussin }
45361d06d6bSBaptiste Daroussin
45461d06d6bSBaptiste Daroussin if (tp->viscol < tp->tcol->rmargin) {
45561d06d6bSBaptiste Daroussin (*tp->advance)(tp, tp->tcol->rmargin
45661d06d6bSBaptiste Daroussin - tp->viscol);
45761d06d6bSBaptiste Daroussin tp->viscol = tp->tcol->rmargin;
45861d06d6bSBaptiste Daroussin }
45961d06d6bSBaptiste Daroussin while (tp->viscol < tp->tcol->rmargin +
4607295610fSBaptiste Daroussin tp->tbl.cols[ic].spacing / 2)
4617295610fSBaptiste Daroussin tbl_direct_border(tp,
4627295610fSBaptiste Daroussin BHORIZ * lhori, 1);
46361d06d6bSBaptiste Daroussin
46461d06d6bSBaptiste Daroussin if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
46561d06d6bSBaptiste Daroussin continue;
46661d06d6bSBaptiste Daroussin
4677295610fSBaptiste Daroussin if (cp != NULL)
4687295610fSBaptiste Daroussin cp = cp->next;
46961d06d6bSBaptiste Daroussin
4707295610fSBaptiste Daroussin rhori = (cp != NULL &&
4717295610fSBaptiste Daroussin cp->pos == TBL_CELL_DHORIZ) ||
4727295610fSBaptiste Daroussin (dp != NULL &&
4737295610fSBaptiste Daroussin dp->pos == TBL_DATA_DHORIZ) ? 2 :
4747295610fSBaptiste Daroussin (cp != NULL &&
4757295610fSBaptiste Daroussin cp->pos == TBL_CELL_HORIZ) ||
4767295610fSBaptiste Daroussin (dp != NULL &&
4777295610fSBaptiste Daroussin dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
4787295610fSBaptiste Daroussin
4797295610fSBaptiste Daroussin if (tp->tbl.cols[ic].spacing)
4807295610fSBaptiste Daroussin tbl_direct_border(tp,
4817295610fSBaptiste Daroussin BLEFT * lhori + BRIGHT * rhori +
4827295610fSBaptiste Daroussin BUP * uvert + BDOWN * dvert, 1);
4837295610fSBaptiste Daroussin
4847295610fSBaptiste Daroussin if (tp->enc == TERMENC_UTF8)
4857295610fSBaptiste Daroussin uvert = dvert = 0;
4867295610fSBaptiste Daroussin
48761d06d6bSBaptiste Daroussin if (tp->tbl.cols[ic].spacing > 2 &&
4887295610fSBaptiste Daroussin (uvert > 1 || dvert > 1 || rhori))
4897295610fSBaptiste Daroussin tbl_direct_border(tp,
4907295610fSBaptiste Daroussin BHORIZ * rhori +
4917295610fSBaptiste Daroussin BUP * (uvert > 1) +
4927295610fSBaptiste Daroussin BDOWN * (dvert > 1), 1);
49361d06d6bSBaptiste Daroussin }
49461d06d6bSBaptiste Daroussin }
49561d06d6bSBaptiste Daroussin
49661d06d6bSBaptiste Daroussin /* Print the vertical frame at the end of each row. */
49761d06d6bSBaptiste Daroussin
4987295610fSBaptiste Daroussin uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
4997295610fSBaptiste Daroussin sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
5007295610fSBaptiste Daroussin if (sp->pos == TBL_SPAN_DATA &&
5017295610fSBaptiste Daroussin uvert < sp->layout->last->vert &&
5027295610fSBaptiste Daroussin sp->layout->last->col + 1 == sp->opts->cols)
5037295610fSBaptiste Daroussin uvert = dvert = sp->layout->last->vert;
5047295610fSBaptiste Daroussin if (sp->next != NULL &&
5057295610fSBaptiste Daroussin dvert < sp->next->layout->last->vert &&
5067295610fSBaptiste Daroussin sp->next->layout->last->col + 1 == sp->opts->cols)
5077295610fSBaptiste Daroussin dvert = sp->next->layout->last->vert;
5087295610fSBaptiste Daroussin if (sp->prev != NULL &&
5097295610fSBaptiste Daroussin uvert < sp->prev->layout->last->vert &&
51061d06d6bSBaptiste Daroussin sp->prev->layout->last->col + 1 == sp->opts->cols &&
51161d06d6bSBaptiste Daroussin (horiz || (IS_HORIZ(sp->layout->last) &&
5127295610fSBaptiste Daroussin !IS_HORIZ(sp->prev->layout->last))))
5137295610fSBaptiste Daroussin uvert = sp->prev->layout->last->vert;
5147295610fSBaptiste Daroussin lhori = sp->pos == TBL_SPAN_DHORIZ ||
5157295610fSBaptiste Daroussin (sp->last != NULL &&
5167295610fSBaptiste Daroussin sp->last->pos == TBL_DATA_DHORIZ &&
5177295610fSBaptiste Daroussin sp->last->layout->col + 1 == sp->opts->cols) ||
5187295610fSBaptiste Daroussin (sp->layout->last->pos == TBL_CELL_DHORIZ &&
5197295610fSBaptiste Daroussin sp->layout->last->col + 1 == sp->opts->cols) ? 2 :
5207295610fSBaptiste Daroussin sp->pos == TBL_SPAN_HORIZ ||
5217295610fSBaptiste Daroussin (sp->last != NULL &&
5227295610fSBaptiste Daroussin sp->last->pos == TBL_DATA_HORIZ &&
5237295610fSBaptiste Daroussin sp->last->layout->col + 1 == sp->opts->cols) ||
5247295610fSBaptiste Daroussin (sp->layout->last->pos == TBL_CELL_HORIZ &&
5257295610fSBaptiste Daroussin sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0;
5267295610fSBaptiste Daroussin fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori;
5277295610fSBaptiste Daroussin if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) {
52861d06d6bSBaptiste Daroussin if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
52961d06d6bSBaptiste Daroussin sp->layout->last->col + 1 < sp->opts->cols)) {
53061d06d6bSBaptiste Daroussin tp->tcol++;
5317295610fSBaptiste Daroussin do {
5327295610fSBaptiste Daroussin tbl_direct_border(tp,
5337295610fSBaptiste Daroussin BHORIZ * lhori, 1);
5347295610fSBaptiste Daroussin } while (tp->viscol < tp->tcol->offset);
53561d06d6bSBaptiste Daroussin }
5367295610fSBaptiste Daroussin tbl_direct_border(tp, fc, 1);
53761d06d6bSBaptiste Daroussin }
53861d06d6bSBaptiste Daroussin (*tp->endline)(tp);
53961d06d6bSBaptiste Daroussin tp->viscol = 0;
54061d06d6bSBaptiste Daroussin } while (more);
54161d06d6bSBaptiste Daroussin
54261d06d6bSBaptiste Daroussin /*
54361d06d6bSBaptiste Daroussin * Clean up after this row. If it is the last line
54461d06d6bSBaptiste Daroussin * of the table, print the box line and clean up
54561d06d6bSBaptiste Daroussin * column data; otherwise, print the allbox line.
54661d06d6bSBaptiste Daroussin */
54761d06d6bSBaptiste Daroussin
54861d06d6bSBaptiste Daroussin term_setcol(tp, 1);
54961d06d6bSBaptiste Daroussin tp->flags &= ~TERMP_MULTICOL;
55061d06d6bSBaptiste Daroussin tp->tcol->rmargin = tp->maxrmargin;
55161d06d6bSBaptiste Daroussin if (sp->next == NULL) {
552*c1c95addSBrooks Davis if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
55345a5aec3SBaptiste Daroussin tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX);
5547295610fSBaptiste Daroussin if (tp->enc == TERMENC_ASCII &&
555*c1c95addSBrooks Davis sp->opts->opts & TBL_OPT_DBOX)
55645a5aec3SBaptiste Daroussin tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX);
55761d06d6bSBaptiste Daroussin assert(tp->tbl.cols);
55861d06d6bSBaptiste Daroussin free(tp->tbl.cols);
55961d06d6bSBaptiste Daroussin tp->tbl.cols = NULL;
56061d06d6bSBaptiste Daroussin } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
56161d06d6bSBaptiste Daroussin (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
56261d06d6bSBaptiste Daroussin sp->next->next != NULL))
56345a5aec3SBaptiste Daroussin tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX);
56461d06d6bSBaptiste Daroussin
5657295610fSBaptiste Daroussin tp->tcol->offset = save_offset;
56661d06d6bSBaptiste Daroussin tp->flags &= ~TERMP_NONOSPACE;
56761d06d6bSBaptiste Daroussin }
56861d06d6bSBaptiste Daroussin
56961d06d6bSBaptiste Daroussin static void
tbl_hrule(struct termp * tp,const struct tbl_span * spp,const struct tbl_span * sp,const struct tbl_span * spn,int flags)5707295610fSBaptiste Daroussin tbl_hrule(struct termp *tp, const struct tbl_span *spp,
57145a5aec3SBaptiste Daroussin const struct tbl_span *sp, const struct tbl_span *spn, int flags)
57261d06d6bSBaptiste Daroussin {
5737295610fSBaptiste Daroussin const struct tbl_cell *cpp; /* Layout cell above this line. */
57445a5aec3SBaptiste Daroussin const struct tbl_cell *cp; /* Layout cell in this line. */
5757295610fSBaptiste Daroussin const struct tbl_cell *cpn; /* Layout cell below this line. */
5767295610fSBaptiste Daroussin const struct tbl_dat *dpn; /* Data cell below this line. */
5777295610fSBaptiste Daroussin const struct roffcol *col; /* Contains width and spacing. */
5787295610fSBaptiste Daroussin int opts; /* For the table as a whole. */
5797295610fSBaptiste Daroussin int bw; /* Box line width. */
5807295610fSBaptiste Daroussin int hw; /* Horizontal line width. */
5817295610fSBaptiste Daroussin int lw, rw; /* Left and right line widths. */
5827295610fSBaptiste Daroussin int uw, dw; /* Vertical line widths. */
58361d06d6bSBaptiste Daroussin
5847295610fSBaptiste Daroussin cpp = spp == NULL ? NULL : spp->layout->first;
58545a5aec3SBaptiste Daroussin cp = sp == NULL ? NULL : sp->layout->first;
5867295610fSBaptiste Daroussin cpn = spn == NULL ? NULL : spn->layout->first;
5877295610fSBaptiste Daroussin dpn = NULL;
5887295610fSBaptiste Daroussin if (spn != NULL) {
5897295610fSBaptiste Daroussin if (spn->pos == TBL_SPAN_DATA)
5907295610fSBaptiste Daroussin dpn = spn->first;
5917295610fSBaptiste Daroussin else if (spn->next != NULL)
5927295610fSBaptiste Daroussin dpn = spn->next->first;
5937295610fSBaptiste Daroussin }
59445a5aec3SBaptiste Daroussin opts = sp->opts->opts;
5957295610fSBaptiste Daroussin bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) :
5967295610fSBaptiste Daroussin opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0;
5977295610fSBaptiste Daroussin hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw :
59845a5aec3SBaptiste Daroussin sp->pos == TBL_SPAN_DHORIZ ? 2 : 1;
59961d06d6bSBaptiste Daroussin
6007295610fSBaptiste Daroussin /* Print the left end of the line. */
6017295610fSBaptiste Daroussin
6027295610fSBaptiste Daroussin if (tp->viscol == 0) {
6037295610fSBaptiste Daroussin (*tp->advance)(tp, tp->tcols->offset);
6047295610fSBaptiste Daroussin tp->viscol = tp->tcols->offset;
6057295610fSBaptiste Daroussin }
6067295610fSBaptiste Daroussin if (flags != 0)
6077295610fSBaptiste Daroussin tbl_direct_border(tp,
6087295610fSBaptiste Daroussin (spp == NULL ? 0 : BUP * bw) +
6097295610fSBaptiste Daroussin (spn == NULL ? 0 : BDOWN * bw) +
6107295610fSBaptiste Daroussin (spp == NULL || cpn == NULL ||
6117295610fSBaptiste Daroussin cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
6127295610fSBaptiste Daroussin
61345a5aec3SBaptiste Daroussin col = tp->tbl.cols;
61461d06d6bSBaptiste Daroussin for (;;) {
61545a5aec3SBaptiste Daroussin if (cp == NULL)
61645a5aec3SBaptiste Daroussin col++;
61745a5aec3SBaptiste Daroussin else
61845a5aec3SBaptiste Daroussin col = tp->tbl.cols + cp->col;
6197295610fSBaptiste Daroussin
6207295610fSBaptiste Daroussin /* Print the horizontal line inside this column. */
6217295610fSBaptiste Daroussin
6227295610fSBaptiste Daroussin lw = cpp == NULL || cpn == NULL ||
6237295610fSBaptiste Daroussin (cpn->pos != TBL_CELL_DOWN &&
62445a5aec3SBaptiste Daroussin (dpn == NULL || dpn->string == NULL ||
62545a5aec3SBaptiste Daroussin strcmp(dpn->string, "\\^") != 0))
6267295610fSBaptiste Daroussin ? hw : 0;
6277295610fSBaptiste Daroussin tbl_direct_border(tp, BHORIZ * lw,
6287295610fSBaptiste Daroussin col->width + col->spacing / 2);
6297295610fSBaptiste Daroussin
6307295610fSBaptiste Daroussin /*
6317295610fSBaptiste Daroussin * Figure out whether a vertical line is crossing
6327295610fSBaptiste Daroussin * at the end of this column,
6337295610fSBaptiste Daroussin * and advance to the next column.
6347295610fSBaptiste Daroussin */
6357295610fSBaptiste Daroussin
6367295610fSBaptiste Daroussin uw = dw = 0;
63761d06d6bSBaptiste Daroussin if (cpp != NULL) {
6387295610fSBaptiste Daroussin if (flags != TBL_OPT_DBOX) {
6397295610fSBaptiste Daroussin uw = cpp->vert;
6407295610fSBaptiste Daroussin if (uw == 0 && opts & TBL_OPT_ALLBOX)
6417295610fSBaptiste Daroussin uw = 1;
6427295610fSBaptiste Daroussin }
64361d06d6bSBaptiste Daroussin cpp = cpp->next;
64445a5aec3SBaptiste Daroussin } else if (spp != NULL && opts & TBL_OPT_ALLBOX)
64545a5aec3SBaptiste Daroussin uw = 1;
64645a5aec3SBaptiste Daroussin if (cp != NULL)
64745a5aec3SBaptiste Daroussin cp = cp->next;
64861d06d6bSBaptiste Daroussin if (cpn != NULL) {
6497295610fSBaptiste Daroussin if (flags != TBL_OPT_DBOX) {
6507295610fSBaptiste Daroussin dw = cpn->vert;
6517295610fSBaptiste Daroussin if (dw == 0 && opts & TBL_OPT_ALLBOX)
6527295610fSBaptiste Daroussin dw = 1;
6537295610fSBaptiste Daroussin }
65461d06d6bSBaptiste Daroussin cpn = cpn->next;
6557295610fSBaptiste Daroussin while (dpn != NULL && dpn->layout != cpn)
6567295610fSBaptiste Daroussin dpn = dpn->next;
65745a5aec3SBaptiste Daroussin } else if (spn != NULL && opts & TBL_OPT_ALLBOX)
65845a5aec3SBaptiste Daroussin dw = 1;
65945a5aec3SBaptiste Daroussin if (col + 1 == tp->tbl.cols + sp->opts->cols)
6607295610fSBaptiste Daroussin break;
6617295610fSBaptiste Daroussin
6627295610fSBaptiste Daroussin /* Vertical lines do not cross spanned cells. */
6637295610fSBaptiste Daroussin
6647295610fSBaptiste Daroussin if (cpp != NULL && cpp->pos == TBL_CELL_SPAN)
6657295610fSBaptiste Daroussin uw = 0;
6667295610fSBaptiste Daroussin if (cpn != NULL && cpn->pos == TBL_CELL_SPAN)
6677295610fSBaptiste Daroussin dw = 0;
6687295610fSBaptiste Daroussin
6697295610fSBaptiste Daroussin /* The horizontal line inside the next column. */
6707295610fSBaptiste Daroussin
6717295610fSBaptiste Daroussin rw = cpp == NULL || cpn == NULL ||
6727295610fSBaptiste Daroussin (cpn->pos != TBL_CELL_DOWN &&
67345a5aec3SBaptiste Daroussin (dpn == NULL || dpn->string == NULL ||
67445a5aec3SBaptiste Daroussin strcmp(dpn->string, "\\^") != 0))
6757295610fSBaptiste Daroussin ? hw : 0;
6767295610fSBaptiste Daroussin
6777295610fSBaptiste Daroussin /* The line crossing at the end of this column. */
6787295610fSBaptiste Daroussin
67961d06d6bSBaptiste Daroussin if (col->spacing)
6807295610fSBaptiste Daroussin tbl_direct_border(tp, BLEFT * lw +
6817295610fSBaptiste Daroussin BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
6827295610fSBaptiste Daroussin
6837295610fSBaptiste Daroussin /*
6847295610fSBaptiste Daroussin * In ASCII output, a crossing may print two characters.
6857295610fSBaptiste Daroussin */
6867295610fSBaptiste Daroussin
6877295610fSBaptiste Daroussin if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2))
6887295610fSBaptiste Daroussin uw = dw = 0;
68961d06d6bSBaptiste Daroussin if (col->spacing > 2)
6907295610fSBaptiste Daroussin tbl_direct_border(tp,
6917295610fSBaptiste Daroussin BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
6927295610fSBaptiste Daroussin
6937295610fSBaptiste Daroussin /* Padding before the start of the next column. */
6947295610fSBaptiste Daroussin
69561d06d6bSBaptiste Daroussin if (col->spacing > 4)
6967295610fSBaptiste Daroussin tbl_direct_border(tp,
6977295610fSBaptiste Daroussin BHORIZ * rw, (col->spacing - 3) / 2);
69861d06d6bSBaptiste Daroussin }
6997295610fSBaptiste Daroussin
7007295610fSBaptiste Daroussin /* Print the right end of the line. */
7017295610fSBaptiste Daroussin
7027295610fSBaptiste Daroussin if (flags != 0) {
7037295610fSBaptiste Daroussin tbl_direct_border(tp,
7047295610fSBaptiste Daroussin (spp == NULL ? 0 : BUP * bw) +
7057295610fSBaptiste Daroussin (spn == NULL ? 0 : BDOWN * bw) +
7067295610fSBaptiste Daroussin (spp == NULL || spn == NULL ||
7077295610fSBaptiste Daroussin spn->layout->last->pos != TBL_CELL_DOWN ?
7087295610fSBaptiste Daroussin BLEFT * hw : 0), 1);
7097295610fSBaptiste Daroussin (*tp->endline)(tp);
7107295610fSBaptiste Daroussin tp->viscol = 0;
71161d06d6bSBaptiste Daroussin }
71261d06d6bSBaptiste Daroussin }
71361d06d6bSBaptiste Daroussin
71461d06d6bSBaptiste Daroussin static void
tbl_data(struct termp * tp,const struct tbl_opts * opts,const struct tbl_cell * cp,const struct tbl_dat * dp,const struct roffcol * col)71561d06d6bSBaptiste Daroussin tbl_data(struct termp *tp, const struct tbl_opts *opts,
71661d06d6bSBaptiste Daroussin const struct tbl_cell *cp, const struct tbl_dat *dp,
71761d06d6bSBaptiste Daroussin const struct roffcol *col)
71861d06d6bSBaptiste Daroussin {
71961d06d6bSBaptiste Daroussin switch (cp->pos) {
72061d06d6bSBaptiste Daroussin case TBL_CELL_HORIZ:
7217295610fSBaptiste Daroussin tbl_fill_border(tp, BHORIZ, col->width);
72261d06d6bSBaptiste Daroussin return;
72361d06d6bSBaptiste Daroussin case TBL_CELL_DHORIZ:
7247295610fSBaptiste Daroussin tbl_fill_border(tp, BHORIZ * 2, col->width);
72561d06d6bSBaptiste Daroussin return;
72661d06d6bSBaptiste Daroussin default:
72761d06d6bSBaptiste Daroussin break;
72861d06d6bSBaptiste Daroussin }
72961d06d6bSBaptiste Daroussin
73061d06d6bSBaptiste Daroussin if (dp == NULL)
73161d06d6bSBaptiste Daroussin return;
73261d06d6bSBaptiste Daroussin
73361d06d6bSBaptiste Daroussin switch (dp->pos) {
73461d06d6bSBaptiste Daroussin case TBL_DATA_NONE:
73561d06d6bSBaptiste Daroussin return;
73661d06d6bSBaptiste Daroussin case TBL_DATA_HORIZ:
73761d06d6bSBaptiste Daroussin case TBL_DATA_NHORIZ:
7387295610fSBaptiste Daroussin tbl_fill_border(tp, BHORIZ, col->width);
73961d06d6bSBaptiste Daroussin return;
74061d06d6bSBaptiste Daroussin case TBL_DATA_NDHORIZ:
74161d06d6bSBaptiste Daroussin case TBL_DATA_DHORIZ:
7427295610fSBaptiste Daroussin tbl_fill_border(tp, BHORIZ * 2, col->width);
74361d06d6bSBaptiste Daroussin return;
74461d06d6bSBaptiste Daroussin default:
74561d06d6bSBaptiste Daroussin break;
74661d06d6bSBaptiste Daroussin }
74761d06d6bSBaptiste Daroussin
74861d06d6bSBaptiste Daroussin switch (cp->pos) {
74961d06d6bSBaptiste Daroussin case TBL_CELL_LONG:
75061d06d6bSBaptiste Daroussin case TBL_CELL_CENTRE:
75161d06d6bSBaptiste Daroussin case TBL_CELL_LEFT:
75261d06d6bSBaptiste Daroussin case TBL_CELL_RIGHT:
75361d06d6bSBaptiste Daroussin tbl_literal(tp, dp, col);
75461d06d6bSBaptiste Daroussin break;
75561d06d6bSBaptiste Daroussin case TBL_CELL_NUMBER:
75661d06d6bSBaptiste Daroussin tbl_number(tp, opts, dp, col);
75761d06d6bSBaptiste Daroussin break;
75861d06d6bSBaptiste Daroussin case TBL_CELL_DOWN:
75961d06d6bSBaptiste Daroussin case TBL_CELL_SPAN:
76061d06d6bSBaptiste Daroussin break;
76161d06d6bSBaptiste Daroussin default:
76261d06d6bSBaptiste Daroussin abort();
76361d06d6bSBaptiste Daroussin }
76461d06d6bSBaptiste Daroussin }
76561d06d6bSBaptiste Daroussin
76661d06d6bSBaptiste Daroussin static void
tbl_fill_string(struct termp * tp,const char * cp,size_t len)7677295610fSBaptiste Daroussin tbl_fill_string(struct termp *tp, const char *cp, size_t len)
76861d06d6bSBaptiste Daroussin {
76961d06d6bSBaptiste Daroussin size_t i, sz;
7707295610fSBaptiste Daroussin
7717295610fSBaptiste Daroussin sz = term_strlen(tp, cp);
7727295610fSBaptiste Daroussin for (i = 0; i < len; i += sz)
7737295610fSBaptiste Daroussin term_word(tp, cp);
7747295610fSBaptiste Daroussin }
7757295610fSBaptiste Daroussin
7767295610fSBaptiste Daroussin static void
tbl_fill_char(struct termp * tp,char c,size_t len)7777295610fSBaptiste Daroussin tbl_fill_char(struct termp *tp, char c, size_t len)
7787295610fSBaptiste Daroussin {
77961d06d6bSBaptiste Daroussin char cp[2];
78061d06d6bSBaptiste Daroussin
78161d06d6bSBaptiste Daroussin cp[0] = c;
78261d06d6bSBaptiste Daroussin cp[1] = '\0';
7837295610fSBaptiste Daroussin tbl_fill_string(tp, cp, len);
7847295610fSBaptiste Daroussin }
78561d06d6bSBaptiste Daroussin
7867295610fSBaptiste Daroussin static void
tbl_fill_border(struct termp * tp,int c,size_t len)7877295610fSBaptiste Daroussin tbl_fill_border(struct termp *tp, int c, size_t len)
7887295610fSBaptiste Daroussin {
7897295610fSBaptiste Daroussin char buf[12];
79061d06d6bSBaptiste Daroussin
7917295610fSBaptiste Daroussin if ((c = borders_locale[c]) > 127) {
7927295610fSBaptiste Daroussin (void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
7937295610fSBaptiste Daroussin tbl_fill_string(tp, buf, len);
7947295610fSBaptiste Daroussin } else
7957295610fSBaptiste Daroussin tbl_fill_char(tp, c, len);
7967295610fSBaptiste Daroussin }
7977295610fSBaptiste Daroussin
7987295610fSBaptiste Daroussin static void
tbl_direct_border(struct termp * tp,int c,size_t len)7997295610fSBaptiste Daroussin tbl_direct_border(struct termp *tp, int c, size_t len)
8007295610fSBaptiste Daroussin {
8017295610fSBaptiste Daroussin size_t i, sz;
8027295610fSBaptiste Daroussin
8037295610fSBaptiste Daroussin c = borders_locale[c];
8047295610fSBaptiste Daroussin sz = (*tp->width)(tp, c);
8057295610fSBaptiste Daroussin for (i = 0; i < len; i += sz) {
8067295610fSBaptiste Daroussin (*tp->letter)(tp, c);
8077295610fSBaptiste Daroussin tp->viscol += sz;
8087295610fSBaptiste Daroussin }
80961d06d6bSBaptiste Daroussin }
81061d06d6bSBaptiste Daroussin
81161d06d6bSBaptiste Daroussin static void
tbl_literal(struct termp * tp,const struct tbl_dat * dp,const struct roffcol * col)81261d06d6bSBaptiste Daroussin tbl_literal(struct termp *tp, const struct tbl_dat *dp,
81361d06d6bSBaptiste Daroussin const struct roffcol *col)
81461d06d6bSBaptiste Daroussin {
81561d06d6bSBaptiste Daroussin size_t len, padl, padr, width;
8167295610fSBaptiste Daroussin int ic, hspans;
81761d06d6bSBaptiste Daroussin
81861d06d6bSBaptiste Daroussin assert(dp->string);
81961d06d6bSBaptiste Daroussin len = term_strlen(tp, dp->string);
82061d06d6bSBaptiste Daroussin width = col->width;
82161d06d6bSBaptiste Daroussin ic = dp->layout->col;
8227295610fSBaptiste Daroussin hspans = dp->hspans;
823*c1c95addSBrooks Davis while (hspans--) {
824*c1c95addSBrooks Davis width += tp->tbl.cols[ic].spacing;
825*c1c95addSBrooks Davis ic++;
826*c1c95addSBrooks Davis width += tp->tbl.cols[ic].width;
827*c1c95addSBrooks Davis }
82861d06d6bSBaptiste Daroussin
82961d06d6bSBaptiste Daroussin padr = width > len ? width - len : 0;
83061d06d6bSBaptiste Daroussin padl = 0;
83161d06d6bSBaptiste Daroussin
83261d06d6bSBaptiste Daroussin switch (dp->layout->pos) {
83361d06d6bSBaptiste Daroussin case TBL_CELL_LONG:
83461d06d6bSBaptiste Daroussin padl = term_len(tp, 1);
83561d06d6bSBaptiste Daroussin padr = padr > padl ? padr - padl : 0;
83661d06d6bSBaptiste Daroussin break;
83761d06d6bSBaptiste Daroussin case TBL_CELL_CENTRE:
83861d06d6bSBaptiste Daroussin if (2 > padr)
83961d06d6bSBaptiste Daroussin break;
84061d06d6bSBaptiste Daroussin padl = padr / 2;
84161d06d6bSBaptiste Daroussin padr -= padl;
84261d06d6bSBaptiste Daroussin break;
84361d06d6bSBaptiste Daroussin case TBL_CELL_RIGHT:
84461d06d6bSBaptiste Daroussin padl = padr;
84561d06d6bSBaptiste Daroussin padr = 0;
84661d06d6bSBaptiste Daroussin break;
84761d06d6bSBaptiste Daroussin default:
84861d06d6bSBaptiste Daroussin break;
84961d06d6bSBaptiste Daroussin }
85061d06d6bSBaptiste Daroussin
8517295610fSBaptiste Daroussin tbl_fill_char(tp, ASCII_NBRSP, padl);
85261d06d6bSBaptiste Daroussin tbl_word(tp, dp);
8537295610fSBaptiste Daroussin tbl_fill_char(tp, ASCII_NBRSP, padr);
85461d06d6bSBaptiste Daroussin }
85561d06d6bSBaptiste Daroussin
85661d06d6bSBaptiste Daroussin static void
tbl_number(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)85761d06d6bSBaptiste Daroussin tbl_number(struct termp *tp, const struct tbl_opts *opts,
85861d06d6bSBaptiste Daroussin const struct tbl_dat *dp,
85961d06d6bSBaptiste Daroussin const struct roffcol *col)
86061d06d6bSBaptiste Daroussin {
8617295610fSBaptiste Daroussin const char *cp, *lastdigit, *lastpoint;
8627295610fSBaptiste Daroussin size_t intsz, padl, totsz;
86361d06d6bSBaptiste Daroussin char buf[2];
86461d06d6bSBaptiste Daroussin
86561d06d6bSBaptiste Daroussin /*
8667295610fSBaptiste Daroussin * Almost the same code as in tblcalc_number():
8677295610fSBaptiste Daroussin * First find the position of the decimal point.
86861d06d6bSBaptiste Daroussin */
86961d06d6bSBaptiste Daroussin
87061d06d6bSBaptiste Daroussin assert(dp->string);
8717295610fSBaptiste Daroussin lastdigit = lastpoint = NULL;
8727295610fSBaptiste Daroussin for (cp = dp->string; cp[0] != '\0'; cp++) {
8737295610fSBaptiste Daroussin if (cp[0] == '\\' && cp[1] == '&') {
8747295610fSBaptiste Daroussin lastdigit = lastpoint = cp;
8757295610fSBaptiste Daroussin break;
8767295610fSBaptiste Daroussin } else if (cp[0] == opts->decimal &&
8777295610fSBaptiste Daroussin (isdigit((unsigned char)cp[1]) ||
8787295610fSBaptiste Daroussin (cp > dp->string && isdigit((unsigned char)cp[-1]))))
8797295610fSBaptiste Daroussin lastpoint = cp;
8807295610fSBaptiste Daroussin else if (isdigit((unsigned char)cp[0]))
8817295610fSBaptiste Daroussin lastdigit = cp;
88261d06d6bSBaptiste Daroussin }
88361d06d6bSBaptiste Daroussin
8847295610fSBaptiste Daroussin /* Then measure both widths. */
8857295610fSBaptiste Daroussin
88661d06d6bSBaptiste Daroussin padl = 0;
8877295610fSBaptiste Daroussin totsz = term_strlen(tp, dp->string);
8887295610fSBaptiste Daroussin if (lastdigit != NULL) {
8897295610fSBaptiste Daroussin if (lastpoint == NULL)
8907295610fSBaptiste Daroussin lastpoint = lastdigit + 1;
8917295610fSBaptiste Daroussin intsz = 0;
8927295610fSBaptiste Daroussin buf[1] = '\0';
8937295610fSBaptiste Daroussin for (cp = dp->string; cp < lastpoint; cp++) {
8947295610fSBaptiste Daroussin buf[0] = cp[0];
8957295610fSBaptiste Daroussin intsz += term_strlen(tp, buf);
8967295610fSBaptiste Daroussin }
8977295610fSBaptiste Daroussin
8987295610fSBaptiste Daroussin /*
8997295610fSBaptiste Daroussin * Pad left to match the decimal position,
9007295610fSBaptiste Daroussin * but avoid exceeding the total column width.
9017295610fSBaptiste Daroussin */
9027295610fSBaptiste Daroussin
9037295610fSBaptiste Daroussin if (col->decimal > intsz && col->width > totsz) {
9047295610fSBaptiste Daroussin padl = col->decimal - intsz;
9057295610fSBaptiste Daroussin if (padl + totsz > col->width)
9067295610fSBaptiste Daroussin padl = col->width - totsz;
9077295610fSBaptiste Daroussin }
9087295610fSBaptiste Daroussin
9097295610fSBaptiste Daroussin /* If it is not a number, simply center the string. */
9107295610fSBaptiste Daroussin
9117295610fSBaptiste Daroussin } else if (col->width > totsz)
9127295610fSBaptiste Daroussin padl = (col->width - totsz) / 2;
9137295610fSBaptiste Daroussin
9147295610fSBaptiste Daroussin tbl_fill_char(tp, ASCII_NBRSP, padl);
91561d06d6bSBaptiste Daroussin tbl_word(tp, dp);
9167295610fSBaptiste Daroussin
9177295610fSBaptiste Daroussin /* Pad right to fill the column. */
9187295610fSBaptiste Daroussin
9197295610fSBaptiste Daroussin if (col->width > padl + totsz)
9207295610fSBaptiste Daroussin tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
92161d06d6bSBaptiste Daroussin }
92261d06d6bSBaptiste Daroussin
92361d06d6bSBaptiste Daroussin static void
tbl_word(struct termp * tp,const struct tbl_dat * dp)92461d06d6bSBaptiste Daroussin tbl_word(struct termp *tp, const struct tbl_dat *dp)
92561d06d6bSBaptiste Daroussin {
92661d06d6bSBaptiste Daroussin int prev_font;
92761d06d6bSBaptiste Daroussin
92861d06d6bSBaptiste Daroussin prev_font = tp->fonti;
9296d38604fSBaptiste Daroussin switch (dp->layout->font) {
9306d38604fSBaptiste Daroussin case ESCAPE_FONTBI:
9316d38604fSBaptiste Daroussin term_fontpush(tp, TERMFONT_BI);
9326d38604fSBaptiste Daroussin break;
9336d38604fSBaptiste Daroussin case ESCAPE_FONTBOLD:
9346d38604fSBaptiste Daroussin case ESCAPE_FONTCB:
93561d06d6bSBaptiste Daroussin term_fontpush(tp, TERMFONT_BOLD);
9366d38604fSBaptiste Daroussin break;
9376d38604fSBaptiste Daroussin case ESCAPE_FONTITALIC:
9386d38604fSBaptiste Daroussin case ESCAPE_FONTCI:
93961d06d6bSBaptiste Daroussin term_fontpush(tp, TERMFONT_UNDER);
9406d38604fSBaptiste Daroussin break;
9416d38604fSBaptiste Daroussin case ESCAPE_FONTROMAN:
9426d38604fSBaptiste Daroussin case ESCAPE_FONTCR:
9436d38604fSBaptiste Daroussin break;
9446d38604fSBaptiste Daroussin default:
9456d38604fSBaptiste Daroussin abort();
9466d38604fSBaptiste Daroussin }
94761d06d6bSBaptiste Daroussin
94861d06d6bSBaptiste Daroussin term_word(tp, dp->string);
94961d06d6bSBaptiste Daroussin
95061d06d6bSBaptiste Daroussin term_fontpopq(tp, prev_font);
95161d06d6bSBaptiste Daroussin }
952