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