Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / dll / win32 / inseng / inf.c
diff --git a/dll/win32/inseng/inf.c b/dll/win32/inseng/inf.c
new file mode 100644 (file)
index 0000000..b446283
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * 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, &section->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, &section);
+        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;
+}