Bug Summary

File:src/usr.bin/mandoc/tbl_term.c
Warning:line 589, column 9
Access to field 'opts' results in a dereference of a null pointer (loaded from variable 'sp')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tbl_term.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/mandoc/obj -resource-dir /usr/local/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/mandoc/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/mandoc/tbl_term.c
1/* $OpenBSD: tbl_term.c,v 1.66 2022/08/28 10:57:52 schwarze Exp $ */
2/*
3 * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
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
35static size_t term_tbl_len(size_t, void *);
36static size_t term_tbl_strlen(const char *, void *);
37static size_t term_tbl_sulen(const struct roffsu *, void *);
38static void tbl_data(struct termp *, const struct tbl_opts *,
39 const struct tbl_cell *,
40 const struct tbl_dat *,
41 const struct roffcol *);
42static void tbl_direct_border(struct termp *, int, size_t);
43static void tbl_fill_border(struct termp *, int, size_t);
44static void tbl_fill_char(struct termp *, char, size_t);
45static void tbl_fill_string(struct termp *, const char *, size_t);
46static void tbl_hrule(struct termp *, const struct tbl_span *,
47 const struct tbl_span *, const struct tbl_span *,
48 int);
49static void tbl_literal(struct termp *, const struct tbl_dat *,
50 const struct roffcol *);
51static void tbl_number(struct termp *, const struct tbl_opts *,
52 const struct tbl_dat *,
53 const struct roffcol *);
54static 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. */
72static 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. */
103static 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. */
134static const int *borders_locale;
135
136
137static size_t
138term_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
146static size_t
147term_tbl_strlen(const char *p, void *arg)
148{
149 return term_strlen((const struct termp *)arg, p);
150}
151
152static size_t
153term_tbl_len(size_t sz, void *arg)
154{
155 return term_len((const struct termp *)arg, sz);
156}
157
158
159void
160term_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 tp->flags &= ~(TERMP_BACKAFTER(1 << 6) | TERMP_BACKBEFORE(1 << 7));
292 tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
293 if (dp != NULL((void *)0) &&
294 (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
295 hspans = dp->hspans;
296 dp = dp->next;
297 }
298 }
299 break;
300 }
301
302 do {
303 /* Print the vertical frame at the start of each row. */
304
305 tp->tcol = tp->tcols;
306 uvert = dvert = sp->opts->opts & TBL_OPT_DBOX(1 << 3) ? 2 :
307 sp->opts->opts & TBL_OPT_BOX(1 << 1) ? 1 : 0;
308 if (sp->pos == TBL_SPAN_DATA && uvert < sp->layout->vert)
309 uvert = dvert = sp->layout->vert;
310 if (sp->next != NULL((void *)0) && sp->next->pos == TBL_SPAN_DATA &&
311 dvert < sp->next->layout->vert)
312 dvert = sp->next->layout->vert;
313 if (sp->prev != NULL((void *)0) && uvert < sp->prev->layout->vert &&
314 (horiz || (IS_HORIZ(sp->layout->first)((sp->layout->first)->pos == TBL_CELL_HORIZ || (sp->
layout->first)->pos == TBL_CELL_DHORIZ)
&&
315 !IS_HORIZ(sp->prev->layout->first)((sp->prev->layout->first)->pos == TBL_CELL_HORIZ
|| (sp->prev->layout->first)->pos == TBL_CELL_DHORIZ
)
)))
316 uvert = sp->prev->layout->vert;
317 rhori = sp->pos == TBL_SPAN_DHORIZ ||
318 (sp->first != NULL((void *)0) && sp->first->pos == TBL_DATA_DHORIZ) ||
319 sp->layout->first->pos == TBL_CELL_DHORIZ ? 2 :
320 sp->pos == TBL_SPAN_HORIZ ||
321 (sp->first != NULL((void *)0) && sp->first->pos == TBL_DATA_HORIZ) ||
322 sp->layout->first->pos == TBL_CELL_HORIZ ? 1 : 0;
323 fc = BUP(3 * 3 * 3) * uvert + BDOWN3 * dvert + BRIGHT1 * rhori;
324 if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
325 (*tp->advance)(tp, tp->tcols->offset);
326 tp->viscol = tp->tcol->offset;
327 tbl_direct_border(tp, fc, 1);
328 }
329
330 /* Print the data cells. */
331
332 more = 0;
333 if (horiz)
334 tbl_hrule(tp, sp->prev, sp, sp->next, 0);
335 else {
336 cp = sp->layout->first;
337 cpn = sp->next == NULL((void *)0) ? NULL((void *)0) :
338 sp->next->layout->first;
339 cpp = sp->prev == NULL((void *)0) ? NULL((void *)0) :
340 sp->prev->layout->first;
341 dp = sp->first;
342 hspans = 0;
343 for (ic = 0; ic < sp->opts->cols; ic++) {
344
345 /*
346 * Figure out whether to print a
347 * vertical line after this cell
348 * and advance to next layout cell.
349 */
350
351 uvert = dvert = fc = 0;
352 if (cp != NULL((void *)0)) {
353 cps = cp;
354 while (cps->next != NULL((void *)0) &&
355 cps->next->pos == TBL_CELL_SPAN)
356 cps = cps->next;
357 if (sp->pos == TBL_SPAN_DATA)
358 uvert = dvert = cps->vert;
359 switch (cp->pos) {
360 case TBL_CELL_HORIZ:
361 fc = BHORIZ((3 * 3) + 1);
362 break;
363 case TBL_CELL_DHORIZ:
364 fc = BHORIZ((3 * 3) + 1) * 2;
365 break;
366 default:
367 break;
368 }
369 }
370 if (cpp != NULL((void *)0)) {
371 if (uvert < cpp->vert &&
372 cp != NULL((void *)0) &&
373 ((IS_HORIZ(cp)((cp)->pos == TBL_CELL_HORIZ || (cp)->pos == TBL_CELL_DHORIZ
)
&&
374 !IS_HORIZ(cpp)((cpp)->pos == TBL_CELL_HORIZ || (cpp)->pos == TBL_CELL_DHORIZ
)
) ||
375 (cp->next != NULL((void *)0) &&
376 cpp->next != NULL((void *)0) &&
377 IS_HORIZ(cp->next)((cp->next)->pos == TBL_CELL_HORIZ || (cp->next)->
pos == TBL_CELL_DHORIZ)
&&
378 !IS_HORIZ(cpp->next)((cpp->next)->pos == TBL_CELL_HORIZ || (cpp->next)->
pos == TBL_CELL_DHORIZ)
)))
379 uvert = cpp->vert;
380 cpp = cpp->next;
381 }
382 if (sp->opts->opts & TBL_OPT_ALLBOX(1 << 0)) {
383 if (uvert == 0)
384 uvert = 1;
385 if (dvert == 0)
386 dvert = 1;
387 }
388 if (cpn != NULL((void *)0)) {
389 if (dvert == 0 ||
390 (dvert < cpn->vert &&
391 tp->enc == TERMENC_UTF8))
392 dvert = cpn->vert;
393 cpn = cpn->next;
394 }
395
396 lhori = (cp != NULL((void *)0) &&
397 cp->pos == TBL_CELL_DHORIZ) ||
398 (dp != NULL((void *)0) &&
399 dp->pos == TBL_DATA_DHORIZ) ? 2 :
400 (cp != NULL((void *)0) &&
401 cp->pos == TBL_CELL_HORIZ) ||
402 (dp != NULL((void *)0) &&
403 dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
404
405 /*
406 * Skip later cells in a span,
407 * figure out whether to start a span,
408 * and advance to next data cell.
409 */
410
411 if (hspans) {
412 hspans--;
413 cp = cp->next;
414 continue;
415 }
416 if (dp != NULL((void *)0) && (ic ||
417 sp->layout->first->pos != TBL_CELL_SPAN)) {
418 hspans = dp->hspans;
419 dp = dp->next;
420 }
421
422 /*
423 * Print one line of text in the cell
424 * and remember whether there is more.
425 */
426
427 tp->tcol++;
428 if (tp->tcol->col < tp->tcol->lastcol)
429 term_flushln(tp);
430 if (tp->tcol->col < tp->tcol->lastcol)
431 more = 1;
432
433 /*
434 * Vertical frames between data cells,
435 * but not after the last column.
436 */
437
438 if (fc == 0 &&
439 ((uvert == 0 && dvert == 0 &&
440 cp != NULL((void *)0) && (cp->next == NULL((void *)0) ||
441 !IS_HORIZ(cp->next)((cp->next)->pos == TBL_CELL_HORIZ || (cp->next)->
pos == TBL_CELL_DHORIZ)
)) ||
442 tp->tcol + 1 ==
443 tp->tcols + tp->lasttcol)) {
444 if (cp != NULL((void *)0))
445 cp = cp->next;
446 continue;
447 }
448
449 if (tp->viscol < tp->tcol->rmargin) {
450 (*tp->advance)(tp, tp->tcol->rmargin
451 - tp->viscol);
452 tp->viscol = tp->tcol->rmargin;
453 }
454 while (tp->viscol < tp->tcol->rmargin +
455 tp->tbl.cols[ic].spacing / 2)
456 tbl_direct_border(tp,
457 BHORIZ((3 * 3) + 1) * lhori, 1);
458
459 if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
460 continue;
461
462 if (cp != NULL((void *)0))
463 cp = cp->next;
464
465 rhori = (cp != NULL((void *)0) &&
466 cp->pos == TBL_CELL_DHORIZ) ||
467 (dp != NULL((void *)0) &&
468 dp->pos == TBL_DATA_DHORIZ) ? 2 :
469 (cp != NULL((void *)0) &&
470 cp->pos == TBL_CELL_HORIZ) ||
471 (dp != NULL((void *)0) &&
472 dp->pos == TBL_DATA_HORIZ) ? 1 : 0;
473
474 if (tp->tbl.cols[ic].spacing)
475 tbl_direct_border(tp,
476 BLEFT(3 * 3) * lhori + BRIGHT1 * rhori +
477 BUP(3 * 3 * 3) * uvert + BDOWN3 * dvert, 1);
478
479 if (tp->enc == TERMENC_UTF8)
480 uvert = dvert = 0;
481
482 if (tp->tbl.cols[ic].spacing > 2 &&
483 (uvert > 1 || dvert > 1 || rhori))
484 tbl_direct_border(tp,
485 BHORIZ((3 * 3) + 1) * rhori +
486 BUP(3 * 3 * 3) * (uvert > 1) +
487 BDOWN3 * (dvert > 1), 1);
488 }
489 }
490
491 /* Print the vertical frame at the end of each row. */
492
493 uvert = dvert = sp->opts->opts & TBL_OPT_DBOX(1 << 3) ? 2 :
494 sp->opts->opts & TBL_OPT_BOX(1 << 1) ? 1 : 0;
495 if (sp->pos == TBL_SPAN_DATA &&
496 uvert < sp->layout->last->vert &&
497 sp->layout->last->col + 1 == sp->opts->cols)
498 uvert = dvert = sp->layout->last->vert;
499 if (sp->next != NULL((void *)0) &&
500 dvert < sp->next->layout->last->vert &&
501 sp->next->layout->last->col + 1 == sp->opts->cols)
502 dvert = sp->next->layout->last->vert;
503 if (sp->prev != NULL((void *)0) &&
504 uvert < sp->prev->layout->last->vert &&
505 sp->prev->layout->last->col + 1 == sp->opts->cols &&
506 (horiz || (IS_HORIZ(sp->layout->last)((sp->layout->last)->pos == TBL_CELL_HORIZ || (sp->
layout->last)->pos == TBL_CELL_DHORIZ)
&&
507 !IS_HORIZ(sp->prev->layout->last)((sp->prev->layout->last)->pos == TBL_CELL_HORIZ ||
(sp->prev->layout->last)->pos == TBL_CELL_DHORIZ
)
)))
508 uvert = sp->prev->layout->last->vert;
509 lhori = sp->pos == TBL_SPAN_DHORIZ ||
510 (sp->last != NULL((void *)0) &&
511 sp->last->pos == TBL_DATA_DHORIZ &&
512 sp->last->layout->col + 1 == sp->opts->cols) ||
513 (sp->layout->last->pos == TBL_CELL_DHORIZ &&
514 sp->layout->last->col + 1 == sp->opts->cols) ? 2 :
515 sp->pos == TBL_SPAN_HORIZ ||
516 (sp->last != NULL((void *)0) &&
517 sp->last->pos == TBL_DATA_HORIZ &&
518 sp->last->layout->col + 1 == sp->opts->cols) ||
519 (sp->layout->last->pos == TBL_CELL_HORIZ &&
520 sp->layout->last->col + 1 == sp->opts->cols) ? 1 : 0;
521 fc = BUP(3 * 3 * 3) * uvert + BDOWN3 * dvert + BLEFT(3 * 3) * lhori;
522 if (uvert > 0 || dvert > 0 || (horiz && sp->opts->rvert)) {
523 if (horiz == 0 && (IS_HORIZ(sp->layout->last)((sp->layout->last)->pos == TBL_CELL_HORIZ || (sp->
layout->last)->pos == TBL_CELL_DHORIZ)
== 0 ||
524 sp->layout->last->col + 1 < sp->opts->cols)) {
525 tp->tcol++;
526 do {
527 tbl_direct_border(tp,
528 BHORIZ((3 * 3) + 1) * lhori, 1);
529 } while (tp->viscol < tp->tcol->offset);
530 }
531 tbl_direct_border(tp, fc, 1);
532 }
533 (*tp->endline)(tp);
534 tp->viscol = 0;
535 } while (more);
536
537 /*
538 * Clean up after this row. If it is the last line
539 * of the table, print the box line and clean up
540 * column data; otherwise, print the allbox line.
541 */
542
543 term_setcol(tp, 1);
544 tp->flags &= ~TERMP_MULTICOL(1 << 20);
545 tp->tcol->rmargin = tp->maxrmargin;
546 if (sp->next == NULL((void *)0)) {
547 if (sp->opts->opts & (TBL_OPT_DBOX(1 << 3) | TBL_OPT_BOX(1 << 1)))
548 tbl_hrule(tp, sp, sp, NULL((void *)0), TBL_OPT_BOX(1 << 1));
549 if (tp->enc == TERMENC_ASCII &&
550 sp->opts->opts & TBL_OPT_DBOX(1 << 3))
551 tbl_hrule(tp, sp, sp, NULL((void *)0), TBL_OPT_DBOX(1 << 3));
552 assert(tp->tbl.cols)((tp->tbl.cols) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/tbl_term.c"
, 552, __func__, "tp->tbl.cols"))
;
553 free(tp->tbl.cols);
554 tp->tbl.cols = NULL((void *)0);
555 } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX(1 << 0) &&
556 (sp->next == NULL((void *)0) || sp->next->pos == TBL_SPAN_DATA ||
557 sp->next->next != NULL((void *)0)))
558 tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX(1 << 0));
559
560 tp->tcol->offset = save_offset;
561 tp->flags &= ~TERMP_NONOSPACE(1 << 2);
562}
563
564static void
565tbl_hrule(struct termp *tp, const struct tbl_span *spp,
566 const struct tbl_span *sp, const struct tbl_span *spn, int flags)
567{
568 const struct tbl_cell *cpp; /* Layout cell above this line. */
569 const struct tbl_cell *cp; /* Layout cell in this line. */
570 const struct tbl_cell *cpn; /* Layout cell below this line. */
571 const struct tbl_dat *dpn; /* Data cell below this line. */
572 const struct roffcol *col; /* Contains width and spacing. */
573 int opts; /* For the table as a whole. */
574 int bw; /* Box line width. */
575 int hw; /* Horizontal line width. */
576 int lw, rw; /* Left and right line widths. */
577 int uw, dw; /* Vertical line widths. */
578
579 cpp = spp == NULL((void *)0) ? NULL((void *)0) : spp->layout->first;
1
Assuming 'spp' is equal to NULL
2
'?' condition is true
580 cp = sp == NULL((void *)0) ? NULL((void *)0) : sp->layout->first;
3
Assuming 'sp' is equal to NULL
4
'?' condition is true
581 cpn = spn == NULL((void *)0) ? NULL((void *)0) : spn->layout->first;
5
Assuming 'spn' is equal to NULL
6
'?' condition is true
582 dpn = NULL((void *)0);
583 if (spn
6.1
'spn' is equal to NULL
!= NULL((void *)0)) {
7
Taking false branch
584 if (spn->pos == TBL_SPAN_DATA)
585 dpn = spn->first;
586 else if (spn->next != NULL((void *)0))
587 dpn = spn->next->first;
588 }
589 opts = sp->opts->opts;
8
Access to field 'opts' results in a dereference of a null pointer (loaded from variable 'sp')
590 bw = opts & TBL_OPT_DBOX(1 << 3) ? (tp->enc == TERMENC_UTF8 ? 2 : 1) :
591 opts & (TBL_OPT_BOX(1 << 1) | TBL_OPT_ALLBOX(1 << 0)) ? 1 : 0;
592 hw = flags == TBL_OPT_DBOX(1 << 3) || flags == TBL_OPT_BOX(1 << 1) ? bw :
593 sp->pos == TBL_SPAN_DHORIZ ? 2 : 1;
594
595 /* Print the left end of the line. */
596
597 if (tp->viscol == 0) {
598 (*tp->advance)(tp, tp->tcols->offset);
599 tp->viscol = tp->tcols->offset;
600 }
601 if (flags != 0)
602 tbl_direct_border(tp,
603 (spp == NULL((void *)0) ? 0 : BUP(3 * 3 * 3) * bw) +
604 (spn == NULL((void *)0) ? 0 : BDOWN3 * bw) +
605 (spp == NULL((void *)0) || cpn == NULL((void *)0) ||
606 cpn->pos != TBL_CELL_DOWN ? BRIGHT1 * hw : 0), 1);
607
608 col = tp->tbl.cols;
609 for (;;) {
610 if (cp == NULL((void *)0))
611 col++;
612 else
613 col = tp->tbl.cols + cp->col;
614
615 /* Print the horizontal line inside this column. */
616
617 lw = cpp == NULL((void *)0) || cpn == NULL((void *)0) ||
618 (cpn->pos != TBL_CELL_DOWN &&
619 (dpn == NULL((void *)0) || dpn->string == NULL((void *)0) ||
620 strcmp(dpn->string, "\\^") != 0))
621 ? hw : 0;
622 tbl_direct_border(tp, BHORIZ((3 * 3) + 1) * lw,
623 col->width + col->spacing / 2);
624
625 /*
626 * Figure out whether a vertical line is crossing
627 * at the end of this column,
628 * and advance to the next column.
629 */
630
631 uw = dw = 0;
632 if (cpp != NULL((void *)0)) {
633 if (flags != TBL_OPT_DBOX(1 << 3)) {
634 uw = cpp->vert;
635 if (uw == 0 && opts & TBL_OPT_ALLBOX(1 << 0))
636 uw = 1;
637 }
638 cpp = cpp->next;
639 } else if (spp != NULL((void *)0) && opts & TBL_OPT_ALLBOX(1 << 0))
640 uw = 1;
641 if (cp != NULL((void *)0))
642 cp = cp->next;
643 if (cpn != NULL((void *)0)) {
644 if (flags != TBL_OPT_DBOX(1 << 3)) {
645 dw = cpn->vert;
646 if (dw == 0 && opts & TBL_OPT_ALLBOX(1 << 0))
647 dw = 1;
648 }
649 cpn = cpn->next;
650 while (dpn != NULL((void *)0) && dpn->layout != cpn)
651 dpn = dpn->next;
652 } else if (spn != NULL((void *)0) && opts & TBL_OPT_ALLBOX(1 << 0))
653 dw = 1;
654 if (col + 1 == tp->tbl.cols + sp->opts->cols)
655 break;
656
657 /* Vertical lines do not cross spanned cells. */
658
659 if (cpp != NULL((void *)0) && cpp->pos == TBL_CELL_SPAN)
660 uw = 0;
661 if (cpn != NULL((void *)0) && cpn->pos == TBL_CELL_SPAN)
662 dw = 0;
663
664 /* The horizontal line inside the next column. */
665
666 rw = cpp == NULL((void *)0) || cpn == NULL((void *)0) ||
667 (cpn->pos != TBL_CELL_DOWN &&
668 (dpn == NULL((void *)0) || dpn->string == NULL((void *)0) ||
669 strcmp(dpn->string, "\\^") != 0))
670 ? hw : 0;
671
672 /* The line crossing at the end of this column. */
673
674 if (col->spacing)
675 tbl_direct_border(tp, BLEFT(3 * 3) * lw +
676 BRIGHT1 * rw + BUP(3 * 3 * 3) * uw + BDOWN3 * dw, 1);
677
678 /*
679 * In ASCII output, a crossing may print two characters.
680 */
681
682 if (tp->enc != TERMENC_ASCII || (uw < 2 && dw < 2))
683 uw = dw = 0;
684 if (col->spacing > 2)
685 tbl_direct_border(tp,
686 BHORIZ((3 * 3) + 1) * rw + BUP(3 * 3 * 3) * uw + BDOWN3 * dw, 1);
687
688 /* Padding before the start of the next column. */
689
690 if (col->spacing > 4)
691 tbl_direct_border(tp,
692 BHORIZ((3 * 3) + 1) * rw, (col->spacing - 3) / 2);
693 }
694
695 /* Print the right end of the line. */
696
697 if (flags != 0) {
698 tbl_direct_border(tp,
699 (spp == NULL((void *)0) ? 0 : BUP(3 * 3 * 3) * bw) +
700 (spn == NULL((void *)0) ? 0 : BDOWN3 * bw) +
701 (spp == NULL((void *)0) || spn == NULL((void *)0) ||
702 spn->layout->last->pos != TBL_CELL_DOWN ?
703 BLEFT(3 * 3) * hw : 0), 1);
704 (*tp->endline)(tp);
705 tp->viscol = 0;
706 }
707}
708
709static void
710tbl_data(struct termp *tp, const struct tbl_opts *opts,
711 const struct tbl_cell *cp, const struct tbl_dat *dp,
712 const struct roffcol *col)
713{
714 switch (cp->pos) {
715 case TBL_CELL_HORIZ:
716 tbl_fill_border(tp, BHORIZ((3 * 3) + 1), col->width);
717 return;
718 case TBL_CELL_DHORIZ:
719 tbl_fill_border(tp, BHORIZ((3 * 3) + 1) * 2, col->width);
720 return;
721 default:
722 break;
723 }
724
725 if (dp == NULL((void *)0))
726 return;
727
728 switch (dp->pos) {
729 case TBL_DATA_NONE:
730 return;
731 case TBL_DATA_HORIZ:
732 case TBL_DATA_NHORIZ:
733 tbl_fill_border(tp, BHORIZ((3 * 3) + 1), col->width);
734 return;
735 case TBL_DATA_NDHORIZ:
736 case TBL_DATA_DHORIZ:
737 tbl_fill_border(tp, BHORIZ((3 * 3) + 1) * 2, col->width);
738 return;
739 default:
740 break;
741 }
742
743 switch (cp->pos) {
744 case TBL_CELL_LONG:
745 case TBL_CELL_CENTRE:
746 case TBL_CELL_LEFT:
747 case TBL_CELL_RIGHT:
748 tbl_literal(tp, dp, col);
749 break;
750 case TBL_CELL_NUMBER:
751 tbl_number(tp, opts, dp, col);
752 break;
753 case TBL_CELL_DOWN:
754 case TBL_CELL_SPAN:
755 break;
756 default:
757 abort();
758 }
759}
760
761static void
762tbl_fill_string(struct termp *tp, const char *cp, size_t len)
763{
764 size_t i, sz;
765
766 sz = term_strlen(tp, cp);
767 for (i = 0; i < len; i += sz)
768 term_word(tp, cp);
769}
770
771static void
772tbl_fill_char(struct termp *tp, char c, size_t len)
773{
774 char cp[2];
775
776 cp[0] = c;
777 cp[1] = '\0';
778 tbl_fill_string(tp, cp, len);
779}
780
781static void
782tbl_fill_border(struct termp *tp, int c, size_t len)
783{
784 char buf[12];
785
786 if ((c = borders_locale[c]) > 127) {
787 (void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
788 tbl_fill_string(tp, buf, len);
789 } else
790 tbl_fill_char(tp, c, len);
791}
792
793static void
794tbl_direct_border(struct termp *tp, int c, size_t len)
795{
796 size_t i, sz;
797
798 c = borders_locale[c];
799 sz = (*tp->width)(tp, c);
800 for (i = 0; i < len; i += sz) {
801 (*tp->letter)(tp, c);
802 tp->viscol += sz;
803 }
804}
805
806static void
807tbl_literal(struct termp *tp, const struct tbl_dat *dp,
808 const struct roffcol *col)
809{
810 size_t len, padl, padr, width;
811 int ic, hspans;
812
813 assert(dp->string)((dp->string) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/tbl_term.c"
, 813, __func__, "dp->string"))
;
814 len = term_strlen(tp, dp->string);
815 width = col->width;
816 ic = dp->layout->col;
817 hspans = dp->hspans;
818 while (hspans--) {
819 width += tp->tbl.cols[ic].spacing;
820 ic++;
821 width += tp->tbl.cols[ic].width;
822 }
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
851static void
852tbl_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
918static void
919tbl_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}