Bug Summary

File:src/usr.bin/mandoc/tbl_term.c
Warning:line 717, column 10
Access to field 'pos' results in a dereference of a null pointer (loaded from variable 'cp')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tbl_term.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -fno-rounding-math -mconstructor-aliases -munwind-tables -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/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/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 -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/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/mandoc/tbl_term.c
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
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)) {
1
Assuming field 'cols' is not equal to NULL
2
Taking false branch
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) {
3
Control jumps to 'case TBL_SPAN_DATA:' at line 229
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)) ||
4
Assuming the condition is true
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++) {
5
Assuming 'ic' is < field 'cols'
6
Loop condition is true. Entering loop body
12
Loop condition is false. Execution continues on line 266
245 if (hspans
6.1
'hspans' is equal to 0
== 0) {
7
Taking true branch
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)
8
Assuming the condition is false
9
Taking false branch
252 coloff += tp->tbl.cols[ic].spacing;
253 if (hspans
9.1
'hspans' is 0
) {
10
Taking false branch
254 hspans--;
255 continue;
256 }
257 if (dp != NULL((void *)0) &&
11
Assuming 'dp' is equal to NULL
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;
13
The value of 'cpn' is assigned to 'cp'
278 dp = sp->first;
279 hspans = 0;
280 for (ic = 0; ic < sp->opts->cols; ic++) {
14
Loop condition is true. Entering loop body
281 if (cpn != NULL((void *)0)) {
15
Assuming 'cpn' is equal to NULL
16
Taking false branch
282 cp = cpn;
283 cpn = cpn->next;
284 }
285 if (hspans
16.1
'hspans' is 0
) {
17
Taking false branch
286 hspans--;
287 continue;
288 }
289 tp->tcol++;
290 tp->col = 0;
291 tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
18
Passing null pointer value via 3rd parameter 'cp'
19
Calling 'tbl_data'
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
567static void
568tbl_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 != NULL((void *)0)) {
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
712static void
713tbl_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) {
20
Access to field 'pos' results in a dereference of a null pointer (loaded from variable 'cp')
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
764static void
765tbl_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
774static void
775tbl_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
784static void
785tbl_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
796static void
797tbl_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
809static void
810tbl_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
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}