pyldb: avoid segfault when adding an element with no name
[sfrench/samba-autobuild/.git] / lib / util / tiniparser.c
1 /*
2  * Trivial smb.conf parsing code
3  * iniparser compatibility layer.
4  *
5  * Copyright Jeremy Allison <jra@samba.org> 2014
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, and the entire permission notice in its entirety,
12  *    including the disclaimer of warranties.
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  * 3. The name of the author may not be used to endorse or promote
17  *    products derived from this software without specific prior
18  *    written permission.
19  *
20  * ALTERNATIVELY, this product may be distributed under the terms of
21  * the GNU Public License Version 3 or later, in which case the provisions
22  * of the GPL are required INSTEAD OF the above restrictions.  (This clause is
23  * necessary due to a potential bad interaction between the GPL and
24  * the restrictions contained in a BSD-style copyright.)
25  *
26  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
27  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36  * OF THE POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdbool.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <stddef.h>
46 #include "tini.h"
47 #include "tiniparser.h"
48
49 struct tiniparser_entry {
50         struct tiniparser_entry *next_entry;
51         char *key;
52         char *value;
53 };
54
55 struct tiniparser_section {
56         struct tiniparser_section *next_section;
57         struct tiniparser_entry *entry_list;
58         char section_name[];
59 };
60
61 struct tiniparser_dictionary {
62         struct tiniparser_section *section_list;
63 };
64
65 /*
66  * Find a section from a given key.
67  * Also return start of subkey.
68  * Return NULL if section name can't be found,
69  * if no section name given, or no subkey given.
70  */
71
72 static struct tiniparser_section *find_section(struct tiniparser_dictionary *d,
73                                         const char *key,
74                                         const char **subkey)
75 {
76         struct tiniparser_section *curr_section;
77         const char *p;
78         size_t section_len;
79
80         if (key == NULL) {
81                 return NULL;
82         }
83         p = strchr(key, ':');
84         if (p == NULL) {
85                 /* No section. */
86                 return NULL;
87         }
88
89         section_len = p - key;
90         /* Ensure we have at least one character of section name. */
91         if (section_len == 0) {
92                 return NULL;
93         }
94         /* Ensure we have at least one character of subkey. */
95         if (p[1] == '\0') {
96                 return NULL;
97         }
98
99         for (curr_section = d->section_list;
100                         curr_section;
101                         curr_section = curr_section->next_section) {
102                 /*
103                  * Check if the key section matches the
104                  * section name *exactly* (with terminating
105                  * null after section_len characters.
106                  */
107                 if ((strncasecmp(key, curr_section->section_name, section_len) == 0) &&
108                                 (curr_section->section_name[section_len] == '\0')) {
109                         *subkey = p+1;
110                         return curr_section;
111                 }
112         }
113         return NULL;
114 }
115
116 static struct tiniparser_entry *find_entry(struct tiniparser_section *section,
117                                         const char *key)
118 {
119         struct tiniparser_entry *curr_entry;
120
121         for (curr_entry = section->entry_list;
122                         curr_entry;
123                         curr_entry = curr_entry->next_entry) {
124                 if (strcasecmp(key,
125                                 curr_entry->key) == 0) {
126                         return curr_entry;
127                 }
128         }
129         return NULL;
130 }
131
132 const char *tiniparser_getstring(struct tiniparser_dictionary *d,
133                         const char *key,
134                         const char *default_value)
135 {
136         struct tiniparser_section *section;
137         struct tiniparser_entry *entry;
138         const char *subkey;
139
140         section = find_section(d, key, &subkey);
141         if (section == NULL) {
142                 return default_value;
143         }
144
145         entry = find_entry(section, subkey);
146         if (entry == NULL) {
147                 return default_value;
148         }
149
150         return entry->value;
151 }
152
153
154 bool tiniparser_getboolean(struct tiniparser_dictionary *d,
155                         const char *key,
156                         bool default_value)
157 {
158         const char *value = tiniparser_getstring(d, key, NULL);
159
160         if (value == NULL) {
161                 return default_value;
162         }
163
164         switch(value[0]) {
165                 case '1':
166                 case 'T':
167                 case 't':
168                 case 'y':
169                 case 'Y':
170                         return true;
171                 case '0':
172                 case 'F':
173                 case 'f':
174                 case 'n':
175                 case 'N':
176                         return false;
177                 default:
178                         break;
179         }
180
181         return default_value;
182 }
183
184 int tiniparser_getint(struct tiniparser_dictionary *d,
185                         const char *key,
186                         int default_value)
187 {
188         const char *value = tiniparser_getstring(d, key, NULL);
189
190         if (value == NULL) {
191                 return default_value;
192         }
193
194         return (int)strtol(value, NULL, 0);
195 }
196
197 static bool value_parser(const char *key,
198                         const char *value,
199                         void *private_data)
200 {
201         struct tiniparser_dictionary *d =
202                 (struct tiniparser_dictionary *)private_data;
203         struct tiniparser_section *section = d->section_list;
204         struct tiniparser_entry *entry = NULL;
205         size_t val_len;
206         size_t key_len;
207
208         if (section == NULL) {
209                 return false;
210         }
211         if (key == NULL) {
212                 return false;
213         }
214         if (value == NULL) {
215                 return false;
216         }
217
218         key_len = strlen(key) + 1;
219         val_len = strlen(value) + 1;
220
221         entry = find_entry(section, key);
222         if (entry) {
223                 /* Replace current value. */
224                 char *new_val = malloc(val_len);
225                 if (new_val == NULL) {
226                         return false;
227                 }
228                 memcpy(new_val, value, val_len);
229                 free(entry->value);
230                 entry->value = new_val;
231                 return true;
232         }
233
234         /* Create a new entry. */
235         entry = malloc(sizeof(struct tiniparser_entry));
236         if (entry == NULL) {
237                 return false;
238         }
239         entry->key = malloc(key_len);
240         if (entry->key == NULL) {
241                 free(entry);
242                 return false;
243         }
244         memcpy(entry->key, key, key_len);
245
246         entry->value = malloc(val_len);
247         if (entry->value == NULL) {
248                 free(entry->key);
249                 free(entry);
250                 return false;
251         }
252         memcpy(entry->value, value, val_len);
253
254         entry->next_entry = section->entry_list;
255         section->entry_list = entry;
256         return true;
257 }
258
259 static bool section_parser(const char *section_name,
260                         void *private_data)
261 {
262         struct tiniparser_section **pp_section;
263         struct tiniparser_section *new_section;
264         struct tiniparser_dictionary *d =
265                 (struct tiniparser_dictionary *)private_data;
266         size_t section_name_len;
267
268         if (section_name == NULL) {
269                 return false;
270         }
271
272         /* Section names can't contain ':' */
273         if (strchr(section_name, ':') != NULL) {
274                 return false;
275         }
276
277         /* Do we already have this section ? */
278         for (pp_section = &d->section_list;
279                         *pp_section;
280                         pp_section = &(*pp_section)->next_section) {
281                 if (strcasecmp(section_name,
282                                 (*pp_section)->section_name) == 0) {
283                         /*
284                          * Move to the front of the list for
285                          * value_parser() to find it.
286                          */
287
288                         /* First save current entry. */
289                         struct tiniparser_section *curr_section = *pp_section;
290
291                         /* Now unlink current entry from list. */
292                         *pp_section = curr_section->next_section;
293
294                         /* Make current entry next point to whole list. */
295                         curr_section->next_section = d->section_list;
296
297                         /* And replace list with current entry at start. */
298                         d->section_list = curr_section;
299
300                         return true;
301                 }
302         }
303
304         section_name_len = strlen(section_name) + 1;
305
306         /* Create new section. */
307         new_section = malloc(
308                 offsetof(struct tiniparser_section, section_name) +
309                 section_name_len);
310         if (new_section == NULL) {
311                 return false;
312         }
313
314         memcpy(new_section->section_name, section_name, section_name_len);
315
316         new_section->entry_list = NULL;
317
318         /* Add it to the head of the singly linked list. */
319         new_section->next_section = d->section_list;
320         d->section_list = new_section;
321         return true;
322 }
323
324 struct tiniparser_dictionary *tiniparser_load(const char *filename)
325 {
326         bool ret;
327         struct tiniparser_dictionary *d = NULL;
328         FILE *fp = fopen(filename, "r");
329
330         if (fp == NULL) {
331                 return NULL;
332         }
333
334         d = malloc(sizeof(struct tiniparser_dictionary));
335         if (d == NULL) {
336                 fclose(fp);
337                 return NULL;
338         }
339         d->section_list = NULL;
340
341         ret = tini_parse(fp,
342                         false,
343                         section_parser,
344                         value_parser,
345                         d);
346         fclose(fp);
347         if (ret == false) {
348                 tiniparser_freedict(d);
349                 d = NULL;
350         }
351         return d;
352 }
353
354 void tiniparser_freedict(struct tiniparser_dictionary *d)
355 {
356         struct tiniparser_section *curr_section, *next_section;
357
358         if (d == NULL) {
359                 return;
360         }
361
362         for (curr_section = d->section_list;
363                         curr_section;
364                         curr_section = next_section) {
365                 struct tiniparser_entry *curr_entry, *next_entry;
366
367                 next_section = curr_section->next_section;
368
369                 for (curr_entry = curr_section->entry_list;
370                                 curr_entry;
371                                 curr_entry = next_entry) {
372                         next_entry = curr_entry->next_entry;
373
374                         free(curr_entry->key);
375                         free(curr_entry->value);
376                         free(curr_entry);
377                 }
378                 free(curr_section);
379         }
380         free(d);
381 }