]> git.proxmox.com Git - libgit2.git/blob - src/config_entries.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / config_entries.c
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 {
17 git_config_entry *entry;
18 bool multivar;
19 } config_entry_map_head;
20
21 typedef struct config_entries_iterator {
22 git_config_iterator parent;
23 git_config_entries *entries;
24 config_entry_list *head;
25 } config_entries_iterator;
26
27 struct git_config_entries {
28 git_refcount rc;
29 git_strmap *map;
30 config_entry_list *list;
31 };
32
33 int git_config_entries_new(git_config_entries **out)
34 {
35 git_config_entries *entries;
36 int error;
37
38 entries = git__calloc(1, sizeof(git_config_entries));
39 GIT_ERROR_CHECK_ALLOC(entries);
40 GIT_REFCOUNT_INC(entries);
41
42 if ((error = git_strmap_new(&entries->map)) < 0)
43 git__free(entries);
44 else
45 *out = entries;
46
47 return error;
48 }
49
50 int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry)
51 {
52 git_config_entry *duplicated;
53 int error;
54
55 duplicated = git__calloc(1, sizeof(git_config_entry));
56 GIT_ERROR_CHECK_ALLOC(duplicated);
57
58 duplicated->name = git__strdup(entry->name);
59 GIT_ERROR_CHECK_ALLOC(duplicated->name);
60
61 if (entry->value) {
62 duplicated->value = git__strdup(entry->value);
63 GIT_ERROR_CHECK_ALLOC(duplicated->value);
64 }
65 duplicated->level = entry->level;
66 duplicated->include_depth = entry->include_depth;
67
68 if ((error = git_config_entries_append(entries, duplicated)) < 0)
69 goto out;
70
71 out:
72 if (error && duplicated) {
73 git__free((char *) duplicated->name);
74 git__free((char *) duplicated->value);
75 git__free(duplicated);
76 }
77 return error;
78 }
79
80 int git_config_entries_dup(git_config_entries **out, git_config_entries *entries)
81 {
82 git_config_entries *result = NULL;
83 config_entry_list *head;
84 int error;
85
86 if ((error = git_config_entries_new(&result)) < 0)
87 goto out;
88
89 for (head = entries->list; head; head = head->next)
90 if ((git_config_entries_dup_entry(result, head->entry)) < 0)
91 goto out;
92
93 *out = result;
94 result = NULL;
95
96 out:
97 git_config_entries_free(result);
98 return error;
99 }
100
101 void git_config_entries_incref(git_config_entries *entries)
102 {
103 GIT_REFCOUNT_INC(entries);
104 }
105
106 static void config_entries_free(git_config_entries *entries)
107 {
108 config_entry_list *list = NULL, *next;
109 config_entry_map_head *head;
110
111 git_strmap_foreach_value(entries->map, head,
112 git__free((char *) head->entry->name); git__free(head)
113 );
114 git_strmap_free(entries->map);
115
116 list = entries->list;
117 while (list != NULL) {
118 next = list->next;
119 git__free((char *) list->entry->value);
120 git__free(list->entry);
121 git__free(list);
122 list = next;
123 }
124
125 git__free(entries);
126 }
127
128 void git_config_entries_free(git_config_entries *entries)
129 {
130 if (entries)
131 GIT_REFCOUNT_DEC(entries, config_entries_free);
132 }
133
134 int git_config_entries_append(git_config_entries *entries, git_config_entry *entry)
135 {
136 config_entry_list *list_head;
137 config_entry_map_head *map_head;
138
139 if ((map_head = git_strmap_get(entries->map, entry->name)) != NULL) {
140 map_head->multivar = true;
141 /*
142 * This is a micro-optimization for configuration files
143 * with a lot of same keys. As for multivars the entry's
144 * key will be the same for all entries, we can just free
145 * all except the first entry's name and just re-use it.
146 */
147 git__free((char *) entry->name);
148 entry->name = map_head->entry->name;
149 } else {
150 map_head = git__calloc(1, sizeof(*map_head));
151 if ((git_strmap_set(entries->map, entry->name, map_head)) < 0)
152 return -1;
153 }
154 map_head->entry = entry;
155
156 list_head = git__calloc(1, sizeof(config_entry_list));
157 GIT_ERROR_CHECK_ALLOC(list_head);
158 list_head->entry = entry;
159
160 if (entries->list)
161 entries->list->last->next = list_head;
162 else
163 entries->list = list_head;
164 entries->list->last = list_head;
165
166 return 0;
167 }
168
169 int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key)
170 {
171 config_entry_map_head *entry;
172 if ((entry = git_strmap_get(entries->map, key)) == NULL)
173 return GIT_ENOTFOUND;
174 *out = entry->entry;
175 return 0;
176 }
177
178 int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key)
179 {
180 config_entry_map_head *entry;
181
182 if ((entry = git_strmap_get(entries->map, key)) == NULL)
183 return GIT_ENOTFOUND;
184
185 if (entry->multivar) {
186 git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
187 return -1;
188 }
189
190 if (entry->entry->include_depth) {
191 git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
192 return -1;
193 }
194
195 *out = entry->entry;
196
197 return 0;
198 }
199
200 static void config_iterator_free(git_config_iterator *iter)
201 {
202 config_entries_iterator *it = (config_entries_iterator *) iter;
203 git_config_entries_free(it->entries);
204 git__free(it);
205 }
206
207 static int config_iterator_next(
208 git_config_entry **entry,
209 git_config_iterator *iter)
210 {
211 config_entries_iterator *it = (config_entries_iterator *) iter;
212
213 if (!it->head)
214 return GIT_ITEROVER;
215
216 *entry = it->head->entry;
217 it->head = it->head->next;
218
219 return 0;
220 }
221
222 int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries)
223 {
224 config_entries_iterator *it;
225
226 it = git__calloc(1, sizeof(config_entries_iterator));
227 GIT_ERROR_CHECK_ALLOC(it);
228 it->parent.next = config_iterator_next;
229 it->parent.free = config_iterator_free;
230 it->head = entries->list;
231 it->entries = entries;
232
233 git_config_entries_incref(entries);
234 *out = &it->parent;
235
236 return 0;
237 }