Bug Summary

File:src/gnu/usr.bin/texinfo/makeinfo/multi.c
Warning:line 448, column 11
The left operand of '+' is a garbage 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 multi.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/gnu/usr.bin/texinfo/obj/makeinfo -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I . -I /usr/src/gnu/usr.bin/texinfo/makeinfo -I .. -I /usr/src/gnu/usr.bin/texinfo/lib -I ../intl -D LOCALEDIR="/usr/share/locale" -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/usr.bin/texinfo/obj/makeinfo -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/gnu/usr.bin/texinfo/makeinfo/multi.c
1/* multi.c -- multiple-column tables (@multitable) for makeinfo.
2 $Id: multi.c,v 1.7 2006/07/17 16:12:36 espie Exp $
3
4 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software
5 Foundation, Inc.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 Originally written by phr@gnu.org (Paul Rubin). */
22
23#include "system.h"
24#include "cmds.h"
25#include "insertion.h"
26#include "makeinfo.h"
27#include "multi.h"
28#include "xml.h"
29
30#define MAXCOLS100 100 /* remove this limit later @@ */
31
32
33/*
34 * Output environments. This is a hack grafted onto existing
35 * structure. The "output environment" used to consist of the
36 * global variables `output_paragraph', `fill_column', etc.
37 * Routines like add_char would manipulate these variables.
38 *
39 * Now, when formatting a multitable, we maintain separate environments
40 * for each column. That way we can build up the columns separately
41 * and write them all out at once. The "current" output environment"
42 * is still kept in those global variables, so that the old output
43 * routines don't have to change. But we provide routines to save
44 * and restore these variables in an "environment table". The
45 * `select_output_environment' function switches from one output
46 * environment to another.
47 *
48 * Environment #0 (i.e., element #0 of the table) is the regular
49 * environment that is used when we're not formatting a multitable.
50 *
51 * Environment #N (where N = 1,2,3,...) is the env. for column #N of
52 * the table, when a multitable is active.
53 */
54
55/* contents of an output environment */
56/* some more vars may end up being needed here later @@ */
57struct env
58{
59 unsigned char *output_paragraph;
60 int output_paragraph_offset;
61 int meta_char_pos;
62 int output_column;
63 int paragraph_is_open;
64 int current_indent;
65 int fill_column;
66} envs[MAXCOLS100]; /* the environment table */
67
68/* index in environment table of currently selected environment */
69static int current_env_no;
70
71/* current column number */
72static int current_column_no;
73
74/* We need to make a difference between template based widths and
75 @columnfractions for HTML tables' sake. Sigh. */
76static int seen_column_fractions;
77
78/* column number of last column in current multitable */
79static int last_column;
80
81/* flags indicating whether horizontal and vertical separators need
82 to be drawn, separating rows and columns in the current multitable. */
83static int hsep, vsep;
84
85/* whether this is the first row. */
86static int first_row;
87
88/* Called to handle a {...} template on the @multitable line.
89 We're at the { and our first job is to find the matching }; as a side
90 effect, we change *PARAMS to point to after it. Our other job is to
91 expand the template text and return the width of that string. */
92static unsigned
93find_template_width (char **params)
94{
95 char *template, *xtemplate;
96 unsigned len;
97 char *start = *params;
98 int brace_level = 0;
99
100 /* The first character should be a {. */
101 if (!params || !*params || **params != '{')
102 {
103 line_error ("find_template width internal error: passed %s",
104 params ? *params : "null");
105 return 0;
106 }
107
108 do
109 {
110 if (**params == '{' && (*params == start || (*params)[-1] != '@'))
111 brace_level++;
112 else if (**params == '}' && (*params)[-1] != '@')
113 brace_level--;
114 else if (**params == 0)
115 {
116 line_error (_("Missing } in @multitable template")((const char *) ("Missing } in @multitable template")));
117 return 0;
118 }
119 (*params)++;
120 }
121 while (brace_level > 0);
122
123 template = substring (start + 1, *params - 1); /* omit braces */
124 xtemplate = expansion (template, 0);
125 len = strlen (xtemplate);
126
127 free (template);
128 free (xtemplate);
129
130 return len;
131}
132
133/* Direct current output to environment number N. Used when
134 switching work from one column of a multitable to the next.
135 Returns previous environment number. */
136static int
137select_output_environment (int n)
138{
139 struct env *e = &envs[current_env_no];
140 int old_env_no = current_env_no;
141
142 /* stash current env info from global vars into the old environment */
143 e->output_paragraph = output_paragraph;
144 e->output_paragraph_offset = output_paragraph_offset;
145 e->meta_char_pos = meta_char_pos;
146 e->output_column = output_column;
147 e->paragraph_is_open = paragraph_is_open;
148 e->current_indent = current_indent;
149 e->fill_column = fill_column;
150
151 /* now copy new environment into global vars */
152 current_env_no = n;
153 e = &envs[current_env_no];
154 output_paragraph = e->output_paragraph;
155 output_paragraph_offset = e->output_paragraph_offset;
156 meta_char_pos = e->meta_char_pos;
157 output_column = e->output_column;
158 paragraph_is_open = e->paragraph_is_open;
159 current_indent = e->current_indent;
160 fill_column = e->fill_column;
161 return old_env_no;
162}
163
164/* Initialize environment number ENV_NO, of width WIDTH.
165 The idea is that we're going to use one environment for each column of
166 a multitable, so we can build them up separately and print them
167 all out at the end. */
168static int
169setup_output_environment (int env_no, int width)
170{
171 int old_env = select_output_environment (env_no);
172
173 /* clobber old environment and set width of new one */
174 init_paragraph ();
175
176 /* make our change */
177 fill_column = width;
178
179 /* Save new environment and restore previous one. */
180 select_output_environment (old_env);
181
182 return env_no;
183}
184
185/* Read the parameters for a multitable from the current command
186 line, save the parameters away, and return the
187 number of columns. */
188static int
189setup_multitable_parameters (void)
190{
191 char *params = insertion_stack->item_function;
192 int nchars;
193 float columnfrac;
194 char command[200]; /* xx no fixed limits */
195 int i = 1;
196
197 /* We implement @hsep and @vsep even though TeX doesn't.
198 We don't get mixing of @columnfractions and templates right,
199 but TeX doesn't either. */
200 hsep = vsep = 0;
201
202 /* Assume no @columnfractions per default. */
203 seen_column_fractions = 0;
204
205 while (*params) {
206 while (whitespace (*params)((*params) == '\t' || (*params) == ' '))
207 params++;
208
209 if (*params == '@') {
210 sscanf (params, "%199s", command);
211 nchars = strlen (command);
212 params += nchars;
213 if (strcmp (command, "@hsep") == 0)
214 hsep++;
215 else if (strcmp (command, "@vsep") == 0)
216 vsep++;
217 else if (strcmp (command, "@columnfractions") == 0) {
218 seen_column_fractions = 1;
219 /* Clobber old environments and create new ones, starting at #1.
220 Environment #0 is the normal output, so don't mess with it. */
221 for ( ; i <= MAXCOLS100; i++) {
222 if (sscanf (params, "%f", &columnfrac) < 1)
223 goto done;
224 /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least)
225 doesn't support it. So skip whitespace (preceding the
226 number) and then non-whitespace (the number). */
227 while (*params && (*params == ' ' || *params == '\t'))
228 params++;
229 /* Hmm, but what about @columnfractions 3foo. Oh well,
230 it's invalid input anyway. */
231 while (*params && *params != ' ' && *params != '\t'
232 && *params != '\n' && *params != '@')
233 params++;
234
235 {
236 /* For html/xml/docbook, translate fractions into integer
237 percentages, adding .005 to avoid rounding problems. For
238 info, we want the character width. */
239 int width = xml || html ? (columnfrac + .005) * 100
240 : (columnfrac * (fill_column - current_indent) + .5);
241 setup_output_environment (i, width);
242 }
243 }
244 }
245
246 } else if (*params == '{') {
247 unsigned template_width = find_template_width (&params);
248
249 /* This gives us two spaces between columns. Seems reasonable.
250 How to take into account current_indent here? */
251 setup_output_environment (i++, template_width + 2);
252
253 } else {
254 warning (_("ignoring stray text `%s' after @multitable")((const char *) ("ignoring stray text `%s' after @multitable"
))
, params);
255 break;
256 }
257 }
258
259done:
260 flush_output ();
261 inhibit_output_flushing ();
262
263 last_column = i - 1;
264 return last_column;
265}
266
267/* Output a row. Calls insert, but also flushes the buffered output
268 when we see a newline, since in multitable every line is a separate
269 paragraph. */
270static void
271out_char (int ch)
272{
273 if (html || xml)
274 add_char (ch);
275 else
276 {
277 int env = select_output_environment (0);
278 insert (ch);
279 if (ch == '\n')
280 {
281 uninhibit_output_flushing ();
282 flush_output ();
283 inhibit_output_flushing ();
284 }
285 select_output_environment (env);
286 }
287}
288
289
290static void
291draw_horizontal_separator (void)
292{
293 int i, j, s;
294
295 if (html)
296 {
297 add_word ("<hr>");
298 return;
299 }
300 if (xml)
301 return;
302
303 for (s = 0; s < envs[0].current_indent; s++)
304 out_char (' ');
305 if (vsep)
306 out_char ('+');
307 for (i = 1; i <= last_column; i++) {
308 for (j = 0; j <= envs[i].fill_column; j++)
309 out_char ('-');
310 if (vsep)
311 out_char ('+');
312 }
313 out_char (' ');
314 out_char ('\n');
315}
316
317
318/* multitable strategy:
319 for each item {
320 for each column in an item {
321 initialize a new paragraph
322 do ordinary formatting into the new paragraph
323 save the paragraph away
324 repeat if there are more paragraphs in the column
325 }
326 dump out the saved paragraphs and free the storage
327 }
328
329 For HTML we construct a simple HTML 3.2 table with <br>s inserted
330 to help non-tables browsers. `@item' inserts a <tr> and `@tab'
331 inserts <td>; we also try to close <tr>. The only real
332 alternative is to rely on the info formatting engine and present
333 preformatted text. */
334
335void
336do_multitable (void)
337{
338 int ncolumns;
339
340 if (multitable_active)
341 {
342 line_error ("Multitables cannot be nested");
343 return;
344 }
345
346 close_single_paragraph ();
347
348 if (xml)
349 {
350 xml_no_para = 1;
351 if (output_paragraph[output_paragraph_offset-1] == '\n')
352 output_paragraph_offset--;
353 }
354
355 /* scan the current item function to get the field widths
356 and number of columns, and set up the output environment list
357 accordingly. */
358 ncolumns = setup_multitable_parameters ();
359 first_row = 1;
360
361 /* <p> for non-tables browsers. @multitable implicitly ends the
362 current paragraph, so this is ok. */
363 if (html)
364 add_html_block_elt ("<p><table summary=\"\">");
365 /* else if (docbook)*/ /* 05-08 */
366 else if (xml)
367 {
368 int *widths = xmalloc (ncolumns * sizeof (int));
369 int i;
370 for (i=0; i<ncolumns; i++)
371 widths[i] = envs[i+1].fill_column;
372 xml_begin_multitable (ncolumns, widths);
373 free (widths);
374 }
375
376 if (hsep)
377 draw_horizontal_separator ();
378
379 /* The next @item command will direct stdout into the first column
380 and start processing. @tab will then switch to the next column,
381 and @item will flush out the saved output and return to the first
382 column. Environment #1 is the first column. (Environment #0 is
383 the normal output) */
384
385 ++multitable_active;
386}
387
388/* advance to the next environment number */
389static void
390nselect_next_environment (void)
391{
392 if (current_env_no >= last_column) {
393 line_error (_("Too many columns in multitable item (max %d)")((const char *) ("Too many columns in multitable item (max %d)"
))
, last_column);
394 return;
395 }
396 select_output_environment (current_env_no + 1);
397}
398
399
400/* do anything needed at the beginning of processing a
401 multitable column. */
402static void
403init_column (void)
404{
405 /* don't indent 1st paragraph in the item */
406 cm_noindent ();
407
408 /* throw away possible whitespace after @item or @tab command */
409 skip_whitespace ()while ((input_text_offset != input_text_length) && ((
input_text[input_text_offset]) == '\t' || (input_text[input_text_offset
]) == ' ')) input_text_offset++
;
410}
411
412static void
413output_multitable_row (void)
414{
415 /* offset in the output paragraph of the next char needing
416 to be output for that column. */
417 int offset[MAXCOLS100];
418 int i, j, s, remaining;
419 int had_newline = 0;
420
421 for (i = 0; i <= last_column; i++)
5
Assuming 'i' is <= 'last_column'
6
Loop condition is true. Entering loop body
7
Assuming 'i' is <= 'last_column'
8
Loop condition is true. Entering loop body
9
Assuming 'i' is > 'last_column'
10
Loop condition is false. Execution continues on line 426
422 offset[i] = 0;
423
424 /* select the current environment, to make sure the env variables
425 get updated */
426 select_output_environment (current_env_no);
427
428#define CHAR_ADDR(n) (offset[i] + (n))
429#define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)])
430
431 /* remove trailing whitespace from each column */
432 for (i = 1; i <= last_column; i++) {
11
Loop condition is true. Entering loop body
19
Loop condition is false. Execution continues on line 444
433 while (envs[i].output_paragraph_offset &&
12
Assuming field 'output_paragraph_offset' is not equal to 0
16
Loop condition is false. Execution continues on line 437
434 cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))(((CHAR_AT (envs[i].output_paragraph_offset - 1)) == '\t' || (
CHAR_AT (envs[i].output_paragraph_offset - 1)) == ' ') || (CHAR_AT
(envs[i].output_paragraph_offset - 1)) == '\r' || (CHAR_AT (
envs[i].output_paragraph_offset - 1)) == '\n')
)
13
Assuming the condition is false
14
Assuming the condition is false
15
Assuming the condition is false
435 envs[i].output_paragraph_offset--;
436
437 if (i == current_env_no)
17
Assuming 'i' is not equal to 'current_env_no'
18
Taking false branch
438 output_paragraph_offset = envs[i].output_paragraph_offset;
439 }
440
441 /* read the current line from each column, outputting them all
442 pasted together. Do this til all lines are output from all
443 columns. */
444 for (;;) {
20
Loop condition is true. Entering loop body
31
Loop condition is true. Entering loop body
445 remaining = 0;
446 /* first, see if there is any work to do */
447 for (i = 1; i <= last_column; i++) {
21
Loop condition is true. Entering loop body
32
Assuming 'i' is <= 'last_column'
33
Loop condition is true. Entering loop body
36
The value 2 is assigned to 'i'
37
Assuming 'i' is <= 'last_column'
38
Loop condition is true. Entering loop body
448 if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) {
22
Assuming the condition is true
23
Taking true branch
34
Assuming the condition is false
35
Taking false branch
39
The left operand of '+' is a garbage value
449 remaining = 1;
450 break;
24
Execution continues on line 453
451 }
452 }
453 if (!remaining
24.1
'remaining' is 1
)
25
Taking false branch
454 break;
455
456 for (s = 0; s < envs[0].current_indent; s++)
26
Loop condition is false. Execution continues on line 459
457 out_char (' ');
458
459 if (vsep)
27
Assuming 'vsep' is not equal to 0
28
Taking true branch
460 out_char ('|');
461
462 for (i = 1; i <= last_column; i++) {
29
Assuming 'i' is > 'last_column'
30
Loop condition is false. Execution continues on line 480
463 for (s = 0; s < envs[i].current_indent; s++)
464 out_char (' ');
465 for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) {
466 if (CHAR_AT (j) == '\n')
467 break;
468 out_char (CHAR_AT (j));
469 }
470 offset[i] += j + 1; /* skip last text plus skip the newline */
471
472 /* Do not output trailing blanks if we're in the last column and
473 there will be no trailing |. */
474 if (i < last_column && !vsep)
475 for (; j <= envs[i].fill_column; j++)
476 out_char (' ');
477 if (vsep)
478 out_char ('|'); /* draw column separator */
479 }
480 out_char ('\n'); /* end of line */
481 had_newline = 1;
482 }
483
484 /* If completely blank item, get blank line despite no other output. */
485 if (!had_newline)
486 out_char ('\n'); /* end of line */
487
488 if (hsep)
489 draw_horizontal_separator ();
490
491 /* Now dispose of the buffered output. */
492 for (i = 1; i <= last_column; i++) {
493 select_output_environment (i);
494 init_paragraph ();
495 }
496}
497
498int after_headitem = 0;
499int headitem_row = 0;
500
501/* start a new item (row) of a multitable */
502int
503multitable_item (void)
504{
505 if (!multitable_active) {
506 line_error ("multitable_item internal error: no active multitable");
507 xexit (1);
508 }
509
510 current_column_no = 1;
511
512 if (html)
513 {
514 if (!first_row)
515 /* <br> for non-tables browsers. */
516 add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td");
517
518 if (seen_column_fractions)
519 add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">",
520 headitem_flag ? "th" : "td",
521 envs[current_column_no].fill_column);
522 else
523 add_word_args ("<tr align=\"left\"><%s valign=\"top\">",
524 headitem_flag ? "th" : "td");
525
526 if (headitem_flag)
527 after_headitem = 1;
528 else
529 after_headitem = 0;
530 first_row = 0;
531 headitem_row = headitem_flag;
532 headitem_flag = 0;
533 return 0;
534 }
535 /* else if (docbook)*/ /* 05-08 */
536 else if (xml)
537 {
538 xml_end_multitable_row (first_row);
539 if (headitem_flag)
540 after_headitem = 1;
541 else
542 after_headitem = 0;
543 first_row = 0;
544 headitem_flag = 0;
545 return 0;
546 }
547 first_row = 0;
548
549 if (current_env_no > 0) {
550 output_multitable_row ();
551 }
552 /* start at column 1 */
553 select_output_environment (1);
554 if (!output_paragraph) {
555 line_error (_("[unexpected] cannot select column #%d in multitable")((const char *) ("[unexpected] cannot select column #%d in multitable"
))
,
556 current_env_no);
557 xexit (1);
558 }
559
560 init_column ();
561
562 if (headitem_flag)
563 hsep = 1;
564 else
565 hsep = 0;
566
567 if (headitem_flag)
568 after_headitem = 1;
569 else
570 after_headitem = 0;
571 headitem_flag = 0;
572
573 return 0;
574}
575
576#undef CHAR_AT
577#undef CHAR_ADDR
578
579/* select a new column in current row of multitable */
580void
581cm_tab (void)
582{
583 if (!multitable_active)
584 error (_("ignoring @tab outside of multitable")((const char *) ("ignoring @tab outside of multitable")));
585
586 current_column_no++;
587
588 if (html)
589 {
590 if (seen_column_fractions)
591 add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">",
592 headitem_row ? "th" : "td",
593 headitem_row ? "th" : "td",
594 envs[current_column_no].fill_column);
595 else
596 add_word_args ("</%s><%s valign=\"top\">",
597 headitem_row ? "th" : "td",
598 headitem_row ? "th" : "td");
599 }
600 /* else if (docbook)*/ /* 05-08 */
601 else if (xml)
602 xml_end_multitable_column ();
603 else
604 nselect_next_environment ();
605
606 init_column ();
607}
608
609/* close a multitable, flushing its output and resetting
610 whatever needs resetting */
611void
612end_multitable (void)
613{
614 if (!html && !docbook)
1
Assuming 'html' is 0
2
Assuming 'docbook' is 0
3
Taking true branch
615 output_multitable_row ();
4
Calling 'output_multitable_row'
616
617 /* Multitables cannot be nested. Otherwise, we'd have to save the
618 previous output environment number on a stack somewhere, and then
619 restore to that environment. */
620 select_output_environment (0);
621 multitable_active = 0;
622 uninhibit_output_flushing ();
623 close_insertion_paragraph ();
624
625 if (html)
626 add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td");
627 /* else if (docbook)*/ /* 05-08 */
628 else if (xml)
629 xml_end_multitable ();
630
631#if 0
632 printf (_("** Multicolumn output from last row:\n")((const char *) ("** Multicolumn output from last row:\n")));
633 for (i = 1; i <= last_column; i++) {
634 select_output_environment (i);
635 printf (_("* column #%d: output = %s\n")((const char *) ("* column #%d: output = %s\n")), i, output_paragraph);
636 }
637#endif
638}