GCC Code Coverage Report


src/
File: src/config/config_cmd.c
Date: 2026-03-31 17:48:22
Lines:
68/70
97.1%
Functions:
2/2
100.0%
Branches:
33/35
94.3%

Line Branch Exec Source
1 #include <assert.h>
2 #include <glib.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include "cmd/cmd.h"
8 #include "config/config.h"
9
10 static error_t parse_opt(int key, char* arg, struct argp_state* state);
11
12 struct config_arguments {
13 struct global_arguments* global;
14 bool use_global_scope;
15 bool use_local_scope;
16 char* key;
17 char* value;
18 };
19
20 static struct argp_option options[] = {
21 {"global", 'g', 0, 0, "Use global (user-wide) configuration", 0},
22 {"local", 'l', 0, 0, "Use local (repository-wide) configuration", 0},
23 {NULL}};
24
25 static char doc[] =
26 "Read and write global (user-wide) or local "
27 "(repository-wide) configurations.";
28
29 static struct argp argp = {options, parse_opt, "<key> [<value>]", doc, NULL,
30 NULL, NULL};
31
32 108 static error_t parse_opt(int key, char* arg, struct argp_state* state) {
33 108 struct config_arguments* args = state->input;
34
35
5/5
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 53 times.
108 switch (key) {
36 6 case 'g':
37 6 args->use_global_scope = true;
38 6 break;
39
40 7 case 'l':
41 7 args->use_local_scope = true;
42 7 break;
43
44 26 case ARGP_KEY_ARG:
45 // Handle positional arguments
46
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 10 times.
26 if (state->arg_num == 0) {
47 16 args->key = arg; // key
48
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 } else if (state->arg_num == 1) {
49 9 args->value = arg; // value
50 } else {
51 1 return E2BIG;
52 }
53 25 break;
54
55 16 case ARGP_KEY_END:
56 // Validate that we have at least the key
57
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (!args->key) {
58 1 argp_error(state, "Missing required <key> argument.\n");
59 1 return EINVAL;
60 }
61 15 break;
62
63 53 default:
64 53 return ARGP_ERR_UNKNOWN;
65 }
66
67 53 return 0;
68 }
69
70 17 extern int gittor_config(struct argp_state* state) {
71 // Set defaults arguments
72 17 struct config_arguments args = {.use_global_scope = false,
73 .use_local_scope = false,
74 .key = NULL,
75 .value = NULL};
76
77 // Change the arguments array for just config
78 17 int argc = state->argc - state->next + 1;
79 17 char** argv = &state->argv[state->next - 1];
80 17 args.global = state->input;
81
82 // Change the command name to gittor config
83 17 const char name[] = "config";
84 17 size_t argv0len = strlen(state->name) + sizeof(name) + 1;
85 17 char* argv0 = argv[0];
86 17 argv[0] = malloc(argv0len);
87
1/1
✓ Branch 1 taken 17 times.
17 g_snprintf(argv[0], argv0len, "%s %s", state->name, name);
88
89 // Parse arguments
90
1/1
✓ Branch 1 taken 17 times.
17 int err = argp_parse(&argp, argc, argv, ARGP_NO_EXIT, 0, &args);
91
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if (err) {
92 2 free(argv[0]);
93 2 argv[0] = argv0;
94 2 state->next += argc - 1;
95 17 return err;
96 }
97
98 // Parse key into group.key
99 15 char* dot = strchr(args.key, '.');
100
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if (!dot) {
101
1/1
✓ Branch 1 taken 1 times.
1 argp_error(state,
102 "Invalid key format '%s'. Expected format: <group>.<key>.\n",
103 args.key);
104 1 free(argv[0]);
105 1 argv[0] = argv0;
106 1 state->next += argc - 1;
107 1 return EINVAL;
108 }
109
1/1
✓ Branch 1 taken 14 times.
14 char* group = g_strndup(args.key, dot - args.key);
110 14 char* key = g_strdup(dot + 1);
111
112 // Determine scope
113 14 config_scope_e scope = CONFIG_SCOPE_LOCAL;
114
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
14 if (args.use_global_scope && !args.use_local_scope) {
115 5 scope = CONFIG_SCOPE_GLOBAL;
116 }
117
118 // Prepare config arguments
119 14 config_id_t config_id = {
120 .group = group,
121 .key = key,
122 };
123
124 // Read or write the configuration
125
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 if (args.value) {
126 // Set configuration
127
2/3
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if (config_set(scope, &config_id, args.value)) {
128 argp_error(state, "Failed to set configuration '%s.%s'.\n", group,
129 key);
130 err = EIO;
131 } else {
132
1/1
✓ Branch 1 taken 8 times.
8 printf("Configuration set: %s.%s = %s\n", group, key, args.value);
133 }
134 } else {
135 // Get configuration
136
1/1
✓ Branch 1 taken 6 times.
6 char* value = config_get(scope, &config_id, NULL); // No default set
137
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (value) {
138
1/1
✓ Branch 1 taken 5 times.
5 printf("%s\n", value);
139 5 free(value);
140 } else {
141
1/1
✓ Branch 1 taken 1 times.
1 argp_error(state, "Configuration '%s.%s' not found.\n", group, key);
142 1 err = ENOENT;
143 }
144 }
145
146 // Reset to global
147 14 free(argv[0]);
148 14 argv[0] = argv0;
149 14 state->next += argc - 1;
150 14 free(group);
151 14 free(key);
152 14 return err;
153 }
154