xref: /reactos/dll/win32/inseng/inf.c (revision 19b18ce2)
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 
55 static void inf_value_free(struct inf_value *value)
56 {
57     heap_free(value);
58 }
59 
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, &section->values, struct inf_value, entry)
64     {
65         list_remove(&val->entry);
66         inf_value_free(val);
67     }
68 
69     heap_free(section);
70 }
71 
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 
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 
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 
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 
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 
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 
214 char *inf_section_get_name(struct inf_section *section)
215 {
216     return strdupA(section->name);
217 }
218 
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 
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 
248 char *inf_value_get_key(struct inf_value *value)
249 {
250     return strdupA(value->key);
251 }
252 
253 char *inf_value_get_value(struct inf_value *value)
254 {
255     return expand_variables(value->section->file, value->value);
256 }
257 
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 
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. */
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 
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 
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, &section);
398         else if (strchr(line, '=') && section)
399             hr = inf_value_parse(section, line);
400     }
401 
402     return hr;
403 }
404 
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