summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libappstream-builder/asb-self-test.c4
-rw-r--r--libappstream-builder/plugins/asb-plugin-firmware.c25
-rw-r--r--libappstream-glib/Makefile.am5
-rw-r--r--libappstream-glib/appstream-glib.h1
-rw-r--r--libappstream-glib/as-inf.c912
-rw-r--r--libappstream-glib/as-inf.h80
-rw-r--r--libappstream-glib/as-self-test.c319
-rw-r--r--libappstream-glib/as-utils.c165
-rw-r--r--libappstream-glib/as-utils.h5
9 files changed, 1283 insertions, 233 deletions
diff --git a/libappstream-builder/asb-self-test.c b/libappstream-builder/asb-self-test.c
index 5bd54fd..30a5f9b 100644
--- a/libappstream-builder/asb-self-test.c
+++ b/libappstream-builder/asb-self-test.c
@@ -415,7 +415,7 @@ asb_test_context_test_func (AsbTestContextMode mode)
"<id>84f40464-9272-4ef7-9399-cd95f12da696</id>\n"
"<name>ColorHug Firmware</name>\n"
"<summary>Firmware for the ColorHug Colorimeter</summary>\n"
- "<developer_name>Hughski Limited</developer_name>\n"
+ "<developer_name>Hughski</developer_name>\n"
"<description><p>Updating the firmware on your ColorHug device "
"improves performance and adds new features.</p></description>\n"
"<icon type=\"stock\">application-x-executable</icon>\n"
@@ -989,7 +989,7 @@ asb_test_firmware_func (void)
"<id>84f40464-9272-4ef7-9399-cd95f12da696</id>\n"
"<name>ColorHug Firmware</name>\n"
"<summary>Firmware for the ColorHug Colorimeter</summary>\n"
- "<developer_name>Hughski Limited</developer_name>\n"
+ "<developer_name>Hughski</developer_name>\n"
"<description><p>Updating the firmware on your ColorHug device "
"improves performance and adds new features.</p></description>\n"
"<icon type=\"stock\">application-x-executable</icon>\n"
diff --git a/libappstream-builder/plugins/asb-plugin-firmware.c b/libappstream-builder/plugins/asb-plugin-firmware.c
index 2bb72e2..20916f8 100644
--- a/libappstream-builder/plugins/asb-plugin-firmware.c
+++ b/libappstream-builder/plugins/asb-plugin-firmware.c
@@ -129,7 +129,6 @@ asb_plugin_process_filename (AsbPlugin *plugin,
_cleanup_free_ gchar *checksum = NULL;
_cleanup_free_ gchar *class = NULL;
_cleanup_free_ gchar *comment = NULL;
- _cleanup_free_ gchar *driver_ver = NULL;
_cleanup_free_ gchar *filename_full = NULL;
_cleanup_free_ gchar *id_new = NULL;
_cleanup_free_ gchar *id = NULL;
@@ -147,8 +146,8 @@ asb_plugin_process_filename (AsbPlugin *plugin,
/* fix up the inf file */
filename_full = g_build_filename (tmpdir, filename, NULL);
- kf = as_utils_load_inf_file (filename_full, error);
- if (kf == NULL)
+ kf = g_key_file_new ();
+ if (!as_inf_load_file (kf, filename_full, AS_INF_LOAD_FLAG_NONE, error))
return FALSE;
/* parse */
@@ -184,7 +183,9 @@ asb_plugin_process_filename (AsbPlugin *plugin,
as_app_set_id_kind (AS_APP (app), AS_ID_KIND_FIRMWARE);
/* add localizable strings */
- vendor = g_key_file_get_string (kf, "Strings", "MfgName", NULL);
+ vendor = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ if (vendor == NULL)
+ vendor = g_key_file_get_string (kf, "Version", "MfgName", NULL);
if (vendor == NULL)
vendor = g_key_file_get_string (kf, "Strings", "Provider", NULL);
if (vendor != NULL)
@@ -192,22 +193,14 @@ asb_plugin_process_filename (AsbPlugin *plugin,
name = g_key_file_get_string (kf, "Strings", "FirmwareDesc", NULL);
if (name != NULL)
as_app_set_name (AS_APP (app), NULL, name, -1);
- comment = g_key_file_get_string (kf, "Strings", "DiskName", NULL);
+ comment = g_key_file_get_string (kf, "SourceDisksNames", "1", NULL);
+ if (comment == NULL)
+ comment = g_key_file_get_string (kf, "Strings", "DiskName", NULL);
if (comment != NULL)
as_app_set_comment (AS_APP (app), NULL, comment, -1);
- /* get the release date and the version in case there's no metainfo */
- driver_ver = g_key_file_get_string (kf, "Version", "DriverVer", NULL);
- if (driver_ver == NULL) {
- g_set_error_literal (error,
- ASB_PLUGIN_ERROR,
- ASB_PLUGIN_ERROR_NOT_SUPPORTED,
- "DriverVer is missing");
- return FALSE;
- }
-
/* parse the DriverVer */
- version = as_utils_parse_driver_version (driver_ver, &timestamp, error);
+ version = as_inf_get_driver_version (kf, &timestamp, error);
if (version == NULL)
return FALSE;
diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am
index 3a97292..179fa55 100644
--- a/libappstream-glib/Makefile.am
+++ b/libappstream-glib/Makefile.am
@@ -72,6 +72,7 @@ libappstream_glib_include_HEADERS = \
as-enums.h \
as-icon.h \
as-image.h \
+ as-inf.h \
as-node.h \
as-problem.h \
as-provide.h \
@@ -94,6 +95,8 @@ libappstream_glib_la_SOURCES = \
as-icon-private.h \
as-image.c \
as-image-private.h \
+ as-inf.c \
+ as-inf.h \
as-node.c \
as-node-private.h \
as-problem.c \
@@ -165,6 +168,8 @@ introspection_sources = \
as-icon.h \
as-image.c \
as-image.h \
+ as-inf.c \
+ as-inf.h \
as-node.c \
as-node.h \
as-problem.c \
diff --git a/libappstream-glib/appstream-glib.h b/libappstream-glib/appstream-glib.h
index 9d79a72..abc1b1c 100644
--- a/libappstream-glib/appstream-glib.h
+++ b/libappstream-glib/appstream-glib.h
@@ -29,6 +29,7 @@
#include <as-enums.h>
#include <as-icon.h>
#include <as-image.h>
+#include <as-inf.h>
#include <as-node.h>
#include <as-problem.h>
#include <as-provide.h>
diff --git a/libappstream-glib/as-inf.c b/libappstream-glib/as-inf.c
new file mode 100644
index 0000000..3390307
--- /dev/null
+++ b/libappstream-glib/as-inf.c
@@ -0,0 +1,912 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:as-inf
+ * @short_description: Helper functions for parsing .inf files
+ * @include: appstream-glib.h
+ * @stability: Stable
+ *
+ * These functions are used internally to libappstream-glib, and some may be
+ * useful to user-applications.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "as-cleanup.h"
+#include "as-inf.h"
+
+/**
+ * as_inf_error_quark:
+ *
+ * Return value: An error quark.
+ *
+ * Since: 0.3.7
+ **/
+GQuark
+as_inf_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("AsInfError");
+ return quark;
+}
+
+typedef struct {
+ GKeyFile *keyfile;
+ GHashTable *dict;
+ gboolean last_line_continuation;
+ gboolean last_line_continuation_ignore;
+ gboolean require_2nd_pass;
+ gchar *group;
+ gchar *last_key;
+ gchar *comment;
+ gchar *comment_group;
+ guint nokey_idx;
+ AsInfLoadFlags flags;
+} AsInfHelper;
+
+/**
+ * as_inf_make_case_insensitive:
+ */
+static gchar *
+as_inf_make_case_insensitive (AsInfHelper *helper, const gchar *text)
+{
+ if (helper->flags & AS_INF_LOAD_FLAG_CASE_INSENSITIVE)
+ return g_ascii_strdown (text, -1);
+ return g_strdup (text);
+}
+
+/**
+ * as_inf_replace_variable:
+ *
+ * Replaces any instance of %val% with the 'val' key value found in @dict.
+ */
+static gchar *
+as_inf_replace_variable (AsInfHelper *helper, const gchar *line, GError **error)
+{
+ GString *new;
+ const gchar *tmp;
+ guint i;
+ _cleanup_strv_free_ gchar **split = NULL;
+
+ /* split up into sections of the delimiter */
+ new = g_string_sized_new (strlen (line));
+ split = g_strsplit (line, "%", -1);
+ for (i = 0; split[i] != NULL; i++) {
+ _cleanup_free_ gchar *lower = NULL;
+
+ /* the text between the substitutions */
+ if (i % 2 == 0) {
+ g_string_append (new, split[i]);
+ continue;
+ }
+
+ /* replace or ignore things not (yet) in the dictionary */
+ lower = as_inf_make_case_insensitive (helper, split[i]);
+ tmp = g_hash_table_lookup (helper->dict, lower);
+ if (tmp == NULL) {
+ if (helper->flags & AS_INF_LOAD_FLAG_STRICT) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "No replacement for '%s' in [Strings]",
+ lower);
+ g_string_free (new, TRUE);
+ return NULL;
+ }
+ g_string_append_printf (new, "%%%s%%", split[i]);
+ continue;
+ }
+ g_string_append (new, tmp);
+ }
+ return g_string_free (new, FALSE);
+}
+
+/**
+ * as_inf_get_dict:
+ *
+ * Puts all the strings in [Strings] into a hash table.
+ */
+static GHashTable *
+as_inf_get_dict (AsInfHelper *helper, GError **error)
+{
+ GHashTable *dict = NULL;
+ gchar *val;
+ guint i;
+ _cleanup_free_ gchar *lower = NULL;
+ _cleanup_hashtable_unref_ GHashTable *dict_tmp = NULL;
+ _cleanup_strv_free_ gchar **keys = NULL;
+
+ dict_tmp = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ lower = as_inf_make_case_insensitive (helper, "Strings");
+ keys = g_key_file_get_keys (helper->keyfile, lower, NULL, NULL);
+ for (i = 0; keys != NULL && keys[i] != NULL; i++) {
+ val = g_key_file_get_string (helper->keyfile, lower, keys[i], error);
+ if (val == NULL)
+ goto out;
+ g_hash_table_insert (dict_tmp, g_strdup (keys[i]), val);
+ }
+ dict = g_hash_table_ref (dict_tmp);
+out:
+ return dict;
+}
+
+/**
+ * as_inf_replace_variables:
+ *
+ * Replaces any key file value with it's substitution from [Strings]
+ */
+static gboolean
+as_inf_replace_variables (AsInfHelper *helper, GError **error)
+{
+ guint i;
+ guint j;
+ _cleanup_strv_free_ gchar **groups = NULL;
+
+ groups = g_key_file_get_groups (helper->keyfile, NULL);
+ for (i = 0; groups[i] != NULL; i++) {
+ _cleanup_strv_free_ gchar **keys = NULL;
+
+ /* ignore the source */
+ if (g_strcmp0 (groups[i], "Strings") == 0)
+ continue;
+
+ /* fix up keys */
+ keys = g_key_file_get_keys (helper->keyfile, groups[i], NULL, NULL);
+ if (keys == NULL)
+ continue;
+ for (j = 0; keys[j] != NULL; j++) {
+ _cleanup_free_ gchar *data_old = NULL;
+ _cleanup_free_ gchar *data_new = NULL;
+
+ /* get the old data for this [group] key */
+ data_old = g_key_file_get_string (helper->keyfile,
+ groups[i],
+ keys[j],
+ NULL);
+ if (data_old == NULL || data_old[0] == '\0')
+ continue;
+
+ /* optimise a little */
+ if (g_strstr_len (data_old, -1, "%") == NULL)
+ continue;
+
+ /* do string replacement */
+ data_new = as_inf_replace_variable (helper,
+ data_old,
+ error);
+ if (data_new == NULL)
+ return FALSE;
+ if (g_strcmp0 (data_old, data_new) == 0)
+ continue;
+ g_key_file_set_string (helper->keyfile,
+ groups[i],
+ keys[j],
+ data_new);
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * as_inf_set_last_key:
+ */
+static void
+as_inf_set_last_key (AsInfHelper *helper, const gchar *key)
+{
+ /* same value */
+ if (key == helper->last_key)
+ return;
+ g_free (helper->last_key);
+ helper->last_key = g_strdup (key);
+}
+
+/**
+ * as_inf_set_comment:
+ */
+static void
+as_inf_set_comment (AsInfHelper *helper, const gchar *comment)
+{
+ /* same value */
+ if (comment == helper->comment)
+ return;
+
+ g_free (helper->comment);
+ helper->comment = g_strdup (comment);
+ if (helper->comment != NULL)
+ g_strchug (helper->comment);
+}
+
+/**
+ * as_inf_strcheckchars:
+ */
+static gboolean
+as_inf_strcheckchars (const gchar *str, const gchar *chars)
+{
+ guint i;
+ guint j;
+ for (i = 0; str[i] != '\0'; i++) {
+ for (j = 0; chars[j] != '\0'; j++) {
+ if (str[i] == chars[j])
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * as_inf_strip_value:
+ *
+ * Strips off any comments and quotes from the .inf key value.
+ */
+static gboolean
+as_inf_strip_value (AsInfHelper *helper, gchar *value, GError **error)
+{
+ gchar *comment;
+ gchar *last_quote;
+
+ /* trivial case; no quotes */
+ if (value[0] != '"') {
+ comment = g_strrstr (value + 1, ";");
+ if (comment != NULL) {
+ *comment = '\0';
+ if (helper->comment == NULL)
+ as_inf_set_comment (helper, comment + 1);
+ g_strchomp (value + 1);
+ }
+ return TRUE;
+ }
+
+ /* is there only one quote? */
+ last_quote = g_strrstr (value, "\"");
+ if (last_quote == value) {
+ if (helper->flags & AS_INF_LOAD_FLAG_STRICT) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "mismatched quotes %s", value);
+ return FALSE;
+ }
+ last_quote = NULL;
+ }
+
+ /* shift the string down */
+ memmove (value, value + 1, strlen (value));
+
+ /* there's a comment past the last quote */
+ comment = g_strrstr (value, ";");
+ if (comment != NULL && comment > last_quote) {
+ *comment = '\0';
+ if (helper->comment == NULL)
+ as_inf_set_comment (helper, comment + 1);
+ g_strchomp (value);
+ }
+
+ /* remove the last quote */
+ if (last_quote != NULL)
+ *(last_quote-1) = '\0';
+
+ return TRUE;
+}
+
+/**
+ * as_inf_set_group:
+ */
+static gboolean
+as_inf_set_group (AsInfHelper *helper, const gchar *group, GError **error)
+{
+ /* same value */
+ if (group == helper->group)
+ return TRUE;
+
+ /* maximum permitted length is 255 chars */
+ if (strlen (group) > 255) {
+ if (helper->flags & AS_INF_LOAD_FLAG_STRICT) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "section name is too long: %s",
+ group);
+ return FALSE;
+ }
+ }
+
+ /* save new value */
+ g_free (helper->group);
+ helper->group = as_inf_make_case_insensitive (helper, group);
+
+ /* section names can contain double quotes */
+ if (!as_inf_strip_value (helper, helper->group, error))
+ return FALSE;
+
+ /* does this section name have any banned chars */
+ if (helper->flags & AS_INF_LOAD_FLAG_STRICT) {
+ if (g_str_has_prefix (helper->group, " ")) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "section name '%s' has leading spaces",
+ helper->group);
+ return FALSE;
+ }
+ if (g_str_has_suffix (helper->group, " ")) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "section name '%s' has trailing spaces",
+ helper->group);
+ return FALSE;
+ }
+ if (as_inf_strcheckchars (helper->group, "\t\n\r;\"")) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "section name '%s' has invalid chars",
+ helper->group);
+ return FALSE;
+ }
+ } else {
+ g_strstrip (helper->group);
+ g_strdelimit (helper->group, "\t\n\r;\"", '_');
+ }
+
+ /* start with new values */
+ helper->last_line_continuation_ignore = FALSE;
+ helper->nokey_idx = 0;
+
+ /* no longer valid */
+ as_inf_set_last_key (helper, NULL);
+
+ /* FIXME: refusing to add a comment to a group that does not exist
+ * is a GLib bug! */
+ if (helper->comment != NULL) {
+ g_key_file_set_comment (helper->keyfile,
+ helper->group,
+ NULL,
+ helper->comment,
+ NULL);
+ as_inf_set_comment (helper, NULL);
+ }
+ return TRUE;
+}
+
+/**
+ * as_inf_set_key:
+ */
+static void
+as_inf_set_key (AsInfHelper *helper, const gchar *key, const gchar *value)
+{
+ helper->last_line_continuation_ignore = FALSE;
+ g_key_file_set_string (helper->keyfile, helper->group, key, value);
+ if (helper->comment != NULL) {
+ g_key_file_set_comment (helper->keyfile,
+ helper->group,
+ key,
+ helper->comment,
+ NULL);
+ as_inf_set_comment (helper, NULL);
+ }
+ as_inf_set_last_key (helper, key);
+}
+
+/**
+ * as_inf_convert_key:
+ */
+static void
+as_inf_convert_key (gchar *key)
+{
+ guint i;
+ for (i = 0; key[i] != '\0'; i++) {
+ if (g_ascii_isalnum (key[i]))
+ continue;
+ if (key[i] == '.' || key[i] == '%')
+ continue;
+ key[i] = '_';
+ }
+}
+
+/**
+ * as_inf_repair_utf8:
+ */
+static gboolean
+as_inf_repair_utf8 (AsInfHelper *helper, gchar *line, GError **error)
+{
+ guint i;
+ guint8 val;
+
+ /* Microsoft keeps using 0x99 as UTF-8 (R), and it's not valid */
+ for (i = 0; line[i] != '\0'; i++) {
+ val = (guint8) line[i];
+ if (val == 0xAE || val == 0x99) {
+ if (helper->flags & AS_INF_LOAD_FLAG_STRICT) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "Invalid UTF-8: 0x%02x", val);
+ return FALSE;
+
+ }
+ line[i] = '?';
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * as_inf_parse_line:
+ */
+static gboolean
+as_inf_parse_line (AsInfHelper *helper, gchar *line, GError **error)
+{
+ gchar *kvsplit;
+ gchar *comment;
+ gchar *tmp;
+ guint len;
+ gboolean ret = TRUE;
+ gboolean continuation = FALSE;
+ _cleanup_free_ gchar *key = NULL;
+
+ /* line too long */
+ if ((helper->flags & AS_INF_LOAD_FLAG_STRICT) > 0 &&
+ strlen (line) > 4096) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "line too long: %s", line);
+ goto out;
+ }
+
+ /* fix up invalid UTF-8 */
+ if (!as_inf_repair_utf8 (helper, line, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* remove leading and trailing whitespace */
+ g_strstrip (line);
+ if (line[0] == '\0')
+ goto out;
+
+ /* remove comments */
+ if (line[0] == ';') {
+ as_inf_set_comment (helper, line + 1);
+ goto out;
+ }
+
+ /* ignore pragmas */
+ if (g_str_has_prefix (line, "#pragma"))
+ goto out;
+ if (g_str_has_prefix (line, "#define"))
+ goto out;
+
+ /* [group] */
+ if (line[0] == '[') {
+
+ /* remove comments */
+ comment = g_strstr_len (line, -1, ";");
+ if (comment != NULL) {
+ *comment = '\0';
+ as_inf_set_comment (helper, comment + 1);
+ g_strchomp (line);
+ }
+
+ /* find group end */
+ tmp = g_strrstr (line, "]");
+ if (tmp == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "[ but no ] : '%s'", line);
+ goto out;
+ }
+ *tmp = '\0';
+ ret = as_inf_set_group (helper, line + 1, error);
+ goto out;
+ }
+
+ /* last char continuation */
+ len = strlen (line);
+ if (line[len-1] == '\\') {
+ line[len-1] = '\0';
+ continuation = TRUE;
+ }
+
+ /* is a multiline key value */
+ if (line[len-1] == '|') {
+ line[len-1] = '\0';
+ continuation = TRUE;
+ }
+
+ /* if the key-value split is before the first quote */
+ kvsplit = g_strstr_len (line, -1, "=");
+ tmp = g_strstr_len (line, -1, "\"");
+ if ((tmp == NULL && kvsplit != NULL) ||
+ (kvsplit != NULL && kvsplit < tmp)) {
+ _cleanup_free_ gchar *key_new = NULL;
+
+ /* key=value before [group] */
+ if (helper->group == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "key not in group: '%s'", line);
+ goto out;
+ }
+
+ *kvsplit = '\0';
+ key = as_inf_make_case_insensitive (helper, line);
+ g_strchomp (key);
+
+ /* remove invalid chars as GKeyFile is more strict than .ini */
+ as_inf_convert_key (key);
+
+ /* convert key names with variables */
+ if (key[0] == '%') {
+ _cleanup_free_ gchar *key_tmp = NULL;
+ if (helper->dict == NULL) {
+ helper->require_2nd_pass = TRUE;
+ helper->last_line_continuation_ignore = TRUE;
+ goto out;
+ }
+ key_tmp = as_inf_replace_variable (helper, key, error);
+ if (key_tmp == NULL)
+ return FALSE;
+ if (key_tmp[0] == '%') {
+ key_new = g_strdup (key + 1);
+ g_strdelimit (key_new, "%", '\0');
+ g_debug ("No replacement for '%s' in [Strings] "
+ "using '%s'", key, key_new);
+ goto out;
+ }
+
+ /* this has to be lowercase */
+ key_new = as_inf_make_case_insensitive (helper, key_tmp);
+ as_inf_convert_key (key_new);
+ } else {
+ key_new = g_strdup (key);
+ }
+
+ /* remove leading and trailing quote */
+ g_strchug (kvsplit + 1);
+ if (!as_inf_strip_value (helper, kvsplit + 1, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* add to keyfile */
+ as_inf_set_key (helper, key_new, kvsplit + 1);
+ goto out;
+ }
+
+ /* last_line_continuation from the last line */
+ if (helper->last_line_continuation) {
+ _cleanup_free_ gchar *old = NULL;
+ _cleanup_free_ gchar *new = NULL;
+
+ /* this is the 1st pass, and we have no key */
+ if (helper->last_line_continuation_ignore)
+ goto out;
+
+ /* eek, no key at all */
+ if (helper->last_key == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "continued last but no key for %s", line);
+ goto out;
+ }
+
+ /* get the old value of this */
+ old = g_key_file_get_string (helper->keyfile,
+ helper->group,
+ helper->last_key,
+ NULL);
+ if (old == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "can't continue [%s] '%s': %s",
+ helper->group, helper->last_key, line);
+ goto out;
+ }
+
+ /* remove leading and trailing quote */
+ if (!as_inf_strip_value (helper, line, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* re-set key with new value */
+ new = g_strdup_printf ("%s%s", old, line);
+ as_inf_set_key (helper, helper->last_key, new);
+ goto out;
+ }
+
+ /* there was left-over data but with no group defined */
+ if (helper->group == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_FAILED,
+ "fake key %s without [group]", line);
+ goto out;
+ }
+
+ /* remove leading and trailing quote */
+ if (!as_inf_strip_value (helper, line, error)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* add fake key */
+ key = g_strdup_printf ("value%03i", helper->nokey_idx++);
+ as_inf_set_key (helper, key, line);
+out:
+ helper->last_line_continuation = continuation;
+ return ret;
+}
+
+/**
+ * as_inf_helper_new:
+ */
+static AsInfHelper *
+as_inf_helper_new (void)
+{
+ AsInfHelper *helper;
+ helper = g_new0 (AsInfHelper, 1);
+ return helper;
+}
+
+/**
+ * as_inf_helper_free:
+ */
+static void
+as_inf_helper_free (AsInfHelper *helper)
+{
+ if (helper->dict != NULL)
+ g_hash_table_unref (helper->dict);
+ g_key_file_unref (helper->keyfile);
+ g_free (helper->comment);
+ g_free (helper->group);
+ g_free (helper->last_key);
+ g_free (helper);
+}
+
+/**
+ * as_inf_load_data:
+ * @keyfile: a #GKeyFile
+ * @data: the .inf file date to parse
+ * @flags: #AsInfLoadFlags, e.g. %AS_INF_LOAD_FLAG_NONE
+ * @error: A #GError or %NULL
+ *
+ * Repairs .inf file data and opens it as a keyfile.
+ *
+ * Important: The group and keynames are all forced to lower case as INF files
+ * are specified as case insensitve and GKeyFile *is* case sensitive.
+ * Any backslashes or spaces in the key name are also converted to '_'.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.5
+ */
+gboolean
+as_inf_load_data (GKeyFile *keyfile,
+ const gchar *data,
+ AsInfLoadFlags flags,
+ GError **error)
+{
+ AsInfHelper *helper;
+ gboolean ret = TRUE;
+ guint i;
+ _cleanup_strv_free_ gchar **lines = NULL;
+
+ /* initialize helper */
+ helper = as_inf_helper_new ();
+ helper->flags = flags;
+ helper->keyfile = g_key_file_ref (keyfile);
+
+ /* verify each line, and make sane */
+ lines = g_strsplit (data, "\n", -1);
+ for (i = 0; lines[i] != NULL; i++) {
+ if (!as_inf_parse_line (helper, lines[i], error)) {
+ g_prefix_error (error,
+ "Failed to parse line %i: ",
+ i + 1);
+ ret = FALSE;
+ goto out;
+ }
+ }
+
+ /* process every key in each group for strings we can substitute */
+ helper->dict = as_inf_get_dict (helper, error);
+ if (helper->dict == NULL) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* lets do this all over again */
+ if (helper->require_2nd_pass) {
+ _cleanup_strv_free_ gchar **lines2 = NULL;
+ lines2 = g_strsplit (data, "\n", -1);
+ for (i = 0; lines2[i] != NULL; i++) {
+ if (!as_inf_parse_line (helper, lines2[i], error)) {
+ g_prefix_error (error,
+ "Failed to parse line %i: ",
+ i + 1);
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+ /* replace key values */
+ if (!as_inf_replace_variables (helper, error)) {
+ ret = FALSE;
+ goto out;
+ }
+out:
+ as_inf_helper_free (helper);
+ return ret;
+}
+
+typedef struct {
+ const gchar *id;
+ const gchar *name;
+ guint id_length;
+} AsInfBOM;
+
+/**
+ * as_inf_load_file:
+ * @keyfile: a #GKeyFile
+ * @filename: the .inf file to open
+ * @flags: #AsInfLoadFlags, e.g. %AS_INF_LOAD_FLAG_NONE
+ * @error: A #GError or %NULL
+ *
+ * Repairs an .inf file and opens it as a keyfile.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.3.5
+ */
+gboolean
+as_inf_load_file (GKeyFile *keyfile,
+ const gchar *filename,
+ AsInfLoadFlags flags,
+ GError **error)
+{
+ const gchar *data_no_bom;
+ gsize len;
+ guint i;
+ _cleanup_free_ gchar *data = NULL;
+ AsInfBOM boms[] = { { "\x00\x00\xfe\xff", "UTF-32BE", 4 },
+ { "\xff\xfe\x00\x00", "UTF-32LE", 4 },
+ { "\xfe\xff", "UTF-16BE", 2 },
+ { "\xff\xfe", "UTF-16LE", 2 },
+ { "\xef\xbb\xbf", "UTF-8", 3 },
+ { NULL, NULL, 0 } };
+
+ if (!g_file_get_contents (filename, &data, &len, error))
+ return FALSE;
+
+ /* detect BOM */
+ data_no_bom = data;
+ for (i = 0; boms[i].id != NULL; i++) {
+
+ /* BOM matches */
+ if (len < boms[i].id_length)
+ continue;
+ if (memcmp (data, boms[i].id, boms[i].id_length) != 0)
+ continue;
+
+ /* ignore UTF-8 BOM */
+ if (g_strcmp0 (boms[i].name, "UTF-8") == 0) {
+ data_no_bom += boms[i].id_length;
+ break;
+ }
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE,
+ "File is encoded with %s and not supported",
+ boms[i].name);
+ return FALSE;
+ }
+
+ /* load data stripped of BOM */
+ return as_inf_load_data (keyfile, data_no_bom, flags, error);
+}
+
+/**
+ * as_inf_get_driver_version:
+ * @keyfile: a #GKeyFile
+ * @timestamp: the returned driverver timestamp, or %NULL
+ * @error: A #GError or %NULL
+ *
+ * Parses the DriverVer string into a recognisable version and timestamp;
+ *
+ * Returns: the version string, or %NULL for error.
+ *
+ * Since: 0.3.5
+ */
+gchar *
+as_inf_get_driver_version (GKeyFile *keyfile, guint64 *timestamp, GError **error)
+{
+ _cleanup_date_time_unref_ GDateTime *dt = NULL;
+ _cleanup_strv_free_ gchar **split = NULL;
+ _cleanup_strv_free_ gchar **dv_split = NULL;
+ _cleanup_free_ gchar *driver_ver = NULL;
+
+ /* get the release date and the version in case there's no metainfo */
+ driver_ver = g_key_file_get_string (keyfile, "Version", "DriverVer", NULL);
+ if (driver_ver == NULL) {
+ g_set_error_literal (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE,
+ "DriverVer is missing");
+ return FALSE;
+ }
+
+ /* split into driver date and version */
+ dv_split = g_strsplit (driver_ver, ",", -1);
+ if (g_strv_length (dv_split) != 2) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE,
+ "DriverVer is invalid: %s", driver_ver);
+ return NULL;
+ }
+
+ /* split up into MM/DD/YYYY, because America */
+ if (timestamp != NULL) {
+ split = g_strsplit (dv_split[0], "/", -1);
+ if (g_strv_length (split) != 3) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE,
+ "DriverVer date invalid: %s",
+ dv_split[0]);
+ return NULL;
+ }
+ dt = g_date_time_new_local (atoi (split[2]),
+ atoi (split[0]),
+ atoi (split[1]),
+ 0, 0, 0);
+ if (dt == NULL) {
+ g_set_error (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE,
+ "DriverVer date invalid: %s",
+ dv_split[0]);
+ return NULL;
+ }
+ *timestamp = g_date_time_to_unix (dt);
+ }
+ return g_strdup (dv_split[1]);
+}
diff --git a/libappstream-glib/as-inf.h b/libappstream-glib/as-inf.h
new file mode 100644
index 0000000..3c92453
--- /dev/null
+++ b/libappstream-glib/as-inf.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__APPSTREAM_GLIB_H) && !defined (AS_COMPILATION)
+#error "Only <appstream-glib.h> can be included directly."
+#endif
+
+#ifndef __AS_INF_H
+#define __AS_INF_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * AsInfError:
+ * @AS_INF_ERROR_FAILED: Generic failure
+ * @AS_INF_ERROR_INVALID_TYPE: Invalid type
+ *
+ * The error type.
+ **/
+typedef enum {
+ AS_INF_ERROR_FAILED,
+ AS_INF_ERROR_INVALID_TYPE,
+ /*< private >*/
+ AS_INF_ERROR_LAST
+} AsInfError;
+
+#define AS_INF_ERROR as_inf_error_quark ()
+
+/**
+ * AsInfLoadFlags:
+ * @AS_INF_LOAD_FLAG_NONE: No flags set
+ * @AS_INF_LOAD_FLAG_STRICT: Be strict when loading the file
+ * @AS_INF_LOAD_FLAG_CASE_INSENSITIVE: Load keys and groups case insensitive
+ *
+ * The flags used when loading INF files.
+ **/
+typedef enum {
+ AS_INF_LOAD_FLAG_NONE = 0,
+ AS_INF_LOAD_FLAG_STRICT = 1 << 0,
+ AS_INF_LOAD_FLAG_CASE_INSENSITIVE = 1 << 1,
+ /*< private >*/
+ AS_INF_LOAD_FLAG_LAST
+} AsInfLoadFlags;
+
+GQuark as_inf_error_quark (void);
+gboolean as_inf_load_data (GKeyFile *keyfile,
+ const gchar *data,
+ AsInfLoadFlags flags,
+ GError **error);
+gboolean as_inf_load_file (GKeyFile *keyfile,
+ const gchar *filename,
+ AsInfLoadFlags flags,
+ GError **error);
+gchar *as_inf_get_driver_version (GKeyFile *keyfile,
+ guint64 *timestamp,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __AS_INF_H */
diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c
index 9822f8d..6e8718e 100644
--- a/libappstream-glib/as-self-test.c
+++ b/libappstream-glib/as-self-test.c
@@ -30,6 +30,7 @@
#include "as-enums.h"
#include "as-icon-private.h"
#include "as-image-private.h"
+#include "as-inf.h"
#include "as-node-private.h"
#include "as-problem.h"
#include "as-provide-private.h"
@@ -3297,33 +3298,6 @@ as_test_store_speed_yaml_func (void)
}
static void
-as_test_utils_driver_ver_func (void)
-{
- gchar *tmp;
- guint64 ts;
- GError *error = NULL;
-
- /* valid */
- tmp = as_utils_parse_driver_version ("03/01/2015,2.0.0", &ts, &error);
- g_assert_no_error (error);
- g_assert_cmpstr (tmp, ==, "2.0.0");
- g_assert_cmpint (ts, ==, 1425168000);
- g_free (tmp);
-
- /* invalid date */
- tmp = as_utils_parse_driver_version ("13/01/2015,2.0.0", &ts, &error);
- g_assert_error (error, AS_UTILS_ERROR, AS_UTILS_ERROR_INVALID_TYPE);
- g_assert_cmpstr (tmp, ==, NULL);
- g_clear_error (&error);
-
- /* no date */
- tmp = as_utils_parse_driver_version ("2.0.0", NULL, &error);
- g_assert_error (error, AS_UTILS_ERROR, AS_UTILS_ERROR_INVALID_TYPE);
- g_assert_cmpstr (tmp, ==, NULL);
- g_clear_error (&error);
-}
-
-static void
as_test_utils_vercmp_func (void)
{
/* same */
@@ -3349,35 +3323,291 @@ as_test_utils_vercmp_func (void)
}
static void
-as_test_utils_inf_func (void)
+as_test_inf_func (void)
{
GError *error = NULL;
+ GKeyFile *kf;
+ const gchar *data;
+ gboolean ret;
+ gchar *tmp;
+ guint64 ts;
+ _cleanup_dir_close_ GDir *dir = NULL;
_cleanup_free_ gchar *filename = NULL;
- _cleanup_free_ gchar *test1 = NULL;
- _cleanup_free_ gchar *test2 = NULL;
- _cleanup_free_ gchar *test3 = NULL;
- _cleanup_keyfile_unref_ GKeyFile *kf= NULL;
+ _cleanup_free_ gchar *infs = NULL;
- /* load example */
+ /* case insensitive */
+ kf = g_key_file_new ();
+ ret = as_inf_load_data (kf, "[Version]\nClass=Firmware",
+ AS_INF_LOAD_FLAG_CASE_INSENSITIVE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "version", "class", NULL);
+ g_assert_cmpstr (tmp, ==, "Firmware");
+ g_free (tmp);
+
+ /* section merging */
+ ret = as_inf_load_data (kf, "[Version]\nPnpLockdown=1", 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "version", "class", NULL);
+ g_assert_cmpstr (tmp, ==, "Firmware");
+ g_free (tmp);
+ tmp = g_key_file_get_string (kf, "Version", "PnpLockdown", NULL);
+ g_assert_cmpstr (tmp, ==, "1");
+ g_free (tmp);
+
+ /* dequoting */
+ ret = as_inf_load_data (kf, "[Version]\n"
+ "PnpLockdown = \" 2 \"\n"
+ "Empty = \"\"", 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "PnpLockdown", NULL);
+ g_assert_cmpstr (tmp, ==, " 2 ");
+ g_free (tmp);
+ tmp = g_key_file_get_string (kf, "Version", "Empty", NULL);
+ g_assert_cmpstr (tmp, ==, "");
+ g_free (tmp);
+
+ /* autovalues */
+ ret = as_inf_load_data (kf, "[RandomList]\nfilename.cap", 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "RandomList", "value000", NULL);
+ g_assert_cmpstr (tmp, ==, "filename.cap");
+ g_free (tmp);
+
+ /* autovalues with quotes */
+ ret = as_inf_load_data (kf, "[RandomList2]\n\"SomeRandomValue=1\"", 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "RandomList2", "value000", NULL);
+ g_assert_cmpstr (tmp, ==, "SomeRandomValue=1");
+ g_free (tmp);
+
+ /* comments */
+ data = "; group priority\n"
+ "[Metadata] ; similar to [Version]\n"
+ "Vendor=Hughski ; vendor name\n"
+ "Owner=\"Richard; Ania\"\n"
+ "; device name priority\n"
+ "Device=\"ColorHug\" ; device name";
+ ret = as_inf_load_data (kf, data, 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Metadata", "Vendor", NULL);
+ g_assert_cmpstr (tmp, ==, "Hughski");
+ g_free (tmp);
+ tmp = g_key_file_get_string (kf, "Metadata", "Device", NULL);
+ g_assert_cmpstr (tmp, ==, "ColorHug");
+ g_free (tmp);
+ tmp = g_key_file_get_string (kf, "Metadata", "Owner", NULL);
+ g_assert_cmpstr (tmp, ==, "Richard; Ania");
+ g_free (tmp);
+
+ /* comment values */
+ tmp = g_key_file_get_comment (kf, "Metadata", "Vendor", NULL);
+ g_assert_cmpstr (tmp, ==, "vendor name\n");
+ g_free (tmp);
+ tmp = g_key_file_get_comment (kf, "Metadata", "Device", NULL);
+ g_assert_cmpstr (tmp, ==, "device name priority\n");
+ g_free (tmp);
+ tmp = g_key_file_get_comment (kf, "Metadata", "Owner", NULL);
+ g_assert_cmpstr (tmp, ==, NULL);
+ g_free (tmp);
+// tmp = g_key_file_get_comment (kf, "metadata", NULL, NULL);
+// g_assert_cmpstr (tmp, ==, "group priority");
+// g_free (tmp);
+
+ /* strings substitution */
+ data = "[Version]\n"
+ "Provider=Vendor was %Provider% now %Unknown% Ltd\n"
+ "[Strings]\n"
+ "Provider=Hughski\n";
+ ret = as_inf_load_data (kf, data, 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "Vendor was Hughski now %Unknown% Ltd");
+ g_free (tmp);
+
+ /* continued lines */
+ data = "[Version]\n"
+ "Provider=Richard & \\\n"
+ "Ania & \\\n"
+ "Friends";
+ ret = as_inf_load_data (kf, data, 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "Richard & Ania & Friends");
+ g_free (tmp);
+
+ /* multiline value */
+ data = "[Metadata]\n"
+ "UpdateDescription = \"This is a very \"long\" update \" |\n"
+ " \"description designed to be shown \" |\n"
+ " \"to the end user.\"";
+ ret = as_inf_load_data (kf, data, 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Metadata", "UpdateDescription", NULL);
+ g_assert_cmpstr (tmp, ==, "This is a very \"long\" update description "
+ "designed to be shown to the end user.");
+ g_free (tmp);
+
+ /* substitution as the key name */
+ data = "[Version]\n"
+ "%Manufacturer.Foo% = \"Devices\"\n"
+ "[Strings]\n"
+ "Manufacturer.Foo = \"Hughski\"\n";
+ ret = as_inf_load_data (kf, data, 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Hughski", NULL);
+ g_assert_cmpstr (tmp, ==, "Devices");
+ g_free (tmp);
+
+ /* key names with double quotes */
+ ret = as_inf_load_data (kf, "[\"Firmware\"]\nFoo=Bar\n", 0, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Firmware", "Foo", NULL);
+ g_assert_cmpstr (tmp, ==, "Bar");
+ g_free (tmp);
+
+ /* unbalanced quotes */
+ ret = as_inf_load_data (kf, "[Version]\nProvider = \"Hughski\n",
+ AS_INF_LOAD_FLAG_STRICT, &error);
+ g_assert_error (error, AS_INF_ERROR, AS_INF_ERROR_FAILED);
+ g_assert (!ret);
+ g_clear_error (&error);
+ ret = as_inf_load_data (kf, "[Version]\nProvider = \"Hughski\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "Hughski");
+ g_free (tmp);
+
+ /* missing string replacement */
+ ret = as_inf_load_data (kf, "[Version]\nProvider = \"%MISSING%\"\n",
+ AS_INF_LOAD_FLAG_STRICT, &error);
+ g_assert_error (error, AS_INF_ERROR, AS_INF_ERROR_FAILED);
+ g_assert (!ret);
+ g_clear_error (&error);
+ ret = as_inf_load_data (kf, "[Version]\nProvider = \"%MISSING%\"\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "%MISSING%");
+ g_free (tmp);
+
+ /* invalid UTF-8 */
+ ret = as_inf_load_data (kf, "[Version]\nProvider = Hughski \xae\n",
+ AS_INF_LOAD_FLAG_STRICT, &error);
+ g_assert_error (error, AS_INF_ERROR, AS_INF_ERROR_FAILED);
+ g_assert (!ret);
+ g_clear_error (&error);
+ ret = as_inf_load_data (kf, "[Version]\nProvider = Hughski \xae\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "Hughski ?");
+ g_free (tmp);
+
+ g_key_file_unref (kf);
+
+ /* parse file */
+ kf = g_key_file_new ();
filename = as_test_get_filename ("example.inf");
- kf = as_utils_load_inf_file (filename, &error);
+ ret = as_inf_load_file (kf, filename, 0, &error);
g_assert_no_error (error);
- g_assert (kf != NULL);
+ g_assert (ret);
/* simple */
- test1 = g_key_file_get_string (kf, "Version", "Class", &error);
- g_assert_no_error (error);
- g_assert_cmpstr (test1, ==, "Firmware");
+ tmp = g_key_file_get_string (kf, "Version", "Class", NULL);
+ g_assert_cmpstr (tmp, ==, "Firmware");
+ g_free (tmp);
/* key replacement */
- test2 = g_key_file_get_string (kf, "Firmware_CopyFiles", "Value000", &error);
- g_assert_no_error (error);
- g_assert_cmpstr (test2, ==, "firmware.bin");
+ tmp = g_key_file_get_string (kf, "Firmware_CopyFiles", "value000", NULL);
+ g_assert_cmpstr (tmp, ==, "firmware.bin");
+ g_free (tmp);
/* double quotes swallowing */
- test3 = g_key_file_get_string (kf, "Strings", "FirmwareDesc", &error);
+ tmp = g_key_file_get_string (kf, "Strings", "FirmwareDesc", NULL);
+ g_assert_cmpstr (tmp, ==, "ColorHug Firmware");
+ g_free (tmp);
+
+ /* string replacement */
+ tmp = g_key_file_get_string (kf, "Version", "Provider", NULL);
+ g_assert_cmpstr (tmp, ==, "Hughski");
+ g_free (tmp);
+
+ /* valid DriverVer */
+ ret = as_inf_load_data (kf, "[Version]\n"
+ "DriverVer = \"03/01/2015,2.0.0\"\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
g_assert_no_error (error);
- g_assert_cmpstr (test3, ==, "ColorHug Firmware");
+ g_assert (ret);
+ tmp = as_inf_get_driver_version (kf, &ts, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (tmp, ==, "2.0.0");
+ g_assert_cmpint (ts, ==, 1425168000);
+ g_free (tmp);
+
+ /* invalid DriverVer date */
+ ret = as_inf_load_data (kf, "[Version]\n"
+ "DriverVer = \"13/01/2015,2.0.0\"\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = as_inf_get_driver_version (kf, &ts, &error);
+ g_assert_error (error, AS_INF_ERROR, AS_INF_ERROR_INVALID_TYPE);
+ g_assert_cmpstr (tmp, ==, NULL);
+ g_clear_error (&error);
+
+ /* no DriverVer date */
+ ret = as_inf_load_data (kf, "[Version]\n"
+ "DriverVer = \"2.0.0\"\n",
+ AS_INF_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ tmp = as_inf_get_driver_version (kf, &ts, &error);
+ g_assert_error (error, AS_INF_ERROR, AS_INF_ERROR_INVALID_TYPE);
+ g_assert_cmpstr (tmp, ==, NULL);
+ g_clear_error (&error);
+
+ g_key_file_unref (kf);
+
+ /* test every .inf file from my Windows XP installation disk */
+ infs = as_test_get_filename ("infs");
+ if (infs != NULL) {
+ dir = g_dir_open (infs, 0, NULL);
+ while ((data = g_dir_read_name (dir)) != NULL) {
+ _cleanup_free_ gchar *path = NULL;
+ path = g_build_filename (infs, data, NULL);
+ kf = g_key_file_new ();
+ ret = as_inf_load_file (kf, path,
+ AS_INF_LOAD_FLAG_NONE,
+ &error);
+ if (g_error_matches (error,
+ AS_INF_ERROR,
+ AS_INF_ERROR_INVALID_TYPE)) {
+ g_clear_error (&error);
+ } else {
+ if (error != NULL)
+ g_print ("Failed to process: %s\n",
+ path);
+ g_assert_no_error (error);
+ g_assert (ret);
+ }
+ g_key_file_unref (kf);
+ }
+ }
}
static void
@@ -3474,6 +3704,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/app{no-markup}", as_test_app_no_markup_func);
g_test_add_func ("/AppStream/app{subsume}", as_test_app_subsume_func);
g_test_add_func ("/AppStream/app{search}", as_test_app_search_func);
+ g_test_add_func ("/AppStream/inf", as_test_inf_func);
g_test_add_func ("/AppStream/node", as_test_node_func);
g_test_add_func ("/AppStream/node{reflow}", as_test_node_reflow_text_func);
g_test_add_func ("/AppStream/node{xml}", as_test_node_xml_func);
@@ -3489,9 +3720,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/utils{icons}", as_test_utils_icons_func);
g_test_add_func ("/AppStream/utils{spdx-token}", as_test_utils_spdx_token_func);
g_test_add_func ("/AppStream/utils{install-filename}", as_test_utils_install_filename_func);
- g_test_add_func ("/AppStream/utils{inf}", as_test_utils_inf_func);
g_test_add_func ("/AppStream/utils{vercmp}", as_test_utils_vercmp_func);
- g_test_add_func ("/AppStream/utils{driver-version}", as_test_utils_driver_ver_func);
g_test_add_func ("/AppStream/yaml", as_test_yaml_func);
g_test_add_func ("/AppStream/store", as_test_store_func);
g_test_add_func ("/AppStream/store{demote}", as_test_store_demote_func);
diff --git a/libappstream-glib/as-utils.c b/libappstream-glib/as-utils.c
index 3a3d7b5..ecc199a 100644
--- a/libappstream-glib/as-utils.c
+++ b/libappstream-glib/as-utils.c
@@ -1526,112 +1526,6 @@ as_utils_search_tokenize (const gchar *search)
}
/**
- * as_utils_line_is_blank:
- */
-static gboolean
-as_utils_line_is_blank (const gchar *str)
-{
- guint i;
- for (i = 0; str[i] != '\0'; i++) {
- if (!g_ascii_isspace (str[i]))
- return FALSE;
- }
- return TRUE;
-}
-
-/**
- * as_utils_remove_quotes:
- */
-static gchar *
-as_utils_remove_quotes (const gchar *txt)
-{
- GString *str;
- guint i;
-
- /* remove double quotes */
- str = g_string_sized_new (strlen (txt));
- for (i = 0; txt[i] != '\0'; i++) {
- if (txt[i] == '"')
- continue;
- g_string_append_c (str, txt[i]);
- }
- return g_string_free (str, FALSE);
-}
-
-/**
- * as_utils_load_inf_file:
- * @filename: the .inf file to open
- * @error: A #GError or %NULL
- *
- * Repairs an .inf file and opens it as a keyfile.
- *
- * Returns: (transfer full): a #GKeyFile, or %NULL for error
- *
- * Since: 0.3.5
- */
-GKeyFile *
-as_utils_load_inf_file (const gchar *filename, GError **error)
-{
- guint i;
- guint idx = 0;
- guint nokey_idx = 0;
- _cleanup_free_ gchar *data_fixed = NULL;
- _cleanup_free_ gchar *data = NULL;
- _cleanup_keyfile_unref_ GKeyFile *kf = NULL;
- _cleanup_strv_free_ gchar **lines_fixed = NULL;
- _cleanup_strv_free_ gchar **lines = NULL;
-
- /* load lines of keyfile */
- if (!g_file_get_contents (filename, &data, NULL, error))
- return NULL;
- lines = g_strsplit (data, "\n", -1);
- lines_fixed = g_new0 (gchar *, g_strv_length (lines));
-
- /* verify each line, and make sane */
- for (i = 0; lines[i] != NULL; i++) {
-
- /* is just whitespace */
- if (as_utils_line_is_blank (lines[i])) {
- lines[i][0] = '\0';
- continue;
- }
-
- /* convert comments */
- if (lines[i][0] == ';') {
- lines_fixed[idx++] = g_strdup_printf ("#%s", lines[i] + 1);
- continue;
- }
-
- /* is valid section header */
- if (g_str_has_prefix (lines[i], "[") &&
- g_strstr_len (lines[i], -1, "]") != NULL) {
- lines_fixed[idx++] = g_strdup (lines[i]);
- nokey_idx = 0;
- continue;
- }
-
- /* value, possibly with quotes */
- if (g_strstr_len (lines[i], -1, "=") != NULL) {
- lines_fixed[idx++] = as_utils_remove_quotes (lines[i]);
- } else {
- /* value with no key */
- lines_fixed[idx++] = g_strdup_printf ("Value%03i=%s",
- nokey_idx++,
- lines[i]);
- continue;
- }
- }
-
- /* load file */
- kf = g_key_file_new ();
- data_fixed = g_strjoinv ("\n", lines_fixed);
- if (!g_key_file_load_from_data (kf, data_fixed, -1,
- G_KEY_FILE_NONE, error))
- return NULL;
- return g_key_file_ref (kf);
-}
-
-/**
* as_utils_vercmp:
* @version_a: the release version, e.g. 1.2.3
* @version_b: the release version, e.g. 1.2.3.1
@@ -1694,62 +1588,3 @@ as_utils_vercmp (const gchar *version_a, const gchar *version_b)
/* we really shouldn't get here */
return 0;
}
-
-/**
- * as_utils_parse_driver_version:
- * @driver_version: the DriverVer string, e.g. "03/01/2015,2.0.0"
- * @timestamp: the returned driverver timestamp, or %NULL
- * @error: A #GError or %NULL
- *
- * Parses the DriverVer string into a recognisable version and timestamp;
- *
- * Returns: the version string, or %NULL for error.
- *
- * Since: 0.3.5
- */
-gchar *
-as_utils_parse_driver_version (const gchar *driver_version,
- guint64 *timestamp,
- GError **error)
-{
- _cleanup_date_time_unref_ GDateTime *dt = NULL;
- _cleanup_strv_free_ gchar **split = NULL;
- _cleanup_strv_free_ gchar **dv_split = NULL;
-
- /* split into driver date and version */
- dv_split = g_strsplit (driver_version, ",", -1);
- if (g_strv_length (dv_split) != 2) {
- g_set_error (error,
- AS_UTILS_ERROR,
- AS_UTILS_ERROR_INVALID_TYPE,
- "DriverVer is invalid: %s", driver_version);
- return NULL;
- }
-
- /* split up into MM/DD/YYYY, because America */
- if (timestamp != NULL) {
- split = g_strsplit (dv_split[0], "/", -1);
- if (g_strv_length (split) != 3) {
- g_set_error (error,
- AS_UTILS_ERROR,
- AS_UTILS_ERROR_INVALID_TYPE,
- "DriverVer date invalid: %s",
- dv_split[0]);
- return NULL;
- }
- dt = g_date_time_new_local (atoi (split[2]),
- atoi (split[0]),
- atoi (split[1]),
- 0, 0, 0);
- if (dt == NULL) {
- g_set_error (error,
- AS_UTILS_ERROR,
- AS_UTILS_ERROR_INVALID_TYPE,
- "DriverVer date invalid: %s",
- dv_split[0]);
- return NULL;
- }
- *timestamp = g_date_time_to_unix (dt);
- }
- return g_strdup (dv_split[1]);
-}
diff --git a/libappstream-glib/as-utils.h b/libappstream-glib/as-utils.h
index f21243a..c551ca5 100644
--- a/libappstream-glib/as-utils.h
+++ b/libappstream-glib/as-utils.h
@@ -128,11 +128,6 @@ gboolean as_utils_install_filename (AsUtilsLocation location,
GError **error);
gboolean as_utils_search_token_valid (const gchar *token);
gchar **as_utils_search_tokenize (const gchar *search);
-GKeyFile *as_utils_load_inf_file (const gchar *filename,
- GError **error);
-gchar *as_utils_parse_driver_version (const gchar *driver_version,
- guint64 *timestamp,
- GError **error);
gint as_utils_vercmp (const gchar *version_a,
const gchar *version_b);