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