diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 4c9364b3d..b7bd83f57 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -232,7 +232,7 @@ fu_plugin_dell_inject_fake_data (FuPlugin *plugin, data->can_switch_modes = TRUE; } -static AsVersionParseFlag +static FuVersionFormat fu_plugin_dell_get_version_format (FuPlugin *plugin) { const gchar *content; @@ -241,17 +241,15 @@ fu_plugin_dell_get_version_format (FuPlugin *plugin) content = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); if (content == NULL) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; /* any quirks match */ group = g_strdup_printf ("SmbiosManufacturer=%s", content); quirk = fu_plugin_lookup_quirk_by_id (plugin, group, FU_QUIRKS_UEFI_VERSION_FORMAT); - if (g_strcmp0 (quirk, "quad") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + if (quirk == NULL) + return FU_VERSION_FORMAT_TRIPLET; + return fu_common_version_format_from_string (quirk); } static gboolean @@ -319,7 +317,7 @@ fu_plugin_usb_device_added (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - AsVersionParseFlag parse_flags; + FuVersionFormat version_format; guint16 pid; guint16 vid; const gchar *query_str; @@ -372,7 +370,7 @@ fu_plugin_usb_device_added (FuPlugin *plugin, g_debug ("Dock cable type: %" G_GUINT32_FORMAT, dock_info->cable_type); g_debug ("Dock location: %d", dock_info->location); g_debug ("Dock component count: %d", dock_info->component_count); - parse_flags = fu_plugin_dell_get_version_format (plugin); + version_format = fu_plugin_dell_get_version_format (plugin); for (guint i = 0; i < dock_info->component_count; i++) { g_autofree gchar *fw_str = NULL; @@ -411,8 +409,8 @@ fu_plugin_usb_device_added (FuPlugin *plugin, continue; } - fw_str = as_utils_version_from_uint32 (dock_info->components[i].fw_version, - parse_flags); + fw_str = fu_common_version_from_uint32 (dock_info->components[i].fw_version, + version_format); if (!fu_plugin_dock_node (plugin, platform, buf.record->dock_info_header.dock_type, @@ -427,8 +425,8 @@ fu_plugin_usb_device_added (FuPlugin *plugin, /* if an old EC or invalid EC version found, create updatable parent */ if (old_ec) - flash_ver_str = as_utils_version_from_uint32 (dock_info->flash_pkg_version, - parse_flags); + flash_ver_str = fu_common_version_from_uint32 (dock_info->flash_pkg_version, + version_format); if (!fu_plugin_dock_node (plugin, platform, buf.record->dock_info_header.dock_type, @@ -607,8 +605,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) g_debug ("Creating primary TPM GUID %s and secondary TPM GUID %s", tpm_guid_raw, tpm_guid_raw_alt); - version_str = as_utils_version_from_uint32 (out->fw_version, - AS_VERSION_PARSE_FLAG_NONE); + version_str = fu_common_version_from_uint32 (out->fw_version, + FU_VERSION_FORMAT_QUAD); /* make it clear that the TPM is a discrete device of the product */ if (!data->smi_obj->fake_smbios) { diff --git a/plugins/dfu/dfu-firmware.c b/plugins/dfu/dfu-firmware.c index 1b56afd42..1b8b5111c 100644 --- a/plugins/dfu/dfu-firmware.c +++ b/plugins/dfu/dfu-firmware.c @@ -21,7 +21,8 @@ #include #include -#include + +#include "fu-common-version.h" #include "dfu-common.h" #include "dfu-firmware.h" @@ -642,8 +643,8 @@ dfu_firmware_to_string (DfuFirmware *firmware) g_return_val_if_fail (DFU_IS_FIRMWARE (firmware), NULL); - release_str = as_utils_version_from_uint16 (priv->release, - AS_VERSION_PARSE_FLAG_USE_BCD); + release_str = fu_common_version_from_uint16 (priv->release, + FU_VERSION_FORMAT_BCD); str = g_string_new (""); g_string_append_printf (str, "vid: 0x%04x\n", priv->vid); g_string_append_printf (str, "pid: 0x%04x\n", priv->pid); diff --git a/plugins/dfu/dfu-tool.c b/plugins/dfu/dfu-tool.c index 03672b632..e9c7e2dc0 100644 --- a/plugins/dfu/dfu-tool.c +++ b/plugins/dfu/dfu-tool.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "dfu-cipher-xtea.h" #include "dfu-device-private.h" @@ -2036,8 +2035,8 @@ dfu_tool_list (DfuToolPrivate *priv, gchar **values, GError **error) dfu_device_set_usb_context (device, usb_context); if (!fu_device_probe (FU_DEVICE (device), NULL)) continue; - version = as_utils_version_from_uint16 (g_usb_device_get_release (usb_device), - AS_VERSION_PARSE_FLAG_USE_BCD); + version = fu_common_version_from_uint16 (g_usb_device_get_release (usb_device), + FU_VERSION_FORMAT_BCD); g_print ("%s %04x:%04x [v%s]:\n", /* TRANSLATORS: detected a DFU device */ _("Found"), diff --git a/plugins/dfu/fu-plugin-dfu.c b/plugins/dfu/fu-plugin-dfu.c index dca1cfc56..64af9951d 100644 --- a/plugins/dfu/fu-plugin-dfu.c +++ b/plugins/dfu/fu-plugin-dfu.c @@ -6,8 +6,6 @@ #include "config.h" -#include - #include "fu-plugin-vfuncs.h" #include "dfu-device.h" diff --git a/plugins/nvme/fu-nvme-device.c b/plugins/nvme/fu-nvme-device.c index 060f7a423..2c9518385 100644 --- a/plugins/nvme/fu-nvme-device.c +++ b/plugins/nvme/fu-nvme-device.c @@ -203,7 +203,7 @@ fu_nvme_device_set_version (FuNvmeDevice *self, const gchar *version, GError **e version); return FALSE; } - version_new = as_utils_version_from_uint32 (tmp, AS_VERSION_PARSE_FLAG_NONE); + version_new = as_utils_version_from_uint32 (tmp, FU_VERSION_FORMAT_QUAD); fu_device_set_version (FU_DEVICE (self), version_new); return TRUE; } diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 44876910c..59de81ac5 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -412,7 +412,7 @@ fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) } } -static AsVersionParseFlag +static FuVersionFormat fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, FuUefiDeviceKind device_kind) { const gchar *content; @@ -421,21 +421,19 @@ fu_plugin_uefi_get_version_format_for_type (FuPlugin *plugin, FuUefiDeviceKind d /* we have no information for devices */ if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; content = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); if (content == NULL) - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + return FU_VERSION_FORMAT_TRIPLET; /* any quirks match */ group = g_strdup_printf ("SmbiosManufacturer=%s", content); quirk = fu_plugin_lookup_quirk_by_id (plugin, group, FU_QUIRKS_UEFI_VERSION_FORMAT); - if (g_strcmp0 (quirk, "quad") == 0) - return AS_VERSION_PARSE_FLAG_NONE; - - /* fall back */ - return AS_VERSION_PARSE_FLAG_USE_TRIPLET; + if (quirk == NULL) + return FU_VERSION_FORMAT_TRIPLET; + return fu_common_version_format_from_string (quirk); } static const gchar * @@ -478,7 +476,7 @@ static gboolean fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **error) { FuUefiDeviceKind device_kind; - AsVersionParseFlag parse_flags; + FuVersionFormat version_format; guint32 version_raw; g_autofree gchar *name = NULL; g_autofree gchar *version_lowest = NULL; @@ -486,17 +484,17 @@ fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **er /* add details to the device */ device_kind = fu_uefi_device_get_kind (dev); - parse_flags = fu_plugin_uefi_get_version_format_for_type (plugin, device_kind); + version_format = fu_plugin_uefi_get_version_format_for_type (plugin, device_kind); version_raw = fu_uefi_device_get_version (dev); - version = as_utils_version_from_uint32 (version_raw, parse_flags); + version = fu_common_version_from_uint32 (version_raw, version_format); fu_device_set_version (dev, version); name = fu_plugin_uefi_get_name_for_type (plugin, fu_uefi_device_get_kind (dev)); if (name != NULL) fu_device_set_name (FU_DEVICE (dev), name); version_raw = fu_uefi_device_get_version_lowest (dev); if (version_raw != 0) { - version_lowest = as_utils_version_from_uint32 (version_raw, - parse_flags); + version_lowest = fu_common_version_from_uint32 (version_raw, + version_format); fu_device_set_version_lowest (FU_DEVICE (dev), version_lowest); } fu_device_add_flag (FU_DEVICE (dev), FWUPD_DEVICE_FLAG_INTERNAL); diff --git a/src/fu-common-version.c b/src/fu-common-version.c new file mode 100644 index 000000000..5088cfc31 --- /dev/null +++ b/src/fu-common-version.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuCommon" + +#include + +#include + +#include "fwupd-error.h" + +#include "fu-common-version.h" + +#define FU_COMMON_VERSION_DECODE_BCD(val) ((((val) >> 4) & 0x0f) * 10 + ((val) & 0x0f)) + +/** + * fu_common_version_format_from_string: + * @str: A string, e.g. `quad` + * + * Converts text to a display version type. + * + * Returns: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Since: 1.2.0 + **/ +FuVersionFormat +fu_common_version_format_from_string (const gchar *str) +{ + if (g_strcmp0 (str, "triplet") == 0) + return FU_VERSION_FORMAT_TRIPLET; + if (g_strcmp0 (str, "quad") == 0) + return FU_VERSION_FORMAT_QUAD; + if (g_strcmp0 (str, "bcd") == 0) + return FU_VERSION_FORMAT_BCD; + if (g_strcmp0 (str, "plain") == 0) + return FU_VERSION_FORMAT_PLAIN; + return FU_VERSION_FORMAT_QUAD; +} + +/** + * fu_common_version_format_to_string: + * @str: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Converts a display version type to text. + * + * Returns: A string, e.g. `quad`, or %NULL if not known + * + * Since: 1.2.0 + **/ +const gchar * +fu_common_version_format_to_string (FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_TRIPLET) + return "triplet"; + if (kind == FU_VERSION_FORMAT_QUAD) + return "quad"; + if (kind == FU_VERSION_FORMAT_BCD) + return "bcd"; + if (kind == FU_VERSION_FORMAT_PLAIN) + return "plain"; + return NULL; +} + +/** + * fu_common_version_from_uint32: + * @val: A uint32le version number + * @kind: version kind used for formatting, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Returns a dotted decimal version string from a 32 bit number. + * + * Returns: A version number, e.g. "1.0.3", or %NULL if not supported + * + * Since: 1.2.0 + **/ +gchar * +fu_common_version_from_uint32 (guint32 val, FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_QUAD) { + /* AA.BB.CC.DD */ + return g_strdup_printf ("%u.%u.%u.%u", + (val >> 24) & 0xff, + (val >> 16) & 0xff, + (val >> 8) & 0xff, + val & 0xff); + } + if (kind == FU_VERSION_FORMAT_TRIPLET) { + /* AA.BB.CCDD */ + return g_strdup_printf ("%u.%u.%u", + (val >> 24) & 0xff, + (val >> 16) & 0xff, + val & 0xffff); + } + if (kind == FU_VERSION_FORMAT_PAIR) { + /* AABB.CCDD */ + return g_strdup_printf ("%u.%u", + (val >> 16) & 0xffff, + val & 0xffff); + } + if (kind == FU_VERSION_FORMAT_PLAIN) { + /* AABBCCDD */ + return g_strdup_printf ("%" G_GUINT32_FORMAT, val); + } + if (kind == FU_VERSION_FORMAT_BCD) { + /* AA.BB.CC.DD, but BCD */ + return g_strdup_printf ("%u.%u.%u.%u", + FU_COMMON_VERSION_DECODE_BCD(val >> 24), + FU_COMMON_VERSION_DECODE_BCD(val >> 16), + FU_COMMON_VERSION_DECODE_BCD(val >> 8), + FU_COMMON_VERSION_DECODE_BCD(val)); + } + return NULL; +} + +/** + * fu_common_version_from_uint16: + * @val: A uint16le version number + * @kind: version kind used for formatting, e.g. %FU_VERSION_FORMAT_TRIPLET + * + * Returns a dotted decimal version string from a 16 bit number. + * + * Returns: A version number, e.g. "1.3", or %NULL if not supported + * + * Since: 1.2.0 + **/ +gchar * +fu_common_version_from_uint16 (guint16 val, FuVersionFormat kind) +{ + if (kind == FU_VERSION_FORMAT_BCD) { + return g_strdup_printf ("%i.%i", + FU_COMMON_VERSION_DECODE_BCD(val >> 8), + FU_COMMON_VERSION_DECODE_BCD(val)); + } + if (kind == FU_VERSION_FORMAT_PAIR) { + return g_strdup_printf ("%u.%u", + (guint) (val >> 8) & 0xff, + (guint) val & 0xff); + } + if (kind == FU_VERSION_FORMAT_PLAIN) { + return g_strdup_printf ("%" G_GUINT16_FORMAT, val); + } + return NULL; +} + +static gint +fu_common_vercmp_char (gchar chr1, gchar chr2) +{ + if (chr1 == chr2) + return 0; + if (chr1 == '~') + return -1; + if (chr2 == '~') + return 1; + return chr1 < chr2 ? -1 : 1; +} + +static gint +fu_common_vercmp_chunk (const gchar *str1, const gchar *str2) +{ + guint i; + + /* trivial */ + if (g_strcmp0 (str1, str2) == 0) + return 0; + if (str1 == NULL) + return 1; + if (str2 == NULL) + return -1; + + /* check each char of the chunk */ + for (i = 0; str1[i] != '\0' && str2[i] != '\0'; i++) { + gint rc = fu_common_vercmp_char (str1[i], str2[i]); + if (rc != 0) + return rc; + } + return fu_common_vercmp_char (str1[i], str2[i]); +} + +/** + * fu_common_version_parse: + * @version: A version number + * + * Returns a dotted decimal version string from a version string. The supported + * formats are: + * + * - Dotted decimal, e.g. "1.2.3" + * - Base 16, a hex number *with* a 0x prefix, e.g. "0x10203" + * - Base 10, a string containing just [0-9], e.g. "66051" + * - Date in YYYYMMDD format, e.g. 20150915 + * + * Anything with a '.' or that doesn't match [0-9] or 0x[a-f,0-9] is considered + * a string and returned without modification. + * + * Returns: A version number, e.g. "1.0.3" + * + * Since: 1.2.0 + */ +gchar * +fu_common_version_parse (const gchar *version) +{ + const gchar *version_noprefix = version; + gchar *endptr = NULL; + guint64 tmp; + guint base; + guint i; + + /* already dotted decimal */ + if (g_strstr_len (version, -1, ".") != NULL) + return g_strdup (version); + + /* is a date */ + if (g_str_has_prefix (version, "20") && + strlen (version) == 8) + return g_strdup (version); + + /* convert 0x prefixed strings to dotted decimal */ + if (g_str_has_prefix (version, "0x")) { + version_noprefix += 2; + base = 16; + } else { + /* for non-numeric content, just return the string */ + for (i = 0; version[i] != '\0'; i++) { + if (!g_ascii_isdigit (version[i])) + return g_strdup (version); + } + base = 10; + } + + /* convert */ + tmp = g_ascii_strtoull (version_noprefix, &endptr, base); + if (endptr != NULL && endptr[0] != '\0') + return g_strdup (version); + if (tmp == 0) + return g_strdup (version); + return fu_common_version_from_uint32 ((guint32) tmp, FU_VERSION_FORMAT_TRIPLET); +} + +/** + * fu_common_vercmp: + * @version_a: the release version, e.g. 1.2.3 + * @version_b: the release version, e.g. 1.2.3.1 + * + * Compares version numbers for sorting. + * + * Returns: -1 if a < b, +1 if a > b, 0 if they are equal, and %G_MAXINT on error + * + * Since: 0.3.5 + */ +gint +fu_common_vercmp (const gchar *version_a, const gchar *version_b) +{ + guint longest_split; + g_autofree gchar *str_a = NULL; + g_autofree gchar *str_b = NULL; + g_auto(GStrv) split_a = NULL; + g_auto(GStrv) split_b = NULL; + + /* sanity check */ + if (version_a == NULL || version_b == NULL) + return G_MAXINT; + + /* optimisation */ + if (g_strcmp0 (version_a, version_b) == 0) + return 0; + + /* split into sections, and try to parse */ + str_a = fu_common_version_parse (version_a); + str_b = fu_common_version_parse (version_b); + split_a = g_strsplit (str_a, ".", -1); + split_b = g_strsplit (str_b, ".", -1); + longest_split = MAX (g_strv_length (split_a), g_strv_length (split_b)); + for (guint i = 0; i < longest_split; i++) { + gchar *endptr_a = NULL; + gchar *endptr_b = NULL; + gint64 ver_a; + gint64 ver_b; + + /* we lost or gained a dot */ + if (split_a[i] == NULL) + return -1; + if (split_b[i] == NULL) + return 1; + + /* compare integers */ + ver_a = g_ascii_strtoll (split_a[i], &endptr_a, 10); + ver_b = g_ascii_strtoll (split_b[i], &endptr_b, 10); + if (ver_a < ver_b) + return -1; + if (ver_a > ver_b) + return 1; + + /* compare strings */ + if ((endptr_a != NULL && endptr_a[0] != '\0') || + (endptr_b != NULL && endptr_b[0] != '\0')) { + gint rc = fu_common_vercmp_chunk (endptr_a, endptr_b); + if (rc < 0) + return -1; + if (rc > 0) + return 1; + } + } + + /* we really shouldn't get here */ + return 0; +} diff --git a/src/fu-common-version.h b/src/fu-common-version.h new file mode 100644 index 000000000..059fc4d83 --- /dev/null +++ b/src/fu-common-version.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017-2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_COMMON_VERSION_H__ +#define __FU_COMMON_VERSION_H__ + +#include + +/** + * FuVersionFormat: + * @FU_VERSION_FORMAT_UNKNOWN: Unknown version format + * @FU_VERSION_FORMAT_PLAIN: Use plain integer version numbers + * @FU_VERSION_FORMAT_QUAD: Use Dell-style AA.BB.CC.DD version numbers + * @FU_VERSION_FORMAT_TRIPLET: Use Microsoft-style AA.BB.CCDD version numbers + * @FU_VERSION_FORMAT_PAIR: Use two AABB.CCDD version numbers + * @FU_VERSION_FORMAT_BCD: Use binary coded decimal notation + * + * The flags used when parsing version numbers. + **/ +typedef enum { + FU_VERSION_FORMAT_UNKNOWN, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_PLAIN, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_QUAD, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_TRIPLET, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_PAIR, /* Since: 1.2.0 */ + FU_VERSION_FORMAT_BCD, /* Since: 1.2.0 */ + /*< private >*/ + FU_VERSION_FORMAT_LAST +} FuVersionFormat; + +FuVersionFormat fu_common_version_format_from_string (const gchar *str); +const gchar *fu_common_version_format_to_string (FuVersionFormat kind); + +gint fu_common_vercmp (const gchar *version_a, + const gchar *version_b); +gchar *fu_common_version_from_uint32 (guint32 val, + FuVersionFormat flags); +gchar *fu_common_version_from_uint16 (guint16 val, + FuVersionFormat flags); +gchar *fu_common_version_parse (const gchar *version); + +#endif /* __FU_COMMON_VERSION_H__ */ diff --git a/src/fu-engine.c b/src/fu-engine.c index 0d9b524e2..e93e2faee 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -1007,7 +1007,7 @@ fu_engine_vendor_fixup_provide_value (AsApp *app) static void fu_engine_vendor_quirk_release_version (FuEngine *self, AsApp *app) { - AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET; + FuVersionFormat flags = FU_VERSION_FORMAT_TRIPLET; GPtrArray *releases; const gchar *quirk; const gchar *version_format; @@ -1024,7 +1024,7 @@ fu_engine_vendor_quirk_release_version (FuEngine *self, AsApp *app) g_auto(GStrv) globs = g_strsplit (quirk, ",", -1); for (guint i = 0; globs[i] != NULL; i++) { if (fnmatch (globs[i], as_app_get_id (app), 0) == 0) { - flags = AS_VERSION_PARSE_FLAG_NONE; + flags = FU_VERSION_FORMAT_QUAD; break; } } @@ -1033,7 +1033,7 @@ fu_engine_vendor_quirk_release_version (FuEngine *self, AsApp *app) /* specified in metadata */ version_format = as_app_get_metadata_item (app, "LVFS::VersionFormat"); if (g_strcmp0 (version_format, "quad") == 0) - flags = AS_VERSION_PARSE_FLAG_NONE; + flags = FU_VERSION_FORMAT_QUAD; /* fix each release */ releases = as_app_get_releases (app); @@ -1056,7 +1056,7 @@ fu_engine_vendor_quirk_release_version (FuEngine *self, AsApp *app) continue; /* convert to dotted decimal */ - version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags); + version_new = fu_common_version_from_uint32 ((guint32) ver_uint32, flags); as_release_set_version (rel, version_new); } } diff --git a/src/fu-install-task.c b/src/fu-install-task.c index 4145eddbe..851abe0bb 100644 --- a/src/fu-install-task.c +++ b/src/fu-install-task.c @@ -10,6 +10,7 @@ #include +#include "fu-common-version.h" #include "fu-device-private.h" #include "fu-install-task.h" #include "fu-keyring-utils.h" @@ -210,7 +211,7 @@ fu_install_task_check_requirements (FuInstallTask *self, /* compare to the lowest supported version, if it exists */ version_lowest = fu_device_get_version_lowest (self->device); if (version_lowest != NULL && - as_utils_vercmp (version_lowest, version) > 0 && + fu_common_vercmp (version_lowest, version) > 0 && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_set_error (error, FWUPD_ERROR, @@ -221,7 +222,7 @@ fu_install_task_check_requirements (FuInstallTask *self, } /* check semver */ - vercmp = as_utils_vercmp (version, version_release); + vercmp = fu_common_vercmp (version, version_release); if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { g_set_error (error, FWUPD_ERROR, diff --git a/src/fu-plugin.h b/src/fu-plugin.h index 4177f8197..1375d9f9c 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -13,6 +13,8 @@ #include #include "fu-common.h" +#include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-device.h" #include "fu-device-locker.h" #include "fu-quirks.h" diff --git a/src/fu-self-test.c b/src/fu-self-test.c index b779f2350..be8c2c4f9 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -17,6 +17,7 @@ #include "fu-common-cab.h" #include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-chunk.h" #include "fu-config.h" #include "fu-device-list.h" @@ -2862,6 +2863,134 @@ fu_common_guid_func (void) g_assert_cmpstr (guid2, ==, "1fbd1f2c-80f4-5d7c-a6ad-35c7b9bd5486"); } +static void +fu_common_version_func (void) +{ + guint i; + struct { + guint32 val; + const gchar *ver; + FuVersionFormat flags; + } version_from_uint32[] = { + { 0x0, "0.0.0.0", FU_VERSION_FORMAT_QUAD }, + { 0xff, "0.0.0.255", FU_VERSION_FORMAT_QUAD }, + { 0xff01, "0.0.255.1", FU_VERSION_FORMAT_QUAD }, + { 0xff0001, "0.255.0.1", FU_VERSION_FORMAT_QUAD }, + { 0xff000100, "255.0.1.0", FU_VERSION_FORMAT_QUAD }, + { 0x0, "0.0.0", FU_VERSION_FORMAT_TRIPLET }, + { 0xff, "0.0.255", FU_VERSION_FORMAT_TRIPLET }, + { 0xff01, "0.0.65281", FU_VERSION_FORMAT_TRIPLET }, + { 0xff0001, "0.255.1", FU_VERSION_FORMAT_TRIPLET }, + { 0xff000100, "255.0.256", FU_VERSION_FORMAT_TRIPLET }, + { 0x0, "0", FU_VERSION_FORMAT_PLAIN }, + { 0xff000100, "4278190336", FU_VERSION_FORMAT_PLAIN }, + { 0, NULL } + }; + struct { + guint16 val; + const gchar *ver; + FuVersionFormat flags; + } version_from_uint16[] = { + { 0x0, "0.0", FU_VERSION_FORMAT_PAIR }, + { 0xff, "0.255", FU_VERSION_FORMAT_PAIR }, + { 0xff01, "255.1", FU_VERSION_FORMAT_PAIR }, + { 0x0, "0.0", FU_VERSION_FORMAT_BCD }, + { 0x0110, "1.10", FU_VERSION_FORMAT_BCD }, + { 0x9999, "99.99", FU_VERSION_FORMAT_BCD }, + { 0x0, "0", FU_VERSION_FORMAT_PLAIN }, + { 0x1234, "4660", FU_VERSION_FORMAT_PLAIN }, + { 0, NULL } + }; + struct { + const gchar *old; + const gchar *new; + } version_parse[] = { + { "0", "0" }, + { "0x1a", "0.0.26" }, + { "257", "0.0.257" }, + { "1.2.3", "1.2.3" }, + { "0xff0001", "0.255.1" }, + { "16711681", "0.255.1" }, + { "20150915", "20150915" }, + { "dave", "dave" }, + { "0x1x", "0x1x" }, + { NULL, NULL } + }; + + /* check version conversion */ + for (i = 0; version_from_uint32[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_from_uint32 (version_from_uint32[i].val, + version_from_uint32[i].flags); + g_assert_cmpstr (ver, ==, version_from_uint32[i].ver); + } + for (i = 0; version_from_uint16[i].ver != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_from_uint16 (version_from_uint16[i].val, + version_from_uint16[i].flags); + g_assert_cmpstr (ver, ==, version_from_uint16[i].ver); + } + + /* check version parsing */ + for (i = 0; version_parse[i].old != NULL; i++) { + g_autofree gchar *ver = NULL; + ver = fu_common_version_parse (version_parse[i].old); + g_assert_cmpstr (ver, ==, version_parse[i].new); + } +} + +static void +fu_common_vercmp_func (void) +{ + /* same */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.003", "001.002.003"), ==, 0); + + /* same, not dotted decimal */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "0x1020003"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("0x10203", "0x10203"), ==, 0); + + /* upgrade and downgrade */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.4"), <, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.000", "001.002.009"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.2"), >, 0); + g_assert_cmpint (fu_common_vercmp ("001.002.009", "001.002.000"), >, 0); + + /* unequal depth */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3.1"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3.1", "1.2.4"), <, 0); + + /* mixed-alpha-numeric */ + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3a"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3b"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3b", "1.2.3a"), >, 0); + + /* alpha version append */ + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3a"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3a", "1.2.3"), >, 0); + + /* alpha only */ + g_assert_cmpint (fu_common_vercmp ("alpha", "alpha"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("alpha", "beta"), <, 0); + g_assert_cmpint (fu_common_vercmp ("beta", "alpha"), >, 0); + + /* alpha-compare */ + g_assert_cmpint (fu_common_vercmp ("1.2a.3", "1.2a.3"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2a.3", "1.2b.3"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2b.3", "1.2a.3"), >, 0); + + /* tilde is all-powerful */ + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc1", "1.2.3~rc1"), ==, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc1", "1.2.3"), <, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3", "1.2.3~rc1"), >, 0); + g_assert_cmpint (fu_common_vercmp ("1.2.3~rc2", "1.2.3~rc1"), >, 0); + + /* invalid */ + g_assert_cmpint (fu_common_vercmp ("1", NULL), ==, G_MAXINT); + g_assert_cmpint (fu_common_vercmp (NULL, "1"), ==, G_MAXINT); + g_assert_cmpint (fu_common_vercmp (NULL, NULL), ==, G_MAXINT); +} + int main (int argc, char **argv) { @@ -2924,6 +3053,8 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func); g_test_add_func ("/fwupd/chunk", fu_chunk_func); g_test_add_func ("/fwupd/common{guid}", fu_common_guid_func); + g_test_add_func ("/fwupd/common{version}", fu_common_version_func); + g_test_add_func ("/fwupd/common{vercmp}", fu_common_vercmp_func); g_test_add_func ("/fwupd/common{strstrip}", fu_common_strstrip_func); g_test_add_func ("/fwupd/common{endian}", fu_common_endian_func); g_test_add_func ("/fwupd/common{cab-success}", fu_common_store_cab_func); diff --git a/src/fu-usb-device.c b/src/fu-usb-device.c index bd455c6df..127d49429 100644 --- a/src/fu-usb-device.c +++ b/src/fu-usb-device.c @@ -8,8 +8,6 @@ #include "config.h" -#include - #include "fu-usb-device-private.h" /** @@ -238,8 +236,8 @@ fu_usb_device_probe (FuDevice *device, GError **error) /* set the version if the release has been set */ release = g_usb_device_get_release (priv->usb_device); if (release != 0x0) { - g_autofree gchar *version = as_utils_version_from_uint16 (release, - AS_VERSION_PARSE_FLAG_USE_BCD); + g_autofree gchar *version = NULL; + version = fu_common_version_from_uint16 (release, FU_VERSION_FORMAT_BCD); fu_device_set_version (device, version); } diff --git a/src/fu-util.c b/src/fu-util.c index 5c2d35b68..3ffa68b13 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -742,8 +742,8 @@ fu_util_install_prepared (FuUtilPrivate *priv, gchar **values, GError **error) continue; /* tell the user what's going to happen */ - vercmp = as_utils_vercmp (fwupd_device_get_version (dev), - fwupd_release_get_version (rel)); + vercmp = fu_common_vercmp (fwupd_device_get_version (dev), + fwupd_release_get_version (rel)); if (vercmp == 0) { /* TRANSLATORS: the first replacement is a display name * e.g. "ColorHugALS" and the second is a version number diff --git a/src/meson.build b/src/meson.build index b5bedb0d4..b4b816444 100644 --- a/src/meson.build +++ b/src/meson.build @@ -27,6 +27,7 @@ libfwupdprivate = static_library( sources : [ 'fu-common.c', 'fu-common-guid.c', + 'fu-common-version.c', 'fu-chunk.c', 'fu-device.c', 'fu-device-locker.c', @@ -111,6 +112,7 @@ fwupdtool = executable( 'fu-common.c', 'fu-common-cab.c', 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -189,6 +191,7 @@ executable( 'fu-common.c', 'fu-common-cab.c', 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -258,6 +261,7 @@ if get_option('tests') 'fu-common.c', 'fu-common-cab.c', 'fu-common-guid.c', + 'fu-common-version.c', 'fu-config.c', 'fu-engine.c', 'fu-keyring.c', @@ -322,6 +326,8 @@ if get_option('introspection') 'fu-common.c', 'fu-common-guid.c', 'fu-common-guid.h', + 'fu-common-version.c', + 'fu-common-version.h', 'fu-common.h', 'fu-device.c', 'fu-device.h',