diff options
author | Richard Hughes <richard@hughsie.com> | 2015-03-07 14:42:08 +0000 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2015-03-08 21:57:58 +0000 |
commit | 2abfdf9ec7537217c5a0469bb3b270872c74bc9f (patch) | |
tree | d09787aa29dd42601b789a05995e48867d3cb455 | |
parent | 6f985ba035085751f16e0979d1ef864cd9d25719 (diff) | |
download | appstream-glib-2abfdf9ec7537217c5a0469bb3b270872c74bc9f.tar.gz |
Make the .inf parser compatible with more files
-rw-r--r-- | libappstream-builder/asb-self-test.c | 4 | ||||
-rw-r--r-- | libappstream-builder/plugins/asb-plugin-firmware.c | 25 | ||||
-rw-r--r-- | libappstream-glib/Makefile.am | 5 | ||||
-rw-r--r-- | libappstream-glib/appstream-glib.h | 1 | ||||
-rw-r--r-- | libappstream-glib/as-inf.c | 912 | ||||
-rw-r--r-- | libappstream-glib/as-inf.h | 80 | ||||
-rw-r--r-- | libappstream-glib/as-self-test.c | 319 | ||||
-rw-r--r-- | libappstream-glib/as-utils.c | 165 | ||||
-rw-r--r-- | libappstream-glib/as-utils.h | 5 |
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, ×tamp, error); + version = as_inf_get_driver_version (kf, ×tamp, 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); |