diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index 461a68b67..00e8d6baf 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -332,7 +332,7 @@ fu_dell_dock_ec_get_dock_info (FuDevice *device, device_entry[i].version.version_8[2], device_entry[i].version.version_8[3]); g_debug ("\tParsed version %s", self->ec_version); - fu_device_set_version (self, self->ec_version); + fu_device_set_version (FU_DEVICE (self), self->ec_version); } else if (map->device_type == FU_DELL_DOCK_DEVICETYPE_MST) { self->raw_versions->mst_version = device_entry[i].version.version_32; diff --git a/src/fu-common-version.c b/src/fu-common-version.c index bc98c2784..fd8869a27 100644 --- a/src/fu-common-version.c +++ b/src/fu-common-version.c @@ -202,6 +202,17 @@ fu_common_vercmp_chunk (const gchar *str1, const gchar *str2) return fu_common_vercmp_char (str1[i], str2[i]); } +static gboolean +_g_ascii_is_digits (const gchar *str) +{ + g_return_val_if_fail (str != NULL, FALSE); + for (gsize i = 0; str[i] != '\0'; i++) { + if (!g_ascii_isdigit (str[i])) + return FALSE; + } + return TRUE; +} + /** * fu_common_version_parse: * @version: A version number @@ -228,7 +239,6 @@ fu_common_version_parse (const gchar *version) gchar *endptr = NULL; guint64 tmp; guint base; - guint i; /* already dotted decimal */ if (g_strstr_len (version, -1, ".") != NULL) @@ -245,10 +255,8 @@ fu_common_version_parse (const gchar *version) 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); - } + if (!_g_ascii_is_digits (version)) + return g_strdup (version); base = 10; } @@ -261,6 +269,56 @@ fu_common_version_parse (const gchar *version) return fu_common_version_from_uint32 ((guint32) tmp, FU_VERSION_FORMAT_TRIPLET); } +/** + * fu_common_version_guess_format: + * @version: A version number, e.g. "1.2.3" + * + * Guesses the version format from the version number. This is only a heuristic + * and plugins and components should explicitly set the version format whenever + * possible. + * + * If the version format cannot be guessed with any degree of accuracy, the + * %FU_VERSION_FORMAT_UNKNOWN constant is returned. + * + * Returns: A #FuVersionFormat, e.g. %FU_VERSION_FORMAT_QUAD + * + * Since: 1.2.0 + */ +FuVersionFormat +fu_common_version_guess_format (const gchar *version) +{ + guint sz; + g_auto(GStrv) split = NULL; + + /* nothing to use */ + if (version == NULL || version[0] == '\0') + return FU_VERSION_FORMAT_UNKNOWN; + + /* no dots, assume just text */ + split = g_strsplit (version, ".", -1); + sz = g_strv_length (split); + if (sz == 1) + return FU_VERSION_FORMAT_PLAIN; + + /* check for only-digit semver version */ + for (guint i = 0; split[i] != NULL; i++) { + /* check sections are plain numbers */ + if (!_g_ascii_is_digits (split[i])) + return FU_VERSION_FORMAT_UNKNOWN; + } + + /* the most common formats */ + if (sz == 2) + return FU_VERSION_FORMAT_PAIR; + if (sz == 3) + return FU_VERSION_FORMAT_TRIPLET; + if (sz == 4) + return FU_VERSION_FORMAT_QUAD; + + /* unknown! */ + return FU_VERSION_FORMAT_UNKNOWN; +} + /** * fu_common_vercmp: * @version_a: the release version, e.g. 1.2.3 diff --git a/src/fu-common-version.h b/src/fu-common-version.h index 8bcbcd059..3538316b9 100644 --- a/src/fu-common-version.h +++ b/src/fu-common-version.h @@ -45,5 +45,6 @@ gchar *fu_common_version_from_uint32 (guint32 val, gchar *fu_common_version_from_uint16 (guint16 val, FuVersionFormat flags); gchar *fu_common_version_parse (const gchar *version); +FuVersionFormat fu_common_version_guess_format (const gchar *version); #endif /* __FU_COMMON_VERSION_H__ */ diff --git a/src/fu-device.c b/src/fu-device.c index e998fdf95..3fd374213 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -14,6 +14,7 @@ #include "fu-common.h" #include "fu-common-guid.h" +#include "fu-common-version.h" #include "fu-device-private.h" #include "fu-mutex.h" @@ -1046,6 +1047,29 @@ fu_device_set_id (FuDevice *self, const gchar *id) fwupd_device_set_id (FWUPD_DEVICE (self), id_hash); } +/** + * fu_device_set_version: + * @self: A #FuDevice + * @version: a string, e.g. `1.2.3` + * + * Sets the device version, autodetecting the version format if required. + * + * Since: 1.2.1 + **/ +void +fu_device_set_version (FuDevice *self, const gchar *version) +{ + FuDevicePrivate *priv = GET_PRIVATE (self); + + g_return_if_fail (FU_IS_DEVICE (self)); + g_return_if_fail (version != NULL); + + /* try to autodetect the version-format */ + if (priv->version_format == FU_VERSION_FORMAT_UNKNOWN) + priv->version_format = fu_common_version_guess_format (version); + fwupd_device_set_version (FWUPD_DEVICE (self), version); +} + /** * fu_device_ensure_id: * @self: A #FuDevice diff --git a/src/fu-device.h b/src/fu-device.h index 1b80db894..9fe7b018e 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -94,7 +94,6 @@ FuDevice *fu_device_new (void); #define fu_device_set_update_state(d,v) fwupd_device_set_update_state(FWUPD_DEVICE(d),v) #define fu_device_set_vendor(d,v) fwupd_device_set_vendor(FWUPD_DEVICE(d),v) #define fu_device_set_vendor_id(d,v) fwupd_device_set_vendor_id(FWUPD_DEVICE(d),v) -#define fu_device_set_version(d,v) fwupd_device_set_version(FWUPD_DEVICE(d),v) #define fu_device_set_version_lowest(d,v) fwupd_device_set_version_lowest(FWUPD_DEVICE(d),v) #define fu_device_set_version_bootloader(d,v) fwupd_device_set_version_bootloader(FWUPD_DEVICE(d),v) #define fu_device_set_flashes_left(d,v) fwupd_device_set_flashes_left(FWUPD_DEVICE(d),v) @@ -158,6 +157,8 @@ void fu_device_set_metadata_integer (FuDevice *self, guint value); void fu_device_set_id (FuDevice *self, const gchar *id); +void fu_device_set_version (FuDevice *self, + const gchar *version); const gchar *fu_device_get_physical_id (FuDevice *self); void fu_device_set_physical_id (FuDevice *self, const gchar *physical_id); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index c6f6c2179..75b21eb57 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -52,6 +52,20 @@ fu_self_test_mkroot (void) g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); } +static void +fu_common_version_guess_format_func (void) +{ + g_assert_cmpint (fu_common_version_guess_format (NULL), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format (""), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1234ac"), ==, FU_VERSION_FORMAT_PLAIN); + g_assert_cmpint (fu_common_version_guess_format ("1.2"), ==, FU_VERSION_FORMAT_PAIR); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3"), ==, FU_VERSION_FORMAT_TRIPLET); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4"), ==, FU_VERSION_FORMAT_QUAD); + g_assert_cmpint (fu_common_version_guess_format ("1.2.3.4.5"), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1a.2b.3"), ==, FU_VERSION_FORMAT_UNKNOWN); + g_assert_cmpint (fu_common_version_guess_format ("1"), ==, FU_VERSION_FORMAT_PLAIN); +} + static void fu_engine_requirements_missing_func (void) { @@ -857,7 +871,8 @@ fu_engine_history_func (void) " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " TrustFlags: none\n" + " VersionFormat: triplet\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -986,7 +1001,8 @@ fu_engine_history_error_func (void) " [Release]\n" " Version: 1.2.3\n" " Checksum: SHA1(%s)\n" - " TrustFlags: none\n", + " TrustFlags: none\n" + " VersionFormat: triplet\n", checksum); ret = fu_test_compare_lines (device_str, device_str_expected, &error); g_assert_no_error (error); @@ -3214,6 +3230,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func); 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{version-guess-format}", fu_common_version_guess_format_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);