1 /*
2  * uhub - A tiny ADC p2p connection hub
3  * Copyright (C) 2007-2014, Jan Vidar Krey
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "uhub.h"
21 
22 #define ADD_CHAR(X) do { *out = X; out++; token_size++; } while(0)
23 #define RESET_TOKEN do { ADD_CHAR('\0'); out = buffer; if (cfg_token_add(tokens, out)) token_count++; token_size = 0; buffer[0] = '\0'; } while (0)
24 
25 struct cfg_tokens
26 {
27     struct linked_list* list;
28 };
29 
cfg_tokenize(const char * line)30 struct cfg_tokens* cfg_tokenize(const char* line)
31 {
32 	struct cfg_tokens* tokens = (struct cfg_tokens*) hub_malloc_zero(sizeof(struct cfg_tokens));
33 	char* buffer = (char*) hub_malloc_zero(strlen(line) + 1);
34 	char* out = buffer;
35 	const char* p = line;
36 	int backslash = 0;
37 	char quote = 0;
38 	size_t token_count = 0;
39 	size_t token_size = 0;
40 	tokens->list = list_create();
41 
42 	for (; *p; p++)
43 	{
44 		switch (*p)
45 		{
46 			case '\\':
47 				if (backslash)
48 				{
49 					ADD_CHAR('\\');
50 					backslash = 0;
51 				}
52 				else
53 				{
54 					backslash = 1;
55 				}
56 				break;
57 
58 			case '#':
59 				if (backslash)
60 				{
61 					ADD_CHAR('#');
62 					backslash = 0;
63 				}
64 				else if (quote)
65 				{
66 					ADD_CHAR('#');
67 				}
68 				else
69 				{
70 					RESET_TOKEN;
71 					hub_free(buffer);
72 					return tokens;
73 				}
74 				break;
75 
76 			case '"':
77 				if (backslash)
78 				{
79 					ADD_CHAR('"');
80 					backslash = 0;
81 				}
82 				else if (quote)
83 				{
84 					quote = 0;
85 				}
86 				else
87 				{
88 					quote = 1;
89 				}
90 				break;
91 
92 			case '\r':
93 				/* Pretend it does not exist! */
94 				break;
95 
96 			case ' ':
97 			case '\t':
98 				if (quote)
99 				{
100 					ADD_CHAR(*p);
101 				}
102 				else if (backslash)
103 				{
104 					ADD_CHAR(*p);
105 					backslash = 0;
106 				}
107 				else
108 				{
109 					RESET_TOKEN;
110 				}
111 				break;
112 
113 			default:
114 				ADD_CHAR(*p);
115 		}
116 	}
117 
118 	RESET_TOKEN;
119 	hub_free(buffer);
120 	return tokens;
121 }
122 
cfg_tokens_free(struct cfg_tokens * tokens)123 void cfg_tokens_free(struct cfg_tokens* tokens)
124 {
125 	if (tokens)
126 	{
127 		list_clear(tokens->list, hub_free);
128 		list_destroy(tokens->list);
129 		hub_free(tokens);
130 	}
131 }
132 
cfg_token_add(struct cfg_tokens * tokens,char * new_token)133 int cfg_token_add(struct cfg_tokens* tokens, char* new_token)
134 {
135 	if (*new_token)
136 	{
137 		list_append(tokens->list, hub_strdup(new_token));
138 		return 1;
139 	}
140 	return 0;
141 }
142 
cfg_token_count(struct cfg_tokens * tokens)143 size_t cfg_token_count(struct cfg_tokens* tokens)
144 {
145 	return list_size(tokens->list);
146 }
147 
cfg_token_get(struct cfg_tokens * tokens,size_t offset)148 char* cfg_token_get(struct cfg_tokens* tokens, size_t offset)
149 {
150 	return list_get_index(tokens->list, offset);
151 }
152 
cfg_token_get_first(struct cfg_tokens * tokens)153 char* cfg_token_get_first(struct cfg_tokens* tokens)
154 {
155 	return list_get_first(tokens->list);
156 }
157 
cfg_token_get_next(struct cfg_tokens * tokens)158 char* cfg_token_get_next(struct cfg_tokens* tokens)
159 {
160 	return list_get_next(tokens->list);
161 }
162 
163 struct cfg_settings
164 {
165 	char* key;
166 	char* value;
167 };
168 
cfg_settings_split(const char * line)169 struct cfg_settings* cfg_settings_split(const char* line)
170 {
171 	struct cfg_settings* s = NULL;
172 	struct cfg_tokens* tok = NULL;
173 	char* pos = NULL;
174 
175 	if (   !line
176 		|| !*line
177 		|| ((pos = (char*) strchr(line, '=')) == NULL)
178 		|| ((s = hub_malloc_zero(sizeof(struct cfg_settings))) == NULL)
179 		|| ((tok = cfg_tokenize(line)) == NULL)
180 		|| (cfg_token_count(tok) < 1)
181 		|| (cfg_token_count(tok) > 3)
182 		|| (cfg_token_count(tok) == 3 && strcmp(cfg_token_get(tok, 1), "="))
183 		)
184 	{
185 		cfg_tokens_free(tok);
186 		cfg_settings_free(s);
187 		return NULL;
188 	}
189 
190 	if (cfg_token_count(tok) == 1)
191 	{
192 		char* key = cfg_token_get_first(tok);
193 		pos = strchr(key, '=');
194 		if (!pos)
195 		{
196 			cfg_tokens_free(tok);
197 			cfg_settings_free(s);
198 			return NULL;
199 		}
200 
201 		pos[0] = 0;
202 		key = strip_white_space(key);
203 
204 		if (!*key)
205 		{
206 			cfg_tokens_free(tok);
207 			cfg_settings_free(s);
208 			return NULL;
209 		}
210 
211 		s->key = strdup(key);
212 		s->value = strdup(strip_white_space(pos+1));
213 	}
214 	else if (cfg_token_count(tok) == 2)
215 	{
216 		char* key = cfg_token_get_first(tok);
217 		char* val = cfg_token_get_next(tok);
218 
219                 if ((pos = strchr(key, '=')))
220 		{
221 			pos[0] = 0;
222 			key = strip_white_space(key);
223 		}
224 		else if ((pos = strchr(val, '=')))
225 		{
226 			val = strip_white_space(pos+1);
227 		}
228 		else
229 		{
230 			cfg_tokens_free(tok);
231 			cfg_settings_free(s);
232 			return NULL;
233 		}
234 
235 		if (!*key)
236 		{
237 			cfg_tokens_free(tok);
238 			cfg_settings_free(s);
239 			return NULL;
240 		}
241 
242 		s->key = strdup(key);
243 		s->value = strdup(val);
244 	}
245 	else
246 	{
247 		s->key = strdup(strip_white_space(cfg_token_get(tok, 0)));
248 		s->value = strdup(strip_white_space(cfg_token_get(tok, 2)));
249 	}
250 	cfg_tokens_free(tok);
251 	return s;
252 }
253 
cfg_settings_get_key(struct cfg_settings * s)254 const char* cfg_settings_get_key(struct cfg_settings* s)
255 {
256 	return s->key;
257 }
258 
cfg_settings_get_value(struct cfg_settings * s)259 const char* cfg_settings_get_value(struct cfg_settings* s)
260 {
261 	return s->value;
262 }
263 
cfg_settings_free(struct cfg_settings * s)264 void cfg_settings_free(struct cfg_settings* s)
265 {
266 	if (s)
267 	{
268 		hub_free(s->key);
269 		hub_free(s->value);
270 		hub_free(s);
271 	}
272 }
273 
274