1 /*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35
36 #include "overlay.h"
37
38 #define DEFAULT_SECTION "window"
39
skip_whitespace(const char * s,const char * end)40 static const char *skip_whitespace(const char *s, const char *end)
41 {
42 while (s < end && isspace(*s))
43 s++;
44 return s;
45 }
46
trim_whitespace(const char * s,const char * end)47 static const char *trim_whitespace(const char *s, const char *end)
48 {
49 if (end == NULL)
50 return end;
51
52 while (end > s && isspace(*--end))
53 ;
54
55 return end + 1;
56 }
57
is_eol(int c)58 static int is_eol(int c)
59 {
60 return c == '\n' || c == '\r';
61 }
62
skip_past_eol(const char * s,const char * end)63 static const char *skip_past_eol(const char *s, const char *end)
64 {
65 while (s < end && !is_eol(*s++))
66 ;
67
68 return s;
69 }
70
find(const char * s,const char * end,int c)71 static const char *find(const char *s, const char *end, int c)
72 {
73 while (s < end && *s != c) {
74 if (*s == '#')
75 break;
76
77 if (*s == '\n')
78 return NULL;
79 s++;
80 }
81
82 return c == '\n' ? s : s < end ? s : NULL;
83 }
84
parse(const char * buf,int len,int (* func)(const char * section,const char * name,const char * value,void * data),void * data)85 static int parse(const char *buf, int len,
86 int (*func)(const char *section,
87 const char *name,
88 const char *value,
89 void *data),
90 void *data)
91 {
92 char section[128] = DEFAULT_SECTION, name[128], value[128];
93 const char *buf_end = buf + len;
94 const char *end;
95 int has_section = 0;
96 int line;
97
98 for (line = 0 ; ++line; buf = skip_past_eol(buf, buf_end)) {
99 buf = skip_whitespace(buf, buf_end);
100 if (buf >= buf_end)
101 break;
102
103 if (*buf == ';' || *buf == '#') {
104 /* comment */
105 } else if (*buf == '[') { /* new section */
106 end = find(++buf, buf_end, ']');
107 if (end == NULL)
108 return line;
109
110 end = trim_whitespace(buf, end);
111 if (end <= buf)
112 continue;
113
114 len = end - buf;
115 if (len == 0 || len >= sizeof(section))
116 return line;
117
118 memcpy(section, buf, len);
119 section[len] = '\0';
120 has_section = 1;
121 } else { /* name = value */
122 const char *sep;
123 int has_value = 1;
124
125 sep = find(buf, buf_end, '=');
126 if (sep == NULL)
127 sep = find(buf, buf_end, ':');
128 if (sep == NULL) {
129 sep = find(buf, buf_end, '\n');
130 has_value = 0;
131 }
132 end = trim_whitespace(buf, sep);
133 if (end <= buf)
134 continue;
135
136 len = end - buf;
137 if (len == 0 || len >= sizeof(name))
138 return line;
139
140 memcpy(name, buf, len);
141 name[len] = '\0';
142
143 if (has_value) {
144 buf = skip_whitespace(sep + 1, buf_end);
145 end = find(buf, buf_end, '\n');
146 end = trim_whitespace(buf, end);
147
148 len = end - buf;
149 if (len >= sizeof(name))
150 return line;
151
152 memcpy(value, buf, len);
153 value[len] = '\0';
154 } else
155 *value = '\0';
156
157 if (!has_section) {
158 char *dot;
159
160 dot = strchr(name, '.');
161 if (dot && dot[1]) {
162 *dot = '\0';
163
164 if (!func(name, dot+1, value, data))
165 return line;
166
167 continue;
168 }
169 }
170
171 if (!func(section, name, value, data))
172 return line;
173 }
174 }
175
176 return 0;
177 }
178
add_value(const char * section,const char * name,const char * value,void * data)179 static int add_value(const char *section,
180 const char *name,
181 const char *value,
182 void *data)
183 {
184 struct config *c = data;
185 struct config_section *s;
186 struct config_value *v, **prev;
187
188 for (s = c->sections; s != NULL; s = s->next)
189 if (strcmp(s->name, section) == 0)
190 break;
191 if (s == NULL) {
192 int len = strlen(section) + 1;
193
194 s = malloc(sizeof(*s)+len);
195 if (s == NULL)
196 return 0;
197
198 memcpy(s->name, section, len);
199 s->values = NULL;
200 s->next = c->sections;
201 c->sections = s;
202 }
203
204 for (prev = &s->values; (v = *prev) != NULL; prev = &v->next) {
205 if (strcmp(v->name, name) == 0) {
206 *prev = v->next;
207 free(v);
208 break;
209 }
210 }
211 {
212 int name_len = strlen(name) + 1;
213 int value_len = strlen(value) + 1;
214
215 v = malloc(sizeof(*v) + name_len + value_len);
216 if (v == NULL)
217 return 0;
218
219 v->name = memcpy(v+1, name, name_len);
220 v->value = memcpy(v->name + name_len, value, value_len);
221
222 v->next = s->values;
223 s->values = v;
224 }
225
226 return 1;
227 }
228
config_init_from_file(struct config * config,const char * filename)229 static int config_init_from_file(struct config *config, const char *filename)
230 {
231 struct stat st;
232 int fd, err = -1;
233 char *str;
234
235 fd = open(filename, 0);
236 if (fd < 0)
237 return -1;
238
239 if (fstat(fd, &st) < 0)
240 goto err_fd;
241
242 if ((str = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (void *)-1)
243 goto err_fd;
244
245 err = parse(str, st.st_size, add_value, config);
246 munmap(str, st.st_size);
247
248 err_fd:
249 close(fd);
250 return err;
251 }
252
config_init(struct config * config)253 void config_init(struct config *config)
254 {
255 memset(config, 0, sizeof(*config));
256 }
257
config_parse_string(struct config * config,const char * str)258 void config_parse_string(struct config *config, const char *str)
259 {
260 int err;
261
262 if (str == NULL)
263 return;
264
265 err = config_init_from_file(config, str);
266 if (err == -1)
267 err = parse(str, strlen(str), add_value, config);
268 if (err) {
269 fprintf(stderr, "Failed to parse config string at line %d\n", err);
270 exit(1);
271 }
272 }
273
config_set_value(struct config * c,const char * section,const char * name,const char * value)274 void config_set_value(struct config *c,
275 const char *section,
276 const char *name,
277 const char *value)
278 {
279 add_value(section, name, value, c);
280 }
281
config_get_value(struct config * c,const char * section,const char * name)282 const char *config_get_value(struct config *c,
283 const char *section,
284 const char *name)
285 {
286 struct config_section *s;
287 struct config_value *v;
288
289 for (s = c->sections; s != NULL; s = s->next) {
290 if (strcmp(s->name, section))
291 continue;
292
293 for (v = s->values; v != NULL; v = v->next) {
294 if (strcmp(v->name, name))
295 continue;
296
297 return v->value;
298 }
299 }
300
301 return NULL;
302 }
303