1 1.1.1.9 wiz /* Id: tbl_term.c,v 1.75 2021/08/10 12:55:04 schwarze Exp */ 2 1.1 joerg /* 3 1.1.1.3 joerg * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv> 4 1.1.1.9 wiz * Copyright (c) 2011-2021 Ingo Schwarze <schwarze (at) openbsd.org> 5 1.1 joerg * 6 1.1 joerg * Permission to use, copy, modify, and distribute this software for any 7 1.1 joerg * purpose with or without fee is hereby granted, provided that the above 8 1.1 joerg * copyright notice and this permission notice appear in all copies. 9 1.1 joerg * 10 1.1 joerg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 1.1 joerg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 1.1 joerg */ 18 1.1 joerg #include "config.h" 19 1.1.1.5 christos 20 1.1.1.5 christos #include <sys/types.h> 21 1.1 joerg 22 1.1 joerg #include <assert.h> 23 1.1.1.8 christos #include <ctype.h> 24 1.1 joerg #include <stdio.h> 25 1.1 joerg #include <stdlib.h> 26 1.1 joerg #include <string.h> 27 1.1 joerg 28 1.1 joerg #include "mandoc.h" 29 1.1.1.8 christos #include "tbl.h" 30 1.1 joerg #include "out.h" 31 1.1 joerg #include "term.h" 32 1.1 joerg 33 1.1.1.7 christos #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \ 34 1.1.1.7 christos (cp)->pos == TBL_CELL_DHORIZ) 35 1.1.1.7 christos 36 1.1.1.8 christos 37 1.1 joerg static size_t term_tbl_len(size_t, void *); 38 1.1 joerg static size_t term_tbl_strlen(const char *, void *); 39 1.1.1.7 christos static size_t term_tbl_sulen(const struct roffsu *, void *); 40 1.1.1.4 joerg static void tbl_data(struct termp *, const struct tbl_opts *, 41 1.1.1.7 christos const struct tbl_cell *, 42 1.1.1.5 christos const struct tbl_dat *, 43 1.1 joerg const struct roffcol *); 44 1.1.1.8 christos static void tbl_direct_border(struct termp *, int, size_t); 45 1.1.1.8 christos static void tbl_fill_border(struct termp *, int, size_t); 46 1.1.1.8 christos static void tbl_fill_char(struct termp *, char, size_t); 47 1.1.1.8 christos static void tbl_fill_string(struct termp *, const char *, size_t); 48 1.1.1.8 christos static void tbl_hrule(struct termp *, const struct tbl_span *, 49 1.1.1.9 wiz const struct tbl_span *, const struct tbl_span *, 50 1.1.1.9 wiz int); 51 1.1.1.5 christos static void tbl_literal(struct termp *, const struct tbl_dat *, 52 1.1 joerg const struct roffcol *); 53 1.1.1.5 christos static void tbl_number(struct termp *, const struct tbl_opts *, 54 1.1.1.5 christos const struct tbl_dat *, 55 1.1 joerg const struct roffcol *); 56 1.1.1.5 christos static void tbl_word(struct termp *, const struct tbl_dat *); 57 1.1 joerg 58 1.1 joerg 59 1.1.1.8 christos /* 60 1.1.1.8 christos * The following border-character tables are indexed 61 1.1.1.8 christos * by ternary (3-based) numbers, as opposed to binary or decimal. 62 1.1.1.8 christos * Each ternary digit describes the line width in one direction: 63 1.1.1.8 christos * 0 means no line, 1 single or light line, 2 double or heavy line. 64 1.1.1.8 christos */ 65 1.1.1.8 christos 66 1.1.1.8 christos /* Positional values of the four directions. */ 67 1.1.1.8 christos #define BRIGHT 1 68 1.1.1.8 christos #define BDOWN 3 69 1.1.1.8 christos #define BLEFT (3 * 3) 70 1.1.1.8 christos #define BUP (3 * 3 * 3) 71 1.1.1.8 christos #define BHORIZ (BLEFT + BRIGHT) 72 1.1.1.8 christos 73 1.1.1.8 christos /* Code points to use for each combination of widths. */ 74 1.1.1.8 christos static const int borders_utf8[81] = { 75 1.1.1.8 christos 0x0020, 0x2576, 0x257a, /* 000 right */ 76 1.1.1.8 christos 0x2577, 0x250c, 0x250d, /* 001 down */ 77 1.1.1.8 christos 0x257b, 0x250e, 0x250f, /* 002 */ 78 1.1.1.8 christos 0x2574, 0x2500, 0x257c, /* 010 left */ 79 1.1.1.8 christos 0x2510, 0x252c, 0x252e, /* 011 left down */ 80 1.1.1.8 christos 0x2512, 0x2530, 0x2532, /* 012 */ 81 1.1.1.8 christos 0x2578, 0x257e, 0x2501, /* 020 left */ 82 1.1.1.8 christos 0x2511, 0x252d, 0x252f, /* 021 left down */ 83 1.1.1.8 christos 0x2513, 0x2531, 0x2533, /* 022 */ 84 1.1.1.8 christos 0x2575, 0x2514, 0x2515, /* 100 up */ 85 1.1.1.8 christos 0x2502, 0x251c, 0x251d, /* 101 up down */ 86 1.1.1.8 christos 0x257d, 0x251f, 0x2522, /* 102 */ 87 1.1.1.8 christos 0x2518, 0x2534, 0x2536, /* 110 up left */ 88 1.1.1.8 christos 0x2524, 0x253c, 0x253e, /* 111 all */ 89 1.1.1.8 christos 0x2527, 0x2541, 0x2546, /* 112 */ 90 1.1.1.8 christos 0x2519, 0x2535, 0x2537, /* 120 up left */ 91 1.1.1.8 christos 0x2525, 0x253d, 0x253f, /* 121 all */ 92 1.1.1.8 christos 0x252a, 0x2545, 0x2548, /* 122 */ 93 1.1.1.8 christos 0x2579, 0x2516, 0x2517, /* 200 up */ 94 1.1.1.8 christos 0x257f, 0x251e, 0x2521, /* 201 up down */ 95 1.1.1.8 christos 0x2503, 0x2520, 0x2523, /* 202 */ 96 1.1.1.8 christos 0x251a, 0x2538, 0x253a, /* 210 up left */ 97 1.1.1.8 christos 0x2526, 0x2540, 0x2544, /* 211 all */ 98 1.1.1.8 christos 0x2528, 0x2542, 0x254a, /* 212 */ 99 1.1.1.8 christos 0x251b, 0x2539, 0x253b, /* 220 up left */ 100 1.1.1.8 christos 0x2529, 0x2543, 0x2547, /* 221 all */ 101 1.1.1.8 christos 0x252b, 0x2549, 0x254b, /* 222 */ 102 1.1.1.8 christos }; 103 1.1.1.8 christos 104 1.1.1.8 christos /* ASCII approximations for these code points, compatible with groff. */ 105 1.1.1.8 christos static const int borders_ascii[81] = { 106 1.1.1.8 christos ' ', '-', '=', /* 000 right */ 107 1.1.1.8 christos '|', '+', '+', /* 001 down */ 108 1.1.1.8 christos '|', '+', '+', /* 002 */ 109 1.1.1.8 christos '-', '-', '=', /* 010 left */ 110 1.1.1.8 christos '+', '+', '+', /* 011 left down */ 111 1.1.1.8 christos '+', '+', '+', /* 012 */ 112 1.1.1.8 christos '=', '=', '=', /* 020 left */ 113 1.1.1.8 christos '+', '+', '+', /* 021 left down */ 114 1.1.1.8 christos '+', '+', '+', /* 022 */ 115 1.1.1.8 christos '|', '+', '+', /* 100 up */ 116 1.1.1.8 christos '|', '+', '+', /* 101 up down */ 117 1.1.1.8 christos '|', '+', '+', /* 102 */ 118 1.1.1.8 christos '+', '+', '+', /* 110 up left */ 119 1.1.1.8 christos '+', '+', '+', /* 111 all */ 120 1.1.1.8 christos '+', '+', '+', /* 112 */ 121 1.1.1.8 christos '+', '+', '+', /* 120 up left */ 122 1.1.1.8 christos '+', '+', '+', /* 121 all */ 123 1.1.1.8 christos '+', '+', '+', /* 122 */ 124 1.1.1.8 christos '|', '+', '+', /* 200 up */ 125 1.1.1.8 christos '|', '+', '+', /* 201 up down */ 126 1.1.1.8 christos '|', '+', '+', /* 202 */ 127 1.1.1.8 christos '+', '+', '+', /* 210 up left */ 128 1.1.1.8 christos '+', '+', '+', /* 211 all */ 129 1.1.1.8 christos '+', '+', '+', /* 212 */ 130 1.1.1.8 christos '+', '+', '+', /* 220 up left */ 131 1.1.1.8 christos '+', '+', '+', /* 221 all */ 132 1.1.1.8 christos '+', '+', '+', /* 222 */ 133 1.1.1.8 christos }; 134 1.1.1.8 christos 135 1.1.1.8 christos /* Either of the above according to the selected output encoding. */ 136 1.1.1.8 christos static const int *borders_locale; 137 1.1.1.8 christos 138 1.1.1.8 christos 139 1.1 joerg static size_t 140 1.1.1.7 christos term_tbl_sulen(const struct roffsu *su, void *arg) 141 1.1 joerg { 142 1.1.1.7 christos int i; 143 1.1.1.7 christos 144 1.1.1.7 christos i = term_hen((const struct termp *)arg, su); 145 1.1.1.7 christos return i > 0 ? i : 0; 146 1.1.1.7 christos } 147 1.1 joerg 148 1.1.1.7 christos static size_t 149 1.1.1.7 christos term_tbl_strlen(const char *p, void *arg) 150 1.1.1.7 christos { 151 1.1.1.6 christos return term_strlen((const struct termp *)arg, p); 152 1.1 joerg } 153 1.1 joerg 154 1.1 joerg static size_t 155 1.1 joerg term_tbl_len(size_t sz, void *arg) 156 1.1 joerg { 157 1.1.1.6 christos return term_len((const struct termp *)arg, sz); 158 1.1 joerg } 159 1.1 joerg 160 1.1.1.8 christos 161 1.1 joerg void 162 1.1 joerg term_tbl(struct termp *tp, const struct tbl_span *sp) 163 1.1 joerg { 164 1.1.1.8 christos const struct tbl_cell *cp, *cpn, *cpp, *cps; 165 1.1 joerg const struct tbl_dat *dp; 166 1.1.1.5 christos static size_t offset; 167 1.1.1.9 wiz size_t save_offset; 168 1.1.1.7 christos size_t coloff, tsz; 169 1.1.1.8 christos int hspans, ic, more; 170 1.1.1.8 christos int dvert, fc, horiz, lhori, rhori, uvert; 171 1.1 joerg 172 1.1 joerg /* Inhibit printing of spaces: we do padding ourselves. */ 173 1.1 joerg 174 1.1.1.7 christos tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE; 175 1.1.1.8 christos save_offset = tp->tcol->offset; 176 1.1 joerg 177 1.1 joerg /* 178 1.1 joerg * The first time we're invoked for a given table block, 179 1.1 joerg * calculate the table widths and decimal positions. 180 1.1 joerg */ 181 1.1 joerg 182 1.1.1.5 christos if (tp->tbl.cols == NULL) { 183 1.1.1.8 christos borders_locale = tp->enc == TERMENC_UTF8 ? 184 1.1.1.8 christos borders_utf8 : borders_ascii; 185 1.1.1.8 christos 186 1.1 joerg tp->tbl.len = term_tbl_len; 187 1.1 joerg tp->tbl.slen = term_tbl_strlen; 188 1.1.1.7 christos tp->tbl.sulen = term_tbl_sulen; 189 1.1 joerg tp->tbl.arg = tp; 190 1.1 joerg 191 1.1.1.7 christos tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); 192 1.1.1.7 christos 193 1.1.1.5 christos /* Center the table as a whole. */ 194 1.1 joerg 195 1.1.1.7 christos offset = tp->tcol->offset; 196 1.1.1.5 christos if (sp->opts->opts & TBL_OPT_CENTRE) { 197 1.1.1.5 christos tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) 198 1.1.1.5 christos ? 2 : !!sp->opts->lvert + !!sp->opts->rvert; 199 1.1.1.7 christos for (ic = 0; ic + 1 < sp->opts->cols; ic++) 200 1.1.1.7 christos tsz += tp->tbl.cols[ic].width + 201 1.1.1.7 christos tp->tbl.cols[ic].spacing; 202 1.1.1.7 christos if (sp->opts->cols) 203 1.1.1.7 christos tsz += tp->tbl.cols[sp->opts->cols - 1].width; 204 1.1.1.7 christos if (offset + tsz > tp->tcol->rmargin) 205 1.1.1.5 christos tsz -= 1; 206 1.1.1.8 christos offset = offset + tp->tcol->rmargin > tsz ? 207 1.1.1.7 christos (offset + tp->tcol->rmargin - tsz) / 2 : 0; 208 1.1.1.8 christos tp->tcol->offset = offset; 209 1.1.1.5 christos } 210 1.1.1.5 christos 211 1.1.1.5 christos /* Horizontal frame at the start of boxed tables. */ 212 1.1.1.5 christos 213 1.1.1.8 christos if (tp->enc == TERMENC_ASCII && 214 1.1.1.8 christos sp->opts->opts & TBL_OPT_DBOX) 215 1.1.1.9 wiz tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX); 216 1.1.1.5 christos if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) 217 1.1.1.9 wiz tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX); 218 1.1.1.3 joerg } 219 1.1 joerg 220 1.1.1.7 christos /* Set up the columns. */ 221 1.1 joerg 222 1.1.1.7 christos tp->flags |= TERMP_MULTICOL; 223 1.1.1.8 christos tp->tcol->offset = offset; 224 1.1.1.7 christos horiz = 0; 225 1.1.1.7 christos switch (sp->pos) { 226 1.1.1.7 christos case TBL_SPAN_HORIZ: 227 1.1.1.7 christos case TBL_SPAN_DHORIZ: 228 1.1.1.7 christos horiz = 1; 229 1.1.1.7 christos term_setcol(tp, 1); 230 1.1.1.7 christos break; 231 1.1.1.7 christos case TBL_SPAN_DATA: 232 1.1.1.7 christos term_setcol(tp, sp->opts->cols + 2); 233 1.1.1.7 christos coloff = tp->tcol->offset; 234 1.1.1.7 christos 235 1.1.1.7 christos /* Set up a column for a left vertical frame. */ 236 1.1.1.7 christos 237 1.1.1.7 christos if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || 238 1.1.1.7 christos sp->opts->lvert) 239 1.1.1.7 christos coloff++; 240 1.1.1.7 christos tp->tcol->rmargin = coloff; 241 1.1.1.5 christos 242 1.1.1.7 christos /* Set up the data columns. */ 243 1.1 joerg 244 1.1.1.7 christos dp = sp->first; 245 1.1.1.8 christos hspans = 0; 246 1.1.1.7 christos for (ic = 0; ic < sp->opts->cols; ic++) { 247 1.1.1.8 christos if (hspans == 0) { 248 1.1.1.7 christos tp->tcol++; 249 1.1.1.7 christos tp->tcol->offset = coloff; 250 1.1.1.7 christos } 251 1.1.1.7 christos coloff += tp->tbl.cols[ic].width; 252 1.1.1.7 christos tp->tcol->rmargin = coloff; 253 1.1.1.7 christos if (ic + 1 < sp->opts->cols) 254 1.1.1.7 christos coloff += tp->tbl.cols[ic].spacing; 255 1.1.1.8 christos if (hspans) { 256 1.1.1.8 christos hspans--; 257 1.1.1.7 christos continue; 258 1.1.1.7 christos } 259 1.1.1.9 wiz if (dp != NULL && 260 1.1.1.9 wiz (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { 261 1.1.1.9 wiz hspans = dp->hspans; 262 1.1.1.7 christos dp = dp->next; 263 1.1.1.9 wiz } 264 1.1.1.7 christos } 265 1.1.1.7 christos 266 1.1.1.7 christos /* Set up a column for a right vertical frame. */ 267 1.1.1.7 christos 268 1.1.1.7 christos tp->tcol++; 269 1.1.1.7 christos tp->tcol->offset = coloff + 1; 270 1.1.1.7 christos tp->tcol->rmargin = tp->maxrmargin; 271 1.1.1.7 christos 272 1.1.1.7 christos /* Spans may have reduced the number of columns. */ 273 1.1.1.7 christos 274 1.1.1.7 christos tp->lasttcol = tp->tcol - tp->tcols; 275 1.1 joerg 276 1.1.1.7 christos /* Fill the buffers for all data columns. */ 277 1.1.1.7 christos 278 1.1.1.7 christos tp->tcol = tp->tcols; 279 1.1.1.7 christos cp = cpn = sp->layout->first; 280 1.1 joerg dp = sp->first; 281 1.1.1.8 christos hspans = 0; 282 1.1.1.5 christos for (ic = 0; ic < sp->opts->cols; ic++) { 283 1.1.1.7 christos if (cpn != NULL) { 284 1.1.1.7 christos cp = cpn; 285 1.1.1.7 christos cpn = cpn->next; 286 1.1.1.7 christos } 287 1.1.1.8 christos if (hspans) { 288 1.1.1.8 christos hspans--; 289 1.1.1.7 christos continue; 290 1.1.1.7 christos } 291 1.1.1.7 christos tp->tcol++; 292 1.1.1.7 christos tp->col = 0; 293 1.1.1.7 christos tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); 294 1.1.1.9 wiz if (dp != NULL && 295 1.1.1.9 wiz (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { 296 1.1.1.9 wiz hspans = dp->hspans; 297 1.1.1.7 christos dp = dp->next; 298 1.1.1.9 wiz } 299 1.1.1.7 christos } 300 1.1.1.7 christos break; 301 1.1.1.7 christos } 302 1.1.1.4 joerg 303 1.1.1.7 christos do { 304 1.1.1.7 christos /* Print the vertical frame at the start of each row. */ 305 1.1.1.3 joerg 306 1.1.1.7 christos tp->tcol = tp->tcols; 307 1.1.1.8 christos uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 : 308 1.1.1.8 christos sp->opts->opts & TBL_OPT_BOX ? 1 : 0; 309 1.1.1.8 christos if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert) 310 1.1.1.8 christos uvert = dvert = sp->layout->vert; 311 1.1.1.8 christos if (sp->next != NULL && sp->next->pos == TBL_SPAN_DATA && 312 1.1.1.8 christos dvert < sp->next->layout->vert) 313 1.1.1.8 christos dvert = sp->next->layout->vert; 314 1.1.1.8 christos if (sp->prev != NULL && uvert < sp->prev->layout->vert && 315 1.1.1.8 christos (horiz || (IS_HORIZ(sp->layout->first) && 316 1.1.1.8 christos !IS_HORIZ(sp->prev->layout->first)))) 317 1.1.1.8 christos uvert = sp->prev->layout->vert; 318 1.1.1.8 christos rhori = sp->pos == TBL_SPAN_DHORIZ || 319 1.1.1.8 christos (sp->first != NULL && sp->first->pos == TBL_DATA_DHORIZ) || 320 1.1.1.8 christos sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 : 321 1.1.1.8 christos sp->pos == TBL_SPAN_HORIZ || 322 1.1.1.8 christos (sp->first != NULL && sp->first->pos == TBL_DATA_HORIZ) || 323 1.1.1.8 christos sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0; 324 1.1.1.8 christos fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori; 325 1.1.1.8 christos if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) { 326 1.1.1.7 christos (*tp->advance)(tp, tp->tcols->offset); 327 1.1.1.8 christos tp->viscol = tp->tcol->offset; 328 1.1.1.8 christos tbl_direct_border(tp, fc, 1); 329 1.1.1.7 christos } 330 1.1.1.7 christos 331 1.1.1.7 christos /* Print the data cells. */ 332 1.1.1.7 christos 333 1.1.1.7 christos more = 0; 334 1.1.1.8 christos if (horiz) 335 1.1.1.9 wiz tbl_hrule(tp, sp->prev, sp, sp->next, 0); 336 1.1.1.8 christos else { 337 1.1.1.7 christos cp = sp->layout->first; 338 1.1.1.7 christos cpn = sp->next == NULL ? NULL : 339 1.1.1.7 christos sp->next->layout->first; 340 1.1.1.7 christos cpp = sp->prev == NULL ? NULL : 341 1.1.1.7 christos sp->prev->layout->first; 342 1.1.1.7 christos dp = sp->first; 343 1.1.1.8 christos hspans = 0; 344 1.1.1.7 christos for (ic = 0; ic < sp->opts->cols; ic++) { 345 1.1.1.7 christos 346 1.1.1.7 christos /* 347 1.1.1.7 christos * Figure out whether to print a 348 1.1.1.7 christos * vertical line after this cell 349 1.1.1.7 christos * and advance to next layout cell. 350 1.1.1.7 christos */ 351 1.1.1.7 christos 352 1.1.1.8 christos uvert = dvert = fc = 0; 353 1.1.1.7 christos if (cp != NULL) { 354 1.1.1.8 christos cps = cp; 355 1.1.1.8 christos while (cps->next != NULL && 356 1.1.1.8 christos cps->next->pos == TBL_CELL_SPAN) 357 1.1.1.8 christos cps = cps->next; 358 1.1.1.8 christos if (sp->pos == TBL_SPAN_DATA) 359 1.1.1.8 christos uvert = dvert = cps->vert; 360 1.1.1.7 christos switch (cp->pos) { 361 1.1.1.7 christos case TBL_CELL_HORIZ: 362 1.1.1.8 christos fc = BHORIZ; 363 1.1.1.7 christos break; 364 1.1.1.7 christos case TBL_CELL_DHORIZ: 365 1.1.1.8 christos fc = BHORIZ * 2; 366 1.1.1.7 christos break; 367 1.1.1.7 christos default: 368 1.1.1.7 christos break; 369 1.1.1.7 christos } 370 1.1.1.7 christos } 371 1.1.1.7 christos if (cpp != NULL) { 372 1.1.1.8 christos if (uvert < cpp->vert && 373 1.1.1.7 christos cp != NULL && 374 1.1.1.7 christos ((IS_HORIZ(cp) && 375 1.1.1.7 christos !IS_HORIZ(cpp)) || 376 1.1.1.7 christos (cp->next != NULL && 377 1.1.1.7 christos cpp->next != NULL && 378 1.1.1.7 christos IS_HORIZ(cp->next) && 379 1.1.1.7 christos !IS_HORIZ(cpp->next)))) 380 1.1.1.8 christos uvert = cpp->vert; 381 1.1.1.7 christos cpp = cpp->next; 382 1.1.1.7 christos } 383 1.1.1.8 christos if (sp->opts->opts & TBL_OPT_ALLBOX) { 384 1.1.1.8 christos if (uvert == 0) 385 1.1.1.8 christos uvert = 1; 386 1.1.1.8 christos if (dvert == 0) 387 1.1.1.8 christos dvert = 1; 388 1.1.1.8 christos } 389 1.1.1.7 christos if (cpn != NULL) { 390 1.1.1.8 christos if (dvert == 0 || 391 1.1.1.8 christos (dvert < cpn->vert && 392 1.1.1.8 christos tp->enc == TERMENC_UTF8)) 393 1.1.1.8 christos dvert = cpn->vert; 394 1.1.1.7 christos cpn = cpn->next; 395 1.1.1.7 christos } 396 1.1.1.8 christos 397 1.1.1.8 christos lhori = (cp != NULL && 398 1.1.1.8 christos cp->pos == TBL_CELL_DHORIZ) || 399 1.1.1.8 christos (dp != NULL && 400 1.1.1.8 christos dp->pos == TBL_DATA_DHORIZ) ? 2 : 401 1.1.1.8 christos (cp != NULL && 402 1.1.1.8 christos cp->pos == TBL_CELL_HORIZ) || 403 1.1.1.8 christos (dp != NULL && 404 1.1.1.8 christos dp->pos == TBL_DATA_HORIZ) ? 1 : 0; 405 1.1.1.7 christos 406 1.1.1.7 christos /* 407 1.1.1.7 christos * Skip later cells in a span, 408 1.1.1.7 christos * figure out whether to start a span, 409 1.1.1.7 christos * and advance to next data cell. 410 1.1.1.7 christos */ 411 1.1.1.7 christos 412 1.1.1.8 christos if (hspans) { 413 1.1.1.8 christos hspans--; 414 1.1.1.8 christos cp = cp->next; 415 1.1.1.7 christos continue; 416 1.1.1.7 christos } 417 1.1.1.9 wiz if (dp != NULL && (ic || 418 1.1.1.9 wiz sp->layout->first->pos != TBL_CELL_SPAN)) { 419 1.1.1.8 christos hspans = dp->hspans; 420 1.1.1.9 wiz dp = dp->next; 421 1.1.1.5 christos } 422 1.1.1.5 christos 423 1.1.1.7 christos /* 424 1.1.1.7 christos * Print one line of text in the cell 425 1.1.1.7 christos * and remember whether there is more. 426 1.1.1.7 christos */ 427 1.1.1.7 christos 428 1.1.1.7 christos tp->tcol++; 429 1.1.1.7 christos if (tp->tcol->col < tp->tcol->lastcol) 430 1.1.1.7 christos term_flushln(tp); 431 1.1.1.7 christos if (tp->tcol->col < tp->tcol->lastcol) 432 1.1.1.7 christos more = 1; 433 1.1.1.7 christos 434 1.1.1.7 christos /* 435 1.1.1.7 christos * Vertical frames between data cells, 436 1.1.1.7 christos * but not after the last column. 437 1.1.1.7 christos */ 438 1.1.1.7 christos 439 1.1.1.8 christos if (fc == 0 && 440 1.1.1.8 christos ((uvert == 0 && dvert == 0 && 441 1.1.1.8 christos cp != NULL && (cp->next == NULL || 442 1.1.1.8 christos !IS_HORIZ(cp->next))) || 443 1.1.1.8 christos tp->tcol + 1 == 444 1.1.1.8 christos tp->tcols + tp->lasttcol)) { 445 1.1.1.8 christos if (cp != NULL) 446 1.1.1.8 christos cp = cp->next; 447 1.1.1.7 christos continue; 448 1.1.1.8 christos } 449 1.1.1.7 christos 450 1.1.1.7 christos if (tp->viscol < tp->tcol->rmargin) { 451 1.1.1.7 christos (*tp->advance)(tp, tp->tcol->rmargin 452 1.1.1.7 christos - tp->viscol); 453 1.1.1.7 christos tp->viscol = tp->tcol->rmargin; 454 1.1.1.7 christos } 455 1.1.1.7 christos while (tp->viscol < tp->tcol->rmargin + 456 1.1.1.8 christos tp->tbl.cols[ic].spacing / 2) 457 1.1.1.8 christos tbl_direct_border(tp, 458 1.1.1.8 christos BHORIZ * lhori, 1); 459 1.1.1.2 joerg 460 1.1.1.7 christos if (tp->tcol + 1 == tp->tcols + tp->lasttcol) 461 1.1.1.7 christos continue; 462 1.1.1.5 christos 463 1.1.1.8 christos if (cp != NULL) 464 1.1.1.8 christos cp = cp->next; 465 1.1.1.8 christos 466 1.1.1.8 christos rhori = (cp != NULL && 467 1.1.1.8 christos cp->pos == TBL_CELL_DHORIZ) || 468 1.1.1.8 christos (dp != NULL && 469 1.1.1.8 christos dp->pos == TBL_DATA_DHORIZ) ? 2 : 470 1.1.1.8 christos (cp != NULL && 471 1.1.1.8 christos cp->pos == TBL_CELL_HORIZ) || 472 1.1.1.8 christos (dp != NULL && 473 1.1.1.8 christos dp->pos == TBL_DATA_HORIZ) ? 1 : 0; 474 1.1.1.8 christos 475 1.1.1.8 christos if (tp->tbl.cols[ic].spacing) 476 1.1.1.8 christos tbl_direct_border(tp, 477 1.1.1.8 christos BLEFT * lhori + BRIGHT * rhori + 478 1.1.1.8 christos BUP * uvert + BDOWN * dvert, 1); 479 1.1.1.8 christos 480 1.1.1.8 christos if (tp->enc == TERMENC_UTF8) 481 1.1.1.8 christos uvert = dvert = 0; 482 1.1.1.7 christos 483 1.1.1.7 christos if (tp->tbl.cols[ic].spacing > 2 && 484 1.1.1.8 christos (uvert > 1 || dvert > 1 || rhori)) 485 1.1.1.8 christos tbl_direct_border(tp, 486 1.1.1.8 christos BHORIZ * rhori + 487 1.1.1.8 christos BUP * (uvert > 1) + 488 1.1.1.8 christos BDOWN * (dvert > 1), 1); 489 1.1.1.7 christos } 490 1.1.1.7 christos } 491 1.1.1.7 christos 492 1.1.1.7 christos /* Print the vertical frame at the end of each row. */ 493 1.1.1.7 christos 494 1.1.1.8 christos uvert = dvert = sp->opts->opts & TBL_OPT_DBOX ? 2 : 495 1.1.1.8 christos sp->opts->opts & TBL_OPT_BOX ? 1 : 0; 496 1.1.1.8 christos if (sp->pos == TBL_SPAN_DATA && 497 1.1.1.8 christos uvert < sp->layout->last->vert && 498 1.1.1.8 christos sp->layout->last->col + 1 == sp->opts->cols) 499 1.1.1.8 christos uvert = dvert = sp->layout->last->vert; 500 1.1.1.8 christos if (sp->next != NULL && 501 1.1.1.8 christos dvert < sp->next->layout->last->vert && 502 1.1.1.8 christos sp->next->layout->last->col + 1 == sp->opts->cols) 503 1.1.1.8 christos dvert = sp->next->layout->last->vert; 504 1.1.1.8 christos if (sp->prev != NULL && 505 1.1.1.8 christos uvert < sp->prev->layout->last->vert && 506 1.1.1.8 christos sp->prev->layout->last->col + 1 == sp->opts->cols && 507 1.1.1.8 christos (horiz || (IS_HORIZ(sp->layout->last) && 508 1.1.1.8 christos !IS_HORIZ(sp->prev->layout->last)))) 509 1.1.1.8 christos uvert = sp->prev->layout->last->vert; 510 1.1.1.8 christos lhori = sp->pos == TBL_SPAN_DHORIZ || 511 1.1.1.8 christos (sp->last != NULL && 512 1.1.1.8 christos sp->last->pos == TBL_DATA_DHORIZ && 513 1.1.1.8 christos sp->last->layout->col + 1 == sp->opts->cols) || 514 1.1.1.8 christos (sp->layout->last->pos == TBL_CELL_DHORIZ && 515 1.1.1.8 christos sp->layout->last->col + 1 == sp->opts->cols) ? 2 : 516 1.1.1.8 christos sp->pos == TBL_SPAN_HORIZ || 517 1.1.1.8 christos (sp->last != NULL && 518 1.1.1.8 christos sp->last->pos == TBL_DATA_HORIZ && 519 1.1.1.8 christos sp->last->layout->col + 1 == sp->opts->cols) || 520 1.1.1.8 christos (sp->layout->last->pos == TBL_CELL_HORIZ && 521 1.1.1.8 christos sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0; 522 1.1.1.8 christos fc = BUP * uvert + BDOWN * dvert + BLEFT * lhori; 523 1.1.1.8 christos if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) { 524 1.1.1.7 christos if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 || 525 1.1.1.7 christos sp->layout->last->col + 1 < sp->opts->cols)) { 526 1.1.1.7 christos tp->tcol++; 527 1.1.1.8 christos do { 528 1.1.1.8 christos tbl_direct_border(tp, 529 1.1.1.8 christos BHORIZ * lhori, 1); 530 1.1.1.8 christos } while (tp->viscol < tp->tcol->offset); 531 1.1.1.7 christos } 532 1.1.1.8 christos tbl_direct_border(tp, fc, 1); 533 1.1.1.7 christos } 534 1.1.1.7 christos (*tp->endline)(tp); 535 1.1.1.7 christos tp->viscol = 0; 536 1.1.1.7 christos } while (more); 537 1.1 joerg 538 1.1 joerg /* 539 1.1.1.7 christos * Clean up after this row. If it is the last line 540 1.1.1.7 christos * of the table, print the box line and clean up 541 1.1.1.7 christos * column data; otherwise, print the allbox line. 542 1.1 joerg */ 543 1.1 joerg 544 1.1.1.7 christos term_setcol(tp, 1); 545 1.1.1.7 christos tp->flags &= ~TERMP_MULTICOL; 546 1.1.1.7 christos tp->tcol->rmargin = tp->maxrmargin; 547 1.1.1.5 christos if (sp->next == NULL) { 548 1.1.1.5 christos if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { 549 1.1.1.9 wiz tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX); 550 1.1.1.4 joerg tp->skipvsp = 1; 551 1.1.1.4 joerg } 552 1.1.1.8 christos if (tp->enc == TERMENC_ASCII && 553 1.1.1.8 christos sp->opts->opts & TBL_OPT_DBOX) { 554 1.1.1.9 wiz tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX); 555 1.1.1.4 joerg tp->skipvsp = 2; 556 1.1.1.4 joerg } 557 1.1 joerg assert(tp->tbl.cols); 558 1.1 joerg free(tp->tbl.cols); 559 1.1 joerg tp->tbl.cols = NULL; 560 1.1.1.7 christos } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX && 561 1.1.1.7 christos (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA || 562 1.1.1.7 christos sp->next->next != NULL)) 563 1.1.1.9 wiz tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX); 564 1.1 joerg 565 1.1.1.8 christos tp->tcol->offset = save_offset; 566 1.1 joerg tp->flags &= ~TERMP_NONOSPACE; 567 1.1 joerg } 568 1.1 joerg 569 1.1 joerg static void 570 1.1.1.8 christos tbl_hrule(struct termp *tp, const struct tbl_span *spp, 571 1.1.1.9 wiz const struct tbl_span *sp, const struct tbl_span *spn, int flags) 572 1.1 joerg { 573 1.1.1.8 christos const struct tbl_cell *cpp; /* Layout cell above this line. */ 574 1.1.1.9 wiz const struct tbl_cell *cp; /* Layout cell in this line. */ 575 1.1.1.8 christos const struct tbl_cell *cpn; /* Layout cell below this line. */ 576 1.1.1.8 christos const struct tbl_dat *dpn; /* Data cell below this line. */ 577 1.1.1.8 christos const struct roffcol *col; /* Contains width and spacing. */ 578 1.1.1.8 christos int opts; /* For the table as a whole. */ 579 1.1.1.8 christos int bw; /* Box line width. */ 580 1.1.1.8 christos int hw; /* Horizontal line width. */ 581 1.1.1.8 christos int lw, rw; /* Left and right line widths. */ 582 1.1.1.8 christos int uw, dw; /* Vertical line widths. */ 583 1.1.1.8 christos 584 1.1.1.8 christos cpp = spp == NULL ? NULL : spp->layout->first; 585 1.1.1.9 wiz cp = sp == NULL ? NULL : sp->layout->first; 586 1.1.1.8 christos cpn = spn == NULL ? NULL : spn->layout->first; 587 1.1.1.8 christos dpn = NULL; 588 1.1.1.8 christos if (spn != NULL) { 589 1.1.1.8 christos if (spn->pos == TBL_SPAN_DATA) 590 1.1.1.8 christos dpn = spn->first; 591 1.1.1.8 christos else if (spn->next != NULL) 592 1.1.1.8 christos dpn = spn->next->first; 593 1.1.1.8 christos } 594 1.1.1.9 wiz opts = sp->opts->opts; 595 1.1.1.8 christos bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) : 596 1.1.1.8 christos opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0; 597 1.1.1.8 christos hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw : 598 1.1.1.9 wiz sp->pos == TBL_SPAN_DHORIZ ? 2 : 1; 599 1.1.1.8 christos 600 1.1.1.8 christos /* Print the left end of the line. */ 601 1.1.1.8 christos 602 1.1.1.8 christos if (tp->viscol == 0) { 603 1.1.1.8 christos (*tp->advance)(tp, tp->tcols->offset); 604 1.1.1.8 christos tp->viscol = tp->tcols->offset; 605 1.1.1.8 christos } 606 1.1.1.8 christos if (flags != 0) 607 1.1.1.8 christos tbl_direct_border(tp, 608 1.1.1.8 christos (spp == NULL ? 0 : BUP * bw) + 609 1.1.1.8 christos (spn == NULL ? 0 : BDOWN * bw) + 610 1.1.1.8 christos (spp == NULL || cpn == NULL || 611 1.1.1.8 christos cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1); 612 1.1.1.8 christos 613 1.1.1.9 wiz col = tp->tbl.cols; 614 1.1.1.5 christos for (;;) { 615 1.1.1.9 wiz if (cp == NULL) 616 1.1.1.9 wiz col++; 617 1.1.1.9 wiz else 618 1.1.1.9 wiz col = tp->tbl.cols + cp->col; 619 1.1.1.8 christos 620 1.1.1.8 christos /* Print the horizontal line inside this column. */ 621 1.1.1.8 christos 622 1.1.1.8 christos lw = cpp == NULL || cpn == NULL || 623 1.1.1.8 christos (cpn->pos != TBL_CELL_DOWN && 624 1.1.1.9 wiz (dpn == NULL || dpn->string == NULL || 625 1.1.1.9 wiz strcmp(dpn->string, "\\^") != 0)) 626 1.1.1.8 christos ? hw : 0; 627 1.1.1.8 christos tbl_direct_border(tp, BHORIZ * lw, 628 1.1.1.8 christos col->width + col->spacing / 2); 629 1.1.1.8 christos 630 1.1.1.8 christos /* 631 1.1.1.8 christos * Figure out whether a vertical line is crossing 632 1.1.1.8 christos * at the end of this column, 633 1.1.1.8 christos * and advance to the next column. 634 1.1.1.8 christos */ 635 1.1.1.8 christos 636 1.1.1.8 christos uw = dw = 0; 637 1.1.1.7 christos if (cpp != NULL) { 638 1.1.1.8 christos if (flags != TBL_OPT_DBOX) { 639 1.1.1.8 christos uw = cpp->vert; 640 1.1.1.8 christos if (uw == 0 && opts & TBL_OPT_ALLBOX) 641 1.1.1.8 christos uw = 1; 642 1.1.1.8 christos } 643 1.1.1.7 christos cpp = cpp->next; 644 1.1.1.9 wiz } else if (spp != NULL && opts & TBL_OPT_ALLBOX) 645 1.1.1.9 wiz uw = 1; 646 1.1.1.9 wiz if (cp != NULL) 647 1.1.1.9 wiz cp = cp->next; 648 1.1.1.7 christos if (cpn != NULL) { 649 1.1.1.8 christos if (flags != TBL_OPT_DBOX) { 650 1.1.1.8 christos dw = cpn->vert; 651 1.1.1.8 christos if (dw == 0 && opts & TBL_OPT_ALLBOX) 652 1.1.1.8 christos dw = 1; 653 1.1.1.8 christos } 654 1.1.1.7 christos cpn = cpn->next; 655 1.1.1.8 christos while (dpn != NULL && dpn->layout != cpn) 656 1.1.1.8 christos dpn = dpn->next; 657 1.1.1.9 wiz } else if (spn != NULL && opts & TBL_OPT_ALLBOX) 658 1.1.1.9 wiz dw = 1; 659 1.1.1.9 wiz if (col + 1 == tp->tbl.cols + sp->opts->cols) 660 1.1.1.8 christos break; 661 1.1.1.8 christos 662 1.1.1.8 christos /* Vertical lines do not cross spanned cells. */ 663 1.1.1.8 christos 664 1.1.1.8 christos if (cpp != NULL && cpp->pos == TBL_CELL_SPAN) 665 1.1.1.8 christos uw = 0; 666 1.1.1.8 christos if (cpn != NULL && cpn->pos == TBL_CELL_SPAN) 667 1.1.1.8 christos dw = 0; 668 1.1.1.8 christos 669 1.1.1.8 christos /* The horizontal line inside the next column. */ 670 1.1.1.8 christos 671 1.1.1.8 christos rw = cpp == NULL || cpn == NULL || 672 1.1.1.8 christos (cpn->pos != TBL_CELL_DOWN && 673 1.1.1.9 wiz (dpn == NULL || dpn->string == NULL || 674 1.1.1.9 wiz strcmp(dpn->string, "\\^") != 0)) 675 1.1.1.8 christos ? hw : 0; 676 1.1.1.8 christos 677 1.1.1.8 christos /* The line crossing at the end of this column. */ 678 1.1.1.8 christos 679 1.1.1.7 christos if (col->spacing) 680 1.1.1.8 christos tbl_direct_border(tp, BLEFT * lw + 681 1.1.1.8 christos BRIGHT * rw + BUP * uw + BDOWN * dw, 1); 682 1.1.1.8 christos 683 1.1.1.8 christos /* 684 1.1.1.8 christos * In ASCII output, a crossing may print two characters. 685 1.1.1.8 christos */ 686 1.1.1.8 christos 687 1.1.1.8 christos if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2)) 688 1.1.1.8 christos uw = dw = 0; 689 1.1.1.7 christos if (col->spacing > 2) 690 1.1.1.8 christos tbl_direct_border(tp, 691 1.1.1.8 christos BHORIZ * rw + BUP * uw + BDOWN * dw, 1); 692 1.1.1.8 christos 693 1.1.1.8 christos /* Padding before the start of the next column. */ 694 1.1.1.8 christos 695 1.1.1.7 christos if (col->spacing > 4) 696 1.1.1.8 christos tbl_direct_border(tp, 697 1.1.1.8 christos BHORIZ * rw, (col->spacing - 3) / 2); 698 1.1.1.4 joerg } 699 1.1.1.8 christos 700 1.1.1.8 christos /* Print the right end of the line. */ 701 1.1.1.8 christos 702 1.1.1.8 christos if (flags != 0) { 703 1.1.1.8 christos tbl_direct_border(tp, 704 1.1.1.8 christos (spp == NULL ? 0 : BUP * bw) + 705 1.1.1.8 christos (spn == NULL ? 0 : BDOWN * bw) + 706 1.1.1.8 christos (spp == NULL || spn == NULL || 707 1.1.1.8 christos spn->layout->last->pos != TBL_CELL_DOWN ? 708 1.1.1.8 christos BLEFT * hw : 0), 1); 709 1.1.1.8 christos (*tp->endline)(tp); 710 1.1.1.8 christos tp->viscol = 0; 711 1.1.1.4 joerg } 712 1.1 joerg } 713 1.1 joerg 714 1.1 joerg static void 715 1.1.1.4 joerg tbl_data(struct termp *tp, const struct tbl_opts *opts, 716 1.1.1.7 christos const struct tbl_cell *cp, const struct tbl_dat *dp, 717 1.1.1.7 christos const struct roffcol *col) 718 1.1 joerg { 719 1.1.1.7 christos switch (cp->pos) { 720 1.1.1.7 christos case TBL_CELL_HORIZ: 721 1.1.1.8 christos tbl_fill_border(tp, BHORIZ, col->width); 722 1.1 joerg return; 723 1.1.1.7 christos case TBL_CELL_DHORIZ: 724 1.1.1.8 christos tbl_fill_border(tp, BHORIZ * 2, col->width); 725 1.1.1.7 christos return; 726 1.1.1.7 christos default: 727 1.1.1.7 christos break; 728 1.1 joerg } 729 1.1 joerg 730 1.1.1.7 christos if (dp == NULL) 731 1.1.1.7 christos return; 732 1.1.1.7 christos 733 1.1 joerg switch (dp->pos) { 734 1.1.1.5 christos case TBL_DATA_NONE: 735 1.1 joerg return; 736 1.1.1.5 christos case TBL_DATA_HORIZ: 737 1.1.1.5 christos case TBL_DATA_NHORIZ: 738 1.1.1.8 christos tbl_fill_border(tp, BHORIZ, col->width); 739 1.1 joerg return; 740 1.1.1.5 christos case TBL_DATA_NDHORIZ: 741 1.1.1.5 christos case TBL_DATA_DHORIZ: 742 1.1.1.8 christos tbl_fill_border(tp, BHORIZ * 2, col->width); 743 1.1 joerg return; 744 1.1 joerg default: 745 1.1 joerg break; 746 1.1 joerg } 747 1.1.1.5 christos 748 1.1.1.7 christos switch (cp->pos) { 749 1.1.1.5 christos case TBL_CELL_LONG: 750 1.1.1.5 christos case TBL_CELL_CENTRE: 751 1.1.1.5 christos case TBL_CELL_LEFT: 752 1.1.1.5 christos case TBL_CELL_RIGHT: 753 1.1 joerg tbl_literal(tp, dp, col); 754 1.1 joerg break; 755 1.1.1.5 christos case TBL_CELL_NUMBER: 756 1.1.1.4 joerg tbl_number(tp, opts, dp, col); 757 1.1 joerg break; 758 1.1.1.5 christos case TBL_CELL_DOWN: 759 1.1.1.7 christos case TBL_CELL_SPAN: 760 1.1.1.2 joerg break; 761 1.1 joerg default: 762 1.1 joerg abort(); 763 1.1 joerg } 764 1.1 joerg } 765 1.1 joerg 766 1.1 joerg static void 767 1.1.1.8 christos tbl_fill_string(struct termp *tp, const char *cp, size_t len) 768 1.1 joerg { 769 1.1.1.8 christos size_t i, sz; 770 1.1.1.8 christos 771 1.1.1.8 christos sz = term_strlen(tp, cp); 772 1.1.1.8 christos for (i = 0; i < len; i += sz) 773 1.1.1.8 christos term_word(tp, cp); 774 1.1.1.8 christos } 775 1.1.1.8 christos 776 1.1.1.8 christos static void 777 1.1.1.8 christos tbl_fill_char(struct termp *tp, char c, size_t len) 778 1.1.1.8 christos { 779 1.1.1.8 christos char cp[2]; 780 1.1 joerg 781 1.1 joerg cp[0] = c; 782 1.1 joerg cp[1] = '\0'; 783 1.1.1.8 christos tbl_fill_string(tp, cp, len); 784 1.1.1.8 christos } 785 1.1 joerg 786 1.1.1.8 christos static void 787 1.1.1.8 christos tbl_fill_border(struct termp *tp, int c, size_t len) 788 1.1.1.8 christos { 789 1.1.1.8 christos char buf[12]; 790 1.1 joerg 791 1.1.1.8 christos if ((c = borders_locale[c]) > 127) { 792 1.1.1.8 christos (void)snprintf(buf, sizeof(buf), "\\[u%04x]", c); 793 1.1.1.8 christos tbl_fill_string(tp, buf, len); 794 1.1.1.8 christos } else 795 1.1.1.8 christos tbl_fill_char(tp, c, len); 796 1.1.1.8 christos } 797 1.1.1.8 christos 798 1.1.1.8 christos static void 799 1.1.1.8 christos tbl_direct_border(struct termp *tp, int c, size_t len) 800 1.1.1.8 christos { 801 1.1.1.8 christos size_t i, sz; 802 1.1.1.8 christos 803 1.1.1.8 christos c = borders_locale[c]; 804 1.1.1.8 christos sz = (*tp->width)(tp, c); 805 1.1.1.8 christos for (i = 0; i < len; i += sz) { 806 1.1.1.8 christos (*tp->letter)(tp, c); 807 1.1.1.8 christos tp->viscol += sz; 808 1.1.1.8 christos } 809 1.1 joerg } 810 1.1 joerg 811 1.1 joerg static void 812 1.1.1.5 christos tbl_literal(struct termp *tp, const struct tbl_dat *dp, 813 1.1 joerg const struct roffcol *col) 814 1.1 joerg { 815 1.1.1.5 christos size_t len, padl, padr, width; 816 1.1.1.8 christos int ic, hspans; 817 1.1 joerg 818 1.1.1.2 joerg assert(dp->string); 819 1.1.1.3 joerg len = term_strlen(tp, dp->string); 820 1.1.1.4 joerg width = col->width; 821 1.1.1.5 christos ic = dp->layout->col; 822 1.1.1.8 christos hspans = dp->hspans; 823 1.1.1.8 christos while (hspans--) 824 1.1.1.5 christos width += tp->tbl.cols[++ic].width + 3; 825 1.1.1.4 joerg 826 1.1.1.4 joerg padr = width > len ? width - len : 0; 827 1.1.1.3 joerg padl = 0; 828 1.1 joerg 829 1.1.1.2 joerg switch (dp->layout->pos) { 830 1.1.1.5 christos case TBL_CELL_LONG: 831 1.1.1.3 joerg padl = term_len(tp, 1); 832 1.1.1.3 joerg padr = padr > padl ? padr - padl : 0; 833 1.1 joerg break; 834 1.1.1.5 christos case TBL_CELL_CENTRE: 835 1.1.1.3 joerg if (2 > padr) 836 1.1.1.2 joerg break; 837 1.1.1.3 joerg padl = padr / 2; 838 1.1.1.2 joerg padr -= padl; 839 1.1 joerg break; 840 1.1.1.5 christos case TBL_CELL_RIGHT: 841 1.1.1.3 joerg padl = padr; 842 1.1.1.3 joerg padr = 0; 843 1.1 joerg break; 844 1.1 joerg default: 845 1.1 joerg break; 846 1.1 joerg } 847 1.1 joerg 848 1.1.1.8 christos tbl_fill_char(tp, ASCII_NBRSP, padl); 849 1.1.1.5 christos tbl_word(tp, dp); 850 1.1.1.8 christos tbl_fill_char(tp, ASCII_NBRSP, padr); 851 1.1 joerg } 852 1.1 joerg 853 1.1 joerg static void 854 1.1.1.4 joerg tbl_number(struct termp *tp, const struct tbl_opts *opts, 855 1.1 joerg const struct tbl_dat *dp, 856 1.1 joerg const struct roffcol *col) 857 1.1 joerg { 858 1.1.1.8 christos const char *cp, *lastdigit, *lastpoint; 859 1.1.1.8 christos size_t intsz, padl, totsz; 860 1.1 joerg char buf[2]; 861 1.1 joerg 862 1.1 joerg /* 863 1.1.1.8 christos * Almost the same code as in tblcalc_number(): 864 1.1.1.8 christos * First find the position of the decimal point. 865 1.1 joerg */ 866 1.1 joerg 867 1.1.1.2 joerg assert(dp->string); 868 1.1.1.8 christos lastdigit = lastpoint = NULL; 869 1.1.1.8 christos for (cp = dp->string; cp[0] != '\0'; cp++) { 870 1.1.1.8 christos if (cp[0] == '\\' && cp[1] == '&') { 871 1.1.1.8 christos lastdigit = lastpoint = cp; 872 1.1.1.8 christos break; 873 1.1.1.8 christos } else if (cp[0] == opts->decimal && 874 1.1.1.8 christos (isdigit((unsigned char)cp[1]) || 875 1.1.1.8 christos (cp > dp->string && isdigit((unsigned char)cp[-1])))) 876 1.1.1.8 christos lastpoint = cp; 877 1.1.1.8 christos else if (isdigit((unsigned char)cp[0])) 878 1.1.1.8 christos lastdigit = cp; 879 1.1.1.8 christos } 880 1.1 joerg 881 1.1.1.8 christos /* Then measure both widths. */ 882 1.1 joerg 883 1.1.1.8 christos padl = 0; 884 1.1.1.8 christos totsz = term_strlen(tp, dp->string); 885 1.1.1.8 christos if (lastdigit != NULL) { 886 1.1.1.8 christos if (lastpoint == NULL) 887 1.1.1.8 christos lastpoint = lastdigit + 1; 888 1.1.1.8 christos intsz = 0; 889 1.1.1.8 christos buf[1] = '\0'; 890 1.1.1.8 christos for (cp = dp->string; cp < lastpoint; cp++) { 891 1.1.1.8 christos buf[0] = cp[0]; 892 1.1.1.8 christos intsz += term_strlen(tp, buf); 893 1.1.1.8 christos } 894 1.1.1.8 christos 895 1.1.1.8 christos /* 896 1.1.1.8 christos * Pad left to match the decimal position, 897 1.1.1.8 christos * but avoid exceeding the total column width. 898 1.1.1.8 christos */ 899 1.1.1.8 christos 900 1.1.1.8 christos if (col->decimal > intsz && col->width > totsz) { 901 1.1.1.8 christos padl = col->decimal - intsz; 902 1.1.1.8 christos if (padl + totsz > col->width) 903 1.1.1.8 christos padl = col->width - totsz; 904 1.1.1.8 christos } 905 1.1 joerg 906 1.1.1.8 christos /* If it is not a number, simply center the string. */ 907 1.1 joerg 908 1.1.1.8 christos } else if (col->width > totsz) 909 1.1.1.8 christos padl = (col->width - totsz) / 2; 910 1.1 joerg 911 1.1.1.8 christos tbl_fill_char(tp, ASCII_NBRSP, padl); 912 1.1.1.5 christos tbl_word(tp, dp); 913 1.1.1.8 christos 914 1.1.1.8 christos /* Pad right to fill the column. */ 915 1.1.1.8 christos 916 1.1.1.8 christos if (col->width > padl + totsz) 917 1.1.1.8 christos tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz); 918 1.1 joerg } 919 1.1 joerg 920 1.1.1.5 christos static void 921 1.1.1.5 christos tbl_word(struct termp *tp, const struct tbl_dat *dp) 922 1.1.1.5 christos { 923 1.1.1.5 christos int prev_font; 924 1.1.1.5 christos 925 1.1.1.5 christos prev_font = tp->fonti; 926 1.1.1.9 wiz switch (dp->layout->font) { 927 1.1.1.9 wiz case ESCAPE_FONTBI: 928 1.1.1.9 wiz term_fontpush(tp, TERMFONT_BI); 929 1.1.1.9 wiz break; 930 1.1.1.9 wiz case ESCAPE_FONTBOLD: 931 1.1.1.9 wiz case ESCAPE_FONTCB: 932 1.1.1.9 wiz term_fontpush(tp, TERMFONT_BOLD); 933 1.1.1.9 wiz break; 934 1.1.1.9 wiz case ESCAPE_FONTITALIC: 935 1.1.1.9 wiz case ESCAPE_FONTCI: 936 1.1.1.9 wiz term_fontpush(tp, TERMFONT_UNDER); 937 1.1.1.9 wiz break; 938 1.1.1.9 wiz case ESCAPE_FONTROMAN: 939 1.1.1.9 wiz case ESCAPE_FONTCR: 940 1.1.1.9 wiz break; 941 1.1.1.9 wiz default: 942 1.1.1.9 wiz abort(); 943 1.1.1.9 wiz } 944 1.1.1.5 christos 945 1.1.1.5 christos term_word(tp, dp->string); 946 1.1.1.5 christos 947 1.1.1.5 christos term_fontpopq(tp, prev_font); 948 1.1.1.5 christos } 949