diff --git a/libfwupdplugin/fu-common-version.c b/libfwupdplugin/fu-common-version.c index 60d34d528..0c0be97f9 100644 --- a/libfwupdplugin/fu-common-version.c +++ b/libfwupdplugin/fu-common-version.c @@ -235,6 +235,73 @@ _g_ascii_is_digits(const gchar *str) return TRUE; } +static guint +fwupd_version_format_number_sections(FwupdVersionFormat fmt) +{ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN || fmt == FWUPD_VERSION_FORMAT_NUMBER || + fmt == FWUPD_VERSION_FORMAT_HEX) + return 1; + if (fmt == FWUPD_VERSION_FORMAT_PAIR || fmt == FWUPD_VERSION_FORMAT_BCD) + return 2; + if (fmt == FWUPD_VERSION_FORMAT_TRIPLET || fmt == FWUPD_VERSION_FORMAT_SURFACE_LEGACY || + fmt == FWUPD_VERSION_FORMAT_SURFACE || fmt == FWUPD_VERSION_FORMAT_DELL_BIOS) + return 3; + if (fmt == FWUPD_VERSION_FORMAT_QUAD || fmt == FWUPD_VERSION_FORMAT_INTEL_ME || + fmt == FWUPD_VERSION_FORMAT_INTEL_ME2) + return 4; + return 0; +} + +/** + * fu_common_version_ensure_semver_full: + * @version: (nullable): a version number, e.g. ` V1.2.3 ` + * @fmt: a version format, e.g. %FWUPD_VERSION_FORMAT_TRIPLET + * + * Builds a semver from the possibly crazy version number. Depending on the @semver value + * the string will be split and a string in the correct format will be returned. + * + * Returns: a version number, e.g. `1.2.3`, or %NULL if the version was not valid + * + * Since: 1.7.6 + */ +gchar * +fu_common_version_ensure_semver_full(const gchar *version, FwupdVersionFormat fmt) +{ + guint sections_actual; + guint sections_expected = fwupd_version_format_number_sections(fmt); + g_autofree gchar *tmp = NULL; + g_auto(GStrv) split = NULL; + g_autoptr(GString) str = g_string_new(NULL); + + /* split into all sections */ + tmp = fu_common_version_ensure_semver(version); + if (tmp == NULL) + return NULL; + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) + return g_steal_pointer(&tmp); + split = g_strsplit(tmp, ".", -1); + sections_actual = g_strv_length(split); + + /* add zero sections as required */ + if (sections_actual < sections_expected) { + for (guint i = 0; i < sections_expected - sections_actual; i++) { + if (str->len > 0) + g_string_append(str, "."); + g_string_append(str, "0"); + } + } + + /* only add enough sections for the format */ + for (guint i = 0; i < sections_actual && i < sections_expected; i++) { + if (str->len > 0) + g_string_append(str, "."); + g_string_append(str, split[i]); + } + + /* success */ + return g_string_free(g_steal_pointer(&str), FALSE); +} + /** * fu_common_version_ensure_semver: * @version: (nullable): a version number, e.g. ` V1.2.3 ` @@ -281,6 +348,10 @@ fu_common_version_ensure_semver(const gchar *version) } } + /* remove any trailing dot */ + if (version_safe->len > 0 && version_safe->str[version_safe->len - 1] == '.') + g_string_truncate(version_safe, version_safe->len - 1); + /* found no digits */ if (digit_cnt == 0) return NULL; diff --git a/libfwupdplugin/fu-common-version.h b/libfwupdplugin/fu-common-version.h index 3eb59bad9..7c0f7515c 100644 --- a/libfwupdplugin/fu-common-version.h +++ b/libfwupdplugin/fu-common-version.h @@ -21,6 +21,8 @@ gchar * fu_common_version_parse_from_format(const gchar *version, FwupdVersionFormat fmt); gchar * fu_common_version_ensure_semver(const gchar *version); +gchar * +fu_common_version_ensure_semver_full(const gchar *version, FwupdVersionFormat fmt); FwupdVersionFormat fu_common_version_guess_format(const gchar *version); gboolean diff --git a/libfwupdplugin/fu-device.c b/libfwupdplugin/fu-device.c index 5e91d0c61..70b4640a6 100644 --- a/libfwupdplugin/fu-device.c +++ b/libfwupdplugin/fu-device.c @@ -2411,7 +2411,9 @@ fu_device_set_version(FuDevice *self, const gchar *version) /* sanitize if required */ if (fu_device_has_internal_flag(self, FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER)) { - version_safe = fu_common_version_ensure_semver(version); + version_safe = + fu_common_version_ensure_semver_full(version, + fu_device_get_version_format(self)); if (g_strcmp0(version, version_safe) != 0) g_debug("converted '%s' to '%s'", version, version_safe); } else { diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index af37f7891..5be04b780 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -2203,6 +2203,28 @@ fu_common_version_semver_func(void) } } +static void +fu_common_version_semver_full_func(void) +{ + struct { + const gchar *old; + const gchar *new; + FwupdVersionFormat fmt; + } map[] = {{"1.2.3", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1.2.3.4", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1.2", "0.1.2", FWUPD_VERSION_FORMAT_TRIPLET}, + {"1", "0.0.1", FWUPD_VERSION_FORMAT_TRIPLET}, + {"CBET1.2.3", "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET}, + {"4.11-1190-g12d8072e6b-dirty", "4.11.1190", FWUPD_VERSION_FORMAT_TRIPLET}, + {"4.11-1190-g12d8072e6b-dirty", "4.11", FWUPD_VERSION_FORMAT_PAIR}, + {NULL, NULL}}; + for (guint i = 0; map[i].old != NULL; i++) { + g_autofree gchar *tmp = + fu_common_version_ensure_semver_full(map[i].old, map[i].fmt); + g_assert_cmpstr(tmp, ==, map[i].new); + } +} + static void fu_common_strtoull_func(void) { @@ -3944,6 +3966,7 @@ main(int argc, char **argv) g_test_add_func("/fwupd/common{strtoull}", fu_common_strtoull_func); g_test_add_func("/fwupd/common{version}", fu_common_version_func); g_test_add_func("/fwupd/common{version-semver}", fu_common_version_semver_func); + g_test_add_func("/fwupd/common{version-semver-full}", fu_common_version_semver_full_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); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 0a85346b6..ccabf2c65 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -995,3 +995,9 @@ LIBFWUPDPLUGIN_1.7.4 { fu_usb_device_set_configuration; local: *; } LIBFWUPDPLUGIN_1.7.3; + +LIBFWUPDPLUGIN_1.7.6 { + global: + fu_common_version_ensure_semver_full; + local: *; +} LIBFWUPDPLUGIN_1.7.4;