Bug Summary

File:src/usr.bin/make/str.c
Warning:line 110, column 21
Although the value stored to 'curlen' is used in the enclosing expression, the value is never actually read from 'curlen'

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 str.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/make/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/make/obj -I /usr/src/usr.bin/make -D HAS_PATHS_H -D HAS_EXTENDED_GETCWD -I /usr/src/usr.bin/make/lst.lib -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/make/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/make/str.c
1/* $OpenBSD: str.c,v 1.32 2019/05/21 17:21:02 espie Exp $ */
2/* $NetBSD: str.c,v 1.13 1996/11/06 17:59:23 christos Exp $ */
3
4/*-
5 * Copyright (c) 1988, 1989, 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 * Copyright (c) 1989 by Berkeley Softworks
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Adam de Boor.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <ctype.h>
39#include <string.h>
40#include "config.h"
41#include "defines.h"
42#include "str.h"
43#include "memory.h"
44#include "buf.h"
45
46/* helpers for Str_Matchi */
47static bool_Bool range_match(char, const char **, const char *);
48static bool_Bool star_match(const char *, const char *, const char *, const char *);
49
50char *
51Str_concati(const char *s1, const char *e1, const char *s2, const char *e2,
52 int sep)
53{
54 size_t len1, len2;
55 char *result;
56
57 /* get the length of both strings */
58 len1 = e1 - s1;
59 len2 = e2 - s2;
60
61 /* space for separator */
62 if (sep)
63 len1++;
64 result = emalloc(len1 + len2 + 1);
65
66 /* copy first string into place */
67 memcpy(result, s1, len1);
68
69 /* add separator character */
70 if (sep)
71 result[len1-1] = sep;
72
73 /* copy second string plus EOS into place */
74 memcpy(result + len1, s2, len2);
75 result[len1+len2] = '\0';
76 return result;
77}
78
79/*-
80 * brk_string --
81 * Fracture a string into an array of words (as delineated by tabs or
82 * spaces) taking quotation marks into account. Leading tabs/spaces
83 * are ignored.
84 *
85 * returns --
86 * Pointer to the array of pointers to the words. Fills up
87 * store_args with its size.
88 * The returned parameters are allocated in a single buffer,
89 * return as *buffer, to be freed later.
90 */
91char **
92brk_string(const char *str, int *store_argc, char **buffer)
93{
94 int argc;
95 char ch;
96 char inquote;
97 const char *p;
98 char *start, *t;
99 size_t len;
100 int argmax = 50; /* start at 50 */
101 size_t curlen = 0;
102 char **argv = ereallocarray(NULL((void *)0), argmax + 1, sizeof(char *));
103
104 /* skip leading space chars. */
105 for (; *str == ' ' || *str == '\t'; ++str)
106 continue;
107
108 /* allocate room for a copy of the string */
109 if ((len = strlen(str) + 1) > curlen)
110 *buffer = emalloc(curlen = len);
Although the value stored to 'curlen' is used in the enclosing expression, the value is never actually read from 'curlen'
111
112 /*
113 * copy the string; at the same time, parse backslashes,
114 * quotes and build the argument list.
115 */
116 argc = 0;
117 inquote = '\0';
118 for (p = str, start = t = *buffer;; ++p) {
119 switch (ch = *p) {
120 case '"':
121 case '\'':
122 if (inquote) {
123 if (inquote == ch)
124 inquote = '\0';
125 else
126 break;
127 } else {
128 inquote = ch;
129 /* Don't miss "" or '' */
130 if (start == NULL((void *)0) && p[1] == inquote) {
131 start = t + 1;
132 break;
133 }
134 }
135 continue;
136 case ' ':
137 case '\t':
138 case '\n':
139 if (inquote)
140 break;
141 if (!start)
142 continue;
143 /* FALLTHROUGH */
144 case '\0':
145 /*
146 * end of a token -- make sure there's enough argv
147 * space and save off a pointer.
148 */
149 if (!start)
150 goto done;
151
152 *t++ = '\0';
153 if (argc == argmax) {
154 argmax *= 2; /* ramp up fast */
155 argv = ereallocarray(argv,
156 (argmax + 1), sizeof(char *));
157 }
158 argv[argc++] = start;
159 start = NULL((void *)0);
160 if (ch == '\n' || ch == '\0')
161 goto done;
162 continue;
163 case '\\':
164 switch (ch = *++p) {
165 case '\0':
166 case '\n':
167 /* hmmm; fix it up as best we can */
168 ch = '\\';
169 --p;
170 break;
171 case 'b':
172 ch = '\b';
173 break;
174 case 'f':
175 ch = '\f';
176 break;
177 case 'n':
178 ch = '\n';
179 break;
180 case 'r':
181 ch = '\r';
182 break;
183 case 't':
184 ch = '\t';
185 break;
186 }
187 break;
188 }
189 if (!start)
190 start = t;
191 *t++ = ch;
192 }
193 done:
194 argv[argc] = NULL((void *)0);
195 *store_argc = argc;
196 return argv;
197}
198
199
200const char *
201iterate_words(const char **end)
202{
203 const char *start, *p;
204 char state = 0;
205 start = *end;
206
207 while (ISSPACE(*start)(isspace((unsigned char)(*start))))
208 start++;
209 if (*start == '\0')
210 return NULL((void *)0);
211
212 for (p = start;; p++)
213 switch(*p) {
214 case '\\':
215 if (p[1] != '\0')
216 p++;
217 break;
218 case '\'':
219 case '"':
220 if (state == *p)
221 state = 0;
222 else if (state == 0)
223 state = *p;
224 break;
225 case ' ':
226 case '\t':
227 if (state != 0)
228 break;
229 /* FALLTHROUGH */
230 case '\0':
231 *end = p;
232 return start;
233 default:
234 break;
235 }
236}
237
238static bool_Bool
239star_match(const char *string, const char *estring,
240 const char *pattern, const char *epattern)
241{
242 /* '*' matches any substring. We handle this by calling ourselves
243 * recursively for each postfix of string, until either we match or
244 * we reach the end of the string. */
245 pattern++;
246 /* Skip over contiguous sequences of `?*', so that
247 * recursive calls only occur on `real' characters. */
248 while (pattern != epattern &&
249 (*pattern == '?' || *pattern == '*')) {
250 if (*pattern == '?') {
251 if (string == estring)
252 return false0;
253 else
254 string++;
255 }
256 pattern++;
257 }
258 if (pattern == epattern)
259 return true1;
260 for (; string != estring; string++)
261 if (Str_Matchi(string, estring, pattern,
262 epattern))
263 return true1;
264 return false0;
265}
266
267static bool_Bool
268range_match(char c, const char **ppat, const char *epattern)
269{
270 if (*ppat == epattern) {
271 if (c == '[')
272 return true1;
273 else
274 return false0;
275 }
276 if (**ppat == '!' || **ppat == '^') {
277 (*ppat)++;
278 return !range_match(c, ppat, epattern);
279 }
280 for (;;) {
281 if (**ppat == '\\') {
282 if (++(*ppat) == epattern)
283 return false0;
284 }
285 if (**ppat == c)
286 break;
287 if ((*ppat)[1] == '-') {
288 if (*ppat + 2 == epattern)
289 return false0;
290 if (**ppat < c && c <= (*ppat)[2])
291 break;
292 if ((*ppat)[2] <= c && c < **ppat)
293 break;
294 *ppat += 3;
295 } else
296 (*ppat)++;
297 /* The test for ']' is done at the end
298 * so that ']' can be used at the
299 * start of the range without '\' */
300 if (*ppat == epattern || **ppat == ']')
301 return false0;
302 }
303 /* Found matching character, skip over rest
304 * of class. */
305 while (**ppat != ']') {
306 if (**ppat == '\\')
307 (*ppat)++;
308 /* A non-terminated character class
309 * is ok. */
310 if (*ppat == epattern)
311 break;
312 (*ppat)++;
313 }
314 return true1;
315}
316
317bool_Bool
318Str_Matchi(const char *string, const char *estring,
319 const char *pattern, const char *epattern)
320{
321 while (pattern != epattern) {
322 /* Check for a "*" as the next pattern character. */
323 if (*pattern == '*')
324 return star_match(string, estring, pattern, epattern);
325 else if (string == estring)
326 return false0;
327 /* Check for a "[" as the next pattern character. It is
328 * followed by a list of characters that are acceptable, or
329 * by a range (two characters separated by "-"). */
330 else if (*pattern == '[') {
331 pattern++;
332 if (!range_match(*string, &pattern, epattern))
333 return false0;
334
335 }
336 /* '?' matches any single character, so shunt test. */
337 else if (*pattern != '?') {
338 /* If the next pattern character is '\', just strip
339 * off the '\' so we do exact matching on the
340 * character that follows. */
341 if (*pattern == '\\') {
342 if (++pattern == epattern)
343 return false0;
344 }
345 /* There's no special character. Just make sure that
346 * the next characters of each string match. */
347 if (*pattern != *string)
348 return false0;
349 }
350 pattern++;
351 string++;
352 }
353 if (string == estring)
354 return true1;
355 else
356 return false0;
357}
358
359
360/*-
361 *-----------------------------------------------------------------------
362 * Str_SYSVMatch --
363 * Check word against pattern for a match (% is wild),
364 *
365 * Results:
366 * Returns the beginning position of a match or null. The number
367 * of characters matched is returned in len.
368 *-----------------------------------------------------------------------
369 */
370const char *
371Str_SYSVMatch(const char *word, const char *pattern, size_t *len)
372{
373 const char *p = pattern;
374 const char *w = word;
375 const char *m;
376
377 if (*p == '\0') {
378 /* Null pattern is the whole string. */
379 *len = strlen(w);
380 return w;
381 }
382
383 if ((m = strchr(p, '%')) != NULL((void *)0)) {
384 /* Check that the prefix matches. */
385 for (; p != m && *w && *w == *p; w++, p++)
386 continue;
387
388 if (p != m)
389 return NULL((void *)0); /* No match. */
390
391 if (*++p == '\0') {
392 /* No more pattern, return the rest of the string. */
393 *len = strlen(w);
394 return w;
395 }
396 }
397
398 m = w;
399
400 /* Find a matching tail. */
401 do {
402 if (strcmp(p, w) == 0) {
403 *len = w - m;
404 return m;
405 }
406 } while (*w++ != '\0');
407
408 return NULL((void *)0);
409}
410
411
412/*-
413 *-----------------------------------------------------------------------
414 * Str_SYSVSubst --
415 * Substitute '%' in the pattern with len characters from src.
416 * If the pattern does not contain a '%' prepend len characters
417 * from src.
418 *
419 * Side Effects:
420 * Adds result to buf
421 *-----------------------------------------------------------------------
422 */
423void
424Str_SYSVSubst(Buffer buf, const char *pat, const char *src, size_t len)
425{
426 const char *m;
427
428 if ((m = strchr(pat, '%')) != NULL((void *)0)) {
429 /* Copy the prefix. */
430 Buf_Addi(buf, pat, m)Buf_AddChars((buf), (m) - (pat), (pat));
431 /* Skip the %. */
432 pat = m + 1;
433 }
434
435 /* Copy the pattern. */
436 Buf_AddChars(buf, len, src);
437
438 /* Append the rest. */
439 Buf_AddString(buf, pat)Buf_AddChars((buf), strlen(pat), (pat));
440}
441
442char *
443Str_dupi(const char *begin, const char *end)
444{
445 char *s;
446
447 s = emalloc(end - begin + 1);
448 memcpy(s, begin, end - begin);
449 s[end-begin] = '\0';
450 return s;
451}
452
453char *
454escape_dupi(const char *begin, const char *end, const char *set)
455{
456 char *s, *t;
457
458 t = s = emalloc(end - begin + 1);
459 while (begin != end) {
460 if (*begin == '\\') {
461 begin++;
462 if (begin == end) {
463 *t++ = '\\';
464 break;
465 }
466 if (strchr(set, *begin) == NULL((void *)0))
467 *t++ = '\\';
468 }
469 *t++ = *begin++;
470 }
471 *t++ = '\0';
472 return s;
473}
474
475char *
476Str_rchri(const char *begin, const char *end, int c)
477{
478 if (begin != end)
479 do {
480 if (*--end == c)
481 return (char *)end;
482 } while (end != begin);
483 return NULL((void *)0);
484}