xref: /freebsd/contrib/mandoc/tbl_term.c (revision 6d38604f)
1*6d38604fSBaptiste Daroussin /*	$Id: tbl_term.c,v 1.75 2021/08/10 12:55:04 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*6d38604fSBaptiste Daroussin  * Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org>
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 
2861d06d6bSBaptiste Daroussin #include "mandoc.h"
297295610fSBaptiste Daroussin #include "tbl.h"
3061d06d6bSBaptiste Daroussin #include "out.h"
3161d06d6bSBaptiste Daroussin #include "term.h"
3261d06d6bSBaptiste Daroussin 
3361d06d6bSBaptiste Daroussin #define	IS_HORIZ(cp)	((cp)->pos == TBL_CELL_HORIZ || \
3461d06d6bSBaptiste Daroussin 			 (cp)->pos == TBL_CELL_DHORIZ)
3561d06d6bSBaptiste Daroussin 
367295610fSBaptiste Daroussin 
3761d06d6bSBaptiste Daroussin static	size_t	term_tbl_len(size_t, void *);
3861d06d6bSBaptiste Daroussin static	size_t	term_tbl_strlen(const char *, void *);
3961d06d6bSBaptiste Daroussin static	size_t	term_tbl_sulen(const struct roffsu *, void *);
4061d06d6bSBaptiste Daroussin static	void	tbl_data(struct termp *, const struct tbl_opts *,
4161d06d6bSBaptiste Daroussin 			const struct tbl_cell *,
4261d06d6bSBaptiste Daroussin 			const struct tbl_dat *,
4361d06d6bSBaptiste Daroussin 			const struct roffcol *);
447295610fSBaptiste Daroussin static	void	tbl_direct_border(struct termp *, int, size_t);
457295610fSBaptiste Daroussin static	void	tbl_fill_border(struct termp *, int, size_t);
467295610fSBaptiste Daroussin static	void	tbl_fill_char(struct termp *, char, size_t);
477295610fSBaptiste Daroussin static	void	tbl_fill_string(struct termp *, const char *, size_t);
487295610fSBaptiste Daroussin static	void	tbl_hrule(struct termp *, const struct tbl_span *,
4945a5aec3SBaptiste Daroussin 			const struct tbl_span *, const struct tbl_span *,
5045a5aec3SBaptiste Daroussin 			int);
5161d06d6bSBaptiste Daroussin static	void	tbl_literal(struct termp *, const struct tbl_dat *,
5261d06d6bSBaptiste Daroussin 			const struct roffcol *);
5361d06d6bSBaptiste Daroussin static	void	tbl_number(struct termp *, const struct tbl_opts *,
5461d06d6bSBaptiste Daroussin 			const struct tbl_dat *,
5561d06d6bSBaptiste Daroussin 			const struct roffcol *);
5661d06d6bSBaptiste Daroussin static	void	tbl_word(struct termp *, const struct tbl_dat *);
5761d06d6bSBaptiste Daroussin 
5861d06d6bSBaptiste Daroussin 
597295610fSBaptiste Daroussin /*
607295610fSBaptiste Daroussin  * The following border-character tables are indexed
617295610fSBaptiste Daroussin  * by ternary (3-based) numbers, as opposed to binary or decimal.
627295610fSBaptiste Daroussin  * Each ternary digit describes the line width in one direction:
637295610fSBaptiste Daroussin  * 0 means no line, 1 single or light line, 2 double or heavy line.
647295610fSBaptiste Daroussin  */
657295610fSBaptiste Daroussin 
667295610fSBaptiste Daroussin /* Positional values of the four directions. */
677295610fSBaptiste Daroussin #define	BRIGHT	1
687295610fSBaptiste Daroussin #define	BDOWN	3
697295610fSBaptiste Daroussin #define	BLEFT	(3 * 3)
707295610fSBaptiste Daroussin #define	BUP	(3 * 3 * 3)
717295610fSBaptiste Daroussin #define	BHORIZ	(BLEFT + BRIGHT)
727295610fSBaptiste Daroussin 
737295610fSBaptiste Daroussin /* Code points to use for each combination of widths. */
747295610fSBaptiste Daroussin static  const int borders_utf8[81] = {
757295610fSBaptiste Daroussin 	0x0020, 0x2576, 0x257a,  /* 000 right */
767295610fSBaptiste Daroussin 	0x2577, 0x250c, 0x250d,  /* 001 down */
777295610fSBaptiste Daroussin 	0x257b, 0x250e, 0x250f,  /* 002 */
787295610fSBaptiste Daroussin 	0x2574, 0x2500, 0x257c,  /* 010 left */
797295610fSBaptiste Daroussin 	0x2510, 0x252c, 0x252e,  /* 011 left down */
807295610fSBaptiste Daroussin 	0x2512, 0x2530, 0x2532,  /* 012 */
817295610fSBaptiste Daroussin 	0x2578, 0x257e, 0x2501,  /* 020 left */
827295610fSBaptiste Daroussin 	0x2511, 0x252d, 0x252f,  /* 021 left down */
837295610fSBaptiste Daroussin 	0x2513, 0x2531, 0x2533,  /* 022 */
847295610fSBaptiste Daroussin 	0x2575, 0x2514, 0x2515,  /* 100 up */
857295610fSBaptiste Daroussin 	0x2502, 0x251c, 0x251d,  /* 101 up down */
867295610fSBaptiste Daroussin 	0x257d, 0x251f, 0x2522,  /* 102 */
877295610fSBaptiste Daroussin 	0x2518, 0x2534, 0x2536,  /* 110 up left */
887295610fSBaptiste Daroussin 	0x2524, 0x253c, 0x253e,  /* 111 all */
897295610fSBaptiste Daroussin 	0x2527, 0x2541, 0x2546,  /* 112 */
907295610fSBaptiste Daroussin 	0x2519, 0x2535, 0x2537,  /* 120 up left */
917295610fSBaptiste Daroussin 	0x2525, 0x253d, 0x253f,  /* 121 all */
927295610fSBaptiste Daroussin 	0x252a, 0x2545, 0x2548,  /* 122 */
937295610fSBaptiste Daroussin 	0x2579, 0x2516, 0x2517,  /* 200 up */
947295610fSBaptiste Daroussin 	0x257f, 0x251e, 0x2521,  /* 201 up down */
957295610fSBaptiste Daroussin 	0x2503, 0x2520, 0x2523,  /* 202 */
967295610fSBaptiste Daroussin 	0x251a, 0x2538, 0x253a,  /* 210 up left */
977295610fSBaptiste Daroussin 	0x2526, 0x2540, 0x2544,  /* 211 all */
987295610fSBaptiste Daroussin 	0x2528, 0x2542, 0x254a,  /* 212 */
997295610fSBaptiste Daroussin 	0x251b, 0x2539, 0x253b,  /* 220 up left */
1007295610fSBaptiste Daroussin 	0x2529, 0x2543, 0x2547,  /* 221 all */
1017295610fSBaptiste Daroussin 	0x252b, 0x2549, 0x254b,  /* 222 */
1027295610fSBaptiste Daroussin };
1037295610fSBaptiste Daroussin 
1047295610fSBaptiste Daroussin /* ASCII approximations for these code points, compatible with groff. */
1057295610fSBaptiste Daroussin static  const int borders_ascii[81] = {
1067295610fSBaptiste Daroussin 	' ', '-', '=',  /* 000 right */
1077295610fSBaptiste Daroussin 	'|', '+', '+',  /* 001 down */
1087295610fSBaptiste Daroussin 	'|', '+', '+',  /* 002 */
1097295610fSBaptiste Daroussin 	'-', '-', '=',  /* 010 left */
1107295610fSBaptiste Daroussin 	'+', '+', '+',  /* 011 left down */
1117295610fSBaptiste Daroussin 	'+', '+', '+',  /* 012 */
1127295610fSBaptiste Daroussin 	'=', '=', '=',  /* 020 left */
1137295610fSBaptiste Daroussin 	'+', '+', '+',  /* 021 left down */
1147295610fSBaptiste Daroussin 	'+', '+', '+',  /* 022 */
1157295610fSBaptiste Daroussin 	'|', '+', '+',  /* 100 up */
1167295610fSBaptiste Daroussin 	'|', '+', '+',  /* 101 up down */
1177295610fSBaptiste Daroussin 	'|', '+', '+',  /* 102 */
1187295610fSBaptiste Daroussin 	'+', '+', '+',  /* 110 up left */
1197295610fSBaptiste Daroussin 	'+', '+', '+',  /* 111 all */
1207295610fSBaptiste Daroussin 	'+', '+', '+',  /* 112 */
1217295610fSBaptiste Daroussin 	'+', '+', '+',  /* 120 up left */
1227295610fSBaptiste Daroussin 	'+', '+', '+',  /* 121 all */
1237295610fSBaptiste Daroussin 	'+', '+', '+',  /* 122 */
1247295610fSBaptiste Daroussin 	'|', '+', '+',  /* 200 up */
1257295610fSBaptiste Daroussin 	'|', '+', '+',  /* 201 up down */
1267295610fSBaptiste Daroussin 	'|', '+', '+',  /* 202 */
1277295610fSBaptiste Daroussin 	'+', '+', '+',  /* 210 up left */
1287295610fSBaptiste Daroussin 	'+', '+', '+',  /* 211 all */
1297295610fSBaptiste Daroussin 	'+', '+', '+',  /* 212 */
1307295610fSBaptiste Daroussin 	'+', '+', '+',  /* 220 up left */
1317295610fSBaptiste Daroussin 	'+', '+', '+',  /* 221 all */
1327295610fSBaptiste Daroussin 	'+', '+', '+',  /* 222 */
1337295610fSBaptiste Daroussin };
1347295610fSBaptiste Daroussin 
1357295610fSBaptiste Daroussin /* Either of the above according to the selected output encoding. */
1367295610fSBaptiste Daroussin static	const int *borders_locale;
1377295610fSBaptiste Daroussin 
1387295610fSBaptiste Daroussin 
13961d06d6bSBaptiste Daroussin static size_t
term_tbl_sulen(const struct roffsu * su,void * arg)14061d06d6bSBaptiste Daroussin term_tbl_sulen(const struct roffsu *su, void *arg)
14161d06d6bSBaptiste Daroussin {
14261d06d6bSBaptiste Daroussin 	int	 i;
14361d06d6bSBaptiste Daroussin 
14461d06d6bSBaptiste Daroussin 	i = term_hen((const struct termp *)arg, su);
14561d06d6bSBaptiste Daroussin 	return i > 0 ? i : 0;
14661d06d6bSBaptiste Daroussin }
14761d06d6bSBaptiste Daroussin 
14861d06d6bSBaptiste Daroussin static size_t
term_tbl_strlen(const char * p,void * arg)14961d06d6bSBaptiste Daroussin term_tbl_strlen(const char *p, void *arg)
15061d06d6bSBaptiste Daroussin {
15161d06d6bSBaptiste Daroussin 	return term_strlen((const struct termp *)arg, p);
15261d06d6bSBaptiste Daroussin }
15361d06d6bSBaptiste Daroussin 
15461d06d6bSBaptiste Daroussin static size_t
term_tbl_len(size_t sz,void * arg)15561d06d6bSBaptiste Daroussin term_tbl_len(size_t sz, void *arg)
15661d06d6bSBaptiste Daroussin {
15761d06d6bSBaptiste Daroussin 	return term_len((const struct termp *)arg, sz);
15861d06d6bSBaptiste Daroussin }
15961d06d6bSBaptiste Daroussin 
1607295610fSBaptiste Daroussin 
16161d06d6bSBaptiste Daroussin void
term_tbl(struct termp * tp,const struct tbl_span * sp)16261d06d6bSBaptiste Daroussin term_tbl(struct termp *tp, const struct tbl_span *sp)
16361d06d6bSBaptiste Daroussin {
1647295610fSBaptiste Daroussin 	const struct tbl_cell	*cp, *cpn, *cpp, *cps;
16561d06d6bSBaptiste Daroussin 	const struct tbl_dat	*dp;
16661d06d6bSBaptiste Daroussin 	static size_t		 offset;
1677295610fSBaptiste Daroussin 	size_t			 save_offset;
16861d06d6bSBaptiste Daroussin 	size_t			 coloff, tsz;
1697295610fSBaptiste Daroussin 	int			 hspans, ic, more;
1707295610fSBaptiste Daroussin 	int			 dvert, fc, horiz, lhori, rhori, uvert;
17161d06d6bSBaptiste Daroussin 
17261d06d6bSBaptiste Daroussin 	/* Inhibit printing of spaces: we do padding ourselves. */
17361d06d6bSBaptiste Daroussin 
17461d06d6bSBaptiste Daroussin 	tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
1757295610fSBaptiste Daroussin 	save_offset = tp->tcol->offset;
17661d06d6bSBaptiste Daroussin 
17761d06d6bSBaptiste Daroussin 	/*
17861d06d6bSBaptiste Daroussin 	 * The first time we're invoked for a given table block,
17961d06d6bSBaptiste Daroussin 	 * calculate the table widths and decimal positions.
18061d06d6bSBaptiste Daroussin 	 */
18161d06d6bSBaptiste Daroussin 
18261d06d6bSBaptiste Daroussin 	if (tp->tbl.cols == NULL) {
1837295610fSBaptiste Daroussin 		borders_locale = tp->enc == TERMENC_UTF8 ?
1847295610fSBaptiste Daroussin 		    borders_utf8 : borders_ascii;
1857295610fSBaptiste Daroussin 
18661d06d6bSBaptiste Daroussin 		tp->tbl.len = term_tbl_len;
18761d06d6bSBaptiste Daroussin 		tp->tbl.slen = term_tbl_strlen;
18861d06d6bSBaptiste Daroussin 		tp->tbl.sulen = term_tbl_sulen;
18961d06d6bSBaptiste Daroussin 		tp->tbl.arg = tp;
19061d06d6bSBaptiste Daroussin 
19161d06d6bSBaptiste Daroussin 		tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
19261d06d6bSBaptiste Daroussin 
19361d06d6bSBaptiste Daroussin 		/* Center the table as a whole. */
19461d06d6bSBaptiste Daroussin 
19561d06d6bSBaptiste Daroussin 		offset = tp->tcol->offset;
19661d06d6bSBaptiste Daroussin 		if (sp->opts->opts & TBL_OPT_CENTRE) {
19761d06d6bSBaptiste Daroussin 			tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
19861d06d6bSBaptiste Daroussin 			    ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
19961d06d6bSBaptiste Daroussin 			for (ic = 0; ic + 1 < sp->opts->cols; ic++)
20061d06d6bSBaptiste Daroussin 				tsz += tp->tbl.cols[ic].width +
20161d06d6bSBaptiste Daroussin 				    tp->tbl.cols[ic].spacing;
20261d06d6bSBaptiste Daroussin 			if (sp->opts->cols)
20361d06d6bSBaptiste Daroussin 				tsz += tp->tbl.cols[sp->opts->cols - 1].width;
20461d06d6bSBaptiste Daroussin 			if (offset + tsz > tp->tcol->rmargin)
20561d06d6bSBaptiste Daroussin 				tsz -= 1;
2067295610fSBaptiste Daroussin 			offset = offset + tp->tcol->rmargin > tsz ?
20761d06d6bSBaptiste Daroussin 			    (offset + tp->tcol->rmargin - tsz) / 2 : 0;
2087295610fSBaptiste Daroussin 			tp->tcol->offset = offset;
20961d06d6bSBaptiste Daroussin 		}
21061d06d6bSBaptiste Daroussin 
21161d06d6bSBaptiste Daroussin 		/* Horizontal frame at the start of boxed tables. */
21261d06d6bSBaptiste Daroussin 
2137295610fSBaptiste Daroussin 		if (tp->enc == TERMENC_ASCII &&
2147295610fSBaptiste Daroussin 		    sp->opts->opts & TBL_OPT_DBOX)
21545a5aec3SBaptiste Daroussin 			tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX);
21661d06d6bSBaptiste Daroussin 		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
21745a5aec3SBaptiste Daroussin 			tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX);
21861d06d6bSBaptiste Daroussin 	}
21961d06d6bSBaptiste Daroussin 
22061d06d6bSBaptiste Daroussin 	/* Set up the columns. */
22161d06d6bSBaptiste Daroussin 
22261d06d6bSBaptiste Daroussin 	tp->flags |= TERMP_MULTICOL;
2237295610fSBaptiste Daroussin 	tp->tcol->offset = offset;
22461d06d6bSBaptiste Daroussin 	horiz = 0;
22561d06d6bSBaptiste Daroussin 	switch (sp->pos) {
22661d06d6bSBaptiste Daroussin 	case TBL_SPAN_HORIZ:
22761d06d6bSBaptiste Daroussin 	case TBL_SPAN_DHORIZ:
22861d06d6bSBaptiste Daroussin 		horiz = 1;
22961d06d6bSBaptiste Daroussin 		term_setcol(tp, 1);
23061d06d6bSBaptiste Daroussin 		break;
23161d06d6bSBaptiste Daroussin 	case TBL_SPAN_DATA:
23261d06d6bSBaptiste Daroussin 		term_setcol(tp, sp->opts->cols + 2);
23361d06d6bSBaptiste Daroussin 		coloff = tp->tcol->offset;
23461d06d6bSBaptiste Daroussin 
23561d06d6bSBaptiste Daroussin 		/* Set up a column for a left vertical frame. */
23661d06d6bSBaptiste Daroussin 
23761d06d6bSBaptiste Daroussin 		if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
23861d06d6bSBaptiste Daroussin 		    sp->opts->lvert)
23961d06d6bSBaptiste Daroussin 			coloff++;
24061d06d6bSBaptiste Daroussin 		tp->tcol->rmargin = coloff;
24161d06d6bSBaptiste Daroussin 
24261d06d6bSBaptiste Daroussin 		/* Set up the data columns. */
24361d06d6bSBaptiste Daroussin 
24461d06d6bSBaptiste Daroussin 		dp = sp->first;
2457295610fSBaptiste Daroussin 		hspans = 0;
24661d06d6bSBaptiste Daroussin 		for (ic = 0; ic < sp->opts->cols; ic++) {
2477295610fSBaptiste Daroussin 			if (hspans == 0) {
24861d06d6bSBaptiste Daroussin 				tp->tcol++;
24961d06d6bSBaptiste Daroussin 				tp->tcol->offset = coloff;
25061d06d6bSBaptiste Daroussin 			}
25161d06d6bSBaptiste Daroussin 			coloff += tp->tbl.cols[ic].width;
25261d06d6bSBaptiste Daroussin 			tp->tcol->rmargin = coloff;
25361d06d6bSBaptiste Daroussin 			if (ic + 1 < sp->opts->cols)
25461d06d6bSBaptiste Daroussin 				coloff += tp->tbl.cols[ic].spacing;
2557295610fSBaptiste Daroussin 			if (hspans) {
2567295610fSBaptiste Daroussin 				hspans--;
25761d06d6bSBaptiste Daroussin 				continue;
25861d06d6bSBaptiste Daroussin 			}
259*6d38604fSBaptiste Daroussin 			if (dp != NULL &&
260*6d38604fSBaptiste Daroussin 			    (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
2617295610fSBaptiste Daroussin 				hspans = dp->hspans;
26261d06d6bSBaptiste Daroussin 				dp = dp->next;
26361d06d6bSBaptiste Daroussin 			}
264*6d38604fSBaptiste Daroussin 		}
26561d06d6bSBaptiste Daroussin 
26661d06d6bSBaptiste Daroussin 		/* Set up a column for a right vertical frame. */
26761d06d6bSBaptiste Daroussin 
26861d06d6bSBaptiste Daroussin 		tp->tcol++;
26961d06d6bSBaptiste Daroussin 		tp->tcol->offset = coloff + 1;
27061d06d6bSBaptiste Daroussin 		tp->tcol->rmargin = tp->maxrmargin;
27161d06d6bSBaptiste Daroussin 
27261d06d6bSBaptiste Daroussin 		/* Spans may have reduced the number of columns. */
27361d06d6bSBaptiste Daroussin 
27461d06d6bSBaptiste Daroussin 		tp->lasttcol = tp->tcol - tp->tcols;
27561d06d6bSBaptiste Daroussin 
27661d06d6bSBaptiste Daroussin 		/* Fill the buffers for all data columns. */
27761d06d6bSBaptiste Daroussin 
27861d06d6bSBaptiste Daroussin 		tp->tcol = tp->tcols;
27961d06d6bSBaptiste Daroussin 		cp = cpn = sp->layout->first;
28061d06d6bSBaptiste Daroussin 		dp = sp->first;
2817295610fSBaptiste Daroussin 		hspans = 0;
28261d06d6bSBaptiste Daroussin 		for (ic = 0; ic < sp->opts->cols; ic++) {
28361d06d6bSBaptiste Daroussin 			if (cpn != NULL) {
28461d06d6bSBaptiste Daroussin 				cp = cpn;
28561d06d6bSBaptiste Daroussin 				cpn = cpn->next;
28661d06d6bSBaptiste Daroussin 			}
2877295610fSBaptiste Daroussin 			if (hspans) {
2887295610fSBaptiste Daroussin 				hspans--;
28961d06d6bSBaptiste Daroussin 				continue;
29061d06d6bSBaptiste Daroussin 			}
29161d06d6bSBaptiste Daroussin 			tp->tcol++;
29261d06d6bSBaptiste Daroussin 			tp->col = 0;
29361d06d6bSBaptiste Daroussin 			tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
294*6d38604fSBaptiste Daroussin 			if (dp != NULL &&
295*6d38604fSBaptiste Daroussin 			    (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
2967295610fSBaptiste Daroussin 				hspans = dp->hspans;
29761d06d6bSBaptiste Daroussin 				dp = dp->next;
29861d06d6bSBaptiste Daroussin 			}
299*6d38604fSBaptiste Daroussin 		}
30061d06d6bSBaptiste Daroussin 		break;
30161d06d6bSBaptiste Daroussin 	}
30261d06d6bSBaptiste Daroussin 
30361d06d6bSBaptiste Daroussin 	do {
30461d06d6bSBaptiste Daroussin 		/* Print the vertical frame at the start of each row. */
30561d06d6bSBaptiste Daroussin 
30661d06d6bSBaptiste Daroussin 		tp->tcol = tp->tcols;
3077295610fSBaptiste Daroussin 		uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
3087295610fSBaptiste Daroussin 		    sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
3097295610fSBaptiste Daroussin 		if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert)
3107295610fSBaptiste Daroussin 			uvert = dvert = sp->layout->vert;
3117295610fSBaptiste Daroussin 		if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA &&
3127295610fSBaptiste Daroussin 		    dvert < sp->next->layout->vert)
3137295610fSBaptiste Daroussin 			dvert = sp->next->layout->vert;
3147295610fSBaptiste Daroussin 		if (sp->prev != NULL && uvert < sp->prev->layout->vert &&
31561d06d6bSBaptiste Daroussin 		    (horiz || (IS_HORIZ(sp->layout->first) &&
3167295610fSBaptiste Daroussin 		      !IS_HORIZ(sp->prev->layout->first))))
3177295610fSBaptiste Daroussin 			uvert = sp->prev->layout->vert;
3187295610fSBaptiste Daroussin 		rhori = sp->pos == TBL_SPAN_DHORIZ ||
3197295610fSBaptiste Daroussin 		    (sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) ||
3207295610fSBaptiste Daroussin 		    sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 :
3217295610fSBaptiste Daroussin 		    sp->pos == TBL_SPAN_HORIZ ||
3227295610fSBaptiste Daroussin 		    (sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) ||
3237295610fSBaptiste Daroussin 		    sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0;
3247295610fSBaptiste Daroussin 		fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
3257295610fSBaptiste Daroussin 		if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
32661d06d6bSBaptiste Daroussin 			(*tp->advance)(tp, tp->tcols->offset);
3277295610fSBaptiste Daroussin 			tp->viscol = tp->tcol->offset;
3287295610fSBaptiste Daroussin 			tbl_direct_border(tp, fc, 1);
32961d06d6bSBaptiste Daroussin 		}
33061d06d6bSBaptiste Daroussin 
33161d06d6bSBaptiste Daroussin 		/* Print the data cells. */
33261d06d6bSBaptiste Daroussin 
33361d06d6bSBaptiste Daroussin 		more = 0;
3347295610fSBaptiste Daroussin 		if (horiz)
33545a5aec3SBaptiste Daroussin 			tbl_hrule(tp, sp->prev, sp, sp->next, 0);
3367295610fSBaptiste Daroussin 		else {
33761d06d6bSBaptiste Daroussin 			cp = sp->layout->first;
33861d06d6bSBaptiste Daroussin 			cpn = sp->next == NULL ? NULL :
33961d06d6bSBaptiste Daroussin 			    sp->next->layout->first;
34061d06d6bSBaptiste Daroussin 			cpp = sp->prev == NULL ? NULL :
34161d06d6bSBaptiste Daroussin 			    sp->prev->layout->first;
34261d06d6bSBaptiste Daroussin 			dp = sp->first;
3437295610fSBaptiste Daroussin 			hspans = 0;
34461d06d6bSBaptiste Daroussin 			for (ic = 0; ic < sp->opts->cols; ic++) {
34561d06d6bSBaptiste Daroussin 
34661d06d6bSBaptiste Daroussin 				/*
34761d06d6bSBaptiste Daroussin 				 * Figure out whether to print a
34861d06d6bSBaptiste Daroussin 				 * vertical line after this cell
34961d06d6bSBaptiste Daroussin 				 * and advance to next layout cell.
35061d06d6bSBaptiste Daroussin 				 */
35161d06d6bSBaptiste Daroussin 
3527295610fSBaptiste Daroussin 				uvert = dvert = fc = 0;
35361d06d6bSBaptiste Daroussin 				if (cp != NULL) {
3547295610fSBaptiste Daroussin 					cps = cp;
3557295610fSBaptiste Daroussin 					while (cps->next != NULL &&
3567295610fSBaptiste Daroussin 					    cps->next->pos == TBL_CELL_SPAN)
3577295610fSBaptiste Daroussin 						cps = cps->next;
3587295610fSBaptiste Daroussin 					if (sp->pos == TBL_SPAN_DATA)
3597295610fSBaptiste Daroussin 						uvert = dvert = cps->vert;
36061d06d6bSBaptiste Daroussin 					switch (cp->pos) {
36161d06d6bSBaptiste Daroussin 					case TBL_CELL_HORIZ:
3627295610fSBaptiste Daroussin 						fc = BHORIZ;
36361d06d6bSBaptiste Daroussin 						break;
36461d06d6bSBaptiste Daroussin 					case TBL_CELL_DHORIZ:
3657295610fSBaptiste Daroussin 						fc = BHORIZ * 2;
36661d06d6bSBaptiste Daroussin 						break;
36761d06d6bSBaptiste Daroussin 					default:
36861d06d6bSBaptiste Daroussin 						break;
36961d06d6bSBaptiste Daroussin 					}
37061d06d6bSBaptiste Daroussin 				}
37161d06d6bSBaptiste Daroussin 				if (cpp != NULL) {
3727295610fSBaptiste Daroussin 					if (uvert < cpp->vert &&
37361d06d6bSBaptiste Daroussin 					    cp != NULL &&
37461d06d6bSBaptiste Daroussin 					    ((IS_HORIZ(cp) &&
37561d06d6bSBaptiste Daroussin 					      !IS_HORIZ(cpp)) ||
37661d06d6bSBaptiste Daroussin 					     (cp->next != NULL &&
37761d06d6bSBaptiste Daroussin 					      cpp->next != NULL &&
37861d06d6bSBaptiste Daroussin 					      IS_HORIZ(cp->next) &&
37961d06d6bSBaptiste Daroussin 					      !IS_HORIZ(cpp->next))))
3807295610fSBaptiste Daroussin 						uvert = cpp->vert;
38161d06d6bSBaptiste Daroussin 					cpp = cpp->next;
38261d06d6bSBaptiste Daroussin 				}
3837295610fSBaptiste Daroussin 				if (sp->opts->opts & TBL_OPT_ALLBOX) {
3847295610fSBaptiste Daroussin 					if (uvert == 0)
3857295610fSBaptiste Daroussin 						uvert = 1;
3867295610fSBaptiste Daroussin 					if (dvert == 0)
3877295610fSBaptiste Daroussin 						dvert = 1;
3887295610fSBaptiste Daroussin 				}
38961d06d6bSBaptiste Daroussin 				if (cpn != NULL) {
3907295610fSBaptiste Daroussin 					if (dvert == 0 ||
3917295610fSBaptiste Daroussin 					    (dvert < cpn->vert &&
3927295610fSBaptiste Daroussin 					     tp->enc == TERMENC_UTF8))
3937295610fSBaptiste Daroussin 						dvert = cpn->vert;
39461d06d6bSBaptiste Daroussin 					cpn = cpn->next;
39561d06d6bSBaptiste Daroussin 				}
3967295610fSBaptiste Daroussin 
3977295610fSBaptiste Daroussin 				lhori = (cp != NULL &&
3987295610fSBaptiste Daroussin 				     cp->pos == TBL_CELL_DHORIZ) ||
3997295610fSBaptiste Daroussin 				    (dp != NULL &&
4007295610fSBaptiste Daroussin 				     dp->pos == TBL_DATA_DHORIZ) ? 2 :
4017295610fSBaptiste Daroussin 				    (cp != NULL &&
4027295610fSBaptiste Daroussin 				     cp->pos == TBL_CELL_HORIZ) ||
4037295610fSBaptiste Daroussin 				    (dp != NULL &&
4047295610fSBaptiste Daroussin 				     dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
40561d06d6bSBaptiste Daroussin 
40661d06d6bSBaptiste Daroussin 				/*
40761d06d6bSBaptiste Daroussin 				 * Skip later cells in a span,
40861d06d6bSBaptiste Daroussin 				 * figure out whether to start a span,
40961d06d6bSBaptiste Daroussin 				 * and advance to next data cell.
41061d06d6bSBaptiste Daroussin 				 */
41161d06d6bSBaptiste Daroussin 
4127295610fSBaptiste Daroussin 				if (hspans) {
4137295610fSBaptiste Daroussin 					hspans--;
4147295610fSBaptiste Daroussin 					cp = cp->next;
41561d06d6bSBaptiste Daroussin 					continue;
41661d06d6bSBaptiste Daroussin 				}
417*6d38604fSBaptiste Daroussin 				if (dp != NULL && (ic ||
418*6d38604fSBaptiste Daroussin 				    sp->layout->first->pos != TBL_CELL_SPAN)) {
4197295610fSBaptiste Daroussin 					hspans = dp->hspans;
42061d06d6bSBaptiste Daroussin 					dp = dp->next;
42161d06d6bSBaptiste Daroussin 				}
42261d06d6bSBaptiste Daroussin 
42361d06d6bSBaptiste Daroussin 				/*
42461d06d6bSBaptiste Daroussin 				 * Print one line of text in the cell
42561d06d6bSBaptiste Daroussin 				 * and remember whether there is more.
42661d06d6bSBaptiste Daroussin 				 */
42761d06d6bSBaptiste Daroussin 
42861d06d6bSBaptiste Daroussin 				tp->tcol++;
42961d06d6bSBaptiste Daroussin 				if (tp->tcol->col < tp->tcol->lastcol)
43061d06d6bSBaptiste Daroussin 					term_flushln(tp);
43161d06d6bSBaptiste Daroussin 				if (tp->tcol->col < tp->tcol->lastcol)
43261d06d6bSBaptiste Daroussin 					more = 1;
43361d06d6bSBaptiste Daroussin 
43461d06d6bSBaptiste Daroussin 				/*
43561d06d6bSBaptiste Daroussin 				 * Vertical frames between data cells,
43661d06d6bSBaptiste Daroussin 				 * but not after the last column.
43761d06d6bSBaptiste Daroussin 				 */
43861d06d6bSBaptiste Daroussin 
4397295610fSBaptiste Daroussin 				if (fc == 0 &&
4407295610fSBaptiste Daroussin 				    ((uvert == 0 && dvert == 0 &&
4417295610fSBaptiste Daroussin 				      cp != NULL && (cp->next == NULL ||
4427295610fSBaptiste Daroussin 				      !IS_HORIZ(cp->next))) ||
4437295610fSBaptiste Daroussin 				     tp->tcol + 1 ==
4447295610fSBaptiste Daroussin 				      tp->tcols + tp->lasttcol)) {
4457295610fSBaptiste Daroussin 					if (cp != NULL)
4467295610fSBaptiste Daroussin 						cp = cp->next;
44761d06d6bSBaptiste Daroussin 					continue;
4487295610fSBaptiste Daroussin 				}
44961d06d6bSBaptiste Daroussin 
45061d06d6bSBaptiste Daroussin 				if (tp->viscol < tp->tcol->rmargin) {
45161d06d6bSBaptiste Daroussin 					(*tp->advance)(tp, tp->tcol->rmargin
45261d06d6bSBaptiste Daroussin 					   - tp->viscol);
45361d06d6bSBaptiste Daroussin 					tp->viscol = tp->tcol->rmargin;
45461d06d6bSBaptiste Daroussin 				}
45561d06d6bSBaptiste Daroussin 				while (tp->viscol < tp->tcol->rmargin +
4567295610fSBaptiste Daroussin 				    tp->tbl.cols[ic].spacing / 2)
4577295610fSBaptiste Daroussin 					tbl_direct_border(tp,
4587295610fSBaptiste Daroussin 					    BHORIZ * lhori, 1);
45961d06d6bSBaptiste Daroussin 
46061d06d6bSBaptiste Daroussin 				if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
46161d06d6bSBaptiste Daroussin 					continue;
46261d06d6bSBaptiste Daroussin 
4637295610fSBaptiste Daroussin 				if (cp != NULL)
4647295610fSBaptiste Daroussin 					cp = cp->next;
46561d06d6bSBaptiste Daroussin 
4667295610fSBaptiste Daroussin 				rhori = (cp != NULL &&
4677295610fSBaptiste Daroussin 				     cp->pos == TBL_CELL_DHORIZ) ||
4687295610fSBaptiste Daroussin 				    (dp != NULL &&
4697295610fSBaptiste Daroussin 				     dp->pos == TBL_DATA_DHORIZ) ? 2 :
4707295610fSBaptiste Daroussin 				    (cp != NULL &&
4717295610fSBaptiste Daroussin 				     cp->pos == TBL_CELL_HORIZ) ||
4727295610fSBaptiste Daroussin 				    (dp != NULL &&
4737295610fSBaptiste Daroussin 				     dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
4747295610fSBaptiste Daroussin 
4757295610fSBaptiste Daroussin 				if (tp->tbl.cols[ic].spacing)
4767295610fSBaptiste Daroussin 					tbl_direct_border(tp,
4777295610fSBaptiste Daroussin 					    BLEFT * lhori + BRIGHT * rhori +
4787295610fSBaptiste Daroussin 					    BUP * uvert + BDOWN * dvert, 1);
4797295610fSBaptiste Daroussin 
4807295610fSBaptiste Daroussin 				if (tp->enc == TERMENC_UTF8)
4817295610fSBaptiste Daroussin 					uvert = dvert = 0;
4827295610fSBaptiste Daroussin 
48361d06d6bSBaptiste Daroussin 				if (tp->tbl.cols[ic].spacing > 2 &&
4847295610fSBaptiste Daroussin 				    (uvert > 1 || dvert > 1 || rhori))
4857295610fSBaptiste Daroussin 					tbl_direct_border(tp,
4867295610fSBaptiste Daroussin 					    BHORIZ * rhori +
4877295610fSBaptiste Daroussin 					    BUP * (uvert > 1) +
4887295610fSBaptiste Daroussin 					    BDOWN * (dvert > 1), 1);
48961d06d6bSBaptiste Daroussin 			}
49061d06d6bSBaptiste Daroussin 		}
49161d06d6bSBaptiste Daroussin 
49261d06d6bSBaptiste Daroussin 		/* Print the vertical frame at the end of each row. */
49361d06d6bSBaptiste Daroussin 
4947295610fSBaptiste Daroussin 		uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 :
4957295610fSBaptiste Daroussin 		    sp->opts->opts & TBL_OPT_BOX ? 1 : 0;
4967295610fSBaptiste Daroussin 		if (sp->pos == TBL_SPAN_DATA &&
4977295610fSBaptiste Daroussin 		    uvert < sp->layout->last->vert &&
4987295610fSBaptiste Daroussin 		    sp->layout->last->col + 1 == sp->opts->cols)
4997295610fSBaptiste Daroussin 			uvert = dvert = sp->layout->last->vert;
5007295610fSBaptiste Daroussin 		if (sp->next != NULL &&
5017295610fSBaptiste Daroussin 		    dvert < sp->next->layout->last->vert &&
5027295610fSBaptiste Daroussin 		    sp->next->layout->last->col + 1 == sp->opts->cols)
5037295610fSBaptiste Daroussin 			dvert = sp->next->layout->last->vert;
5047295610fSBaptiste Daroussin 		if (sp->prev != NULL &&
5057295610fSBaptiste Daroussin 		    uvert < sp->prev->layout->last->vert &&
50661d06d6bSBaptiste Daroussin 		    sp->prev->layout->last->col + 1 == sp->opts->cols &&
50761d06d6bSBaptiste Daroussin 		    (horiz || (IS_HORIZ(sp->layout->last) &&
5087295610fSBaptiste Daroussin 		     !IS_HORIZ(sp->prev->layout->last))))
5097295610fSBaptiste Daroussin 			uvert = sp->prev->layout->last->vert;
5107295610fSBaptiste Daroussin 		lhori = sp->pos == TBL_SPAN_DHORIZ ||
5117295610fSBaptiste Daroussin 		    (sp->last != NULL &&
5127295610fSBaptiste Daroussin 		     sp->last->pos == TBL_DATA_DHORIZ &&
5137295610fSBaptiste Daroussin 		     sp->last->layout->col + 1 == sp->opts->cols) ||
5147295610fSBaptiste Daroussin 		    (sp->layout->last->pos == TBL_CELL_DHORIZ &&
5157295610fSBaptiste Daroussin 		     sp->layout->last->col + 1 == sp->opts->cols) ? 2 :
5167295610fSBaptiste Daroussin 		    sp->pos == TBL_SPAN_HORIZ ||
5177295610fSBaptiste Daroussin 		    (sp->last != NULL &&
5187295610fSBaptiste Daroussin 		     sp->last->pos == TBL_DATA_HORIZ &&
5197295610fSBaptiste Daroussin 		     sp->last->layout->col + 1 == sp->opts->cols) ||
5207295610fSBaptiste Daroussin 		    (sp->layout->last->pos == TBL_CELL_HORIZ &&
5217295610fSBaptiste Daroussin 		     sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0;
5227295610fSBaptiste Daroussin 		fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori;
5237295610fSBaptiste Daroussin 		if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) {
52461d06d6bSBaptiste Daroussin 			if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
52561d06d6bSBaptiste Daroussin 			    sp->layout->last->col + 1 < sp->opts->cols)) {
52661d06d6bSBaptiste Daroussin 				tp->tcol++;
5277295610fSBaptiste Daroussin 				do {
5287295610fSBaptiste Daroussin 					tbl_direct_border(tp,
5297295610fSBaptiste Daroussin 					    BHORIZ * lhori, 1);
5307295610fSBaptiste Daroussin 				} while (tp->viscol < tp->tcol->offset);
53161d06d6bSBaptiste Daroussin 			}
5327295610fSBaptiste Daroussin 			tbl_direct_border(tp, fc, 1);
53361d06d6bSBaptiste Daroussin 		}
53461d06d6bSBaptiste Daroussin 		(*tp->endline)(tp);
53561d06d6bSBaptiste Daroussin 		tp->viscol = 0;
53661d06d6bSBaptiste Daroussin 	} while (more);
53761d06d6bSBaptiste Daroussin 
53861d06d6bSBaptiste Daroussin 	/*
53961d06d6bSBaptiste Daroussin 	 * Clean up after this row.  If it is the last line
54061d06d6bSBaptiste Daroussin 	 * of the table, print the box line and clean up
54161d06d6bSBaptiste Daroussin 	 * column data; otherwise, print the allbox line.
54261d06d6bSBaptiste Daroussin 	 */
54361d06d6bSBaptiste Daroussin 
54461d06d6bSBaptiste Daroussin 	term_setcol(tp, 1);
54561d06d6bSBaptiste Daroussin 	tp->flags &= ~TERMP_MULTICOL;
54661d06d6bSBaptiste Daroussin 	tp->tcol->rmargin = tp->maxrmargin;
54761d06d6bSBaptiste Daroussin 	if (sp->next == NULL) {
54861d06d6bSBaptiste Daroussin 		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
54945a5aec3SBaptiste Daroussin 			tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX);
55061d06d6bSBaptiste Daroussin 			tp->skipvsp = 1;
55161d06d6bSBaptiste Daroussin 		}
5527295610fSBaptiste Daroussin 		if (tp->enc == TERMENC_ASCII &&
5537295610fSBaptiste Daroussin 		    sp->opts->opts & TBL_OPT_DBOX) {
55445a5aec3SBaptiste Daroussin 			tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX);
55561d06d6bSBaptiste Daroussin 			tp->skipvsp = 2;
55661d06d6bSBaptiste Daroussin 		}
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;
8237295610fSBaptiste Daroussin 	while (hspans--)
82461d06d6bSBaptiste Daroussin 		width += tp->tbl.cols[++ic].width + 3;
82561d06d6bSBaptiste Daroussin 
82661d06d6bSBaptiste Daroussin 	padr = width > len ? width - len : 0;
82761d06d6bSBaptiste Daroussin 	padl = 0;
82861d06d6bSBaptiste Daroussin 
82961d06d6bSBaptiste Daroussin 	switch (dp->layout->pos) {
83061d06d6bSBaptiste Daroussin 	case TBL_CELL_LONG:
83161d06d6bSBaptiste Daroussin 		padl = term_len(tp, 1);
83261d06d6bSBaptiste Daroussin 		padr = padr > padl ? padr - padl : 0;
83361d06d6bSBaptiste Daroussin 		break;
83461d06d6bSBaptiste Daroussin 	case TBL_CELL_CENTRE:
83561d06d6bSBaptiste Daroussin 		if (2 > padr)
83661d06d6bSBaptiste Daroussin 			break;
83761d06d6bSBaptiste Daroussin 		padl = padr / 2;
83861d06d6bSBaptiste Daroussin 		padr -= padl;
83961d06d6bSBaptiste Daroussin 		break;
84061d06d6bSBaptiste Daroussin 	case TBL_CELL_RIGHT:
84161d06d6bSBaptiste Daroussin 		padl = padr;
84261d06d6bSBaptiste Daroussin 		padr = 0;
84361d06d6bSBaptiste Daroussin 		break;
84461d06d6bSBaptiste Daroussin 	default:
84561d06d6bSBaptiste Daroussin 		break;
84661d06d6bSBaptiste Daroussin 	}
84761d06d6bSBaptiste Daroussin 
8487295610fSBaptiste Daroussin 	tbl_fill_char(tp, ASCII_NBRSP, padl);
84961d06d6bSBaptiste Daroussin 	tbl_word(tp, dp);
8507295610fSBaptiste Daroussin 	tbl_fill_char(tp, ASCII_NBRSP, padr);
85161d06d6bSBaptiste Daroussin }
85261d06d6bSBaptiste Daroussin 
85361d06d6bSBaptiste Daroussin static void
tbl_number(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)85461d06d6bSBaptiste Daroussin tbl_number(struct termp *tp, const struct tbl_opts *opts,
85561d06d6bSBaptiste Daroussin 		const struct tbl_dat *dp,
85661d06d6bSBaptiste Daroussin 		const struct roffcol *col)
85761d06d6bSBaptiste Daroussin {
8587295610fSBaptiste Daroussin 	const char	*cp, *lastdigit, *lastpoint;
8597295610fSBaptiste Daroussin 	size_t		 intsz, padl, totsz;
86061d06d6bSBaptiste Daroussin 	char		 buf[2];
86161d06d6bSBaptiste Daroussin 
86261d06d6bSBaptiste Daroussin 	/*
8637295610fSBaptiste Daroussin 	 * Almost the same code as in tblcalc_number():
8647295610fSBaptiste Daroussin 	 * First find the position of the decimal point.
86561d06d6bSBaptiste Daroussin 	 */
86661d06d6bSBaptiste Daroussin 
86761d06d6bSBaptiste Daroussin 	assert(dp->string);
8687295610fSBaptiste Daroussin 	lastdigit = lastpoint = NULL;
8697295610fSBaptiste Daroussin 	for (cp = dp->string; cp[0] != '\0'; cp++) {
8707295610fSBaptiste Daroussin 		if (cp[0] == '\\' && cp[1] == '&') {
8717295610fSBaptiste Daroussin 			lastdigit = lastpoint = cp;
8727295610fSBaptiste Daroussin 			break;
8737295610fSBaptiste Daroussin 		} else if (cp[0] == opts->decimal &&
8747295610fSBaptiste Daroussin 		    (isdigit((unsigned char)cp[1]) ||
8757295610fSBaptiste Daroussin 		     (cp > dp->string && isdigit((unsigned char)cp[-1]))))
8767295610fSBaptiste Daroussin 			lastpoint = cp;
8777295610fSBaptiste Daroussin 		else if (isdigit((unsigned char)cp[0]))
8787295610fSBaptiste Daroussin 			lastdigit = cp;
87961d06d6bSBaptiste Daroussin 	}
88061d06d6bSBaptiste Daroussin 
8817295610fSBaptiste Daroussin 	/* Then measure both widths. */
8827295610fSBaptiste Daroussin 
88361d06d6bSBaptiste Daroussin 	padl = 0;
8847295610fSBaptiste Daroussin 	totsz = term_strlen(tp, dp->string);
8857295610fSBaptiste Daroussin 	if (lastdigit != NULL) {
8867295610fSBaptiste Daroussin 		if (lastpoint == NULL)
8877295610fSBaptiste Daroussin 			lastpoint = lastdigit + 1;
8887295610fSBaptiste Daroussin 		intsz = 0;
8897295610fSBaptiste Daroussin 		buf[1] = '\0';
8907295610fSBaptiste Daroussin 		for (cp = dp->string; cp < lastpoint; cp++) {
8917295610fSBaptiste Daroussin 			buf[0] = cp[0];
8927295610fSBaptiste Daroussin 			intsz += term_strlen(tp, buf);
8937295610fSBaptiste Daroussin 		}
8947295610fSBaptiste Daroussin 
8957295610fSBaptiste Daroussin 		/*
8967295610fSBaptiste Daroussin 		 * Pad left to match the decimal position,
8977295610fSBaptiste Daroussin 		 * but avoid exceeding the total column width.
8987295610fSBaptiste Daroussin 		 */
8997295610fSBaptiste Daroussin 
9007295610fSBaptiste Daroussin 		if (col->decimal > intsz && col->width > totsz) {
9017295610fSBaptiste Daroussin 			padl = col->decimal - intsz;
9027295610fSBaptiste Daroussin 			if (padl + totsz > col->width)
9037295610fSBaptiste Daroussin 				padl = col->width - totsz;
9047295610fSBaptiste Daroussin 		}
9057295610fSBaptiste Daroussin 
9067295610fSBaptiste Daroussin 	/* If it is not a number, simply center the string. */
9077295610fSBaptiste Daroussin 
9087295610fSBaptiste Daroussin 	} else if (col->width > totsz)
9097295610fSBaptiste Daroussin 		padl = (col->width - totsz) / 2;
9107295610fSBaptiste Daroussin 
9117295610fSBaptiste Daroussin 	tbl_fill_char(tp, ASCII_NBRSP, padl);
91261d06d6bSBaptiste Daroussin 	tbl_word(tp, dp);
9137295610fSBaptiste Daroussin 
9147295610fSBaptiste Daroussin 	/* Pad right to fill the column.  */
9157295610fSBaptiste Daroussin 
9167295610fSBaptiste Daroussin 	if (col->width > padl + totsz)
9177295610fSBaptiste Daroussin 		tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
91861d06d6bSBaptiste Daroussin }
91961d06d6bSBaptiste Daroussin 
92061d06d6bSBaptiste Daroussin static void
tbl_word(struct termp * tp,const struct tbl_dat * dp)92161d06d6bSBaptiste Daroussin tbl_word(struct termp *tp, const struct tbl_dat *dp)
92261d06d6bSBaptiste Daroussin {
92361d06d6bSBaptiste Daroussin 	int		 prev_font;
92461d06d6bSBaptiste Daroussin 
92561d06d6bSBaptiste Daroussin 	prev_font = tp->fonti;
926*6d38604fSBaptiste Daroussin 	switch (dp->layout->font) {
927*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTBI:
928*6d38604fSBaptiste Daroussin 			term_fontpush(tp, TERMFONT_BI);
929*6d38604fSBaptiste Daroussin 			break;
930*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTBOLD:
931*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCB:
93261d06d6bSBaptiste Daroussin 			term_fontpush(tp, TERMFONT_BOLD);
933*6d38604fSBaptiste Daroussin 			break;
934*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTITALIC:
935*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCI:
93661d06d6bSBaptiste Daroussin 			term_fontpush(tp, TERMFONT_UNDER);
937*6d38604fSBaptiste Daroussin 			break;
938*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTROMAN:
939*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCR:
940*6d38604fSBaptiste Daroussin 			break;
941*6d38604fSBaptiste Daroussin 		default:
942*6d38604fSBaptiste Daroussin 			abort();
943*6d38604fSBaptiste Daroussin 	}
94461d06d6bSBaptiste Daroussin 
94561d06d6bSBaptiste Daroussin 	term_word(tp, dp->string);
94661d06d6bSBaptiste Daroussin 
94761d06d6bSBaptiste Daroussin 	term_fontpopq(tp, prev_font);
94861d06d6bSBaptiste Daroussin }
949