Bug Summary

File:src/usr.bin/ctags/C.c
Warning:line 422, column 17
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 C.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/ctags/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/ctags -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/ctags/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/ctags/C.c
1/* $OpenBSD: C.c,v 1.15 2014/12/08 03:58:56 jsg Exp $ */
2/* $NetBSD: C.c,v 1.3 1995/03/26 20:14:02 glass Exp $ */
3
4/*
5 * Copyright (c) 1987, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <limits.h>
34#include <stdio.h>
35#include <string.h>
36
37#include "ctags.h"
38
39static int func_entry(void);
40static void hash_entry(void);
41static void skip_string(int);
42static int str_entry(int);
43
44/*
45 * c_entries --
46 * read .c and .h files and call appropriate routines
47 */
48void
49c_entries(void)
50{
51 int c; /* current character */
52 int level; /* brace level */
53 int token; /* if reading a token */
54 int t_def; /* if reading a typedef */
55 int t_level; /* typedef's brace level */
56 char *sp; /* buffer pointer */
57 char tok[MAXTOKEN250]; /* token buffer */
58
59 lineftell = ftell(inf);
60 sp = tok; token = t_def = NO0; t_level = -1; level = 0; lineno = 1;
61 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
) {
1
Assuming '__isthreaded' is not equal to 0
2
'?' condition is false
3
Assuming the condition is true
4
Loop condition is true. Entering loop body
62 switch (c) {
5
Control jumps to 'case 35:' at line 128
63 /*
64 * Here's where it DOESN'T handle: {
65 * foo(a)
66 * {
67 * #ifdef notdef
68 * }
69 * #endif
70 * if (a)
71 * puts("hello, world");
72 * }
73 */
74 case '{':
75 ++level;
76 goto endtok;
77 case '}':
78 /*
79 * if level goes below zero, try and fix
80 * it, even though we've already messed up
81 */
82 if (--level < 0)
83 level = 0;
84 goto endtok;
85
86 case '\n':
87 SETLINE{++lineno;lineftell = ftell(inf);};
88 /*
89 * the above 3 cases are similar in that they
90 * are special characters that also end tokens.
91 */
92endtok: if (sp > tok) {
93 *sp = EOS'\0';
94 token = YES1;
95 sp = tok;
96 }
97 else
98 token = NO0;
99 continue;
100
101 /*
102 * We ignore quoted strings and character constants
103 * completely.
104 */
105 case '"':
106 case '\'':
107 (void)skip_string(c);
108 break;
109
110 /*
111 * comments can be fun; note the state is unchanged after
112 * return, in case we found:
113 * "foo() XX comment XX { int bar; }"
114 */
115 case '/':
116 if (GETC(==, '*')((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)'*')
) {
117 skip_comment(c);
118 continue;
119 } else if (c == '/') {
120 skip_comment(c);
121 continue;
122 }
123 (void)ungetc(c, inf);
124 c = '/';
125 goto storec;
126
127 /* hash marks flag #define's. */
128 case '#':
129 if (sp
5.1
'sp' is equal to 'tok'
== tok) {
6
Taking true branch
130 hash_entry();
7
Calling 'hash_entry'
131 break;
132 }
133 goto storec;
134
135 /*
136 * if we have a current token, parenthesis on
137 * level zero indicates a function.
138 */
139 case '(':
140 do {
141 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
142 return;
143 } while (iswhite(c)(_wht[(unsigned)c]));
144 if (c == '*')
145 break;
146 else
147 ungetc(c, inf);
148 if (!level && token) {
149 int curline;
150
151 if (sp != tok)
152 *sp = EOS'\0';
153 /*
154 * grab the line immediately, we may
155 * already be wrong, for example,
156 * foo\n
157 * (arg1,
158 */
159 get_line();
160 curline = lineno;
161 if (func_entry()) {
162 ++level;
163 pfnote(tok, curline);
164 }
165 break;
166 }
167 goto storec;
168
169 /*
170 * semi-colons indicate the end of a typedef; if we find a
171 * typedef we search for the next semi-colon of the same
172 * level as the typedef. Ignoring "structs", they are
173 * tricky, since you can find:
174 *
175 * "typedef int time_t;"
176 * "typedef unsigned int u_int;"
177 * "typedef unsigned int u_int [10];"
178 *
179 * If looking at a typedef, we save a copy of the last token
180 * found. Then, when we find the ';' we take the current
181 * token if it starts with a valid token name, else we take
182 * the one we saved. There's probably some reasonable
183 * alternative to this...
184 */
185 case ';':
186 if (t_def && level == t_level) {
187 t_def = NO0;
188 get_line();
189 if (sp != tok)
190 *sp = EOS'\0';
191 pfnote(tok, lineno);
192 break;
193 }
194 goto storec;
195
196 /*
197 * store characters until one that can't be part of a token
198 * comes along; check the current token against certain
199 * reserved words.
200 */
201 default:
202 /*
203 * to treat following function.
204 * func (arg) {
205 * ....
206 * }
207 */
208 if (c == ' ' || c == '\t') {
209 int save = c;
210 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
&& (c == ' ' || c == '\t'))
211 ;
212 if (c == EOF(-1))
213 return;
214 (void)ungetc(c, inf);
215 c = save;
216 }
217 storec: if (!intoken(c)(_itk[(unsigned)c])) {
218 if (sp == tok)
219 break;
220 *sp = EOS'\0';
221 /* no typedefs inside typedefs */
222 if (!t_def &&
223 !memcmp(tok, "typedef",8)) {
224 t_def = YES1;
225 t_level = level;
226 break;
227 }
228 /* catch "typedef struct" */
229 if ((!t_def || t_level < level)
230 && (!memcmp(tok, "struct", 7)
231 || !memcmp(tok, "union", 6)
232 || !memcmp(tok, "enum", 5))) {
233 /*
234 * get line immediately;
235 * may change before '{'
236 */
237 get_line();
238 if (str_entry(c))
239 ++level;
240 break;
241 /* } */
242 }
243 sp = tok;
244 }
245 else if (sp != tok || begtoken(c)(_btk[(unsigned)c])) {
246 /* hell... truncate it */
247 if (sp == tok + sizeof tok - 1)
248 *sp = EOS'\0';
249 else
250 *sp++ = c;
251 token = YES1;
252 }
253 continue;
254 }
255
256 sp = tok;
257 token = NO0;
258 }
259}
260
261/*
262 * func_entry --
263 * handle a function reference
264 */
265static int
266func_entry(void)
267{
268 int c; /* current character */
269 int level = 0; /* for matching '()' */
270 static char attribute[] = "__attribute__";
271 char maybe_attribute[sizeof attribute + 1];
272 char *anext;
273
274 /*
275 * Find the end of the assumed function declaration.
276 * Note that ANSI C functions can have type definitions so keep
277 * track of the parentheses nesting level.
278 */
279 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
) {
280 switch (c) {
281 case '\'':
282 case '"':
283 /* skip strings and character constants */
284 skip_string(c);
285 break;
286 case '/':
287 /* skip comments */
288 if (GETC(==, '*')((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)'*')
)
289 skip_comment(c);
290 else if (c == '/')
291 skip_comment(c);
292 break;
293 case '(':
294 level++;
295 break;
296 case ')':
297 if (level == 0)
298 goto fnd;
299 level--;
300 break;
301 case '\n':
302 SETLINE{++lineno;lineftell = ftell(inf);};
303 }
304 }
305 return (NO0);
306fnd:
307 /*
308 * we assume that the character after a function's right paren
309 * is a token character if it's a function and a non-token
310 * character if it's a declaration. Comments don't count...
311 */
312 for (anext = maybe_attribute;;) {
313 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
&& iswhite(c)(_wht[(unsigned)c]))
314 if (c == '\n')
315 SETLINE{++lineno;lineftell = ftell(inf);};
316 if (c == EOF(-1))
317 return NO0;
318 /*
319 * Recognize the GNU __attribute__ extension, which would
320 * otherwise make the heuristic test DTWT
321 */
322 if (anext == maybe_attribute) {
323 if (intoken(c)(_itk[(unsigned)c])) {
324 *anext++ = c;
325 continue;
326 }
327 } else {
328 if (intoken(c)(_itk[(unsigned)c])) {
329 if (anext - maybe_attribute < (int)(sizeof attribute - 1))
330 *anext++ = c;
331 else
332 break;
333 continue;
334 } else {
335 *anext++ = '\0';
336 if (strcmp(maybe_attribute, attribute) == 0) {
337 (void)ungetc(c, inf);
338 return NO0;
339 }
340 break;
341 }
342 }
343 if (intoken(c)(_itk[(unsigned)c]) || c == '{')
344 break;
345 if (c == '/' && GETC(==, '*')((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)'*')
)
346 skip_comment(c);
347 else if (c == '/')
348 skip_comment(c);
349 else { /* don't ever "read" '/' */
350 (void)ungetc(c, inf);
351 return (NO0);
352 }
353 }
354 if (c != '{')
355 (void)skip_key('{');
356 return (YES1);
357}
358
359/*
360 * hash_entry --
361 * handle a line starting with a '#'
362 */
363static void
364hash_entry(void)
365{
366 int c; /* character read */
367 int curline; /* line started on */
368 char *sp; /* buffer pointer */
369 char tok[MAXTOKEN250]; /* storage buffer */
370
371 /*
372 * to treat following macro.
373 * # macro(arg) ....
374 */
375 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
&& (c == ' ' || c == '\t'))
8
'?' condition is false
9
Assuming the condition is false
376 ;
377 (void)ungetc(c, inf);
378
379 curline = lineno;
380 for (sp = tok;;) { /* get next token */
10
Loop condition is true. Entering loop body
381 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
11
'?' condition is false
12
Assuming the condition is false
13
Taking false branch
382 return;
383 if (iswhite(c)(_wht[(unsigned)c]))
14
Assuming the condition is true
15
Taking true branch
384 break;
16
Execution continues on line 391
385 /* hell... truncate it */
386 if (sp == tok + sizeof tok - 1)
387 *sp = EOS'\0';
388 else
389 *sp++ = c;
390 }
391 *sp = EOS'\0';
392 if (memcmp(tok, "define", 6)) /* only interested in #define's */
17
Assuming the condition is true
18
Taking true branch
393 goto skip;
19
Control jumps to line 420
394 for (;;) { /* this doesn't handle "#define \n" */
395 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
396 return;
397 if (!iswhite(c)(_wht[(unsigned)c]))
398 break;
399 }
400 for (sp = tok;;) { /* get next token */
401 /* hell... truncate it */
402 if (sp == tok + sizeof tok - 1)
403 *sp = EOS'\0';
404 else
405 *sp++ = c;
406 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
407 return;
408 /*
409 * this is where it DOESN'T handle
410 * "#define \n"
411 */
412 if (!intoken(c)(_itk[(unsigned)c]))
413 break;
414 }
415 *sp = EOS'\0';
416 if (dflag || c == '(') { /* only want macros */
417 get_line();
418 pfnote(tok, curline);
419 }
420skip: if (c == '\n') { /* get rid of rest of define */
20
Assuming the condition is true
21
Taking true branch
421 SETLINE{++lineno;lineftell = ftell(inf);}
422 if (*(sp - 1) != '\\')
22
The left operand of '!=' is a garbage value
423 return;
424 }
425 (void)skip_key('\n');
426}
427
428/*
429 * str_entry --
430 * handle a struct, union or enum entry
431 */
432static int
433str_entry(int c)
434{
435 int curline; /* line started on */
436 char *sp; /* buffer pointer */
437 char tok[LINE_MAX2048]; /* storage buffer */
438
439 curline = lineno;
440 while (iswhite(c)(_wht[(unsigned)c]))
441 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
442 return (NO0);
443 if (c == '{') /* it was "struct {" */
444 return (YES1);
445 for (sp = tok;;) { /* get next token */
446 /* hell... truncate it */
447 if (sp == tok + sizeof tok - 1)
448 *sp = EOS'\0';
449 else
450 *sp++ = c;
451 if (GETC(==, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)(-1))
)
452 return (NO0);
453 if (!intoken(c)(_itk[(unsigned)c]))
454 break;
455 }
456 switch (c) {
457 case '{': /* it was "struct foo{" */
458 --sp;
459 break;
460 case '\n': /* it was "struct foo\n" */
461 SETLINE{++lineno;lineftell = ftell(inf);};
462 /*FALLTHROUGH*/
463 default: /* probably "struct foo " */
464 while (GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
)
465 if (!iswhite(c)(_wht[(unsigned)c]))
466 break;
467 if (c != '{') {
468 (void)ungetc(c, inf);
469 return (NO0);
470 }
471 }
472 *sp = EOS'\0';
473 pfnote(tok, curline);
474 return (YES1);
475}
476
477/*
478 * skip_comment --
479 * skip over comment
480 */
481void
482skip_comment(int commenttype)
483{
484 int c; /* character read */
485 int star; /* '*' flag */
486
487 for (star = 0; GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
;)
488 switch(c) {
489 /* comments don't nest, nor can they be escaped. */
490 case '*':
491 star = YES1;
492 break;
493 case '/':
494 if (commenttype == '*' && star)
495 return;
496 break;
497 case '\n':
498 if (commenttype == '/') {
499 /* We don't really parse C, so sometimes it
500 * is necessary to see the newline
501 */
502 ungetc(c, inf);
503 return;
504 }
505 SETLINE{++lineno;lineftell = ftell(inf);};
506 /*FALLTHROUGH*/
507 default:
508 star = NO0;
509 break;
510 }
511}
512
513/*
514 * skip_string --
515 * skip to the end of a string or character constant.
516 */
517static void
518skip_string(int key)
519{
520 int c,
521 skip;
522
523 for (skip = NO0; GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
; )
524 switch (c) {
525 case '\\': /* a backslash escapes anything */
526 skip = !skip; /* we toggle in case it's "\\" */
527 break;
528 case '\n':
529 SETLINE{++lineno;lineftell = ftell(inf);};
530 /*FALLTHROUGH*/
531 default:
532 if (c == key && !skip)
533 return;
534 skip = NO0;
535 }
536}
537
538/*
539 * skip_key --
540 * skip to next char "key"
541 */
542int
543skip_key(int key)
544{
545 int c,
546 skip,
547 retval;
548
549 for (skip = retval = NO0; GETC(!=, EOF)((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) != (int)(-1))
;)
550 switch(c) {
551 case '\\': /* a backslash escapes anything */
552 skip = !skip; /* we toggle in case it's "\\" */
553 break;
554 case ';': /* special case for yacc; if one */
555 case '|': /* of these chars occurs, we may */
556 retval = YES1; /* have moved out of the rule */
557 break; /* not used by C */
558 case '\'':
559 case '"':
560 /* skip strings and character constants */
561 skip_string(c);
562 break;
563 case '/':
564 /* skip comments */
565 if (GETC(==, '*')((c = (!__isthreaded ? (--(inf)->_r < 0 ? __srget(inf) :
(int)(*(inf)->_p++)) : (getc)(inf))) == (int)'*')
) {
566 skip_comment(c);
567 break;
568 } else if (c == '/') {
569 skip_comment(c);
570 break;
571 }
572 (void)ungetc(c, inf);
573 c = '/';
574 goto norm;
575 case '\n':
576 SETLINE{++lineno;lineftell = ftell(inf);};
577 /*FALLTHROUGH*/
578 default:
579 norm:
580 if (c == key && !skip)
581 return (retval);
582 skip = NO0;
583 }
584 return (retval);
585}