pyldb: avoid segfault when adding an element with no name
[nivanova/samba-autobuild/.git] / lib / util / tini.c
1 /*
2  * Trivial smb.conf parsing code
3  *
4  * Copyright Volker Lendecke <vl@samba.org> 2014
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, and the entire permission notice in its entirety,
11  *    including the disclaimer of warranties.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote
16  *    products derived from this software without specific prior
17  *    written permission.
18  *
19  * ALTERNATIVELY, this product may be distributed under the terms of
20  * the GNU Public License Version 3 or later, in which case the
21  * provisions of the GPL are required INSTEAD OF the above restrictions.
22  * (This clause is necessary due to a potential bad interaction between the
23  * GPL and the restrictions contained in a BSD-style copyright.)
24  *
25  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
26  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35  * OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdbool.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <string.h>
44 #include "tini.h"
45
46 static bool c_isspace(char c)
47 {
48         unsigned char uc = c;
49         if (c != uc) {
50                 return false;
51         }
52         return isspace(uc);
53 }
54
55 static int next_content(FILE *f)
56 {
57         int c;
58
59         for (c = fgetc(f); c != EOF; c = fgetc(f)) {
60                 if (!c_isspace(c)) {
61                         break;
62                 }
63                 if (c == '\n') {
64                         break;
65                 }
66         }
67
68         return c;
69 }
70
71 static int next_end_of_line(FILE *f)
72 {
73         int c;
74
75         for (c = fgetc(f); c != EOF; c = fgetc(f)) {
76                 if (c == '\n') {
77                         break;
78                 }
79         }
80         return c;
81 }
82
83 static bool make_space(char **buf, size_t *buflen, size_t position)
84 {
85         char *tmp;
86
87         if (position < *buflen) {
88                 return true;
89         }
90         tmp = realloc(*buf, (*buflen) * 2);
91         if (tmp == NULL) {
92                 return false;
93         }
94         *buf = tmp;
95         *buflen *= 2;
96         return true;
97 }
98
99 /*
100  * Get a conf line into *pbuf (which must be a malloc'ed buffer already).
101  *
102  * Ignores leading spaces
103  * Ignores comment lines
104  * Ignores empty lines
105  * Takes care of continuation lines
106  * Zaps multiple spaces into one
107  */
108
109 static int get_line(FILE *f, char **pbuf, size_t *pbuflen)
110 {
111         int c;
112         char *buf;
113         size_t buflen, pos;
114
115         buf = *pbuf;
116         buflen = *pbuflen;
117         pos = 0;
118
119 next_line:
120
121         c = next_content(f);
122         if (c == EOF) {
123                 return ENOENT;
124         }
125
126         if ((c == '#') || (c == ';')) {
127                 /*
128                  * Line starting with a comment, skip
129                  */
130                 c = next_end_of_line(f);
131                 if (c == EOF) {
132                         return ENOENT;
133                 }
134                 goto next_line;
135         }
136
137         if (c == '\n') {
138                 /*
139                  * Blank line, skip
140                  */
141                 goto next_line;
142         }
143
144         for ( ; c != EOF ; c = fgetc(f)) {
145
146                 if (c == '\n') {
147
148                         if ((pos > 0) && (buf[pos-1] == '\\')) {
149                                 /*
150                                  * Line ends in "\". Continuation.
151                                  */
152                                 pos -= 1;
153                                 continue;
154                         }
155
156                         if ((pos > 1) && (buf[pos-2] == '\\') &&
157                             c_isspace(buf[pos-1])) {
158                                 /*
159                                  * Line ends in "\ ". Mind that we zap
160                                  * multiple spaces into one. Continuation.
161                                  */
162                                 pos -= 2;
163                                 continue;
164                         }
165
166                         /*
167                          * No continuation, done with the line
168                          */
169                         break;
170                 }
171
172                 if ((pos > 0) && c_isspace(buf[pos-1]) && c_isspace(c)) {
173                         /*
174                          * Zap multiple spaces to one
175                          */
176                         continue;
177                 }
178
179                 if (!make_space(&buf, &buflen, pos)) {
180                         return ENOMEM;
181                 }
182                 buf[pos++] = c;
183         }
184
185         if (!make_space(&buf, &buflen, pos)) {
186                 return ENOMEM;
187         }
188         buf[pos++] = '\0';
189
190         *pbuf = buf;
191         return 0;
192 }
193
194 static bool parse_section(
195         char *buf, bool (*sfunc)(const char *section, void *private_data),
196         void *private_data)
197 {
198         char *p, *q;
199
200         p = buf+1;              /* skip [ */
201
202         q = strchr(p, ']');
203         if (q == NULL) {
204                 return false;
205         }
206         *q = '\0';
207
208         return sfunc(p, private_data);
209 }
210
211 static char *trim_one_space(char *buf)
212 {
213         size_t len;
214
215         if (c_isspace(buf[0])) {
216                 buf += 1;
217         }
218         len = strlen(buf);
219         if (len == 0) {
220                 return buf;
221         }
222         if (c_isspace(buf[len-1])) {
223                 buf[len-1] = '\0';
224         }
225
226         return buf;
227 }
228
229 static bool parse_param(char *buf,
230                         bool allow_empty_value,
231                         bool (*pfunc)(const char *name, const char *value,
232                                       void *private_data),
233                         void *private_data)
234 {
235         char *equals;
236         char *name;
237         const char *value;
238         size_t len;
239         bool no_value = false;
240
241         equals = strchr(buf, '=');
242         if (equals != NULL) {
243                 *equals = '\0';
244         } else {
245                 if (allow_empty_value) {
246                         no_value = true;
247                 } else {
248                         return true;
249                 }
250         }
251
252         name = trim_one_space(buf);
253         len = strlen(buf);
254         if (len == 0) {
255                 return false;
256         }
257
258         if (no_value) {
259                 value = "";
260         } else {
261                 value = trim_one_space(equals+1);
262         }
263
264         return pfunc(name, value, private_data);
265 }
266
267 bool tini_parse(FILE *f,
268                 bool allow_empty_value,
269                 bool (*sfunc)(const char *section, void *private_data),
270                 bool (*pfunc)(const char *name, const char *value,
271                               void *private_data),
272                 void *private_data)
273 {
274         char *buf;
275         size_t buflen;
276
277         buflen = 256;
278
279         buf = malloc(buflen);
280         if (buf == NULL) {
281                 return false;
282         }
283
284         while (true) {
285                 int ret;
286                 bool ok;
287
288                 ret = get_line(f, &buf, &buflen);
289
290                 if (ret == ENOENT) {
291                         /* No lines anymore */
292                         break;
293                 }
294
295                 if (ret != 0) {
296                         /* Real error */
297                         free(buf);
298                         return false;
299                 }
300
301                 switch(buf[0]) {
302                 case 0:
303                         continue;
304                         break;
305                 case '[':
306                         ok = parse_section(buf, sfunc, private_data);
307                         break;
308                 default:
309                         ok = parse_param(buf, allow_empty_value, pfunc, private_data);
310                         break;
311                 }
312
313                 if (!ok) {
314                         free(buf);
315                         return false;
316                 }
317         }
318         free(buf);
319         return true;
320 }