1*61b0e532Sschwarze /* $OpenBSD: tbl_term.c,v 1.66 2022/08/28 10:57:52 schwarze Exp $ */
2393cb51eSschwarze /*
32a491d59Sschwarze * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
404e980cbSschwarze * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5393cb51eSschwarze *
6393cb51eSschwarze * Permission to use, copy, modify, and distribute this software for any
7393cb51eSschwarze * purpose with or without fee is hereby granted, provided that the above
8393cb51eSschwarze * copyright notice and this permission notice appear in all copies.
9393cb51eSschwarze *
10393cb51eSschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11393cb51eSschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12393cb51eSschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13393cb51eSschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14393cb51eSschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15393cb51eSschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16393cb51eSschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17393cb51eSschwarze */
18fd1e7ec9Sschwarze #include <sys/types.h>
19fd1e7ec9Sschwarze
20393cb51eSschwarze #include <assert.h>
21a7d5d23dSschwarze #include <ctype.h>
22393cb51eSschwarze #include <stdio.h>
23393cb51eSschwarze #include <stdlib.h>
24393cb51eSschwarze #include <string.h>
25393cb51eSschwarze
262791bd1cSschwarze #include "mandoc.h"
27fae2491eSschwarze #include "tbl.h"
28c37b5927Sschwarze #include "out.h"
29c37b5927Sschwarze #include "term.h"
30393cb51eSschwarze
31eaa4dcb4Sschwarze #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \
32eaa4dcb4Sschwarze (cp)->pos == TBL_CELL_DHORIZ)
33eaa4dcb4Sschwarze
34868ce2e9Sschwarze
35ec04407bSschwarze static size_t term_tbl_len(size_t, void *);
36ec04407bSschwarze static size_t term_tbl_strlen(const char *, void *);
372c3e66c4Sschwarze static size_t term_tbl_sulen(const struct roffsu *, void *);
38c7497e73Sschwarze static void tbl_data(struct termp *, const struct tbl_opts *,
39eaa4dcb4Sschwarze const struct tbl_cell *,
402791bd1cSschwarze const struct tbl_dat *,
41ec04407bSschwarze const struct roffcol *);
42868ce2e9Sschwarze static void tbl_direct_border(struct termp *, int, size_t);
43868ce2e9Sschwarze static void tbl_fill_border(struct termp *, int, size_t);
44868ce2e9Sschwarze static void tbl_fill_char(struct termp *, char, size_t);
45868ce2e9Sschwarze static void tbl_fill_string(struct termp *, const char *, size_t);
460673fa65Sschwarze static void tbl_hrule(struct termp *, const struct tbl_span *,
47b3e6a325Sschwarze const struct tbl_span *, const struct tbl_span *,
48b3e6a325Sschwarze int);
49ec04407bSschwarze static void tbl_literal(struct termp *, const struct tbl_dat *,
50ec04407bSschwarze const struct roffcol *);
51c7497e73Sschwarze static void tbl_number(struct termp *, const struct tbl_opts *,
522791bd1cSschwarze const struct tbl_dat *,
53ec04407bSschwarze const struct roffcol *);
54fd1e7ec9Sschwarze static void tbl_word(struct termp *, const struct tbl_dat *);
55ec04407bSschwarze
56ec04407bSschwarze
57868ce2e9Sschwarze /*
58868ce2e9Sschwarze * The following border-character tables are indexed
59868ce2e9Sschwarze * by ternary (3-based) numbers, as opposed to binary or decimal.
60868ce2e9Sschwarze * Each ternary digit describes the line width in one direction:
61868ce2e9Sschwarze * 0 means no line, 1 single or light line, 2 double or heavy line.
62868ce2e9Sschwarze */
63868ce2e9Sschwarze
64868ce2e9Sschwarze /* Positional values of the four directions. */
65868ce2e9Sschwarze #define BRIGHT 1
66868ce2e9Sschwarze #define BDOWN 3
67868ce2e9Sschwarze #define BLEFT (3 * 3)
68868ce2e9Sschwarze #define BUP (3 * 3 * 3)
69868ce2e9Sschwarze #define BHORIZ (BLEFT + BRIGHT)
70868ce2e9Sschwarze
71868ce2e9Sschwarze /* Code points to use for each combination of widths. */
72868ce2e9Sschwarze static const int borders_utf8[81] = {
73868ce2e9Sschwarze 0x0020, 0x2576, 0x257a, /* 000 right */
74868ce2e9Sschwarze 0x2577, 0x250c, 0x250d, /* 001 down */
75868ce2e9Sschwarze 0x257b, 0x250e, 0x250f, /* 002 */
76868ce2e9Sschwarze 0x2574, 0x2500, 0x257c, /* 010 left */
77868ce2e9Sschwarze 0x2510, 0x252c, 0x252e, /* 011 left down */
78868ce2e9Sschwarze 0x2512, 0x2530, 0x2532, /* 012 */
79868ce2e9Sschwarze 0x2578, 0x257e, 0x2501, /* 020 left */
80868ce2e9Sschwarze 0x2511, 0x252d, 0x252f, /* 021 left down */
81868ce2e9Sschwarze 0x2513, 0x2531, 0x2533, /* 022 */
82868ce2e9Sschwarze 0x2575, 0x2514, 0x2515, /* 100 up */
83868ce2e9Sschwarze 0x2502, 0x251c, 0x251d, /* 101 up down */
84868ce2e9Sschwarze 0x257d, 0x251f, 0x2522, /* 102 */
85868ce2e9Sschwarze 0x2518, 0x2534, 0x2536, /* 110 up left */
86868ce2e9Sschwarze 0x2524, 0x253c, 0x253e, /* 111 all */
87868ce2e9Sschwarze 0x2527, 0x2541, 0x2546, /* 112 */
88868ce2e9Sschwarze 0x2519, 0x2535, 0x2537, /* 120 up left */
89868ce2e9Sschwarze 0x2525, 0x253d, 0x253f, /* 121 all */
90868ce2e9Sschwarze 0x252a, 0x2545, 0x2548, /* 122 */
91868ce2e9Sschwarze 0x2579, 0x2516, 0x2517, /* 200 up */
92868ce2e9Sschwarze 0x257f, 0x251e, 0x2521, /* 201 up down */
93868ce2e9Sschwarze 0x2503, 0x2520, 0x2523, /* 202 */
94868ce2e9Sschwarze 0x251a, 0x2538, 0x253a, /* 210 up left */
95868ce2e9Sschwarze 0x2526, 0x2540, 0x2544, /* 211 all */
96868ce2e9Sschwarze 0x2528, 0x2542, 0x254a, /* 212 */
97868ce2e9Sschwarze 0x251b, 0x2539, 0x253b, /* 220 up left */
98868ce2e9Sschwarze 0x2529, 0x2543, 0x2547, /* 221 all */
99868ce2e9Sschwarze 0x252b, 0x2549, 0x254b, /* 222 */
100868ce2e9Sschwarze };
101868ce2e9Sschwarze
102868ce2e9Sschwarze /* ASCII approximations for these code points, compatible with groff. */
103868ce2e9Sschwarze static const int borders_ascii[81] = {
104868ce2e9Sschwarze ' ', '-', '=', /* 000 right */
105868ce2e9Sschwarze '|', '+', '+', /* 001 down */
106868ce2e9Sschwarze '|', '+', '+', /* 002 */
107868ce2e9Sschwarze '-', '-', '=', /* 010 left */
108868ce2e9Sschwarze '+', '+', '+', /* 011 left down */
109868ce2e9Sschwarze '+', '+', '+', /* 012 */
110868ce2e9Sschwarze '=', '=', '=', /* 020 left */
111868ce2e9Sschwarze '+', '+', '+', /* 021 left down */
112868ce2e9Sschwarze '+', '+', '+', /* 022 */
113868ce2e9Sschwarze '|', '+', '+', /* 100 up */
114868ce2e9Sschwarze '|', '+', '+', /* 101 up down */
115868ce2e9Sschwarze '|', '+', '+', /* 102 */
116868ce2e9Sschwarze '+', '+', '+', /* 110 up left */
117868ce2e9Sschwarze '+', '+', '+', /* 111 all */
118868ce2e9Sschwarze '+', '+', '+', /* 112 */
119868ce2e9Sschwarze '+', '+', '+', /* 120 up left */
120868ce2e9Sschwarze '+', '+', '+', /* 121 all */
121868ce2e9Sschwarze '+', '+', '+', /* 122 */
122868ce2e9Sschwarze '|', '+', '+', /* 200 up */
123868ce2e9Sschwarze '|', '+', '+', /* 201 up down */
124868ce2e9Sschwarze '|', '+', '+', /* 202 */
125868ce2e9Sschwarze '+', '+', '+', /* 210 up left */
126868ce2e9Sschwarze '+', '+', '+', /* 211 all */
127868ce2e9Sschwarze '+', '+', '+', /* 212 */
128868ce2e9Sschwarze '+', '+', '+', /* 220 up left */
129868ce2e9Sschwarze '+', '+', '+', /* 221 all */
130868ce2e9Sschwarze '+', '+', '+', /* 222 */
131868ce2e9Sschwarze };
132868ce2e9Sschwarze
133868ce2e9Sschwarze /* Either of the above according to the selected output encoding. */
134868ce2e9Sschwarze static const int *borders_locale;
135868ce2e9Sschwarze
136868ce2e9Sschwarze
137ec04407bSschwarze static size_t
term_tbl_sulen(const struct roffsu * su,void * arg)1382c3e66c4Sschwarze term_tbl_sulen(const struct roffsu *su, void *arg)
1392c3e66c4Sschwarze {
1404fcba843Sschwarze int i;
1414fcba843Sschwarze
1424fcba843Sschwarze i = term_hen((const struct termp *)arg, su);
1434fcba843Sschwarze return i > 0 ? i : 0;
1442c3e66c4Sschwarze }
1452c3e66c4Sschwarze
1462c3e66c4Sschwarze static size_t
term_tbl_strlen(const char * p,void * arg)147ec04407bSschwarze term_tbl_strlen(const char *p, void *arg)
148ec04407bSschwarze {
149526e306bSschwarze return term_strlen((const struct termp *)arg, p);
150ec04407bSschwarze }
151ec04407bSschwarze
152ec04407bSschwarze static size_t
term_tbl_len(size_t sz,void * arg)153ec04407bSschwarze term_tbl_len(size_t sz, void *arg)
154ec04407bSschwarze {
155526e306bSschwarze return term_len((const struct termp *)arg, sz);
156ec04407bSschwarze }
157393cb51eSschwarze
158868ce2e9Sschwarze
1592791bd1cSschwarze void
term_tbl(struct termp * tp,const struct tbl_span * sp)1602791bd1cSschwarze term_tbl(struct termp *tp, const struct tbl_span *sp)
161393cb51eSschwarze {
162868ce2e9Sschwarze const struct tbl_cell *cp, *cpn, *cpp, *cps;
1632791bd1cSschwarze const struct tbl_dat *dp;
1647d87a934Sschwarze static size_t offset;
1654aa95b9fSschwarze size_t save_offset;
1668afa4451Sschwarze size_t coloff, tsz;
167868ce2e9Sschwarze int hspans, ic, more;
168f03619d4Sschwarze int dvert, fc, horiz, lhori, rhori, uvert;
169ec04407bSschwarze
1702791bd1cSschwarze /* Inhibit printing of spaces: we do padding ourselves. */
1712791bd1cSschwarze
1728afa4451Sschwarze tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
1734aa95b9fSschwarze save_offset = tp->tcol->offset;
174393cb51eSschwarze
175393cb51eSschwarze /*
176ec04407bSschwarze * The first time we're invoked for a given table block,
177ec04407bSschwarze * calculate the table widths and decimal positions.
178393cb51eSschwarze */
179393cb51eSschwarze
180cb151596Sschwarze if (tp->tbl.cols == NULL) {
181868ce2e9Sschwarze borders_locale = tp->enc == TERMENC_UTF8 ?
182868ce2e9Sschwarze borders_utf8 : borders_ascii;
183868ce2e9Sschwarze
184ec04407bSschwarze tp->tbl.len = term_tbl_len;
185ec04407bSschwarze tp->tbl.slen = term_tbl_strlen;
1862c3e66c4Sschwarze tp->tbl.sulen = term_tbl_sulen;
187ec04407bSschwarze tp->tbl.arg = tp;
188ec04407bSschwarze
189d1c3ec49Sschwarze tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
190393cb51eSschwarze
1917d87a934Sschwarze /* Center the table as a whole. */
1927d87a934Sschwarze
193e93ea447Sschwarze offset = tp->tcol->offset;
1947d87a934Sschwarze if (sp->opts->opts & TBL_OPT_CENTRE) {
1957d87a934Sschwarze tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
1967d87a934Sschwarze ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
197e8a65004Sschwarze for (ic = 0; ic + 1 < sp->opts->cols; ic++)
198e8a65004Sschwarze tsz += tp->tbl.cols[ic].width +
199e8a65004Sschwarze tp->tbl.cols[ic].spacing;
200e8a65004Sschwarze if (sp->opts->cols)
201e8a65004Sschwarze tsz += tp->tbl.cols[sp->opts->cols - 1].width;
202e93ea447Sschwarze if (offset + tsz > tp->tcol->rmargin)
2037d87a934Sschwarze tsz -= 1;
2044aa95b9fSschwarze offset = offset + tp->tcol->rmargin > tsz ?
205e93ea447Sschwarze (offset + tp->tcol->rmargin - tsz) / 2 : 0;
2064aa95b9fSschwarze tp->tcol->offset = offset;
2077d87a934Sschwarze }
2087d87a934Sschwarze
2092791bd1cSschwarze /* Horizontal frame at the start of boxed tables. */
2102791bd1cSschwarze
211868ce2e9Sschwarze if (tp->enc == TERMENC_ASCII &&
212868ce2e9Sschwarze sp->opts->opts & TBL_OPT_DBOX)
213b3e6a325Sschwarze tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX);
214fd9b947eSschwarze if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
215b3e6a325Sschwarze tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX);
2160ebcd99eSschwarze }
2172791bd1cSschwarze
2188afa4451Sschwarze /* Set up the columns. */
2192791bd1cSschwarze
2208afa4451Sschwarze tp->flags |= TERMP_MULTICOL;
2214aa95b9fSschwarze tp->tcol->offset = offset;
2228afa4451Sschwarze horiz = 0;
2238afa4451Sschwarze switch (sp->pos) {
2248afa4451Sschwarze case TBL_SPAN_HORIZ:
2258afa4451Sschwarze case TBL_SPAN_DHORIZ:
2268afa4451Sschwarze horiz = 1;
2278afa4451Sschwarze term_setcol(tp, 1);
2288afa4451Sschwarze break;
2298afa4451Sschwarze case TBL_SPAN_DATA:
2308afa4451Sschwarze term_setcol(tp, sp->opts->cols + 2);
2318afa4451Sschwarze coloff = tp->tcol->offset;
232fd9b947eSschwarze
2338afa4451Sschwarze /* Set up a column for a left vertical frame. */
2348afa4451Sschwarze
2358afa4451Sschwarze if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
2368afa4451Sschwarze sp->opts->lvert)
2378afa4451Sschwarze coloff++;
2388afa4451Sschwarze tp->tcol->rmargin = coloff;
2398afa4451Sschwarze
2408afa4451Sschwarze /* Set up the data columns. */
2418afa4451Sschwarze
2428afa4451Sschwarze dp = sp->first;
243a93944b2Sschwarze hspans = 0;
2448afa4451Sschwarze for (ic = 0; ic < sp->opts->cols; ic++) {
245a93944b2Sschwarze if (hspans == 0) {
2468afa4451Sschwarze tp->tcol++;
2478afa4451Sschwarze tp->tcol->offset = coloff;
2488afa4451Sschwarze }
2498afa4451Sschwarze coloff += tp->tbl.cols[ic].width;
2508afa4451Sschwarze tp->tcol->rmargin = coloff;
2518afa4451Sschwarze if (ic + 1 < sp->opts->cols)
252e8a65004Sschwarze coloff += tp->tbl.cols[ic].spacing;
253a93944b2Sschwarze if (hspans) {
254a93944b2Sschwarze hspans--;
2558afa4451Sschwarze continue;
2568afa4451Sschwarze }
257414e7448Sschwarze if (dp != NULL &&
258414e7448Sschwarze (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
259a93944b2Sschwarze hspans = dp->hspans;
2608afa4451Sschwarze dp = dp->next;
2618afa4451Sschwarze }
262414e7448Sschwarze }
2638afa4451Sschwarze
2648afa4451Sschwarze /* Set up a column for a right vertical frame. */
2658afa4451Sschwarze
2668afa4451Sschwarze tp->tcol++;
267e8a65004Sschwarze tp->tcol->offset = coloff + 1;
268eaa4dcb4Sschwarze tp->tcol->rmargin = tp->maxrmargin;
2698afa4451Sschwarze
2708afa4451Sschwarze /* Spans may have reduced the number of columns. */
2718afa4451Sschwarze
2728afa4451Sschwarze tp->lasttcol = tp->tcol - tp->tcols;
2738afa4451Sschwarze
2748afa4451Sschwarze /* Fill the buffers for all data columns. */
2758afa4451Sschwarze
2768afa4451Sschwarze tp->tcol = tp->tcols;
277eaa4dcb4Sschwarze cp = cpn = sp->layout->first;
2788afa4451Sschwarze dp = sp->first;
279a93944b2Sschwarze hspans = 0;
2808afa4451Sschwarze for (ic = 0; ic < sp->opts->cols; ic++) {
281eaa4dcb4Sschwarze if (cpn != NULL) {
282eaa4dcb4Sschwarze cp = cpn;
283eaa4dcb4Sschwarze cpn = cpn->next;
284eaa4dcb4Sschwarze }
285a93944b2Sschwarze if (hspans) {
286a93944b2Sschwarze hspans--;
2878afa4451Sschwarze continue;
2888afa4451Sschwarze }
2898afa4451Sschwarze tp->tcol++;
2908afa4451Sschwarze tp->col = 0;
2912a491d59Sschwarze tp->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE);
292eaa4dcb4Sschwarze tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
293414e7448Sschwarze if (dp != NULL &&
294414e7448Sschwarze (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
295a93944b2Sschwarze hspans = dp->hspans;
2968afa4451Sschwarze dp = dp->next;
2978afa4451Sschwarze }
298414e7448Sschwarze }
2998afa4451Sschwarze break;
3008afa4451Sschwarze }
3018afa4451Sschwarze
3028afa4451Sschwarze do {
3038afa4451Sschwarze /* Print the vertical frame at the start of each row. */
3048afa4451Sschwarze
3058afa4451Sschwarze tp->tcol = tp->tcols;
306868ce2e9Sschwarze uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
307868ce2e9Sschwarze sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
308868ce2e9Sschwarze if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert)
309868ce2e9Sschwarze uvert = dvert = sp->layout->vert;
310868ce2e9Sschwarze if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA &&
311868ce2e9Sschwarze dvert < sp->next->layout->vert)
312868ce2e9Sschwarze dvert = sp->next->layout->vert;
313868ce2e9Sschwarze if (sp->prev != NULL && uvert < sp->prev->layout->vert &&
314eaa4dcb4Sschwarze (horiz || (IS_HORIZ(sp->layout->first) &&
315868ce2e9Sschwarze !IS_HORIZ(sp->prev->layout->first))))
316868ce2e9Sschwarze uvert = sp->prev->layout->vert;
317f03619d4Sschwarze rhori = sp->pos == TBL_SPAN_DHORIZ ||
318f03619d4Sschwarze (sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) ||
319868ce2e9Sschwarze sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 :
320868ce2e9Sschwarze sp->pos == TBL_SPAN_HORIZ ||
321f03619d4Sschwarze (sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) ||
322868ce2e9Sschwarze sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0;
323f03619d4Sschwarze fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
324868ce2e9Sschwarze if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
3258afa4451Sschwarze (*tp->advance)(tp, tp->tcols->offset);
326868ce2e9Sschwarze tp->viscol = tp->tcol->offset;
327868ce2e9Sschwarze tbl_direct_border(tp, fc, 1);
3288afa4451Sschwarze }
3292791bd1cSschwarze
3308afa4451Sschwarze /* Print the data cells. */
3312791bd1cSschwarze
3328afa4451Sschwarze more = 0;
333868ce2e9Sschwarze if (horiz)
334b3e6a325Sschwarze tbl_hrule(tp, sp->prev, sp, sp->next, 0);
335868ce2e9Sschwarze else {
336fd9b947eSschwarze cp = sp->layout->first;
337eaa4dcb4Sschwarze cpn = sp->next == NULL ? NULL :
338eaa4dcb4Sschwarze sp->next->layout->first;
339eaa4dcb4Sschwarze cpp = sp->prev == NULL ? NULL :
340eaa4dcb4Sschwarze sp->prev->layout->first;
3412791bd1cSschwarze dp = sp->first;
342a93944b2Sschwarze hspans = 0;
3435f6d1ba3Sschwarze for (ic = 0; ic < sp->opts->cols; ic++) {
3448afa4451Sschwarze
345eaa4dcb4Sschwarze /*
346eaa4dcb4Sschwarze * Figure out whether to print a
347eaa4dcb4Sschwarze * vertical line after this cell
348eaa4dcb4Sschwarze * and advance to next layout cell.
349eaa4dcb4Sschwarze */
3508afa4451Sschwarze
351868ce2e9Sschwarze uvert = dvert = fc = 0;
3528afa4451Sschwarze if (cp != NULL) {
353868ce2e9Sschwarze cps = cp;
354868ce2e9Sschwarze while (cps->next != NULL &&
355868ce2e9Sschwarze cps->next->pos == TBL_CELL_SPAN)
356868ce2e9Sschwarze cps = cps->next;
357868ce2e9Sschwarze if (sp->pos == TBL_SPAN_DATA)
358868ce2e9Sschwarze uvert = dvert = cps->vert;
359eaa4dcb4Sschwarze switch (cp->pos) {
360eaa4dcb4Sschwarze case TBL_CELL_HORIZ:
361868ce2e9Sschwarze fc = BHORIZ;
362eaa4dcb4Sschwarze break;
363eaa4dcb4Sschwarze case TBL_CELL_DHORIZ:
364868ce2e9Sschwarze fc = BHORIZ * 2;
365eaa4dcb4Sschwarze break;
366eaa4dcb4Sschwarze default:
367eaa4dcb4Sschwarze break;
368eaa4dcb4Sschwarze }
369eaa4dcb4Sschwarze }
370eaa4dcb4Sschwarze if (cpp != NULL) {
371868ce2e9Sschwarze if (uvert < cpp->vert &&
372eaa4dcb4Sschwarze cp != NULL &&
373eaa4dcb4Sschwarze ((IS_HORIZ(cp) &&
374eaa4dcb4Sschwarze !IS_HORIZ(cpp)) ||
375eaa4dcb4Sschwarze (cp->next != NULL &&
376eaa4dcb4Sschwarze cpp->next != NULL &&
377eaa4dcb4Sschwarze IS_HORIZ(cp->next) &&
378eaa4dcb4Sschwarze !IS_HORIZ(cpp->next))))
379868ce2e9Sschwarze uvert = cpp->vert;
380eaa4dcb4Sschwarze cpp = cpp->next;
381eaa4dcb4Sschwarze }
382868ce2e9Sschwarze if (sp->opts->opts & TBL_OPT_ALLBOX) {
383868ce2e9Sschwarze if (uvert == 0)
384868ce2e9Sschwarze uvert = 1;
385868ce2e9Sschwarze if (dvert == 0)
386868ce2e9Sschwarze dvert = 1;
387868ce2e9Sschwarze }
388eaa4dcb4Sschwarze if (cpn != NULL) {
389868ce2e9Sschwarze if (dvert == 0 ||
390868ce2e9Sschwarze (dvert < cpn->vert &&
391868ce2e9Sschwarze tp->enc == TERMENC_UTF8))
392868ce2e9Sschwarze dvert = cpn->vert;
393eaa4dcb4Sschwarze cpn = cpn->next;
394eaa4dcb4Sschwarze }
39529981601Sschwarze
396f03619d4Sschwarze lhori = (cp != NULL &&
397f03619d4Sschwarze cp->pos == TBL_CELL_DHORIZ) ||
398f03619d4Sschwarze (dp != NULL &&
399f03619d4Sschwarze dp->pos == TBL_DATA_DHORIZ) ? 2 :
400f03619d4Sschwarze (cp != NULL &&
401f03619d4Sschwarze cp->pos == TBL_CELL_HORIZ) ||
402f03619d4Sschwarze (dp != NULL &&
403f03619d4Sschwarze dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
404f03619d4Sschwarze
405eaa4dcb4Sschwarze /*
406eaa4dcb4Sschwarze * Skip later cells in a span,
407eaa4dcb4Sschwarze * figure out whether to start a span,
408eaa4dcb4Sschwarze * and advance to next data cell.
409eaa4dcb4Sschwarze */
41029981601Sschwarze
411a93944b2Sschwarze if (hspans) {
412a93944b2Sschwarze hspans--;
413868ce2e9Sschwarze cp = cp->next;
41429981601Sschwarze continue;
41529981601Sschwarze }
416414e7448Sschwarze if (dp != NULL && (ic ||
417414e7448Sschwarze sp->layout->first->pos != TBL_CELL_SPAN)) {
418a93944b2Sschwarze hspans = dp->hspans;
41929981601Sschwarze dp = dp->next;
42029981601Sschwarze }
42129981601Sschwarze
422eaa4dcb4Sschwarze /*
423eaa4dcb4Sschwarze * Print one line of text in the cell
424eaa4dcb4Sschwarze * and remember whether there is more.
425eaa4dcb4Sschwarze */
42629981601Sschwarze
42729981601Sschwarze tp->tcol++;
42829981601Sschwarze if (tp->tcol->col < tp->tcol->lastcol)
42929981601Sschwarze term_flushln(tp);
43029981601Sschwarze if (tp->tcol->col < tp->tcol->lastcol)
43129981601Sschwarze more = 1;
43229981601Sschwarze
43329981601Sschwarze /*
43429981601Sschwarze * Vertical frames between data cells,
43529981601Sschwarze * but not after the last column.
43629981601Sschwarze */
43729981601Sschwarze
4385191762fSschwarze if (fc == 0 &&
4395191762fSschwarze ((uvert == 0 && dvert == 0 &&
4405191762fSschwarze cp != NULL && (cp->next == NULL ||
441868ce2e9Sschwarze !IS_HORIZ(cp->next))) ||
4425191762fSschwarze tp->tcol + 1 ==
4435191762fSschwarze tp->tcols + tp->lasttcol)) {
4445191762fSschwarze if (cp != NULL)
445868ce2e9Sschwarze cp = cp->next;
446fd9b947eSschwarze continue;
447868ce2e9Sschwarze }
448fd9b947eSschwarze
449e8a65004Sschwarze if (tp->viscol < tp->tcol->rmargin) {
4508afa4451Sschwarze (*tp->advance)(tp, tp->tcol->rmargin
451eaa4dcb4Sschwarze - tp->viscol);
452eaa4dcb4Sschwarze tp->viscol = tp->tcol->rmargin;
4538351ebcfSschwarze }
454e8a65004Sschwarze while (tp->viscol < tp->tcol->rmargin +
455868ce2e9Sschwarze tp->tbl.cols[ic].spacing / 2)
456f03619d4Sschwarze tbl_direct_border(tp,
457f03619d4Sschwarze BHORIZ * lhori, 1);
458eaa4dcb4Sschwarze
459eaa4dcb4Sschwarze if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
460eaa4dcb4Sschwarze continue;
461eaa4dcb4Sschwarze
462f03619d4Sschwarze if (cp != NULL)
463868ce2e9Sschwarze cp = cp->next;
464f03619d4Sschwarze
465f03619d4Sschwarze rhori = (cp != NULL &&
466f03619d4Sschwarze cp->pos == TBL_CELL_DHORIZ) ||
467f03619d4Sschwarze (dp != NULL &&
468f03619d4Sschwarze dp->pos == TBL_DATA_DHORIZ) ? 2 :
469f03619d4Sschwarze (cp != NULL &&
470f03619d4Sschwarze cp->pos == TBL_CELL_HORIZ) ||
471f03619d4Sschwarze (dp != NULL &&
472f03619d4Sschwarze dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
473f03619d4Sschwarze
474868ce2e9Sschwarze if (tp->tbl.cols[ic].spacing)
475f03619d4Sschwarze tbl_direct_border(tp,
476f03619d4Sschwarze BLEFT * lhori + BRIGHT * rhori +
477868ce2e9Sschwarze BUP * uvert + BDOWN * dvert, 1);
478eaa4dcb4Sschwarze
479868ce2e9Sschwarze if (tp->enc == TERMENC_UTF8)
480868ce2e9Sschwarze uvert = dvert = 0;
481868ce2e9Sschwarze
482e8a65004Sschwarze if (tp->tbl.cols[ic].spacing > 2 &&
483f03619d4Sschwarze (uvert > 1 || dvert > 1 || rhori))
484f03619d4Sschwarze tbl_direct_border(tp,
485f03619d4Sschwarze BHORIZ * rhori +
486868ce2e9Sschwarze BUP * (uvert > 1) +
487868ce2e9Sschwarze BDOWN * (dvert > 1), 1);
4888afa4451Sschwarze }
4898afa4451Sschwarze }
490393cb51eSschwarze
4918afa4451Sschwarze /* Print the vertical frame at the end of each row. */
4920ebcd99eSschwarze
493868ce2e9Sschwarze uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
494868ce2e9Sschwarze sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
495868ce2e9Sschwarze if (sp->pos == TBL_SPAN_DATA &&
496868ce2e9Sschwarze uvert < sp->layout->last->vert &&
497868ce2e9Sschwarze sp->layout->last->col + 1 == sp->opts->cols)
498868ce2e9Sschwarze uvert = dvert = sp->layout->last->vert;
499868ce2e9Sschwarze if (sp->next != NULL &&
500868ce2e9Sschwarze dvert < sp->next->layout->last->vert &&
501868ce2e9Sschwarze sp->next->layout->last->col + 1 == sp->opts->cols)
502868ce2e9Sschwarze dvert = sp->next->layout->last->vert;
503868ce2e9Sschwarze if (sp->prev != NULL &&
504868ce2e9Sschwarze uvert < sp->prev->layout->last->vert &&
505eaa4dcb4Sschwarze sp->prev->layout->last->col + 1 == sp->opts->cols &&
506eaa4dcb4Sschwarze (horiz || (IS_HORIZ(sp->layout->last) &&
507868ce2e9Sschwarze !IS_HORIZ(sp->prev->layout->last))))
508868ce2e9Sschwarze uvert = sp->prev->layout->last->vert;
509f03619d4Sschwarze lhori = sp->pos == TBL_SPAN_DHORIZ ||
510f03619d4Sschwarze (sp->last != NULL &&
511f03619d4Sschwarze sp->last->pos == TBL_DATA_DHORIZ &&
512f03619d4Sschwarze sp->last->layout->col + 1 == sp->opts->cols) ||
513868ce2e9Sschwarze (sp->layout->last->pos == TBL_CELL_DHORIZ &&
514868ce2e9Sschwarze sp->layout->last->col + 1 == sp->opts->cols) ? 2 :
515868ce2e9Sschwarze sp->pos == TBL_SPAN_HORIZ ||
516f03619d4Sschwarze (sp->last != NULL &&
517f03619d4Sschwarze sp->last->pos == TBL_DATA_HORIZ &&
518f03619d4Sschwarze sp->last->layout->col + 1 == sp->opts->cols) ||
519868ce2e9Sschwarze (sp->layout->last->pos == TBL_CELL_HORIZ &&
520868ce2e9Sschwarze sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0;
521f03619d4Sschwarze fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori;
522868ce2e9Sschwarze if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) {
523eaa4dcb4Sschwarze if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
524eaa4dcb4Sschwarze sp->layout->last->col + 1 < sp->opts->cols)) {
5258afa4451Sschwarze tp->tcol++;
526f03619d4Sschwarze do {
527f03619d4Sschwarze tbl_direct_border(tp,
528f03619d4Sschwarze BHORIZ * lhori, 1);
529f03619d4Sschwarze } while (tp->viscol < tp->tcol->offset);
5308afa4451Sschwarze }
531868ce2e9Sschwarze tbl_direct_border(tp, fc, 1);
5328afa4451Sschwarze }
5338afa4451Sschwarze (*tp->endline)(tp);
5348afa4451Sschwarze tp->viscol = 0;
5358afa4451Sschwarze } while (more);
5362791bd1cSschwarze
5372791bd1cSschwarze /*
538eaa4dcb4Sschwarze * Clean up after this row. If it is the last line
539eaa4dcb4Sschwarze * of the table, print the box line and clean up
540eaa4dcb4Sschwarze * column data; otherwise, print the allbox line.
5412791bd1cSschwarze */
5422791bd1cSschwarze
5438afa4451Sschwarze term_setcol(tp, 1);
5448afa4451Sschwarze tp->flags &= ~TERMP_MULTICOL;
5458afa4451Sschwarze tp->tcol->rmargin = tp->maxrmargin;
546cb151596Sschwarze if (sp->next == NULL) {
547*61b0e532Sschwarze if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
548b3e6a325Sschwarze tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX);
549868ce2e9Sschwarze if (tp->enc == TERMENC_ASCII &&
550*61b0e532Sschwarze sp->opts->opts & TBL_OPT_DBOX)
551b3e6a325Sschwarze tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX);
552ec04407bSschwarze assert(tp->tbl.cols);
553ec04407bSschwarze free(tp->tbl.cols);
554ec04407bSschwarze tp->tbl.cols = NULL;
5556c9ac24eSschwarze } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
5566c9ac24eSschwarze (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
5576c9ac24eSschwarze sp->next->next != NULL))
558b3e6a325Sschwarze tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX);
55906875e85Sschwarze
5604aa95b9fSschwarze tp->tcol->offset = save_offset;
5618afa4451Sschwarze tp->flags &= ~TERMP_NONOSPACE;
5622791bd1cSschwarze }
563393cb51eSschwarze
564393cb51eSschwarze static void
tbl_hrule(struct termp * tp,const struct tbl_span * spp,const struct tbl_span * sp,const struct tbl_span * spn,int flags)5650673fa65Sschwarze tbl_hrule(struct termp *tp, const struct tbl_span *spp,
566b3e6a325Sschwarze const struct tbl_span *sp, const struct tbl_span *spn, int flags)
567393cb51eSschwarze {
5687d622754Sschwarze const struct tbl_cell *cpp; /* Layout cell above this line. */
569b3e6a325Sschwarze const struct tbl_cell *cp; /* Layout cell in this line. */
5707d622754Sschwarze const struct tbl_cell *cpn; /* Layout cell below this line. */
5717d622754Sschwarze const struct tbl_dat *dpn; /* Data cell below this line. */
5720673fa65Sschwarze const struct roffcol *col; /* Contains width and spacing. */
5730673fa65Sschwarze int opts; /* For the table as a whole. */
5740673fa65Sschwarze int bw; /* Box line width. */
5750673fa65Sschwarze int hw; /* Horizontal line width. */
5760673fa65Sschwarze int lw, rw; /* Left and right line widths. */
5770673fa65Sschwarze int uw, dw; /* Vertical line widths. */
578393cb51eSschwarze
5790673fa65Sschwarze cpp = spp == NULL ? NULL : spp->layout->first;
580b3e6a325Sschwarze cp = sp == NULL ? NULL : sp->layout->first;
5810673fa65Sschwarze cpn = spn == NULL ? NULL : spn->layout->first;
5827d622754Sschwarze dpn = NULL;
5837d622754Sschwarze if (spn != NULL) {
5847d622754Sschwarze if (spn->pos == TBL_SPAN_DATA)
5857d622754Sschwarze dpn = spn->first;
5867d622754Sschwarze else if (spn->next != NULL)
5877d622754Sschwarze dpn = spn->next->first;
5887d622754Sschwarze }
589b3e6a325Sschwarze opts = sp->opts->opts;
5900673fa65Sschwarze bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) :
5910673fa65Sschwarze opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0;
5920673fa65Sschwarze hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw :
593b3e6a325Sschwarze sp->pos == TBL_SPAN_DHORIZ ? 2 : 1;
5940673fa65Sschwarze
5950673fa65Sschwarze /* Print the left end of the line. */
5960673fa65Sschwarze
597868ce2e9Sschwarze if (tp->viscol == 0) {
598868ce2e9Sschwarze (*tp->advance)(tp, tp->tcols->offset);
599868ce2e9Sschwarze tp->viscol = tp->tcols->offset;
600868ce2e9Sschwarze }
6010673fa65Sschwarze if (flags != 0)
6020673fa65Sschwarze tbl_direct_border(tp,
6030673fa65Sschwarze (spp == NULL ? 0 : BUP * bw) +
6040673fa65Sschwarze (spn == NULL ? 0 : BDOWN * bw) +
6050673fa65Sschwarze (spp == NULL || cpn == NULL ||
6060673fa65Sschwarze cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
6070673fa65Sschwarze
6087514a273Sschwarze col = tp->tbl.cols;
609fd9b947eSschwarze for (;;) {
6107514a273Sschwarze if (cp == NULL)
6117514a273Sschwarze col++;
6127514a273Sschwarze else
613b3e6a325Sschwarze col = tp->tbl.cols + cp->col;
6140673fa65Sschwarze
6150673fa65Sschwarze /* Print the horizontal line inside this column. */
6160673fa65Sschwarze
6170673fa65Sschwarze lw = cpp == NULL || cpn == NULL ||
6187d622754Sschwarze (cpn->pos != TBL_CELL_DOWN &&
6195f6e3232Sschwarze (dpn == NULL || dpn->string == NULL ||
6205f6e3232Sschwarze strcmp(dpn->string, "\\^") != 0))
6217d622754Sschwarze ? hw : 0;
6220673fa65Sschwarze tbl_direct_border(tp, BHORIZ * lw,
6230673fa65Sschwarze col->width + col->spacing / 2);
6240673fa65Sschwarze
6250673fa65Sschwarze /*
6260673fa65Sschwarze * Figure out whether a vertical line is crossing
6270673fa65Sschwarze * at the end of this column,
6280673fa65Sschwarze * and advance to the next column.
6290673fa65Sschwarze */
6300673fa65Sschwarze
6310673fa65Sschwarze uw = dw = 0;
632eaa4dcb4Sschwarze if (cpp != NULL) {
6330673fa65Sschwarze if (flags != TBL_OPT_DBOX) {
6340673fa65Sschwarze uw = cpp->vert;
6350673fa65Sschwarze if (uw == 0 && opts & TBL_OPT_ALLBOX)
6360673fa65Sschwarze uw = 1;
6370673fa65Sschwarze }
638eaa4dcb4Sschwarze cpp = cpp->next;
6397514a273Sschwarze } else if (spp != NULL && opts & TBL_OPT_ALLBOX)
6407514a273Sschwarze uw = 1;
641b3e6a325Sschwarze if (cp != NULL)
642b3e6a325Sschwarze cp = cp->next;
643eaa4dcb4Sschwarze if (cpn != NULL) {
6440673fa65Sschwarze if (flags != TBL_OPT_DBOX) {
6450673fa65Sschwarze dw = cpn->vert;
6460673fa65Sschwarze if (dw == 0 && opts & TBL_OPT_ALLBOX)
6470673fa65Sschwarze dw = 1;
6480673fa65Sschwarze }
649eaa4dcb4Sschwarze cpn = cpn->next;
6507d622754Sschwarze while (dpn != NULL && dpn->layout != cpn)
6517d622754Sschwarze dpn = dpn->next;
6527514a273Sschwarze } else if (spn != NULL && opts & TBL_OPT_ALLBOX)
6537514a273Sschwarze dw = 1;
6547514a273Sschwarze if (col + 1 == tp->tbl.cols + sp->opts->cols)
6550673fa65Sschwarze break;
6560673fa65Sschwarze
6570673fa65Sschwarze /* Vertical lines do not cross spanned cells. */
6580673fa65Sschwarze
6590673fa65Sschwarze if (cpp != NULL && cpp->pos == TBL_CELL_SPAN)
6600673fa65Sschwarze uw = 0;
6610673fa65Sschwarze if (cpn != NULL && cpn->pos == TBL_CELL_SPAN)
6620673fa65Sschwarze dw = 0;
6630673fa65Sschwarze
6640673fa65Sschwarze /* The horizontal line inside the next column. */
6650673fa65Sschwarze
6660673fa65Sschwarze rw = cpp == NULL || cpn == NULL ||
6677d622754Sschwarze (cpn->pos != TBL_CELL_DOWN &&
6685f6e3232Sschwarze (dpn == NULL || dpn->string == NULL ||
6695f6e3232Sschwarze strcmp(dpn->string, "\\^") != 0))
6707d622754Sschwarze ? hw : 0;
6710673fa65Sschwarze
6720673fa65Sschwarze /* The line crossing at the end of this column. */
6730673fa65Sschwarze
674868ce2e9Sschwarze if (col->spacing)
6750673fa65Sschwarze tbl_direct_border(tp, BLEFT * lw +
6760673fa65Sschwarze BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
6770673fa65Sschwarze
6780673fa65Sschwarze /*
6790673fa65Sschwarze * In ASCII output, a crossing may print two characters.
6800673fa65Sschwarze */
6810673fa65Sschwarze
6820673fa65Sschwarze if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2))
6830673fa65Sschwarze uw = dw = 0;
684868ce2e9Sschwarze if (col->spacing > 2)
6850673fa65Sschwarze tbl_direct_border(tp,
6860673fa65Sschwarze BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
6870673fa65Sschwarze
6880673fa65Sschwarze /* Padding before the start of the next column. */
6890673fa65Sschwarze
690868ce2e9Sschwarze if (col->spacing > 4)
6910673fa65Sschwarze tbl_direct_border(tp,
6920673fa65Sschwarze BHORIZ * rw, (col->spacing - 3) / 2);
693868ce2e9Sschwarze }
6940673fa65Sschwarze
6950673fa65Sschwarze /* Print the right end of the line. */
6960673fa65Sschwarze
6970673fa65Sschwarze if (flags != 0) {
6980673fa65Sschwarze tbl_direct_border(tp,
6990673fa65Sschwarze (spp == NULL ? 0 : BUP * bw) +
7000673fa65Sschwarze (spn == NULL ? 0 : BDOWN * bw) +
7010673fa65Sschwarze (spp == NULL || spn == NULL ||
7020673fa65Sschwarze spn->layout->last->pos != TBL_CELL_DOWN ?
7030673fa65Sschwarze BLEFT * hw : 0), 1);
704868ce2e9Sschwarze (*tp->endline)(tp);
705868ce2e9Sschwarze tp->viscol = 0;
706393cb51eSschwarze }
707fd9b947eSschwarze }
708393cb51eSschwarze
7092791bd1cSschwarze 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)710c7497e73Sschwarze tbl_data(struct termp *tp, const struct tbl_opts *opts,
711eaa4dcb4Sschwarze const struct tbl_cell *cp, const struct tbl_dat *dp,
712ec04407bSschwarze const struct roffcol *col)
7132791bd1cSschwarze {
714eaa4dcb4Sschwarze switch (cp->pos) {
715eaa4dcb4Sschwarze case TBL_CELL_HORIZ:
716868ce2e9Sschwarze tbl_fill_border(tp, BHORIZ, col->width);
717eaa4dcb4Sschwarze return;
718eaa4dcb4Sschwarze case TBL_CELL_DHORIZ:
719868ce2e9Sschwarze tbl_fill_border(tp, BHORIZ * 2, col->width);
720eaa4dcb4Sschwarze return;
721eaa4dcb4Sschwarze default:
722eaa4dcb4Sschwarze break;
723eaa4dcb4Sschwarze }
7242791bd1cSschwarze
7257522ddcbSschwarze if (dp == NULL)
7262791bd1cSschwarze return;
7272791bd1cSschwarze
7282791bd1cSschwarze switch (dp->pos) {
72949aff9f8Sschwarze case TBL_DATA_NONE:
7302791bd1cSschwarze return;
73149aff9f8Sschwarze case TBL_DATA_HORIZ:
73249aff9f8Sschwarze case TBL_DATA_NHORIZ:
733868ce2e9Sschwarze tbl_fill_border(tp, BHORIZ, col->width);
7342791bd1cSschwarze return;
73549aff9f8Sschwarze case TBL_DATA_NDHORIZ:
73649aff9f8Sschwarze case TBL_DATA_DHORIZ:
737868ce2e9Sschwarze tbl_fill_border(tp, BHORIZ * 2, col->width);
7382791bd1cSschwarze return;
7392791bd1cSschwarze default:
7402791bd1cSschwarze break;
7412791bd1cSschwarze }
7422791bd1cSschwarze
743eaa4dcb4Sschwarze switch (cp->pos) {
74449aff9f8Sschwarze case TBL_CELL_LONG:
74549aff9f8Sschwarze case TBL_CELL_CENTRE:
74649aff9f8Sschwarze case TBL_CELL_LEFT:
74749aff9f8Sschwarze case TBL_CELL_RIGHT:
748ec04407bSschwarze tbl_literal(tp, dp, col);
7492791bd1cSschwarze break;
75049aff9f8Sschwarze case TBL_CELL_NUMBER:
751c7497e73Sschwarze tbl_number(tp, opts, dp, col);
7522791bd1cSschwarze break;
75349aff9f8Sschwarze case TBL_CELL_DOWN:
7547522ddcbSschwarze case TBL_CELL_SPAN:
7558351ebcfSschwarze break;
7562791bd1cSschwarze default:
7572791bd1cSschwarze abort();
7582791bd1cSschwarze }
7592791bd1cSschwarze }
760ec04407bSschwarze
7612791bd1cSschwarze static void
tbl_fill_string(struct termp * tp,const char * cp,size_t len)762868ce2e9Sschwarze tbl_fill_string(struct termp *tp, const char *cp, size_t len)
763393cb51eSschwarze {
764ec04407bSschwarze size_t i, sz;
765868ce2e9Sschwarze
766868ce2e9Sschwarze sz = term_strlen(tp, cp);
767868ce2e9Sschwarze for (i = 0; i < len; i += sz)
768868ce2e9Sschwarze term_word(tp, cp);
769868ce2e9Sschwarze }
770868ce2e9Sschwarze
771868ce2e9Sschwarze static void
tbl_fill_char(struct termp * tp,char c,size_t len)772868ce2e9Sschwarze tbl_fill_char(struct termp *tp, char c, size_t len)
773868ce2e9Sschwarze {
7742791bd1cSschwarze char cp[2];
775393cb51eSschwarze
7762791bd1cSschwarze cp[0] = c;
7772791bd1cSschwarze cp[1] = '\0';
778868ce2e9Sschwarze tbl_fill_string(tp, cp, len);
779868ce2e9Sschwarze }
7802791bd1cSschwarze
781868ce2e9Sschwarze static void
tbl_fill_border(struct termp * tp,int c,size_t len)782868ce2e9Sschwarze tbl_fill_border(struct termp *tp, int c, size_t len)
783868ce2e9Sschwarze {
784868ce2e9Sschwarze char buf[12];
7852791bd1cSschwarze
786868ce2e9Sschwarze if ((c = borders_locale[c]) > 127) {
787868ce2e9Sschwarze (void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
788868ce2e9Sschwarze tbl_fill_string(tp, buf, len);
789868ce2e9Sschwarze } else
790868ce2e9Sschwarze tbl_fill_char(tp, c, len);
791868ce2e9Sschwarze }
792868ce2e9Sschwarze
793868ce2e9Sschwarze static void
tbl_direct_border(struct termp * tp,int c,size_t len)794868ce2e9Sschwarze tbl_direct_border(struct termp *tp, int c, size_t len)
795868ce2e9Sschwarze {
796868ce2e9Sschwarze size_t i, sz;
797868ce2e9Sschwarze
798868ce2e9Sschwarze c = borders_locale[c];
799868ce2e9Sschwarze sz = (*tp->width)(tp, c);
800868ce2e9Sschwarze for (i = 0; i < len; i += sz) {
801868ce2e9Sschwarze (*tp->letter)(tp, c);
802868ce2e9Sschwarze tp->viscol += sz;
803868ce2e9Sschwarze }
804393cb51eSschwarze }
805393cb51eSschwarze
8062791bd1cSschwarze static void
tbl_literal(struct termp * tp,const struct tbl_dat * dp,const struct roffcol * col)807ec04407bSschwarze tbl_literal(struct termp *tp, const struct tbl_dat *dp,
808ec04407bSschwarze const struct roffcol *col)
8092791bd1cSschwarze {
8105f6d1ba3Sschwarze size_t len, padl, padr, width;
811a93944b2Sschwarze int ic, hspans;
8122791bd1cSschwarze
8138351ebcfSschwarze assert(dp->string);
8140ebcd99eSschwarze len = term_strlen(tp, dp->string);
815fd436d91Sschwarze width = col->width;
8165f6d1ba3Sschwarze ic = dp->layout->col;
817a93944b2Sschwarze hspans = dp->hspans;
8186497b0adSschwarze while (hspans--) {
8196497b0adSschwarze width += tp->tbl.cols[ic].spacing;
8206497b0adSschwarze ic++;
8216497b0adSschwarze width += tp->tbl.cols[ic].width;
8226497b0adSschwarze }
823fd436d91Sschwarze
824fd436d91Sschwarze padr = width > len ? width - len : 0;
8250ebcd99eSschwarze padl = 0;
8262791bd1cSschwarze
8278351ebcfSschwarze switch (dp->layout->pos) {
82849aff9f8Sschwarze case TBL_CELL_LONG:
8290ebcd99eSschwarze padl = term_len(tp, 1);
8300ebcd99eSschwarze padr = padr > padl ? padr - padl : 0;
8312791bd1cSschwarze break;
83249aff9f8Sschwarze case TBL_CELL_CENTRE:
8330ebcd99eSschwarze if (2 > padr)
834ed0596dcSschwarze break;
8350ebcd99eSschwarze padl = padr / 2;
836ed0596dcSschwarze padr -= padl;
8372791bd1cSschwarze break;
83849aff9f8Sschwarze case TBL_CELL_RIGHT:
8390ebcd99eSschwarze padl = padr;
8400ebcd99eSschwarze padr = 0;
8412791bd1cSschwarze break;
8422791bd1cSschwarze default:
8432791bd1cSschwarze break;
8442791bd1cSschwarze }
8452791bd1cSschwarze
846868ce2e9Sschwarze tbl_fill_char(tp, ASCII_NBRSP, padl);
847fd1e7ec9Sschwarze tbl_word(tp, dp);
848868ce2e9Sschwarze tbl_fill_char(tp, ASCII_NBRSP, padr);
8492791bd1cSschwarze }
850393cb51eSschwarze
851393cb51eSschwarze static void
tbl_number(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)852c7497e73Sschwarze tbl_number(struct termp *tp, const struct tbl_opts *opts,
8532791bd1cSschwarze const struct tbl_dat *dp,
854ec04407bSschwarze const struct roffcol *col)
855393cb51eSschwarze {
856a7d5d23dSschwarze const char *cp, *lastdigit, *lastpoint;
857a7d5d23dSschwarze size_t intsz, padl, totsz;
858ec04407bSschwarze char buf[2];
8592791bd1cSschwarze
8602791bd1cSschwarze /*
861a7d5d23dSschwarze * Almost the same code as in tblcalc_number():
862a7d5d23dSschwarze * First find the position of the decimal point.
8632791bd1cSschwarze */
8642791bd1cSschwarze
8658351ebcfSschwarze assert(dp->string);
866a7d5d23dSschwarze lastdigit = lastpoint = NULL;
867a7d5d23dSschwarze for (cp = dp->string; cp[0] != '\0'; cp++) {
868a7d5d23dSschwarze if (cp[0] == '\\' && cp[1] == '&') {
869a7d5d23dSschwarze lastdigit = lastpoint = cp;
870a7d5d23dSschwarze break;
871a7d5d23dSschwarze } else if (cp[0] == opts->decimal &&
872a7d5d23dSschwarze (isdigit((unsigned char)cp[1]) ||
873a7d5d23dSschwarze (cp > dp->string && isdigit((unsigned char)cp[-1]))))
874a7d5d23dSschwarze lastpoint = cp;
875a7d5d23dSschwarze else if (isdigit((unsigned char)cp[0]))
876a7d5d23dSschwarze lastdigit = cp;
8772791bd1cSschwarze }
878393cb51eSschwarze
879a7d5d23dSschwarze /* Then measure both widths. */
880a7d5d23dSschwarze
881f2c51b35Sschwarze padl = 0;
882a7d5d23dSschwarze totsz = term_strlen(tp, dp->string);
883a7d5d23dSschwarze if (lastdigit != NULL) {
884a7d5d23dSschwarze if (lastpoint == NULL)
885a7d5d23dSschwarze lastpoint = lastdigit + 1;
886a7d5d23dSschwarze intsz = 0;
887a7d5d23dSschwarze buf[1] = '\0';
888a7d5d23dSschwarze for (cp = dp->string; cp < lastpoint; cp++) {
889a7d5d23dSschwarze buf[0] = cp[0];
890a7d5d23dSschwarze intsz += term_strlen(tp, buf);
891a7d5d23dSschwarze }
892a7d5d23dSschwarze
893a7d5d23dSschwarze /*
894a7d5d23dSschwarze * Pad left to match the decimal position,
895a7d5d23dSschwarze * but avoid exceeding the total column width.
896a7d5d23dSschwarze */
897a7d5d23dSschwarze
898a7d5d23dSschwarze if (col->decimal > intsz && col->width > totsz) {
899a7d5d23dSschwarze padl = col->decimal - intsz;
900a7d5d23dSschwarze if (padl + totsz > col->width)
901a7d5d23dSschwarze padl = col->width - totsz;
902a7d5d23dSschwarze }
903a7d5d23dSschwarze
904a7d5d23dSschwarze /* If it is not a number, simply center the string. */
905a7d5d23dSschwarze
906a7d5d23dSschwarze } else if (col->width > totsz)
907a7d5d23dSschwarze padl = (col->width - totsz) / 2;
908a7d5d23dSschwarze
909868ce2e9Sschwarze tbl_fill_char(tp, ASCII_NBRSP, padl);
910fd1e7ec9Sschwarze tbl_word(tp, dp);
911a7d5d23dSschwarze
912a7d5d23dSschwarze /* Pad right to fill the column. */
913a7d5d23dSschwarze
914a7d5d23dSschwarze if (col->width > padl + totsz)
915868ce2e9Sschwarze tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
916393cb51eSschwarze }
917393cb51eSschwarze
918fd1e7ec9Sschwarze static void
tbl_word(struct termp * tp,const struct tbl_dat * dp)919fd1e7ec9Sschwarze tbl_word(struct termp *tp, const struct tbl_dat *dp)
920fd1e7ec9Sschwarze {
921f29a1da1Sschwarze int prev_font;
922fd1e7ec9Sschwarze
923f29a1da1Sschwarze prev_font = tp->fonti;
9247d063611Sschwarze switch (dp->layout->font) {
9257d063611Sschwarze case ESCAPE_FONTBI:
9267d063611Sschwarze term_fontpush(tp, TERMFONT_BI);
9277d063611Sschwarze break;
9287d063611Sschwarze case ESCAPE_FONTBOLD:
9297d063611Sschwarze case ESCAPE_FONTCB:
930fd1e7ec9Sschwarze term_fontpush(tp, TERMFONT_BOLD);
9317d063611Sschwarze break;
9327d063611Sschwarze case ESCAPE_FONTITALIC:
9337d063611Sschwarze case ESCAPE_FONTCI:
934fd1e7ec9Sschwarze term_fontpush(tp, TERMFONT_UNDER);
9357d063611Sschwarze break;
9367d063611Sschwarze case ESCAPE_FONTROMAN:
9377d063611Sschwarze case ESCAPE_FONTCR:
9387d063611Sschwarze break;
9397d063611Sschwarze default:
9407d063611Sschwarze abort();
9417d063611Sschwarze }
942fd1e7ec9Sschwarze
943fd1e7ec9Sschwarze term_word(tp, dp->string);
944fd1e7ec9Sschwarze
945fd1e7ec9Sschwarze term_fontpopq(tp, prev_font);
946fd1e7ec9Sschwarze }
947