Bug Summary

File:libexec/got-read-gitconfig/../../lib/gitconfig.c
Warning:line 305, column 6
Potential leak of memory pointed to by 'section'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd6.9 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name gitconfig.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 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/11.1.0 -I /home/ben/Projects/got/libexec/got-read-gitconfig/../../include -I /home/ben/Projects/got/libexec/got-read-gitconfig/../../lib -D GOT_LIBEXECDIR=/home/ben/bin -D GOT_VERSION=0.53-current -internal-isystem /usr/local/lib/clang/11.1.0/include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/ben/Projects/got/libexec/got-read-gitconfig/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -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 -o /home/ben/Projects/got/scan/2021-05-28-230913-68537-1 -x c /home/ben/Projects/got/libexec/got-read-gitconfig/../../lib/gitconfig.c
1/* $OpenBSD: conf.c,v 1.107 2017/10/27 08:29:32 mpi Exp $ */
2/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */
3
4/*
5 * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
6 * Copyright (c) 2000, 2001, 2002 Håkan Olsson. 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 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30#include <sys/queue.h>
31#include <sys/stat.h>
32
33#include <ctype.h>
34#include <fcntl.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <errno(*__errno()).h>
41
42#include "got_error.h"
43
44#include "got_lib_gitconfig.h"
45
46#ifndef nitems
47#define nitems(_a)(sizeof(_a) / sizeof((_a)[0])) (sizeof(_a) / sizeof((_a)[0]))
48#endif
49
50#define LOG_MISC0 0
51#define LOG_REPORT1 1
52#ifdef GITCONFIG_DEBUG
53#define LOG_DBG(x) log_debug x
54#else
55#define LOG_DBG(x)
56#endif
57
58#define log_printprintf printf
59#define log_errorprintf printf
60
61#ifdef GITCONFIG_DEBUG
62static void
63log_debug(int cls, int level, const char *fmt, ...)
64{
65 va_list ap;
66
67 va_start(ap, fmt)__builtin_va_start(ap, fmt);
68 vfprintf(stderr(&__sF[2]), fmt, ap);
69 va_end(ap)__builtin_va_end(ap);
70}
71#endif
72
73struct got_gitconfig_trans {
74 TAILQ_ENTRY(got_gitconfig_trans)struct { struct got_gitconfig_trans *tqe_next; struct got_gitconfig_trans
**tqe_prev; }
link;
75 int trans;
76 enum got_gitconfig_op {
77 CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION
78 } op;
79 char *section;
80 char *tag;
81 char *value;
82 int override;
83 int is_default;
84};
85
86TAILQ_HEAD(got_gitconfig_trans_head, got_gitconfig_trans)struct got_gitconfig_trans_head { struct got_gitconfig_trans *
tqh_first; struct got_gitconfig_trans **tqh_last; }
;
87
88struct got_gitconfig_binding {
89 LIST_ENTRY(got_gitconfig_binding)struct { struct got_gitconfig_binding *le_next; struct got_gitconfig_binding
**le_prev; }
link;
90 char *section;
91 char *tag;
92 char *value;
93 int is_default;
94};
95
96LIST_HEAD(got_gitconfig_bindings, got_gitconfig_binding)struct got_gitconfig_bindings { struct got_gitconfig_binding *
lh_first; }
;
97
98struct got_gitconfig {
99 struct got_gitconfig_bindings bindings[256];
100 struct got_gitconfig_trans_head trans_queue;
101 char *addr;
102 int seq;
103};
104
105static __inline__ u_int8_t
106conf_hash(char *s)
107{
108 u_int8_t hash = 0;
109
110 while (*s) {
111 hash = ((hash << 1) | (hash >> 7)) ^ tolower((unsigned char)*s);
112 s++;
113 }
114 return hash;
115}
116
117/*
118 * Insert a tag-value combination from LINE (the equal sign is at POS)
119 */
120static int
121conf_remove_now(struct got_gitconfig *conf, char *section, char *tag)
122{
123 struct got_gitconfig_binding *cb, *next;
124
125 for (cb = LIST_FIRST(&conf->bindings[conf_hash(section)])((&conf->bindings[conf_hash(section)])->lh_first); cb;
126 cb = next) {
127 next = LIST_NEXT(cb, link)((cb)->link.le_next);
128 if (strcasecmp(cb->section, section) == 0 &&
129 strcasecmp(cb->tag, tag) == 0) {
130 LIST_REMOVE(cb, link)do { if ((cb)->link.le_next != ((void *)0)) (cb)->link.
le_next->link.le_prev = (cb)->link.le_prev; *(cb)->link
.le_prev = (cb)->link.le_next; ; ; } while (0)
;
131 LOG_DBG((LOG_MISC, 95, "[%s]:%s->%s removed", section,
132 tag, cb->value));
133 free(cb->section);
134 free(cb->tag);
135 free(cb->value);
136 free(cb);
137 return 0;
138 }
139 }
140 return 1;
141}
142
143static int
144conf_remove_section_now(struct got_gitconfig *conf, char *section)
145{
146 struct got_gitconfig_binding *cb, *next;
147 int unseen = 1;
148
149 for (cb = LIST_FIRST(&conf->bindings[conf_hash(section)])((&conf->bindings[conf_hash(section)])->lh_first); cb;
150 cb = next) {
151 next = LIST_NEXT(cb, link)((cb)->link.le_next);
152 if (strcasecmp(cb->section, section) == 0) {
153 unseen = 0;
154 LIST_REMOVE(cb, link)do { if ((cb)->link.le_next != ((void *)0)) (cb)->link.
le_next->link.le_prev = (cb)->link.le_prev; *(cb)->link
.le_prev = (cb)->link.le_next; ; ; } while (0)
;
155 LOG_DBG((LOG_MISC, 95, "[%s]:%s->%s removed", section,
156 cb->tag, cb->value));
157 free(cb->section);
158 free(cb->tag);
159 free(cb->value);
160 free(cb);
161 }
162 }
163 return unseen;
164}
165
166/*
167 * Insert a tag-value combination from LINE (the equal sign is at POS)
168 * into SECTION of our configuration database.
169 */
170static int
171conf_set_now(struct got_gitconfig *conf, char *section, char *tag,
172 char *value, int override, int is_default)
173{
174 struct got_gitconfig_binding *node = 0;
175
176 if (override)
177 conf_remove_now(conf, section, tag);
178 else if (got_gitconfig_get_str(conf, section, tag)) {
179 if (!is_default)
180 LOG_DBG((LOG_MISC,
181 "conf_set_now: duplicate tag [%s]:%s, "
182 "ignoring...\n", section, tag));
183 return 1;
184 }
185 node = calloc(1, sizeof *node);
186 if (!node) {
187 log_errorprintf("conf_set_now: calloc (1, %lu) failed",
188 (unsigned long)sizeof *node);
189 return 1;
190 }
191 node->section = node->tag = node->value = NULL((void *)0);
192 if ((node->section = strdup(section)) == NULL((void *)0))
193 goto fail;
194 if ((node->tag = strdup(tag)) == NULL((void *)0))
195 goto fail;
196 if ((node->value = strdup(value)) == NULL((void *)0))
197 goto fail;
198 node->is_default = is_default;
199
200 LIST_INSERT_HEAD(&conf->bindings[conf_hash(section)], node, link)do { if (((node)->link.le_next = (&conf->bindings[conf_hash
(section)])->lh_first) != ((void *)0)) (&conf->bindings
[conf_hash(section)])->lh_first->link.le_prev = &(node
)->link.le_next; (&conf->bindings[conf_hash(section
)])->lh_first = (node); (node)->link.le_prev = &(&
conf->bindings[conf_hash(section)])->lh_first; } while (
0)
;
201 LOG_DBG((LOG_MISC, 95, "conf_set_now: [%s]:%s->%s", node->section,
202 node->tag, node->value));
203 return 0;
204fail:
205 free(node->value);
206 free(node->tag);
207 free(node->section);
208 free(node);
209 return 1;
210}
211
212/*
213 * Parse the line LINE of SZ bytes. Skip Comments, recognize section
214 * headers and feed tag-value pairs into our configuration database.
215 */
216static const struct got_error *
217conf_parse_line(char **section, struct got_gitconfig *conf, int trans,
218 char *line, int ln, size_t sz)
219{
220 char *val;
221 size_t i;
222 int j;
223
224 /* Lines starting with '#' or ';' are comments. */
225 if (*line == '#' || *line == ';')
23
Assuming the condition is false
24
Assuming the condition is false
25
Taking false branch
226 return NULL((void *)0);
227
228 /* '[section]' parsing... */
229 if (*line == '[') {
26
Assuming the condition is true
27
Taking true branch
230 for (i = 1; i < sz; i++)
28
Loop condition is true. Entering loop body
231 if (line[i] == ']')
29
Assuming the condition is true
30
Taking true branch
232 break;
31
Execution continues on line 233
233 free(*section);
234 if (i
31.1
'i' is not equal to 'sz'
== sz) {
32
Taking false branch
235 log_printprintf("conf_parse_line: %d:"
236 "unmatched ']', ignoring until next section", ln);
237 *section = NULL((void *)0);
238 return NULL((void *)0);
239 }
240 *section = malloc(i);
33
Memory is allocated
241 if (*section == NULL((void *)0))
34
Assuming the condition is false
35
Taking false branch
242 return got_error_from_errno("malloc");
243 strlcpy(*section, line + 1, i);
244 return NULL((void *)0);
245 }
246 while (isspace((unsigned char)*line))
247 line++;
248
249 /* Deal with assignments. */
250 for (i = 0; i < sz; i++)
251 if (line[i] == '=') {
252 /* If no section, we are ignoring the lines. */
253 if (!*section) {
254 log_printprintf("conf_parse_line: %d: ignoring line "
255 "due to no section", ln);
256 return NULL((void *)0);
257 }
258 line[strcspn(line, " \t=")] = '\0';
259 val = line + i + 1 + strspn(line + i + 1, " \t");
260 /* Skip trailing whitespace, if any */
261 for (j = sz - (val - line) - 1; j > 0 &&
262 isspace((unsigned char)val[j]); j--)
263 val[j] = '\0';
264 /* XXX Perhaps should we not ignore errors? */
265 got_gitconfig_set(conf, trans, *section, line, val,
266 0, 0);
267 return NULL((void *)0);
268 }
269 /* Other non-empty lines are weird. */
270 i = strspn(line, " \t");
271 if (line[i])
272 log_printprintf("conf_parse_line: %d: syntax error", ln);
273
274 return NULL((void *)0);
275}
276
277/* Parse the mapped configuration file. */
278static const struct got_error *
279conf_parse(struct got_gitconfig *conf, int trans, char *buf, size_t sz)
280{
281 const struct got_error *err = NULL((void *)0);
282 char *cp = buf;
283 char *bufend = buf + sz;
284 char *line, *section = NULL((void *)0);
285 int ln = 1;
286
287 line = cp;
288 while (cp < bufend) {
8
Assuming 'cp' is < 'bufend'
9
Loop condition is true. Entering loop body
12
Assuming 'cp' is < 'bufend'
13
Loop condition is true. Entering loop body
16
Assuming 'cp' is < 'bufend'
17
Loop condition is true. Entering loop body
38
Assuming 'cp' is >= 'bufend'
39
Loop condition is false. Execution continues on line 305
289 if (*cp == '\n') {
10
Assuming the condition is false
11
Taking false branch
14
Assuming the condition is false
15
Taking false branch
18
Assuming the condition is true
19
Taking true branch
290 /* Check for escaped newlines. */
291 if (cp
19.1
'cp' is > 'buf'
> buf && *(cp - 1) == '\\')
20
Assuming the condition is false
21
Taking false branch
292 *(cp - 1) = *cp = ' ';
293 else {
294 *cp = '\0';
295 err = conf_parse_line(&section, conf, trans,
22
Calling 'conf_parse_line'
36
Returned allocated memory via 1st parameter
296 line, ln, cp - line);
297 if (err
36.1
'err' is null
)
37
Taking false branch
298 return err;
299 line = cp + 1;
300 }
301 ln++;
302 }
303 cp++;
304 }
305 if (cp != line)
40
Potential leak of memory pointed to by 'section'
306 log_printprintf("conf_parse: last line unterminated, ignored.");
307 return NULL((void *)0);
308}
309
310const struct got_error *
311got_gitconfig_open(struct got_gitconfig **conf, int fd)
312{
313 size_t i;
314
315 *conf = calloc(1, sizeof(**conf));
316 if (*conf == NULL((void *)0))
317 return got_error_from_errno("malloc");
318
319 for (i = 0; i < nitems((*conf)->bindings)(sizeof((*conf)->bindings) / sizeof(((*conf)->bindings)
[0]))
; i++)
320 LIST_INIT(&(*conf)->bindings[i])do { ((&(*conf)->bindings[i])->lh_first) = ((void *
)0); } while (0)
;
321 TAILQ_INIT(&(*conf)->trans_queue)do { (&(*conf)->trans_queue)->tqh_first = ((void *)
0); (&(*conf)->trans_queue)->tqh_last = &(&
(*conf)->trans_queue)->tqh_first; } while (0)
;
322 return got_gitconfig_reinit(*conf, fd);
323}
324
325static void
326conf_clear(struct got_gitconfig *conf)
327{
328 struct got_gitconfig_binding *cb;
329 size_t i;
330
331 if (conf->addr) {
332 for (i = 0; i < nitems(conf->bindings)(sizeof(conf->bindings) / sizeof((conf->bindings)[0])); i++)
333 for (cb = LIST_FIRST(&conf->bindings[i])((&conf->bindings[i])->lh_first); cb;
334 cb = LIST_FIRST(&conf->bindings[i])((&conf->bindings[i])->lh_first))
335 conf_remove_now(conf, cb->section, cb->tag);
336 free(conf->addr);
337 conf->addr = NULL((void *)0);
338 }
339}
340
341/* Execute all queued operations for this transaction. Cleanup. */
342static int
343conf_end(struct got_gitconfig *conf, int transaction, int commit)
344{
345 struct got_gitconfig_trans *node, *next;
346
347 for (node = TAILQ_FIRST(&conf->trans_queue)((&conf->trans_queue)->tqh_first); node; node = next) {
348 next = TAILQ_NEXT(node, link)((node)->link.tqe_next);
349 if (node->trans == transaction) {
350 if (commit)
351 switch (node->op) {
352 case CONF_SET:
353 conf_set_now(conf, node->section,
354 node->tag, node->value,
355 node->override, node->is_default);
356 break;
357 case CONF_REMOVE:
358 conf_remove_now(conf, node->section,
359 node->tag);
360 break;
361 case CONF_REMOVE_SECTION:
362 conf_remove_section_now(conf, node->section);
363 break;
364 default:
365 log_printprintf("got_gitconfig_end: unknown "
366 "operation: %d", node->op);
367 }
368 TAILQ_REMOVE(&conf->trans_queue, node, link)do { if (((node)->link.tqe_next) != ((void *)0)) (node)->
link.tqe_next->link.tqe_prev = (node)->link.tqe_prev; else
(&conf->trans_queue)->tqh_last = (node)->link.tqe_prev
; *(node)->link.tqe_prev = (node)->link.tqe_next; ; ; }
while (0)
;
369 free(node->section);
370 free(node->tag);
371 free(node->value);
372 free(node);
373 }
374 }
375 return 0;
376}
377
378
379void
380got_gitconfig_close(struct got_gitconfig *conf)
381{
382 conf_clear(conf);
383 free(conf);
384}
385
386static int
387conf_begin(struct got_gitconfig *conf)
388{
389 return ++conf->seq;
390}
391
392/* Open the config file and map it into our address space, then parse it. */
393const struct got_error *
394got_gitconfig_reinit(struct got_gitconfig *conf, int fd)
395{
396 const struct got_error *err = NULL((void *)0);
397 int trans;
398 size_t sz;
399 char *new_conf_addr = 0;
400 struct stat st;
401
402 if (fstat(fd, &st)) {
1
Assuming the condition is false
2
Taking false branch
403 err = got_error_from_errno("fstat");
404 goto fail;
405 }
406
407 sz = st.st_size;
408 new_conf_addr = malloc(sz);
409 if (new_conf_addr == NULL((void *)0)) {
3
Assuming 'new_conf_addr' is not equal to NULL
4
Taking false branch
410 err = got_error_from_errno("malloc");
411 goto fail;
412 }
413 /* XXX I assume short reads won't happen here. */
414 if (read(fd, new_conf_addr, sz) != (int)sz) {
5
Assuming the condition is false
6
Taking false branch
415 err = got_error_from_errno("read");
416 goto fail;
417 }
418
419 trans = conf_begin(conf);
420
421 err = conf_parse(conf, trans, new_conf_addr, sz);
7
Calling 'conf_parse'
422 if (err)
423 goto fail;
424
425 /* Free potential existing configuration. */
426 conf_clear(conf);
427 conf_end(conf, trans, 1);
428 conf->addr = new_conf_addr;
429 return NULL((void *)0);
430
431fail:
432 free(new_conf_addr);
433 return err;
434}
435
436/*
437 * Return the numeric value denoted by TAG in section SECTION or DEF
438 * if that tag does not exist.
439 */
440int
441got_gitconfig_get_num(struct got_gitconfig *conf, char *section, char *tag,
442 int def)
443{
444 char *value = got_gitconfig_get_str(conf, section, tag);
445
446 if (value)
447 return atoi(value);
448 return def;
449}
450
451/* Validate X according to the range denoted by TAG in section SECTION. */
452int
453got_gitconfig_match_num(struct got_gitconfig *conf, char *section, char *tag,
454 int x)
455{
456 char *value = got_gitconfig_get_str(conf, section, tag);
457 int val, min, max, n;
458
459 if (!value)
460 return 0;
461 n = sscanf(value, "%d,%d:%d", &val, &min, &max);
462 switch (n) {
463 case 1:
464 LOG_DBG((LOG_MISC, 95, "got_gitconfig_match_num: %s:%s %d==%d?",
465 section, tag, val, x));
466 return x == val;
467 case 3:
468 LOG_DBG((LOG_MISC, 95, "got_gitconfig_match_num: %s:%s %d<=%d<=%d?",
469 section, tag, min, x, max));
470 return min <= x && max >= x;
471 default:
472 log_errorprintf("got_gitconfig_match_num: section %s tag %s: invalid number "
473 "spec %s", section, tag, value);
474 }
475 return 0;
476}
477
478/* Return the string value denoted by TAG in section SECTION. */
479char *
480got_gitconfig_get_str(struct got_gitconfig *conf, char *section, char *tag)
481{
482 struct got_gitconfig_binding *cb;
483
484 for (cb = LIST_FIRST(&conf->bindings[conf_hash(section)])((&conf->bindings[conf_hash(section)])->lh_first); cb;
485 cb = LIST_NEXT(cb, link)((cb)->link.le_next))
486 if (strcasecmp(section, cb->section) == 0 &&
487 strcasecmp(tag, cb->tag) == 0) {
488 LOG_DBG((LOG_MISC, 95, "got_gitconfig_get_str: [%s]:%s->%s",
489 section, tag, cb->value));
490 return cb->value;
491 }
492 LOG_DBG((LOG_MISC, 95,
493 "got_gitconfig_get_str: configuration value not found [%s]:%s", section,
494 tag));
495 return 0;
496}
497
498const struct got_error *
499got_gitconfig_get_section_list(struct got_gitconfig_list **sections,
500 struct got_gitconfig *conf)
501{
502 const struct got_error *err = NULL((void *)0);
503 struct got_gitconfig_list *list = NULL((void *)0);
504 struct got_gitconfig_list_node *node = 0;
505 struct got_gitconfig_binding *cb;
506 size_t i;
507
508 *sections = NULL((void *)0);
509
510 list = malloc(sizeof *list);
511 if (!list)
512 return got_error_from_errno("malloc");
513 TAILQ_INIT(&list->fields)do { (&list->fields)->tqh_first = ((void *)0); (&
list->fields)->tqh_last = &(&list->fields)->
tqh_first; } while (0)
;
514 list->cnt = 0;
515 for (i = 0; i < nitems(conf->bindings)(sizeof(conf->bindings) / sizeof((conf->bindings)[0])); i++) {
516 for (cb = LIST_FIRST(&conf->bindings[i])((&conf->bindings[i])->lh_first); cb;
517 cb = LIST_NEXT(cb, link)((cb)->link.le_next)) {
518 int section_present = 0;
519 TAILQ_FOREACH(node, &list->fields, link)for((node) = ((&list->fields)->tqh_first); (node) !=
((void *)0); (node) = ((node)->link.tqe_next))
{
520 if (strcmp(node->field, cb->section) == 0) {
521 section_present = 1;
522 break;
523 }
524 }
525 if (section_present)
526 continue;
527 list->cnt++;
528 node = calloc(1, sizeof *node);
529 if (!node) {
530 err = got_error_from_errno("calloc");
531 goto cleanup;
532 }
533 node->field = strdup(cb->section);
534 if (!node->field) {
535 err = got_error_from_errno("strdup");
536 goto cleanup;
537 }
538 TAILQ_INSERT_TAIL(&list->fields, node, link)do { (node)->link.tqe_next = ((void *)0); (node)->link.
tqe_prev = (&list->fields)->tqh_last; *(&list->
fields)->tqh_last = (node); (&list->fields)->tqh_last
= &(node)->link.tqe_next; } while (0)
;
539 }
540 }
541
542 *sections = list;
543 return NULL((void *)0);
544
545cleanup:
546 free(node);
547 if (list)
548 got_gitconfig_free_list(list);
549 return err;
550}
551
552/*
553 * Build a list of string values out of the comma separated value denoted by
554 * TAG in SECTION.
555 */
556struct got_gitconfig_list *
557got_gitconfig_get_list(struct got_gitconfig *conf, char *section, char *tag)
558{
559 char *liststr = 0, *p, *field, *t;
560 struct got_gitconfig_list *list = 0;
561 struct got_gitconfig_list_node *node = 0;
562
563 list = malloc(sizeof *list);
564 if (!list)
565 goto cleanup;
566 TAILQ_INIT(&list->fields)do { (&list->fields)->tqh_first = ((void *)0); (&
list->fields)->tqh_last = &(&list->fields)->
tqh_first; } while (0)
;
567 list->cnt = 0;
568 liststr = got_gitconfig_get_str(conf, section, tag);
569 if (!liststr)
570 goto cleanup;
571 liststr = strdup(liststr);
572 if (!liststr)
573 goto cleanup;
574 p = liststr;
575 while ((field = strsep(&p, ",")) != NULL((void *)0)) {
576 /* Skip leading whitespace */
577 while (isspace((unsigned char)*field))
578 field++;
579 /* Skip trailing whitespace */
580 if (p)
581 for (t = p - 1; t > field && isspace((unsigned char)*t); t--)
582 *t = '\0';
583 if (*field == '\0') {
584 log_printprintf("got_gitconfig_get_list: empty field, ignoring...");
585 continue;
586 }
587 list->cnt++;
588 node = calloc(1, sizeof *node);
589 if (!node)
590 goto cleanup;
591 node->field = strdup(field);
592 if (!node->field)
593 goto cleanup;
594 TAILQ_INSERT_TAIL(&list->fields, node, link)do { (node)->link.tqe_next = ((void *)0); (node)->link.
tqe_prev = (&list->fields)->tqh_last; *(&list->
fields)->tqh_last = (node); (&list->fields)->tqh_last
= &(node)->link.tqe_next; } while (0)
;
595 }
596 free(liststr);
597 return list;
598
599cleanup:
600 free(node);
601 if (list)
602 got_gitconfig_free_list(list);
603 free(liststr);
604 return 0;
605}
606
607struct got_gitconfig_list *
608got_gitconfig_get_tag_list(struct got_gitconfig *conf, char *section)
609{
610 struct got_gitconfig_list *list = 0;
611 struct got_gitconfig_list_node *node = 0;
612 struct got_gitconfig_binding *cb;
613
614 list = malloc(sizeof *list);
615 if (!list)
616 goto cleanup;
617 TAILQ_INIT(&list->fields)do { (&list->fields)->tqh_first = ((void *)0); (&
list->fields)->tqh_last = &(&list->fields)->
tqh_first; } while (0)
;
618 list->cnt = 0;
619 for (cb = LIST_FIRST(&conf->bindings[conf_hash(section)])((&conf->bindings[conf_hash(section)])->lh_first); cb;
620 cb = LIST_NEXT(cb, link)((cb)->link.le_next))
621 if (strcasecmp(section, cb->section) == 0) {
622 list->cnt++;
623 node = calloc(1, sizeof *node);
624 if (!node)
625 goto cleanup;
626 node->field = strdup(cb->tag);
627 if (!node->field)
628 goto cleanup;
629 TAILQ_INSERT_TAIL(&list->fields, node, link)do { (node)->link.tqe_next = ((void *)0); (node)->link.
tqe_prev = (&list->fields)->tqh_last; *(&list->
fields)->tqh_last = (node); (&list->fields)->tqh_last
= &(node)->link.tqe_next; } while (0)
;
630 }
631 return list;
632
633cleanup:
634 free(node);
635 if (list)
636 got_gitconfig_free_list(list);
637 return 0;
638}
639
640void
641got_gitconfig_free_list(struct got_gitconfig_list *list)
642{
643 struct got_gitconfig_list_node *node = TAILQ_FIRST(&list->fields)((&list->fields)->tqh_first);
644
645 while (node) {
646 TAILQ_REMOVE(&list->fields, node, link)do { if (((node)->link.tqe_next) != ((void *)0)) (node)->
link.tqe_next->link.tqe_prev = (node)->link.tqe_prev; else
(&list->fields)->tqh_last = (node)->link.tqe_prev
; *(node)->link.tqe_prev = (node)->link.tqe_next; ; ; }
while (0)
;
647 free(node->field);
648 free(node);
649 node = TAILQ_FIRST(&list->fields)((&list->fields)->tqh_first);
650 }
651 free(list);
652}
653
654static int
655got_gitconfig_trans_node(struct got_gitconfig *conf, int transaction,
656 enum got_gitconfig_op op, char *section, char *tag, char *value,
657 int override, int is_default)
658{
659 struct got_gitconfig_trans *node;
660
661 node = calloc(1, sizeof *node);
662 if (!node) {
663 log_errorprintf("got_gitconfig_trans_node: calloc (1, %lu) failed",
664 (unsigned long)sizeof *node);
665 return 1;
666 }
667 node->trans = transaction;
668 node->op = op;
669 node->override = override;
670 node->is_default = is_default;
671 if (section && (node->section = strdup(section)) == NULL((void *)0))
672 goto fail;
673 if (tag && (node->tag = strdup(tag)) == NULL((void *)0))
674 goto fail;
675 if (value && (node->value = strdup(value)) == NULL((void *)0))
676 goto fail;
677 TAILQ_INSERT_TAIL(&conf->trans_queue, node, link)do { (node)->link.tqe_next = ((void *)0); (node)->link.
tqe_prev = (&conf->trans_queue)->tqh_last; *(&conf
->trans_queue)->tqh_last = (node); (&conf->trans_queue
)->tqh_last = &(node)->link.tqe_next; } while (0)
;
678 return 0;
679
680fail:
681 free(node->section);
682 free(node->tag);
683 free(node->value);
684 free(node);
685 return 1;
686}
687
688/* Queue a set operation. */
689int
690got_gitconfig_set(struct got_gitconfig *conf, int transaction, char *section,
691 char *tag, char *value, int override, int is_default)
692{
693 return got_gitconfig_trans_node(conf, transaction, CONF_SET, section,
694 tag, value, override, is_default);
695}
696
697/* Queue a remove operation. */
698int
699got_gitconfig_remove(struct got_gitconfig *conf, int transaction,
700 char *section, char *tag)
701{
702 return got_gitconfig_trans_node(conf, transaction, CONF_REMOVE,
703 section, tag, NULL((void *)0), 0, 0);
704}
705
706/* Queue a remove section operation. */
707int
708got_gitconfig_remove_section(struct got_gitconfig *conf, int transaction,
709 char *section)
710{
711 return got_gitconfig_trans_node(conf, transaction, CONF_REMOVE_SECTION,
712 section, NULL((void *)0), NULL((void *)0), 0, 0);
713}