Bug Summary

File:subcmd/unitools/cfgfile.c
Warning:line 147, column 8
Access to field 'first' results in a dereference of a null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cfgfile.c -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 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/kfp/aldor/aldor/aldor/subcmd/unitools -fcoverage-compilation-dir=/home/kfp/aldor/aldor/aldor/subcmd/unitools -resource-dir /usr/local/lib/clang/18 -D PACKAGE_NAME="aldor" -D PACKAGE_TARNAME="aldor" -D PACKAGE_VERSION="1.4.0" -D PACKAGE_STRING="aldor 1.4.0" -D PACKAGE_BUGREPORT="aldor@xinutec.org" -D PACKAGE_URL="" -D PACKAGE="aldor" -D VERSION="1.4.0" -D YYTEXT_POINTER=1 -D HAVE_STDIO_H=1 -D HAVE_STDLIB_H=1 -D HAVE_STRING_H=1 -D HAVE_INTTYPES_H=1 -D HAVE_STDINT_H=1 -D HAVE_STRINGS_H=1 -D HAVE_SYS_STAT_H=1 -D HAVE_SYS_TYPES_H=1 -D HAVE_UNISTD_H=1 -D STDC_HEADERS=1 -D HAVE_LIBREADLINE=1 -D HAVE_READLINE_READLINE_H=1 -D HAVE_READLINE_HISTORY=1 -D HAVE_READLINE_HISTORY_H=1 -D USE_GLOOP_SHELL=1 -D GENERATOR_COROUTINES=0 -D HAVE_DLFCN_H=1 -D LT_OBJDIR=".libs/" -I . -I ./../../src -I ../../src -internal-isystem /usr/local/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2026-01-15-223856-845667-1 -x c cfgfile.c
1/*****************************************************************************
2 *
3 * cfgfile.h: Configuration file handling
4 *
5 * Copyright (c) 1990-2007 Aldor Software Organization Ltd (Aldor.org).
6 *
7 ****************************************************************************/
8
9#include "cfgfile.h"
10#include "debug.h"
11#include "file.h"
12#include "opsys.h"
13#include "stdio.h0"
14#include "store.h"
15#include "strops.h"
16
17Bool cfgDebug = false((int) 0);
18
19#define cfgDEBUGif (!cfgDebug) { } else afprintf DEBUG_IF(cfg)if (!cfgDebug) { } else afprintf
20
21static String cfgGetLine(FILE *file, Bool *atEof);
22static Bool cfgIsSection(char *line, char *name);
23static void cfgReadAddError(String);
24static void cfgReadDupItemError(ConfigItem, String);
25static Bool cfgEqual(ConfigItem, ConfigItem);
26static ConfigItemList cfgGetKeys(FILE *file, String section);
27static ConfigItem cfgParseLine(String line);
28
29CREATE_LIST(ConfigItem)struct ConfigItem_listOpsStruct const *ConfigItem_listPointer
= (struct ConfigItem_listOpsStruct const *) &ptrlistOps
;
30
31/*********************************************************************************
32 *
33 * :: Extracting config information
34 *
35 *********************************************************************************/
36#define IsWhiteSpace(c)((c)==' ' || (c)=='\n' || (c) == '\t') ((c)==' ' || (c)=='\n' || (c) == '\t')
37
38static StringList cfgExpandList (StringList *plst,
39 StringList **end,
40 ConfigItemList lst,
41 Bool keep);
42static Bool cfgCheckCondition(String name, ConfigItemList cfg);
43static ConfigItem cfgExpandVar(ConfigItem, ConfigItemList);
44
45static String cfgKeyNameValue(String str);
46static String cfgStringValue(String str);
47static StringList cfgStringListValue(String str);
48static Bool cfgBooleanValue(String str);
49static String cfgKeyNameValue(String str);
50
51static ConfigItem
52cfgExpandVar(ConfigItem item, ConfigItemList lst)
53{
54 char c;
55 int i;
56
57 if (!item) return NULL((void*)0);
58 i = 0;
59 while ( (c = cfgVal(item)((item)->optVal)[i]) == ' ' || c == '\t') i++;
60 if (cfgVal(item)((item)->optVal)[i] == '$') {
61 item = cfgLookup(cfgKeyNameValue(&cfgVal(item)((item)->optVal)[i+1]), lst);
62 return cfgExpandVar(item, lst);
63 }
64 return item;
65}
66
67ConfigItemList
68cfgLookupList(char *string, ConfigItemList lst)
69{
70 ConfigItemList result = listNil(ConfigItem)((ConfigItemList) 0);
71
72 cfgDEBUGif (!cfgDebug) { } else afprintf(dbOut, "Getting key list: %s\n", string);
73
74 while (lst != listNil(ConfigItem)((ConfigItemList) 0)) {
75 if (strEqual(string, cfgName(car(lst))((((lst)->first))->optName)))
76 result = listCons(ConfigItem)(ConfigItem_listPointer->Cons)(car(lst)((lst)->first), result);
77 lst = cdr(lst)((lst)->rest);
78 }
79 return listNReverse(ConfigItem)(ConfigItem_listPointer->NReverse)(result);
80}
81
82ConfigItem
83cfgLookup(char *string, ConfigItemList lst)
84{
85 cfgDEBUGif (!cfgDebug) { } else afprintf(dbOut, "Getting key: %s\n", string);
86
87 while (lst != listNil(ConfigItem)((ConfigItemList) 0)) {
88 if (strEqual(string, cfgName(car(lst))((((lst)->first))->optName)))
89 return car(lst)((lst)->first);
90 lst = cdr(lst)((lst)->rest);
91 }
92 return NULL((void*)0);
93}
94
95static StringList
96cfgLookupUnexpandedStringList(String str, ConfigItemList lst)
97{
98 ConfigItem item = cfgLookup(str, lst);
99
100 /*
101 * Ought to store this for the client to report just as we
102 * do for cfgRead().
103 */
104 if (!item) {
105 printf("config file: failed to find key `%s'\n", str);
106 return NULL((void*)0);
107 }
108 return cfgStringListValue(cfgVal(item)((item)->optVal));
109}
110
111
112StringList
113cfgLookupStringList(String str, ConfigItemList lst)
114{
115 StringList ll, *foo;
116 ll = cfgLookupUnexpandedStringList(str, lst);
117
118 if (ll == listNil(String)((StringList) 0))
1
Assuming 'll' is not equal to listNil(String)
2
Taking false branch
119 return NULL((void*)0);
120 ll = cfgExpandList(&ll, &foo, lst, true1);
3
Calling 'cfgExpandList'
121 return ll;
122}
123
124#define cfgIfTerminator(x)((x)[1] == '\0' && ((x)[0] == ':' || (x)[0] == ';')) ((x)[1] == '\0' && ((x)[0] == ':' || (x)[0] == ';'))
125
126static StringList
127cfgExpandList(StringList *plst, StringList **end, ConfigItemList cfg, Bool keep)
128{
129 StringList *pp;
130 StringList head;
131 String error = NULL((void*)0);
132 pp = plst;
133 head = *plst;
134 while (*pp != listNil(String)((StringList) 0) && !cfgIfTerminator(car(*pp))((((*pp)->first))[1] == '\0' && ((((*pp)->first
))[0] == ':' || (((*pp)->first))[0] == ';'))
) {
4
Assuming the condition is false
5
Loop condition is true. Entering loop body
11
Assuming the condition is true
12
Assuming the condition is false
13
Loop condition is true. Entering loop body
17
Execution continues on line 134
18
Assuming pointer value is null
135 String name;
136 if (car(*pp)((*pp)->first)[0] != '$') {
6
Assuming the condition is false
7
Taking false branch
14
Assuming the condition is true
15
Taking true branch
137 if (keep
15.1
'keep' is 1
) pp = &cdr(*pp)((*pp)->rest);
16
Taking true branch
138 else *pp = cdr(*pp)((*pp)->rest);
139 continue;
140 }
141 name = &(car(*pp)((*pp)->first)[1]);
142 if (name[0] == '?') {
8
Assuming the condition is true
9
Taking true branch
143 Bool flg = cfgCheckCondition(&name[1], cfg);
144 StringList *iend;
145 *pp = cdr(*pp)((*pp)->rest);
146 *pp = cfgExpandList(pp, &iend, cfg, flg
9.1
'flg' is 1
&& keep)
;
10
Calling 'cfgExpandList'
20
Returning from 'cfgExpandList'
147 if (car(*iend)((*iend)->first)[0] != ':')
21
Access to field 'first' results in a dereference of a null pointer
148 error = "Missing `:'";
149 if (cdr(*iend)((*iend)->rest)) *iend = cdr(*iend)((*iend)->rest);
150 else
151 error = "Nothing after `:'.";
152 cfgExpandList(iend, &iend, cfg, !flg && keep);
153 /* Should check for ';' */
154 *iend = cdr(*iend)((*iend)->rest);
155 pp = iend;
156 }
157 else {
158 StringList ll;
159 if (!keep) *pp = cdr(*pp)((*pp)->rest);
160 else {
161 ll = cfgLookupUnexpandedStringList(name, cfg);
162 *pp = listNConcat(String)(String_listPointer->NConcat)(ll, cdr(*pp)((*pp)->rest));
163 }
164 }
165 }
166 if (error
18.1
'error' is null
)
19
Taking false branch
167 printf("Error in cfg: %s\n", error);
168 *end = pp;
169 return *plst;
170}
171
172
173String
174cfgLookupKeyName(String str, ConfigItemList lst)
175{
176 ConfigItem item = cfgLookup(str, lst);
177 item = cfgExpandVar(item, lst);
178
179 if (!item) {
180 return NULL((void*)0);
181 }
182 return cfgKeyNameValue(cfgVal(item)((item)->optVal));
183}
184
185StringList
186cfgLookupKeyNameList(String str, ConfigItemList lst)
187{
188 ConfigItemList items = cfgLookupList(str, lst);
189 StringList result = listNil(String)((StringList) 0);
190
191
192 /* Process each item in the list */
193 for (; items; items = cdr(items)((items)->rest)) {
194 String value;
195 ConfigItem item;
196
197
198 /* Get the next item */
199 item = car(items)((items)->first);
200
201
202 /* Expand variables */
203 item = cfgExpandVar(item, lst);
204
205
206 /* Try the next one if no value found */
207 if (!item) continue;
208
209
210 /* Convert key into a value */
211 value = cfgKeyNameValue(cfgVal(item)((item)->optVal));
212
213
214 /* Add it to the result list */
215 result = listCons(String)(String_listPointer->Cons)(value, result);
216 }
217
218 return listNReverse(String)(String_listPointer->NReverse)(result);
219}
220
221String
222cfgLookupString(String str, ConfigItemList lst)
223{
224 ConfigItem item = cfgLookup(str, lst);
225 item = cfgExpandVar(item, lst);
226
227 if (!item) {
228 return NULL((void*)0);
229 }
230 return cfgStringValue(cfgVal(item)((item)->optVal));
231}
232
233Bool
234cfgLookupBoolean(String str, ConfigItemList lst)
235{
236 ConfigItem item = cfgLookup(str, lst);
237 item = cfgExpandVar(item, lst);
238
239 if (!item) {
240 return 0; /* Hmmm */
241 }
242 return cfgBooleanValue(cfgVal(item)((item)->optVal));
243}
244
245static StringList
246cfgStringListValue(String str)
247{
248 int argc;
249 char **argv;
250 StringList lst;
251 int i;
252
253 if (str[0] == ',')
254 cstrParseCommaified(str+1, &argc, &argv);
255 else
256 cstrParseUnquoted(str, &argc, &argv);
257
258 lst = listNil(String)((StringList) 0);
259 for (i=0; i<argc; i++) lst = listCons(String)(String_listPointer->Cons)(argv[argc - i - 1], lst);
260
261 return lst;
262}
263
264static Bool
265cfgBooleanValue(String str)
266{
267 String s = cfgKeyNameValue(str);
268
269 if (strEqual(s, "yes")) return true1;
270 if (strEqual(s, "no")) return false((int) 0);
271 if (strEqual(s, "true")) return true1;
272 if (strEqual(s, "false")) return false((int) 0);
273
274 printf("Illegal value for boolean: %s\n", s);
275 return false((int) 0);
276}
277
278static String
279cfgKeyNameValue(String str)
280{
281 char **argv;
282 int argc;
283 cstrParseUnquoted(str, &argc, &argv);
284 if (argc != 1) {
285 printf("Bad identifier format: `%s'\n", str);
286 return "";
287 }
288
289 str = argv[0];
290 stoFree(argv);
291 return str;
292}
293
294static String
295cfgStringValue(String str)
296{
297 char *p, *start, *new;
298 /* Strip spaces front and rear, ignore leading and trailing quotes */
299 p = str;
300 while (IsWhiteSpace(*p)((*p)==' ' || (*p)=='\n' || (*p) == '\t') && *p != '\0') p++;
301 if (*p == '"') p++;
302 if (*p == '\0') return strCopy("");
303 start = p;
304 p = start + strlen(start) - 1;
305 while (IsWhiteSpace(*p)((*p)==' ' || (*p)=='\n' || (*p) == '\t') && p != start) p--;
306 if (*p == '"') p--;
307 new = strCopy(start);
308 new[p - start +1] = '\0';
309 return new;
310}
311
312/*************************************************************************************
313 *
314 * :: Parsing input strings
315 *
316 *************************************************************************************/
317
318
319
320void
321cstrParseUnquoted(char *str, int *pargc, char ***pargv)
322{
323 char *p = str;
324 char *wstart;
325 char **argv;
326 char c;
327 int n, lim;
328
329 n = 0;
330 lim = 0;
331 argv = NULL((void*)0);
332 while (1) {
333 while (IsWhiteSpace(*p)((*p)==' ' || (*p)=='\n' || (*p) == '\t') && *p != '\0') p++;
334 if (*p == '\0') break;
335 wstart = p;
336 /* May want to be clever here wrt to '"' */
337 while (!IsWhiteSpace(*p)((*p)==' ' || (*p)=='\n' || (*p) == '\t') && *p != '\0') p++;
338 c = *p;
339 *p = '\0';
340 if (n==lim) {
341 char **targv = argv;
342 int i;
343 argv = (char **) stoAlloc(OB_Other0, (n + 5)*sizeof(char *));
344 lim += 5;
345 for (i=0; i<n; i++) argv[i] = targv[i];
346 if (targv) stoFree(targv);
347 }
348 argv[n++] = strCopy(wstart);
349 *p = c;
350 if (c == '\0') break;
351 }
352
353 *pargc = n;
354 *pargv = argv;
355}
356
357
358void
359cstrParseCommaified(char *opts, int *pargc, char ***pargv)
360{
361 String str = strCopy(opts);
362 char **argv;
363 char *p, *q;
364 int argc, i;
365
366 p = str;
367 q = p;
368 argc = 1;
369 while (*p != '\0') {
370 if (*p == '\\')
371 p++;
372 else if (*p == ',') {
373 *p = '\0';
374 argc++;
375 }
376 *q = *p;
377 p++; q++;
378 }
379 *q = '\0';
380 p = str;
381 argv = (char **) stoAlloc(OB_Other0, argc * sizeof(char *));
382 for (i=0; i<argc; i++) {
383 argv[i] = p;
384 while (*p != '\0') p++;
385 p++;
386 }
387
388 *pargc = argc;
389 *pargv = argv;
390}
391
392/**********************************************************************************
393 *
394 * :: Conditions
395 *
396 **********************************************************************************/
397
398/*
399 * Function should return 1 for true, 0 for false, and -1 if
400 * not set
401 */
402
403static int (*cfgCondFn)(String);
404
405localstatic void cfgFreeConfPath(void);
406
407void
408cfgSetCondFunc(int (*check)(String))
409{
410 cfgCondFn = check;
411}
412
413static Bool
414cfgCheckCondition(String name, ConfigItemList cfg)
415{
416 int val;
417 if ( (val = (*cfgCondFn)(name)) != -1)
418 return val;
419
420 return cfgLookupBoolean(name, cfg);
421}
422
423/**********************************************************************************
424 *
425 * :: Reading information
426 *
427 **********************************************************************************/
428
429static StringList cfgPath;
430
431ConfigItem
432cfgNew(char *key, char *val)
433{
434 ConfigItem item;
435 int nkey, nval;
436 nkey = strlen(key);
437 nval = strlen(val);
438
439 item = (ConfigItem) stoAlloc(OB_Other0, fullsizeof(struct _ConfigItem, nkey + nval + 2, char)(sizeof(struct _ConfigItem) + (nkey + nval + 2) * sizeof(char
) - 10 * sizeof(char))
);
440 item->optName = item->content;
441 strcpy(item->optName, key);
442 item->optVal = item->content + nkey + 1;
443 strcpy(item->optVal, val);
444
445 return item;
446}
447
448void cfgFree(ConfigItem item)
449{
450 stoFree(item);
451}
452
453void cfgSetConfPath(char *path)
454{
455 Bool done;
456 String s = strCopy(path);
457 char *start, *p;
458 p = s;
459 cfgPath = listNil(String)((StringList) 0);
460 done = false((int) 0);
461 while (!done) {
462 char c;
463 start = p;
464 while (*p != '\0' && *p != osPathSeparator())
465 p++;
466 c = *p;
467 *p = '\0';
468 cfgPath = listCons(String)(String_listPointer->Cons)(start, cfgPath);
469 if (c == '\0') done = true1;
470 p++;
471 }
472}
473
474localstatic void
475cfgFreeConfPath(void)
476{
477 if (cfgPath) strFree(car(cfgPath)((cfgPath)->first));
478 listFree(String)(String_listPointer->Free)(cfgPath);
479}
480
481FileName
482cfgFindFile(String basename, String ext)
483{
484 StringList lst;
485
486 lst = cfgPath;
487 while (lst != listNil(String)((StringList) 0)) {
488 FileName name = fnameNew(car(lst)((lst)->first), basename, ext);
489 if (fileIsOpenable(name, osIoRdMode))
490 return name;
491 lst = cdr(lst)((lst)->rest);
492 }
493
494 return NULL((void*)0);
495}
496
497
498/*
499 * List of errors discovered during cfgRead(). The list is
500 * reset to nothing each time cfgRead() is invoked and may
501 * be accessed by calling cfgReadGetErrors().
502 */
503static StringList CfgReadErrs = listNil(String)((StringList) 0);
504
505
506ConfigItemList
507cfgRead(FILE *file, char *name)
508{
509 Bool done = false((int) 0);
510
511 /* Discard the previous error list */
512 cfgReadClearErrors();
513
514
515 /* Locate, and then read, the specified section */
516 while (!done) {
517 String line = cfgGetLine(file, &done);
518 if (cfgIsSection(line, name))
519 return cfgGetKeys(file, name);
520 }
521 return NULL((void*)0);
522}
523
524
525/* Return the list of errors from the previous cfgRead() call */
526StringList
527cfgReadGetErrors(void)
528{
529 return CfgReadErrs;
530}
531
532
533/* Reset the error list */
534void
535cfgReadClearErrors(void)
536{
537 /* Discard the previous error list */
538 listFreeDeeply(String)(String_listPointer->FreeDeeply)(CfgReadErrs, strFree);
539
540
541 /* Start a new error list */
542 CfgReadErrs = listNil(String)((StringList) 0);
543}
544
545
546/* Add a new error to the list */
547static void
548cfgReadAddError(String error)
549{
550 CfgReadErrs = listCons(String)(String_listPointer->Cons)(strCopy(error), CfgReadErrs);
551}
552
553
554/* Report that we've seen this item before */
555static void
556cfgReadDupItemError(ConfigItem item, String section)
557{
558 String msg;
559
560 /* Compose the error message */
561 msg = strlConcat(
562 "Duplicate key `", cfgName(item)((item)->optName),
563 "' found in section [", section, "]",
564 " of the compiler configuration file.",
565 (String)NULL((void*)0)
566 );
567
568
569 /* Add the error to the list */
570 cfgReadAddError(msg);
571}
572
573
574/* Equality based on key name */
575static Bool
576cfgEqual(ConfigItem itemA, ConfigItem itemB)
577{
578 return strEqual(cfgName(itemA)((itemA)->optName), cfgName(itemB)((itemB)->optName));
579}
580
581static Bool
582cfgIsSection(char *line, char *name)
583{
584 char *p = line;
585 while (*p != ']' && *p != '\0') p++;
586 *p = 0;
587 return (strEqual(line+1, name));
588}
589
590
591static ConfigItemList
592cfgGetKeys(FILE *file, String section)
593{
594 ConfigItemList lst;
595 Bool atEof;
596
597 atEof = false((int) 0);
598 lst = listNil(ConfigItem)((ConfigItemList) 0);
599 while (true1) {
600 ConfigItem item;
601 String line = cfgGetLine(file, &atEof);
602
603
604 /* Stop if we hit the next section */
605 if (line[0] == '[') break;
606
607
608 /* Parse the line to see if contains an item */
609 item = cfgParseLine(line);
610
611
612 /* Check for duplicates (allow `inherit') */
613 if (item && !strEqual(cfgName(item)((item)->optName), "inherit"))
614 {
615 if (listMember(ConfigItem)(ConfigItem_listPointer->Member)(lst, item, cfgEqual))
616 cfgReadDupItemError(item, section);
617 }
618
619
620 /*
621 * We always add the item to the list even if one with
622 * the same key exists already. The client may wish to
623 * deal with duplicate keys themselves (e.g. inherit).
624 */
625 if (item) lst = listCons(ConfigItem)(ConfigItem_listPointer->Cons)(item, lst);
626
627
628 /* Stop if we've reached the end of the file */
629 if (atEof) break;
630
631
632 /* Release temporary storage */
633 strFree(line);
634 }
635 return lst;
636}
637
638static ConfigItem
639cfgParseLine(String line)
640{
641 String key;
642 String val;
643 char *p = line, *keyEnd;
644 /* skip whitespace */
645 while (*p == ' ' || *p == '\t') p++;
646
647 if (*p == '#') return NULL((void*)0);
648 key = p;
649
650 while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0') p++;
651 keyEnd = p;
652
653 if (*p == '\0') return NULL((void*)0);
654
655 while (*p != '=') p++;
656
657 p++;
658 val = p;
659 *keyEnd = '\0';
660
661 return cfgNew(key, val);
662}
663
664static String
665cfgGetLine(FILE *file, Bool *atEof)
666{
667 String s;
668 int n, lim, c;
669
670 lim = 50;
671 s = strAlloc(lim);
672 n = 0;
673
674 c = fgetc(file);
675 while (c != '\n' && c != EOF(-1)) {
676 if (n == lim) {
677 int const incr = 20;
678 String tmp = s;
679 s = strAlloc(lim + incr);
680 strncpy(s, tmp, n);
681 strFree(tmp);
682 lim += incr;
683 }
684 s[n] = c;
685 c = fgetc(file);
686 n++;
687 }
688 while (n < lim)
689 s[n++] = '\0';
690 if (c == EOF(-1)) *atEof = true1;
691 return s;
692}
693