]>
Commit | Line | Data |
---|---|---|
ac3d33df JK |
1 | /* |
2 | * Copyright (C) the libgit2 contributors. All rights reserved. | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
8 | #include "config_entries.h" | |
9 | ||
10 | typedef struct config_entry_list { | |
11 | struct config_entry_list *next; | |
12 | struct config_entry_list *last; | |
13 | git_config_entry *entry; | |
14 | } config_entry_list; | |
15 | ||
16 | typedef struct config_entries_iterator { | |
17 | git_config_iterator parent; | |
18 | git_config_entries *entries; | |
19 | config_entry_list *head; | |
20 | } config_entries_iterator; | |
21 | ||
22 | struct git_config_entries { | |
23 | git_refcount rc; | |
24 | git_strmap *map; | |
25 | config_entry_list *list; | |
26 | }; | |
27 | ||
28 | static void config_entry_list_free(config_entry_list *list) | |
29 | { | |
30 | config_entry_list *next; | |
31 | ||
32 | while (list != NULL) { | |
33 | next = list->next; | |
34 | ||
35 | git__free((char*) list->entry->name); | |
36 | git__free((char *) list->entry->value); | |
37 | git__free(list->entry); | |
38 | git__free(list); | |
39 | ||
40 | list = next; | |
41 | }; | |
42 | } | |
43 | ||
44 | static void config_entry_list_append(config_entry_list **list, config_entry_list *entry) | |
45 | { | |
46 | if (*list) | |
47 | (*list)->last->next = entry; | |
48 | else | |
49 | *list = entry; | |
50 | (*list)->last = entry; | |
51 | } | |
52 | ||
53 | int git_config_entries_new(git_config_entries **out) | |
54 | { | |
55 | git_config_entries *entries; | |
56 | int error; | |
57 | ||
58 | entries = git__calloc(1, sizeof(git_config_entries)); | |
59 | GIT_ERROR_CHECK_ALLOC(entries); | |
60 | GIT_REFCOUNT_INC(entries); | |
61 | ||
62 | if ((error = git_strmap_alloc(&entries->map)) < 0) | |
63 | git__free(entries); | |
64 | else | |
65 | *out = entries; | |
66 | ||
67 | return error; | |
68 | } | |
69 | ||
70 | int git_config_entries_dup(git_config_entries **out, git_config_entries *entries) | |
71 | { | |
72 | git_config_entries *result = NULL; | |
73 | config_entry_list *head; | |
74 | int error; | |
75 | ||
76 | if ((error = git_config_entries_new(&result)) < 0) | |
77 | goto out; | |
78 | ||
79 | for (head = entries->list; head; head = head->next) { | |
80 | git_config_entry *dup; | |
81 | ||
82 | dup = git__calloc(1, sizeof(git_config_entry)); | |
83 | dup->name = git__strdup(head->entry->name); | |
84 | GIT_ERROR_CHECK_ALLOC(dup->name); | |
85 | if (head->entry->value) { | |
86 | dup->value = git__strdup(head->entry->value); | |
87 | GIT_ERROR_CHECK_ALLOC(dup->value); | |
88 | } | |
89 | dup->level = head->entry->level; | |
90 | dup->include_depth = head->entry->include_depth; | |
91 | ||
92 | if ((error = git_config_entries_append(result, dup)) < 0) | |
93 | goto out; | |
94 | } | |
95 | ||
96 | *out = result; | |
97 | result = NULL; | |
98 | ||
99 | out: | |
100 | git_config_entries_free(result); | |
101 | return error; | |
102 | } | |
103 | ||
104 | void git_config_entries_incref(git_config_entries *entries) | |
105 | { | |
106 | GIT_REFCOUNT_INC(entries); | |
107 | } | |
108 | ||
109 | static void config_entries_free(git_config_entries *entries) | |
110 | { | |
111 | config_entry_list *list = NULL, *next; | |
112 | ||
113 | git_strmap_foreach_value(entries->map, list, config_entry_list_free(list)); | |
114 | git_strmap_free(entries->map); | |
115 | ||
116 | list = entries->list; | |
117 | while (list != NULL) { | |
118 | next = list->next; | |
119 | git__free(list); | |
120 | list = next; | |
121 | } | |
122 | ||
123 | git__free(entries); | |
124 | } | |
125 | ||
126 | void git_config_entries_free(git_config_entries *entries) | |
127 | { | |
128 | if (entries) | |
129 | GIT_REFCOUNT_DEC(entries, config_entries_free); | |
130 | } | |
131 | ||
132 | int git_config_entries_append(git_config_entries *entries, git_config_entry *entry) | |
133 | { | |
134 | config_entry_list *existing, *var; | |
135 | int error = 0; | |
136 | size_t pos; | |
137 | ||
138 | var = git__calloc(1, sizeof(config_entry_list)); | |
139 | GIT_ERROR_CHECK_ALLOC(var); | |
140 | var->entry = entry; | |
141 | ||
142 | pos = git_strmap_lookup_index(entries->map, entry->name); | |
143 | if (!git_strmap_valid_index(entries->map, pos)) { | |
144 | /* | |
145 | * We only ever inspect `last` from the first config | |
146 | * entry in a multivar. In case where this new entry is | |
147 | * the first one in the entry map, it will also be the | |
148 | * last one at the time of adding it, which is | |
149 | * why we set `last` here to itself. Otherwise we | |
150 | * do not have to set `last` and leave it set to | |
151 | * `NULL`. | |
152 | */ | |
153 | var->last = var; | |
154 | ||
155 | git_strmap_insert(entries->map, entry->name, var, &error); | |
156 | ||
157 | if (error > 0) | |
158 | error = 0; | |
159 | } else { | |
160 | existing = git_strmap_value_at(entries->map, pos); | |
161 | config_entry_list_append(&existing, var); | |
162 | } | |
163 | ||
164 | var = git__calloc(1, sizeof(config_entry_list)); | |
165 | GIT_ERROR_CHECK_ALLOC(var); | |
166 | var->entry = entry; | |
167 | config_entry_list_append(&entries->list, var); | |
168 | ||
169 | return error; | |
170 | } | |
171 | ||
172 | int config_entry_get(config_entry_list **out, git_config_entries *entries, const char *key) | |
173 | { | |
174 | size_t pos; | |
175 | ||
176 | pos = git_strmap_lookup_index(entries->map, key); | |
177 | ||
178 | /* no error message; the config system will write one */ | |
179 | if (!git_strmap_valid_index(entries->map, pos)) | |
180 | return GIT_ENOTFOUND; | |
181 | ||
182 | *out = git_strmap_value_at(entries->map, pos); | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key) | |
188 | { | |
189 | config_entry_list *entry; | |
190 | int error; | |
191 | ||
192 | if ((error = config_entry_get(&entry, entries, key)) < 0) | |
193 | return error; | |
194 | *out = entry->last->entry; | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key) | |
200 | { | |
201 | config_entry_list *entry; | |
202 | int error; | |
203 | ||
204 | if ((error = config_entry_get(&entry, entries, key)) < 0) | |
205 | return error; | |
206 | ||
207 | if (entry->next != NULL) { | |
208 | git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar"); | |
209 | return -1; | |
210 | } | |
211 | ||
212 | if (entry->entry->include_depth) { | |
213 | git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included"); | |
214 | return -1; | |
215 | } | |
216 | ||
217 | *out = entry->entry; | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | void config_iterator_free(git_config_iterator *iter) | |
223 | { | |
224 | config_entries_iterator *it = (config_entries_iterator *) iter; | |
225 | git_config_entries_free(it->entries); | |
226 | git__free(it); | |
227 | } | |
228 | ||
229 | int config_iterator_next( | |
230 | git_config_entry **entry, | |
231 | git_config_iterator *iter) | |
232 | { | |
233 | config_entries_iterator *it = (config_entries_iterator *) iter; | |
234 | ||
235 | if (!it->head) | |
236 | return GIT_ITEROVER; | |
237 | ||
238 | *entry = it->head->entry; | |
239 | it->head = it->head->next; | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
244 | int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries) | |
245 | { | |
246 | config_entries_iterator *it; | |
247 | ||
248 | it = git__calloc(1, sizeof(config_entries_iterator)); | |
249 | GIT_ERROR_CHECK_ALLOC(it); | |
250 | it->parent.next = config_iterator_next; | |
251 | it->parent.free = config_iterator_free; | |
252 | it->head = entries->list; | |
253 | it->entries = entries; | |
254 | ||
255 | git_config_entries_incref(entries); | |
256 | *out = &it->parent; | |
257 | ||
258 | return 0; | |
259 | } |