+++ /dev/null
-/*
- * Copyright 2016 Michael Müller
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "inseng_private.h"
-
-struct inf_value
-{
- struct list entry;
- char *key;
- char *value;
-
- struct inf_section *section;
-};
-
-struct inf_section
-{
- struct list entry;
- char *name;
- struct list values;
-
- struct inf_file *file;
-};
-
-struct inf_file
-{
- char *content;
- DWORD size;
- struct list sections;
-};
-
-static void inf_value_free(struct inf_value *value)
-{
- heap_free(value);
-}
-
-static void inf_section_free(struct inf_section *section)
-{
- struct inf_value *val, *val_next;
- LIST_FOR_EACH_ENTRY_SAFE(val, val_next, §ion->values, struct inf_value, entry)
- {
- list_remove(&val->entry);
- inf_value_free(val);
- }
-
- heap_free(section);
-}
-
-static const char *get_substitution(struct inf_file *inf, const char *name, int len)
-{
- struct inf_section *sec;
- struct inf_value *value = NULL;
-
- sec = inf_get_section(inf, "Strings");
- if (!sec) return NULL;
-
- while (inf_section_next_value(sec, &value))
- {
- if (strlen(value->key) == len && !strncasecmp(value->key, name, len))
- return value->value;
- }
-
- return NULL;
-}
-
-static int expand_variables_buffer(struct inf_file *inf, const char *str, char *output)
-{
- const char *p, *var_start = NULL;
- int var_len, len = 0;
- const char *substitution;
-
- for (p = str; *p; p++)
- {
- if (*p != '%')
- {
- if (var_start)
- var_len++;
- else
- {
- if (output)
- *output++ = *p;
- len++;
- }
-
- continue;
- }
-
- if (!var_start)
- {
- var_start = p;
- var_len = 0;
-
- continue;
- }
-
- if (!var_len)
- {
- /* just an escaped % */
- if (output)
- *output++ = '%';
- len += 1;
-
- var_start = NULL;
- continue;
- }
-
- substitution = get_substitution(inf, var_start + 1, var_len);
- if (!substitution)
- {
- if (output)
- {
- memcpy(output, var_start, var_len + 2);
- output += var_len + 2;
- }
- len += var_len + 2;
- }
- else
- {
- int sub_len = strlen(substitution);
-
- if (output)
- {
- memcpy(output, substitution, sub_len);
- output += sub_len;
- }
- len += sub_len;
- }
-
- var_start = NULL;
- }
-
- if (output) *output = 0;
- return len + 1;
-}
-
-static char *expand_variables(struct inf_file *inf, const char *str)
-{
- char *buffer;
- int len;
-
- len = expand_variables_buffer(inf, str, NULL);
- buffer = heap_alloc(len);
- if (!len) return NULL;
-
- expand_variables_buffer(inf, str, buffer);
- return buffer;
-}
-
-void inf_free(struct inf_file *inf)
-{
- struct inf_section *sec, *sec_next;
- LIST_FOR_EACH_ENTRY_SAFE(sec, sec_next, &inf->sections, struct inf_section, entry)
- {
- list_remove(&sec->entry);
- inf_section_free(sec);
- }
-
- heap_free(inf->content);
- heap_free(inf);
-}
-
-BOOL inf_next_section(struct inf_file *inf, struct inf_section **sec)
-{
- struct list *next_entry, *cur_position;
-
- if (*sec)
- cur_position = &(*sec)->entry;
- else
- cur_position = &inf->sections;
-
- next_entry = list_next(&inf->sections, cur_position);
- if (!next_entry) return FALSE;
-
- *sec = CONTAINING_RECORD(next_entry, struct inf_section, entry);
- return TRUE;
-}
-
-struct inf_section *inf_get_section(struct inf_file *inf, const char *name)
-{
- struct inf_section *sec = NULL;
-
- while (inf_next_section(inf, &sec))
- {
- if (!strcasecmp(sec->name, name))
- return sec;
- }
-
- return NULL;
-}
-
-char *inf_section_get_name(struct inf_section *section)
-{
- return strdupA(section->name);
-}
-
-BOOL inf_section_next_value(struct inf_section *sec, struct inf_value **value)
-{
- struct list *next_entry, *cur_position;
-
- if (*value)
- cur_position = &(*value)->entry;
- else
- cur_position = &sec->values;
-
- next_entry = list_next(&sec->values, cur_position);
- if (!next_entry) return FALSE;
-
- *value = CONTAINING_RECORD(next_entry, struct inf_value, entry);
- return TRUE;
-}
-
-struct inf_value *inf_get_value(struct inf_section *sec, const char *key)
-{
- struct inf_value *value = NULL;
-
- while (inf_section_next_value(sec, &value))
- {
- if (!strcasecmp(value->key, key))
- return value;
- }
-
- return NULL;
-}
-
-char *inf_value_get_key(struct inf_value *value)
-{
- return strdupA(value->key);
-}
-
-char *inf_value_get_value(struct inf_value *value)
-{
- return expand_variables(value->section->file, value->value);
-}
-
-char *trim(char *str, char **last_chr, BOOL strip_quotes)
-{
- char *last;
-
- for (; *str; str++)
- {
- if (*str != '\t' && *str != ' ')
- break;
- }
-
- if (!*str)
- {
- if (last_chr) *last_chr = str;
- return str;
- }
-
- last = str + strlen(str) - 1;
-
- for (; last > str; last--)
- {
- if (*last != '\t' && *last != ' ')
- break;
- *last = 0;
- }
-
- if (strip_quotes && last != str)
- {
- if (*last == '"' && *str == '"')
- {
- str++;
- *last = 0;
- }
- }
-
- if (last_chr) *last_chr = last;
- return str;
-}
-
-static char *get_next_line(char **str, char **last_chr)
-{
- BOOL in_next_line = FALSE;
- char *start, *next;
-
- start = *str;
- if (!start || !*start) return NULL;
-
- for (next = start; *next; next++)
- {
- if (*next == '\n' || *next == '\r')
- {
- *next = 0;
- in_next_line = TRUE;
- }
- else if (in_next_line)
- {
- break;
- }
- }
-
- *str = next;
- return trim(start, last_chr, FALSE);
-}
-
-/* This function only fails in case of an memory allocation error
- * and does not touch section in case the parsing failed. */
-static HRESULT inf_section_parse(struct inf_file *inf, char *line, char *last_chr, struct inf_section **section)
-{
- struct inf_section *sec;
- char *comment;
- char *name;
-
- if (*line != '[')
- return S_OK;
-
- line++;
-
- comment = strchr(line, ';');
- if (comment)
- {
- *comment = 0;
- line = trim(line, &last_chr, FALSE);
- }
-
- if (*last_chr != ']')
- return S_OK;
-
- *last_chr = 0;
- name = trim(line, NULL, FALSE);
- if (!name) return S_OK;
-
- sec = heap_zero_alloc(sizeof(*sec));
- if (!sec) return E_OUTOFMEMORY;
-
- sec->name = name;
- sec->file = inf;
- list_init(&sec->values);
-
- list_add_tail(&inf->sections, &sec->entry);
-
- *section = sec;
- return S_OK;
-}
-
-static HRESULT inf_value_parse(struct inf_section *sec, char *line)
-{
- struct inf_value *key_val;
- char *key, *value, *del;
-
- del = strchr(line, '=');
- if (!del) return S_OK;
-
- *del = 0;
- key = line;
- value = del + 1;
-
- key = trim(key, NULL, FALSE);
- value = trim(value, NULL, TRUE);
-
- key_val = heap_zero_alloc(sizeof(*key_val));
- if (!key_val) return E_OUTOFMEMORY;
-
- key_val->key = key;
- key_val->value = value;
- key_val->section = sec;
-
- list_add_tail(&sec->values, &key_val->entry);
- return S_OK;
-}
-
-static HRESULT inf_process_content(struct inf_file *inf)
-{
- struct inf_section *section = NULL;
- char *content = inf->content;
- char *line, *last_chr;
- HRESULT hr = S_OK;
-
- while (SUCCEEDED(hr) && (line = get_next_line(&content, &last_chr)))
- {
- if (*line == '[')
- hr = inf_section_parse(inf, line, last_chr, §ion);
- else if (strchr(line, '=') && section)
- hr = inf_value_parse(section, line);
- }
-
- return hr;
-}
-
-HRESULT inf_load(const char *path, struct inf_file **inf_file)
-{
- LARGE_INTEGER file_size;
- struct inf_file *inf;
- HRESULT hr = E_FAIL;
- HANDLE file;
- DWORD read;
-
- file = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (file == INVALID_HANDLE_VALUE) return E_FAIL;
-
- inf = heap_zero_alloc(sizeof(*inf));
- if (!inf) goto error;
-
- if (!GetFileSizeEx(file, &file_size))
- goto error;
-
- inf->size = file_size.QuadPart;
-
- inf->content = heap_zero_alloc(inf->size);
- if (!inf->content) goto error;
-
- list_init(&inf->sections);
-
- if (!ReadFile(file, inf->content, inf->size, &read, NULL) || read != inf->size)
- goto error;
-
- hr = inf_process_content(inf);
- if (FAILED(hr)) goto error;
-
- CloseHandle(file);
- *inf_file = inf;
- return S_OK;
-
-error:
- if (inf) inf_free(inf);
- CloseHandle(file);
- return hr;
-}