Bug Summary

File:src/usr.bin/mandoc/term.c
Warning:line 700, column 6
3rd function call argument is an uninitialized value

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 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/term.c
1/* $OpenBSD: term.c,v 1.145 2022/01/10 17:59:45 schwarze Exp $ */
2/*
3 * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008, 2009, 2010, 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 AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "mandoc.h"
28#include "mandoc_aux.h"
29#include "out.h"
30#include "term.h"
31#include "main.h"
32
33static size_t cond_width(const struct termp *, int, int *);
34static void adjbuf(struct termp_col *, size_t);
35static void bufferc(struct termp *, char);
36static void encode(struct termp *, const char *, size_t);
37static void encode1(struct termp *, int);
38static void endline(struct termp *);
39static void term_field(struct termp *, size_t, size_t);
40static void term_fill(struct termp *, size_t *, size_t *,
41 size_t);
42
43
44void
45term_setcol(struct termp *p, size_t maxtcol)
46{
47 if (maxtcol > p->maxtcol) {
48 p->tcols = mandoc_recallocarray(p->tcols,
49 p->maxtcol, maxtcol, sizeof(*p->tcols));
50 p->maxtcol = maxtcol;
51 }
52 p->lasttcol = maxtcol - 1;
53 p->tcol = p->tcols;
54}
55
56void
57term_free(struct termp *p)
58{
59 term_tab_free();
60 for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++)
61 free(p->tcol->buf);
62 free(p->tcols);
63 free(p->fontq);
64 free(p);
65}
66
67void
68term_begin(struct termp *p, term_margin head,
69 term_margin foot, const struct roff_meta *arg)
70{
71
72 p->headf = head;
73 p->footf = foot;
74 p->argf = arg;
75 (*p->begin)(p);
76}
77
78void
79term_end(struct termp *p)
80{
81
82 (*p->end)(p);
83}
84
85/*
86 * Flush a chunk of text. By default, break the output line each time
87 * the right margin is reached, and continue output on the next line
88 * at the same offset as the chunk itself. By default, also break the
89 * output line at the end of the chunk. There are many flags modifying
90 * this behaviour, see the comments in the body of the function.
91 */
92void
93term_flushln(struct termp *p)
94{
95 size_t vbl; /* Number of blanks to prepend to the output. */
96 size_t vbr; /* Actual visual position of the end of field. */
97 size_t vfield; /* Desired visual field width. */
98 size_t vtarget; /* Desired visual position of the right margin. */
99 size_t ic; /* Character position in the input buffer. */
100 size_t nbr; /* Number of characters to print in this field. */
101
102 /*
103 * Normally, start writing at the left margin, but with the
104 * NOPAD flag, start writing at the current position instead.
105 */
106
107 vbl = (p->flags & TERMP_NOPAD(1 << 12)) || p->tcol->offset < p->viscol ?
108 0 : p->tcol->offset - p->viscol;
109 if (p->minbl && vbl < p->minbl)
110 vbl = p->minbl;
111
112 if ((p->flags & TERMP_MULTICOL(1 << 20)) == 0)
113 p->tcol->col = 0;
114
115 /* Loop over output lines. */
116
117 for (;;) {
118 vfield = p->tcol->rmargin > p->viscol + vbl ?
119 p->tcol->rmargin - p->viscol - vbl : 0;
120
121 /*
122 * Normally, break the line at the the right margin
123 * of the field, but with the NOBREAK flag, only
124 * break it at the max right margin of the screen,
125 * and with the BRNEVER flag, never break it at all.
126 */
127
128 vtarget = (p->flags & TERMP_NOBREAK(1 << 8)) == 0 ? vfield :
129 p->maxrmargin > p->viscol + vbl ?
130 p->maxrmargin - p->viscol - vbl : 0;
131
132 /*
133 * Figure out how much text will fit in the field.
134 * If there is whitespace only, print nothing.
135 */
136
137 term_fill(p, &nbr, &vbr,
138 p->flags & TERMP_BRNEVER(1 << 16) ? SIZE_MAX0xffffffffffffffffUL : vtarget);
139 if (nbr == 0)
140 break;
141
142 /*
143 * With the CENTER or RIGHT flag, increase the indentation
144 * to center the text between the left and right margins
145 * or to adjust it to the right margin, respectively.
146 */
147
148 if (vbr < vtarget) {
149 if (p->flags & TERMP_CENTER(1 << 21))
150 vbl += (vtarget - vbr) / 2;
151 else if (p->flags & TERMP_RIGHT(1 << 22))
152 vbl += vtarget - vbr;
153 }
154
155 /* Finally, print the field content. */
156
157 term_field(p, vbl, nbr);
158
159 /*
160 * If there is no text left in the field, exit the loop.
161 * If the BRTRSP flag is set, consider trailing
162 * whitespace significant when deciding whether
163 * the field fits or not.
164 */
165
166 for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
167 switch (p->tcol->buf[ic]) {
168 case '\t':
169 if (p->flags & TERMP_BRTRSP(1 << 9))
170 vbr = term_tab_next(vbr);
171 continue;
172 case ' ':
173 if (p->flags & TERMP_BRTRSP(1 << 9))
174 vbr += (*p->width)(p, ' ');
175 continue;
176 case '\n':
177 case ASCII_BREAK29:
178 continue;
179 default:
180 break;
181 }
182 break;
183 }
184 if (ic == p->tcol->lastcol)
185 break;
186
187 /*
188 * At the location of an automtic line break, input
189 * space characters are consumed by the line break.
190 */
191
192 while (p->tcol->col < p->tcol->lastcol &&
193 p->tcol->buf[p->tcol->col] == ' ')
194 p->tcol->col++;
195
196 /*
197 * In multi-column mode, leave the rest of the text
198 * in the buffer to be handled by a subsequent
199 * invocation, such that the other columns of the
200 * table can be handled first.
201 * In single-column mode, simply break the line.
202 */
203
204 if (p->flags & TERMP_MULTICOL(1 << 20))
205 return;
206
207 endline(p);
208 p->viscol = 0;
209
210 /*
211 * Normally, start the next line at the same indentation
212 * as this one, but with the BRIND flag, start it at the
213 * right margin instead. This is used together with
214 * NOBREAK for the tags in various kinds of tagged lists.
215 */
216
217 vbl = p->flags & TERMP_BRIND(1 << 10) ?
218 p->tcol->rmargin : p->tcol->offset;
219 }
220
221 /* Reset output state in preparation for the next field. */
222
223 p->col = p->tcol->col = p->tcol->lastcol = 0;
224 p->minbl = p->trailspace;
225 p->flags &= ~(TERMP_BACKAFTER(1 << 6) | TERMP_BACKBEFORE(1 << 7) | TERMP_NOPAD(1 << 12));
226
227 if (p->flags & TERMP_MULTICOL(1 << 20))
228 return;
229
230 /*
231 * The HANG flag means that the next field
232 * always follows on the same line.
233 * The NOBREAK flag means that the next field
234 * follows on the same line unless the field was overrun.
235 * Normally, break the line at the end of each field.
236 */
237
238 if ((p->flags & TERMP_HANG(1 << 11)) == 0 &&
239 ((p->flags & TERMP_NOBREAK(1 << 8)) == 0 ||
240 vbr + term_len(p, p->trailspace) > vfield))
241 endline(p);
242}
243
244/*
245 * Store the number of input characters to print in this field in *nbr
246 * and their total visual width to print in *vbr.
247 * If there is only whitespace in the field, both remain zero.
248 * The desired visual width of the field is provided by vtarget.
249 * If the first word is longer, the field will be overrun.
250 */
251static void
252term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
253{
254 size_t ic; /* Character position in the input buffer. */
255 size_t vis; /* Visual position of the current character. */
256 size_t vn; /* Visual position of the next character. */
257 int breakline; /* Break at the end of this word. */
258 int graph; /* Last character was non-blank. */
259
260 *nbr = *vbr = vis = 0;
261 breakline = graph = 0;
262 for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
263 switch (p->tcol->buf[ic]) {
264 case '\b': /* Escape \o (overstrike) or backspace markup. */
265 assert(ic > 0)((ic > 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/term.c"
, 265, __func__, "ic > 0"))
;
266 vis -= (*p->width)(p, p->tcol->buf[ic - 1]);
267 continue;
268
269 case '\t': /* Normal ASCII whitespace. */
270 case ' ':
271 case ASCII_BREAK29: /* Escape \: (breakpoint). */
272 switch (p->tcol->buf[ic]) {
273 case '\t':
274 vn = term_tab_next(vis);
275 break;
276 case ' ':
277 vn = vis + (*p->width)(p, ' ');
278 break;
279 case ASCII_BREAK29:
280 vn = vis;
281 break;
282 default:
283 abort();
284 }
285 /* Can break at the end of a word. */
286 if (breakline || vn > vtarget)
287 break;
288 if (graph) {
289 *nbr = ic;
290 *vbr = vis;
291 graph = 0;
292 }
293 vis = vn;
294 continue;
295
296 case '\n': /* Escape \p (break at the end of the word). */
297 breakline = 1;
298 continue;
299
300 case ASCII_HYPH30: /* Breakable hyphen. */
301 graph = 1;
302 /*
303 * We are about to decide whether to break the
304 * line or not, so we no longer need this hyphen
305 * to be marked as breakable. Put back a real
306 * hyphen such that we get the correct width.
307 */
308 p->tcol->buf[ic] = '-';
309 vis += (*p->width)(p, '-');
310 if (vis > vtarget) {
311 ic++;
312 break;
313 }
314 *nbr = ic + 1;
315 *vbr = vis;
316 continue;
317
318 case ASCII_NBRSP31: /* Non-breakable space. */
319 p->tcol->buf[ic] = ' ';
320 /* FALLTHROUGH */
321 default: /* Printable character. */
322 graph = 1;
323 vis += (*p->width)(p, p->tcol->buf[ic]);
324 if (vis > vtarget && *nbr > 0)
325 return;
326 continue;
327 }
328 break;
329 }
330
331 /*
332 * If the last word extends to the end of the field without any
333 * trailing whitespace, the loop could not check yet whether it
334 * can remain on this line. So do the check now.
335 */
336
337 if (graph && (vis <= vtarget || *nbr == 0)) {
338 *nbr = ic;
339 *vbr = vis;
340 }
341}
342
343/*
344 * Print the contents of one field
345 * with an indentation of vbl visual columns,
346 * and an input string length of nbr characters.
347 */
348static void
349term_field(struct termp *p, size_t vbl, size_t nbr)
350{
351 size_t ic; /* Character position in the input buffer. */
352 size_t vis; /* Visual position of the current character. */
353 size_t dv; /* Visual width of the current character. */
354 size_t vn; /* Visual position of the next character. */
355
356 vis = 0;
357 for (ic = p->tcol->col; ic < nbr; ic++) {
358
359 /*
360 * To avoid the printing of trailing whitespace,
361 * do not print whitespace right away, only count it.
362 */
363
364 switch (p->tcol->buf[ic]) {
365 case '\n':
366 case ASCII_BREAK29:
367 continue;
368 case '\t':
369 vn = term_tab_next(vis);
370 vbl += vn - vis;
371 vis = vn;
372 continue;
373 case ' ':
374 case ASCII_NBRSP31:
375 dv = (*p->width)(p, ' ');
376 vbl += dv;
377 vis += dv;
378 continue;
379 default:
380 break;
381 }
382
383 /*
384 * We found a non-blank character to print,
385 * so write preceding white space now.
386 */
387
388 if (vbl > 0) {
389 (*p->advance)(p, vbl);
390 p->viscol += vbl;
391 vbl = 0;
392 }
393
394 /* Print the character and adjust the visual position. */
395
396 (*p->letter)(p, p->tcol->buf[ic]);
397 if (p->tcol->buf[ic] == '\b') {
398 dv = (*p->width)(p, p->tcol->buf[ic - 1]);
399 p->viscol -= dv;
400 vis -= dv;
401 } else {
402 dv = (*p->width)(p, p->tcol->buf[ic]);
403 p->viscol += dv;
404 vis += dv;
405 }
406 }
407 p->tcol->col = nbr;
408}
409
410static void
411endline(struct termp *p)
412{
413 if ((p->flags & (TERMP_NEWMC(1 << 18) | TERMP_ENDMC(1 << 19))) == TERMP_ENDMC(1 << 19)) {
414 p->mc = NULL((void *)0);
415 p->flags &= ~TERMP_ENDMC(1 << 19);
416 }
417 if (p->mc != NULL((void *)0)) {
418 if (p->viscol && p->maxrmargin >= p->viscol)
419 (*p->advance)(p, p->maxrmargin - p->viscol + 1);
420 p->flags |= TERMP_NOBUF(1 << 17) | TERMP_NOSPACE(1 << 1);
421 term_word(p, p->mc);
422 p->flags &= ~(TERMP_NOBUF(1 << 17) | TERMP_NEWMC(1 << 18));
423 }
424 p->viscol = 0;
425 p->minbl = 0;
426 (*p->endline)(p);
427}
428
429/*
430 * A newline only breaks an existing line; it won't assert vertical
431 * space. All data in the output buffer is flushed prior to the newline
432 * assertion.
433 */
434void
435term_newln(struct termp *p)
436{
437
438 p->flags |= TERMP_NOSPACE(1 << 1);
439 if (p->tcol->lastcol || p->viscol)
440 term_flushln(p);
441}
442
443/*
444 * Asserts a vertical space (a full, empty line-break between lines).
445 * Note that if used twice, this will cause two blank spaces and so on.
446 * All data in the output buffer is flushed prior to the newline
447 * assertion.
448 */
449void
450term_vspace(struct termp *p)
451{
452
453 term_newln(p);
454 p->viscol = 0;
455 p->minbl = 0;
456 if (0 < p->skipvsp)
457 p->skipvsp--;
458 else
459 (*p->endline)(p);
460}
461
462/* Swap current and previous font; for \fP and .ft P */
463void
464term_fontlast(struct termp *p)
465{
466 enum termfont f;
467
468 f = p->fontl;
469 p->fontl = p->fontq[p->fonti];
470 p->fontq[p->fonti] = f;
471}
472
473/* Set font, save current, discard previous; for \f, .ft, .B etc. */
474void
475term_fontrepl(struct termp *p, enum termfont f)
476{
477
478 p->fontl = p->fontq[p->fonti];
479 p->fontq[p->fonti] = f;
480}
481
482/* Set font, save previous. */
483void
484term_fontpush(struct termp *p, enum termfont f)
485{
486
487 p->fontl = p->fontq[p->fonti];
488 if (++p->fonti == p->fontsz) {
489 p->fontsz += 8;
490 p->fontq = mandoc_reallocarray(p->fontq,
491 p->fontsz, sizeof(*p->fontq));
492 }
493 p->fontq[p->fonti] = f;
494}
495
496/* Flush to make the saved pointer current again. */
497void
498term_fontpopq(struct termp *p, int i)
499{
500
501 assert(i >= 0)((i >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/term.c"
, 501, __func__, "i >= 0"))
;
502 if (p->fonti > i)
503 p->fonti = i;
504}
505
506/* Pop one font off the stack. */
507void
508term_fontpop(struct termp *p)
509{
510
511 assert(p->fonti)((p->fonti) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/term.c"
, 511, __func__, "p->fonti"))
;
512 p->fonti--;
513}
514
515/*
516 * Handle pwords, partial words, which may be either a single word or a
517 * phrase that cannot be broken down (such as a literal string). This
518 * handles word styling.
519 */
520void
521term_word(struct termp *p, const char *word)
522{
523 struct roffsu su;
524 const char nbrsp[2] = { ASCII_NBRSP31, 0 };
525 const char *seq, *cp;
526 int sz, uc;
527 size_t csz, lsz, ssz;
1
'ssz' declared without an initial value
528 enum mandoc_esc esc;
529
530 if ((p->flags & TERMP_NOBUF(1 << 17)) == 0) {
2
Assuming the condition is false
3
Taking false branch
531 if ((p->flags & TERMP_NOSPACE(1 << 1)) == 0) {
532 if ((p->flags & TERMP_KEEP(1 << 4)) == 0) {
533 bufferc(p, ' ');
534 if (p->flags & TERMP_SENTENCE(1 << 0))
535 bufferc(p, ' ');
536 } else
537 bufferc(p, ASCII_NBRSP31);
538 }
539 if (p->flags & TERMP_PREKEEP(1 << 5))
540 p->flags |= TERMP_KEEP(1 << 4);
541 if (p->flags & TERMP_NONOSPACE(1 << 2))
542 p->flags |= TERMP_NOSPACE(1 << 1);
543 else
544 p->flags &= ~TERMP_NOSPACE(1 << 1);
545 p->flags &= ~(TERMP_SENTENCE(1 << 0) | TERMP_NONEWLINE(1 << 15));
546 p->skipvsp = 0;
547 }
548
549 while ('\0' != *word) {
4
Assuming the condition is true
5
Loop condition is true. Entering loop body
550 if ('\\' != *word) {
6
Assuming the condition is false
7
Taking false branch
551 if (TERMP_NBRWORD(1 << 3) & p->flags) {
552 if (' ' == *word) {
553 encode(p, nbrsp, 1);
554 word++;
555 continue;
556 }
557 ssz = strcspn(word, "\\ ");
558 } else
559 ssz = strcspn(word, "\\");
560 encode(p, word, ssz);
561 word += (int)ssz;
562 continue;
563 }
564
565 word++;
566 esc = mandoc_escape(&word, &seq, &sz);
567 switch (esc) {
8
Control jumps to 'case ESCAPE_HLINE:' at line 656
568 case ESCAPE_UNICODE:
569 uc = mchars_num2uc(seq + 1, sz - 1);
570 break;
571 case ESCAPE_NUMBERED:
572 uc = mchars_num2char(seq, sz);
573 if (uc < 0)
574 continue;
575 break;
576 case ESCAPE_SPECIAL:
577 if (p->enc == TERMENC_ASCII) {
578 cp = mchars_spec2str(seq, sz, &ssz);
579 if (cp != NULL((void *)0))
580 encode(p, cp, ssz);
581 } else {
582 uc = mchars_spec2cp(seq, sz);
583 if (uc > 0)
584 encode1(p, uc);
585 }
586 continue;
587 case ESCAPE_UNDEF:
588 uc = *seq;
589 break;
590 case ESCAPE_FONTBOLD:
591 case ESCAPE_FONTCB:
592 term_fontrepl(p, TERMFONT_BOLD);
593 continue;
594 case ESCAPE_FONTITALIC:
595 case ESCAPE_FONTCI:
596 term_fontrepl(p, TERMFONT_UNDER);
597 continue;
598 case ESCAPE_FONTBI:
599 term_fontrepl(p, TERMFONT_BI);
600 continue;
601 case ESCAPE_FONT:
602 case ESCAPE_FONTCR:
603 case ESCAPE_FONTROMAN:
604 term_fontrepl(p, TERMFONT_NONE);
605 continue;
606 case ESCAPE_FONTPREV:
607 term_fontlast(p);
608 continue;
609 case ESCAPE_BREAK:
610 bufferc(p, '\n');
611 continue;
612 case ESCAPE_NOSPACE:
613 if (p->flags & TERMP_BACKAFTER(1 << 6))
614 p->flags &= ~TERMP_BACKAFTER(1 << 6);
615 else if (*word == '\0')
616 p->flags |= (TERMP_NOSPACE(1 << 1) | TERMP_NONEWLINE(1 << 15));
617 continue;
618 case ESCAPE_DEVICE:
619 if (p->type == TERMTYPE_PDF)
620 encode(p, "pdf", 3);
621 else if (p->type == TERMTYPE_PS)
622 encode(p, "ps", 2);
623 else if (p->enc == TERMENC_ASCII)
624 encode(p, "ascii", 5);
625 else
626 encode(p, "utf8", 4);
627 continue;
628 case ESCAPE_HORIZ:
629 if (*seq == '|') {
630 seq++;
631 uc = -p->col;
632 } else
633 uc = 0;
634 if (a2roffsu(seq, &su, SCALE_EM) == NULL((void *)0))
635 continue;
636 uc += term_hen(p, &su);
637 if (uc > 0) {
638 while (uc > 0) {
639 bufferc(p, ASCII_NBRSP31);
640 uc -= term_len(p, 1);
641 }
642 } else if (p->col > (size_t)(-uc)) {
643 p->col += uc;
644 } else {
645 uc += p->col;
646 p->col = 0;
647 if (p->tcol->offset > (size_t)(-uc)) {
648 p->ti += uc;
649 p->tcol->offset += uc;
650 } else {
651 p->ti -= p->tcol->offset;
652 p->tcol->offset = 0;
653 }
654 }
655 continue;
656 case ESCAPE_HLINE:
657 if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL((void *)0))
9
Assuming the condition is false
10
Taking false branch
658 continue;
659 uc = term_hen(p, &su);
660 if (uc <= 0) {
11
Assuming 'uc' is > 0
12
Taking false branch
661 if (p->tcol->rmargin <= p->tcol->offset)
662 continue;
663 lsz = p->tcol->rmargin - p->tcol->offset;
664 } else
665 lsz = uc;
666 if (*cp == seq[-1])
13
Assuming the condition is false
14
Taking false branch
667 uc = -1;
668 else if (*cp == '\\') {
15
Assuming the condition is false
16
Taking false branch
669 seq = cp + 1;
670 esc = mandoc_escape(&seq, &cp, &sz);
671 switch (esc) {
672 case ESCAPE_UNICODE:
673 uc = mchars_num2uc(cp + 1, sz - 1);
674 break;
675 case ESCAPE_NUMBERED:
676 uc = mchars_num2char(cp, sz);
677 break;
678 case ESCAPE_SPECIAL:
679 uc = mchars_spec2cp(cp, sz);
680 break;
681 case ESCAPE_UNDEF:
682 uc = *seq;
683 break;
684 default:
685 uc = -1;
686 break;
687 }
688 } else
689 uc = *cp;
690 if (uc < 0x20 || (uc > 0x7E && uc < 0xA0))
17
Assuming 'uc' is >= 32
18
Assuming 'uc' is <= 126
691 uc = '_';
692 if (p->enc == TERMENC_ASCII) {
19
Assuming field 'enc' is not equal to TERMENC_ASCII
20
Taking false branch
693 cp = ascii_uc2str(uc);
694 csz = term_strlen(p, cp);
695 ssz = strlen(cp);
696 } else
697 csz = (*p->width)(p, uc);
698 while (lsz >= csz) {
21
Assuming 'lsz' is >= 'csz'
22
Loop condition is true. Entering loop body
699 if (p->enc == TERMENC_ASCII)
23
Assuming field 'enc' is equal to TERMENC_ASCII
24
Taking true branch
700 encode(p, cp, ssz);
25
3rd function call argument is an uninitialized value
701 else
702 encode1(p, uc);
703 lsz -= csz;
704 }
705 continue;
706 case ESCAPE_SKIPCHAR:
707 p->flags |= TERMP_BACKAFTER(1 << 6);
708 continue;
709 case ESCAPE_OVERSTRIKE:
710 cp = seq + sz;
711 while (seq < cp) {
712 if (*seq == '\\') {
713 mandoc_escape(&seq, NULL((void *)0), NULL((void *)0));
714 continue;
715 }
716 encode1(p, *seq++);
717 if (seq < cp) {
718 if (p->flags & TERMP_BACKBEFORE(1 << 7))
719 p->flags |= TERMP_BACKAFTER(1 << 6);
720 else
721 p->flags |= TERMP_BACKBEFORE(1 << 7);
722 }
723 }
724 /* Trim trailing backspace/blank pair. */
725 if (p->tcol->lastcol > 2 &&
726 (p->tcol->buf[p->tcol->lastcol - 1] == ' ' ||
727 p->tcol->buf[p->tcol->lastcol - 1] == '\t'))
728 p->tcol->lastcol -= 2;
729 if (p->col > p->tcol->lastcol)
730 p->col = p->tcol->lastcol;
731 continue;
732 default:
733 continue;
734 }
735
736 /*
737 * Common handling for Unicode and numbered
738 * character escape sequences.
739 */
740
741 if (p->enc == TERMENC_ASCII) {
742 cp = ascii_uc2str(uc);
743 encode(p, cp, strlen(cp));
744 } else {
745 if ((uc < 0x20 && uc != 0x09) ||
746 (uc > 0x7E && uc < 0xA0))
747 uc = 0xFFFD;
748 encode1(p, uc);
749 }
750 }
751 p->flags &= ~TERMP_NBRWORD(1 << 3);
752}
753
754static void
755adjbuf(struct termp_col *c, size_t sz)
756{
757 if (c->maxcols == 0)
758 c->maxcols = 1024;
759 while (c->maxcols <= sz)
760 c->maxcols <<= 2;
761 c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf));
762}
763
764static void
765bufferc(struct termp *p, char c)
766{
767 if (p->flags & TERMP_NOBUF(1 << 17)) {
768 (*p->letter)(p, c);
769 return;
770 }
771 if (p->col + 1 >= p->tcol->maxcols)
772 adjbuf(p->tcol, p->col + 1);
773 if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP31))
774 p->tcol->buf[p->col] = c;
775 if (p->tcol->lastcol < ++p->col)
776 p->tcol->lastcol = p->col;
777}
778
779/*
780 * See encode().
781 * Do this for a single (probably unicode) value.
782 * Does not check for non-decorated glyphs.
783 */
784static void
785encode1(struct termp *p, int c)
786{
787 enum termfont f;
788
789 if (p->flags & TERMP_NOBUF(1 << 17)) {
790 (*p->letter)(p, c);
791 return;
792 }
793
794 if (p->col + 7 >= p->tcol->maxcols)
795 adjbuf(p->tcol, p->col + 7);
796
797 f = (c == ASCII_HYPH30 || c > 127 || isgraph(c)) ?
798 p->fontq[p->fonti] : TERMFONT_NONE;
799
800 if (p->flags & TERMP_BACKBEFORE(1 << 7)) {
801 if (p->tcol->buf[p->col - 1] == ' ' ||
802 p->tcol->buf[p->col - 1] == '\t')
803 p->col--;
804 else
805 p->tcol->buf[p->col++] = '\b';
806 p->flags &= ~TERMP_BACKBEFORE(1 << 7);
807 }
808 if (f == TERMFONT_UNDER || f == TERMFONT_BI) {
809 p->tcol->buf[p->col++] = '_';
810 p->tcol->buf[p->col++] = '\b';
811 }
812 if (f == TERMFONT_BOLD || f == TERMFONT_BI) {
813 if (c == ASCII_HYPH30)
814 p->tcol->buf[p->col++] = '-';
815 else
816 p->tcol->buf[p->col++] = c;
817 p->tcol->buf[p->col++] = '\b';
818 }
819 if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP31))
820 p->tcol->buf[p->col] = c;
821 if (p->tcol->lastcol < ++p->col)
822 p->tcol->lastcol = p->col;
823 if (p->flags & TERMP_BACKAFTER(1 << 6)) {
824 p->flags |= TERMP_BACKBEFORE(1 << 7);
825 p->flags &= ~TERMP_BACKAFTER(1 << 6);
826 }
827}
828
829static void
830encode(struct termp *p, const char *word, size_t sz)
831{
832 size_t i;
833
834 if (p->flags & TERMP_NOBUF(1 << 17)) {
835 for (i = 0; i < sz; i++)
836 (*p->letter)(p, word[i]);
837 return;
838 }
839
840 if (p->col + 2 + (sz * 5) >= p->tcol->maxcols)
841 adjbuf(p->tcol, p->col + 2 + (sz * 5));
842
843 for (i = 0; i < sz; i++) {
844 if (ASCII_HYPH30 == word[i] ||
845 isgraph((unsigned char)word[i]))
846 encode1(p, word[i]);
847 else {
848 if (p->tcol->lastcol <= p->col ||
849 (word[i] != ' ' && word[i] != ASCII_NBRSP31))
850 p->tcol->buf[p->col] = word[i];
851 p->col++;
852
853 /*
854 * Postpone the effect of \z while handling
855 * an overstrike sequence from ascii_uc2str().
856 */
857
858 if (word[i] == '\b' &&
859 (p->flags & TERMP_BACKBEFORE(1 << 7))) {
860 p->flags &= ~TERMP_BACKBEFORE(1 << 7);
861 p->flags |= TERMP_BACKAFTER(1 << 6);
862 }
863 }
864 }
865 if (p->tcol->lastcol < p->col)
866 p->tcol->lastcol = p->col;
867}
868
869void
870term_setwidth(struct termp *p, const char *wstr)
871{
872 struct roffsu su;
873 int iop, width;
874
875 iop = 0;
876 width = 0;
877 if (NULL((void *)0) != wstr) {
878 switch (*wstr) {
879 case '+':
880 iop = 1;
881 wstr++;
882 break;
883 case '-':
884 iop = -1;
885 wstr++;
886 break;
887 default:
888 break;
889 }
890 if (a2roffsu(wstr, &su, SCALE_MAX) != NULL((void *)0))
891 width = term_hspan(p, &su);
892 else
893 iop = 0;
894 }
895 (*p->setwidth)(p, iop, width);
896}
897
898size_t
899term_len(const struct termp *p, size_t sz)
900{
901
902 return (*p->width)(p, ' ') * sz;
903}
904
905static size_t
906cond_width(const struct termp *p, int c, int *skip)
907{
908
909 if (*skip) {
910 (*skip) = 0;
911 return 0;
912 } else
913 return (*p->width)(p, c);
914}
915
916size_t
917term_strlen(const struct termp *p, const char *cp)
918{
919 size_t sz, rsz, i;
920 int ssz, skip, uc;
921 const char *seq, *rhs;
922 enum mandoc_esc esc;
923 static const char rej[] = { '\\', ASCII_NBRSP31, ASCII_HYPH30,
924 ASCII_BREAK29, '\0' };
925
926 /*
927 * Account for escaped sequences within string length
928 * calculations. This follows the logic in term_word() as we
929 * must calculate the width of produced strings.
930 */
931
932 sz = 0;
933 skip = 0;
934 while ('\0' != *cp) {
935 rsz = strcspn(cp, rej);
936 for (i = 0; i < rsz; i++)
937 sz += cond_width(p, *cp++, &skip);
938
939 switch (*cp) {
940 case '\\':
941 cp++;
942 rhs = NULL((void *)0);
943 esc = mandoc_escape(&cp, &seq, &ssz);
944 switch (esc) {
945 case ESCAPE_UNICODE:
946 uc = mchars_num2uc(seq + 1, ssz - 1);
947 break;
948 case ESCAPE_NUMBERED:
949 uc = mchars_num2char(seq, ssz);
950 if (uc < 0)
951 continue;
952 break;
953 case ESCAPE_SPECIAL:
954 if (p->enc == TERMENC_ASCII) {
955 rhs = mchars_spec2str(seq, ssz, &rsz);
956 if (rhs != NULL((void *)0))
957 break;
958 } else {
959 uc = mchars_spec2cp(seq, ssz);
960 if (uc > 0)
961 sz += cond_width(p, uc, &skip);
962 }
963 continue;
964 case ESCAPE_UNDEF:
965 uc = *seq;
966 break;
967 case ESCAPE_DEVICE:
968 if (p->type == TERMTYPE_PDF) {
969 rhs = "pdf";
970 rsz = 3;
971 } else if (p->type == TERMTYPE_PS) {
972 rhs = "ps";
973 rsz = 2;
974 } else if (p->enc == TERMENC_ASCII) {
975 rhs = "ascii";
976 rsz = 5;
977 } else {
978 rhs = "utf8";
979 rsz = 4;
980 }
981 break;
982 case ESCAPE_SKIPCHAR:
983 skip = 1;
984 continue;
985 case ESCAPE_OVERSTRIKE:
986 rsz = 0;
987 rhs = seq + ssz;
988 while (seq < rhs) {
989 if (*seq == '\\') {
990 mandoc_escape(&seq, NULL((void *)0), NULL((void *)0));
991 continue;
992 }
993 i = (*p->width)(p, *seq++);
994 if (rsz < i)
995 rsz = i;
996 }
997 sz += rsz;
998 continue;
999 default:
1000 continue;
1001 }
1002
1003 /*
1004 * Common handling for Unicode and numbered
1005 * character escape sequences.
1006 */
1007
1008 if (rhs == NULL((void *)0)) {
1009 if (p->enc == TERMENC_ASCII) {
1010 rhs = ascii_uc2str(uc);
1011 rsz = strlen(rhs);
1012 } else {
1013 if ((uc < 0x20 && uc != 0x09) ||
1014 (uc > 0x7E && uc < 0xA0))
1015 uc = 0xFFFD;
1016 sz += cond_width(p, uc, &skip);
1017 continue;
1018 }
1019 }
1020
1021 if (skip) {
1022 skip = 0;
1023 break;
1024 }
1025
1026 /*
1027 * Common handling for all escape sequences
1028 * printing more than one character.
1029 */
1030
1031 for (i = 0; i < rsz; i++)
1032 sz += (*p->width)(p, *rhs++);
1033 break;
1034 case ASCII_NBRSP31:
1035 sz += cond_width(p, ' ', &skip);
1036 cp++;
1037 break;
1038 case ASCII_HYPH30:
1039 sz += cond_width(p, '-', &skip);
1040 cp++;
1041 break;
1042 default:
1043 break;
1044 }
1045 }
1046
1047 return sz;
1048}
1049
1050int
1051term_vspan(const struct termp *p, const struct roffsu *su)
1052{
1053 double r;
1054 int ri;
1055
1056 switch (su->unit) {
1057 case SCALE_BU:
1058 r = su->scale / 40.0;
1059 break;
1060 case SCALE_CM:
1061 r = su->scale * 6.0 / 2.54;
1062 break;
1063 case SCALE_FS:
1064 r = su->scale * 65536.0 / 40.0;
1065 break;
1066 case SCALE_IN:
1067 r = su->scale * 6.0;
1068 break;
1069 case SCALE_MM:
1070 r = su->scale * 0.006;
1071 break;
1072 case SCALE_PC:
1073 r = su->scale;
1074 break;
1075 case SCALE_PT:
1076 r = su->scale / 12.0;
1077 break;
1078 case SCALE_EN:
1079 case SCALE_EM:
1080 r = su->scale * 0.6;
1081 break;
1082 case SCALE_VS:
1083 r = su->scale;
1084 break;
1085 default:
1086 abort();
1087 }
1088 ri = r > 0.0 ? r + 0.4995 : r - 0.4995;
1089 return ri < 66 ? ri : 1;
1090}
1091
1092/*
1093 * Convert a scaling width to basic units, rounding towards 0.
1094 */
1095int
1096term_hspan(const struct termp *p, const struct roffsu *su)
1097{
1098
1099 return (*p->hspan)(p, su);
1100}
1101
1102/*
1103 * Convert a scaling width to basic units, rounding to closest.
1104 */
1105int
1106term_hen(const struct termp *p, const struct roffsu *su)
1107{
1108 int bu;
1109
1110 if ((bu = (*p->hspan)(p, su)) >= 0)
1111 return (bu + 11) / 24;
1112 else
1113 return -((-bu + 11) / 24);
1114}