1 /*
2 * Copyright 2016 Michael Müller
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <stdarg.h>
20 #include <string.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25
26 #include "inseng_private.h"
27
28 #include "wine/list.h"
29
30 struct inf_value
31 {
32 struct list entry;
33 char *key;
34 char *value;
35
36 struct inf_section *section;
37 };
38
39 struct inf_section
40 {
41 struct list entry;
42 char *name;
43 struct list values;
44
45 struct inf_file *file;
46 };
47
48 struct inf_file
49 {
50 char *content;
51 DWORD size;
52 struct list sections;
53 };
54
inf_value_free(struct inf_value * value)55 static void inf_value_free(struct inf_value *value)
56 {
57 heap_free(value);
58 }
59
inf_section_free(struct inf_section * section)60 static void inf_section_free(struct inf_section *section)
61 {
62 struct inf_value *val, *val_next;
63 LIST_FOR_EACH_ENTRY_SAFE(val, val_next, §ion->values, struct inf_value, entry)
64 {
65 list_remove(&val->entry);
66 inf_value_free(val);
67 }
68
69 heap_free(section);
70 }
71
get_substitution(struct inf_file * inf,const char * name,int len)72 static const char *get_substitution(struct inf_file *inf, const char *name, int len)
73 {
74 struct inf_section *sec;
75 struct inf_value *value = NULL;
76
77 sec = inf_get_section(inf, "Strings");
78 if (!sec) return NULL;
79
80 while (inf_section_next_value(sec, &value))
81 {
82 if (strlen(value->key) == len && !strncasecmp(value->key, name, len))
83 return value->value;
84 }
85
86 return NULL;
87 }
88
expand_variables_buffer(struct inf_file * inf,const char * str,char * output)89 static int expand_variables_buffer(struct inf_file *inf, const char *str, char *output)
90 {
91 const char *p, *var_start = NULL;
92 int var_len = 0, len = 0;
93 const char *substitution;
94
95 for (p = str; *p; p++)
96 {
97 if (*p != '%')
98 {
99 if (var_start)
100 var_len++;
101 else
102 {
103 if (output)
104 *output++ = *p;
105 len++;
106 }
107
108 continue;
109 }
110
111 if (!var_start)
112 {
113 var_start = p;
114 var_len = 0;
115
116 continue;
117 }
118
119 if (!var_len)
120 {
121 /* just an escaped % */
122 if (output)
123 *output++ = '%';
124 len += 1;
125
126 var_start = NULL;
127 continue;
128 }
129
130 substitution = get_substitution(inf, var_start + 1, var_len);
131 if (!substitution)
132 {
133 if (output)
134 {
135 memcpy(output, var_start, var_len + 2);
136 output += var_len + 2;
137 }
138 len += var_len + 2;
139 }
140 else
141 {
142 int sub_len = strlen(substitution);
143
144 if (output)
145 {
146 memcpy(output, substitution, sub_len);
147 output += sub_len;
148 }
149 len += sub_len;
150 }
151
152 var_start = NULL;
153 }
154
155 if (output) *output = 0;
156 return len + 1;
157 }
158
expand_variables(struct inf_file * inf,const char * str)159 static char *expand_variables(struct inf_file *inf, const char *str)
160 {
161 char *buffer;
162 int len;
163
164 len = expand_variables_buffer(inf, str, NULL);
165 buffer = heap_alloc(len);
166 if (!len) return NULL;
167
168 expand_variables_buffer(inf, str, buffer);
169 return buffer;
170 }
171
inf_free(struct inf_file * inf)172 void inf_free(struct inf_file *inf)
173 {
174 struct inf_section *sec, *sec_next;
175 LIST_FOR_EACH_ENTRY_SAFE(sec, sec_next, &inf->sections, struct inf_section, entry)
176 {
177 list_remove(&sec->entry);
178 inf_section_free(sec);
179 }
180
181 heap_free(inf->content);
182 heap_free(inf);
183 }
184
inf_next_section(struct inf_file * inf,struct inf_section ** sec)185 BOOL inf_next_section(struct inf_file *inf, struct inf_section **sec)
186 {
187 struct list *next_entry, *cur_position;
188
189 if (*sec)
190 cur_position = &(*sec)->entry;
191 else
192 cur_position = &inf->sections;
193
194 next_entry = list_next(&inf->sections, cur_position);
195 if (!next_entry) return FALSE;
196
197 *sec = CONTAINING_RECORD(next_entry, struct inf_section, entry);
198 return TRUE;
199 }
200
inf_get_section(struct inf_file * inf,const char * name)201 struct inf_section *inf_get_section(struct inf_file *inf, const char *name)
202 {
203 struct inf_section *sec = NULL;
204
205 while (inf_next_section(inf, &sec))
206 {
207 if (!strcasecmp(sec->name, name))
208 return sec;
209 }
210
211 return NULL;
212 }
213
inf_section_get_name(struct inf_section * section)214 char *inf_section_get_name(struct inf_section *section)
215 {
216 return strdupA(section->name);
217 }
218
inf_section_next_value(struct inf_section * sec,struct inf_value ** value)219 BOOL inf_section_next_value(struct inf_section *sec, struct inf_value **value)
220 {
221 struct list *next_entry, *cur_position;
222
223 if (*value)
224 cur_position = &(*value)->entry;
225 else
226 cur_position = &sec->values;
227
228 next_entry = list_next(&sec->values, cur_position);
229 if (!next_entry) return FALSE;
230
231 *value = CONTAINING_RECORD(next_entry, struct inf_value, entry);
232 return TRUE;
233 }
234
inf_get_value(struct inf_section * sec,const char * key)235 struct inf_value *inf_get_value(struct inf_section *sec, const char *key)
236 {
237 struct inf_value *value = NULL;
238
239 while (inf_section_next_value(sec, &value))
240 {
241 if (!strcasecmp(value->key, key))
242 return value;
243 }
244
245 return NULL;
246 }
247
inf_value_get_key(struct inf_value * value)248 char *inf_value_get_key(struct inf_value *value)
249 {
250 return strdupA(value->key);
251 }
252
inf_value_get_value(struct inf_value * value)253 char *inf_value_get_value(struct inf_value *value)
254 {
255 return expand_variables(value->section->file, value->value);
256 }
257
trim(char * str,char ** last_chr,BOOL strip_quotes)258 char *trim(char *str, char **last_chr, BOOL strip_quotes)
259 {
260 char *last;
261
262 for (; *str; str++)
263 {
264 if (*str != '\t' && *str != ' ')
265 break;
266 }
267
268 if (!*str)
269 {
270 if (last_chr) *last_chr = str;
271 return str;
272 }
273
274 last = str + strlen(str) - 1;
275
276 for (; last > str; last--)
277 {
278 if (*last != '\t' && *last != ' ')
279 break;
280 *last = 0;
281 }
282
283 if (strip_quotes && last != str)
284 {
285 if (*last == '"' && *str == '"')
286 {
287 str++;
288 *last = 0;
289 }
290 }
291
292 if (last_chr) *last_chr = last;
293 return str;
294 }
295
get_next_line(char ** str,char ** last_chr)296 static char *get_next_line(char **str, char **last_chr)
297 {
298 BOOL in_next_line = FALSE;
299 char *start, *next;
300
301 start = *str;
302 if (!start || !*start) return NULL;
303
304 for (next = start; *next; next++)
305 {
306 if (*next == '\n' || *next == '\r')
307 {
308 *next = 0;
309 in_next_line = TRUE;
310 }
311 else if (in_next_line)
312 {
313 break;
314 }
315 }
316
317 *str = next;
318 return trim(start, last_chr, FALSE);
319 }
320
321 /* This function only fails in case of an memory allocation error
322 * and does not touch section in case the parsing failed. */
inf_section_parse(struct inf_file * inf,char * line,char * last_chr,struct inf_section ** section)323 static HRESULT inf_section_parse(struct inf_file *inf, char *line, char *last_chr, struct inf_section **section)
324 {
325 struct inf_section *sec;
326 char *comment;
327 char *name;
328
329 if (*line != '[')
330 return S_OK;
331
332 line++;
333
334 comment = strchr(line, ';');
335 if (comment)
336 {
337 *comment = 0;
338 line = trim(line, &last_chr, FALSE);
339 }
340
341 if (*last_chr != ']')
342 return S_OK;
343
344 *last_chr = 0;
345 name = trim(line, NULL, FALSE);
346 if (!name) return S_OK;
347
348 sec = heap_alloc_zero(sizeof(*sec));
349 if (!sec) return E_OUTOFMEMORY;
350
351 sec->name = name;
352 sec->file = inf;
353 list_init(&sec->values);
354
355 list_add_tail(&inf->sections, &sec->entry);
356
357 *section = sec;
358 return S_OK;
359 }
360
inf_value_parse(struct inf_section * sec,char * line)361 static HRESULT inf_value_parse(struct inf_section *sec, char *line)
362 {
363 struct inf_value *key_val;
364 char *key, *value, *del;
365
366 del = strchr(line, '=');
367 if (!del) return S_OK;
368
369 *del = 0;
370 key = line;
371 value = del + 1;
372
373 key = trim(key, NULL, FALSE);
374 value = trim(value, NULL, TRUE);
375
376 key_val = heap_alloc_zero(sizeof(*key_val));
377 if (!key_val) return E_OUTOFMEMORY;
378
379 key_val->key = key;
380 key_val->value = value;
381 key_val->section = sec;
382
383 list_add_tail(&sec->values, &key_val->entry);
384 return S_OK;
385 }
386
inf_process_content(struct inf_file * inf)387 static HRESULT inf_process_content(struct inf_file *inf)
388 {
389 struct inf_section *section = NULL;
390 char *content = inf->content;
391 char *line, *last_chr;
392 HRESULT hr = S_OK;
393
394 while (SUCCEEDED(hr) && (line = get_next_line(&content, &last_chr)))
395 {
396 if (*line == '[')
397 hr = inf_section_parse(inf, line, last_chr, §ion);
398 else if (strchr(line, '=') && section)
399 hr = inf_value_parse(section, line);
400 }
401
402 return hr;
403 }
404
inf_load(const char * path,struct inf_file ** inf_file)405 HRESULT inf_load(const char *path, struct inf_file **inf_file)
406 {
407 LARGE_INTEGER file_size;
408 struct inf_file *inf;
409 HRESULT hr = E_FAIL;
410 HANDLE file;
411 DWORD read;
412
413 file = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
414 if (file == INVALID_HANDLE_VALUE) return E_FAIL;
415
416 inf = heap_alloc_zero(sizeof(*inf));
417 if (!inf) goto error;
418
419 if (!GetFileSizeEx(file, &file_size))
420 goto error;
421
422 inf->size = file_size.QuadPart;
423
424 inf->content = heap_alloc_zero(inf->size);
425 if (!inf->content) goto error;
426
427 list_init(&inf->sections);
428
429 if (!ReadFile(file, inf->content, inf->size, &read, NULL) || read != inf->size)
430 goto error;
431
432 hr = inf_process_content(inf);
433 if (FAILED(hr)) goto error;
434
435 CloseHandle(file);
436 *inf_file = inf;
437 return S_OK;
438
439 error:
440 if (inf) inf_free(inf);
441 CloseHandle(file);
442 return hr;
443 }
444