From ccec0538498cf1f8171efea92c9eaa43d4875894 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 1 May 2017 19:16:44 +0100 Subject: Support non-numeric version numbers correctly Fall back to strcmp when both sections are non-numeric. Also add a 'vercmp' command to appstream-util to allow us to use this on the command line. --- client/as-util.c | 40 ++++++++++++++++++++++++++++++++++++++++ libappstream-glib/as-self-test.c | 9 +++++++-- libappstream-glib/as-utils.c | 30 +++++++++++++++++++++++------- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/client/as-util.c b/client/as-util.c index 20ddb7b..fea9a64 100644 --- a/client/as-util.c +++ b/client/as-util.c @@ -4129,6 +4129,40 @@ as_util_markup_import (AsUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +as_util_vercmp (AsUtilPrivate *priv, gchar **values, GError **error) +{ + gint rc; + + /* check args */ + if (g_strv_length (values) != 2) { + g_set_error_literal (error, + AS_ERROR, + AS_ERROR_INVALID_ARGUMENTS, + "expected VERSION1 VERSION2"); + return FALSE; + } + + /* compare */ + rc = as_utils_vercmp (values[0], values[1]); + if (rc == G_MAXINT) { + g_set_error_literal (error, + AS_ERROR, + AS_ERROR_INVALID_ARGUMENTS, + "failed to compare version numbers"); + return FALSE; + } + + /* print results */ + if (rc == 0) + g_print ("%s = %s\n", values[0], values[1]); + else if (rc < 0) + g_print ("%s < %s\n", values[0], values[1]); + else if (rc > 0) + g_print ("%s > %s\n", values[0], values[1]); + return TRUE; +} + static void as_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) @@ -4414,6 +4448,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Watch AppStream locations for changes"), as_util_watch); + as_util_add (priv->cmd_array, + "vercmp", + NULL, + /* TRANSLATORS: command description */ + _("Compare version numbers"), + as_util_vercmp); /* sort by command name */ g_ptr_array_sort (priv->cmd_array, diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c index 48a4ba2..93935e4 100644 --- a/libappstream-glib/as-self-test.c +++ b/libappstream-glib/as-self-test.c @@ -4959,11 +4959,16 @@ as_test_utils_vercmp_func (void) g_assert_cmpint (as_utils_vercmp ("1.2.3", "1.2.3.1"), <, 0); g_assert_cmpint (as_utils_vercmp ("1.2.3.1", "1.2.4"), <, 0); - /* non-numeric */ + /* mixed-alpha-numeric */ g_assert_cmpint (as_utils_vercmp ("1.2xxx.3", "1.2.3"), ==, G_MAXINT); - g_assert_cmpint (as_utils_vercmp ("1.2a.3", "1.2b.3"), ==, G_MAXINT); + g_assert_cmpint (as_utils_vercmp ("1.2.3", "1.2xxx.3"), ==, G_MAXINT); g_assert_cmpint (as_utils_vercmp ("1.2.-3", "1.2.3"), ==, G_MAXINT); + /* alpha-compare */ + g_assert_cmpint (as_utils_vercmp ("1.2a.3", "1.2a.3"), ==, 0); + g_assert_cmpint (as_utils_vercmp ("1.2a.3", "1.2b.3"), <, 0); + g_assert_cmpint (as_utils_vercmp ("1.2b.3", "1.2a.3"), >, 0); + /* invalid */ g_assert_cmpint (as_utils_vercmp ("1", NULL), ==, G_MAXINT); g_assert_cmpint (as_utils_vercmp (NULL, "1"), ==, G_MAXINT); diff --git a/libappstream-glib/as-utils.c b/libappstream-glib/as-utils.c index 76e4dd6..6303ac3 100644 --- a/libappstream-glib/as-utils.c +++ b/libappstream-glib/as-utils.c @@ -1378,6 +1378,8 @@ as_utils_vercmp (const gchar *version_a, const gchar *version_b) split_b = g_strsplit (str_b, ".", -1); longest_split = MAX (g_strv_length (split_a), g_strv_length (split_b)); for (i = 0; i < longest_split; i++) { + gboolean isnum_a = TRUE; + gboolean isnum_b = TRUE; /* we lost or gained a dot */ if (split_a[i] == NULL) @@ -1388,18 +1390,32 @@ as_utils_vercmp (const gchar *version_a, const gchar *version_b) /* compare integers */ ver_a = g_ascii_strtoll (split_a[i], &endptr, 10); if (endptr != NULL && endptr[0] != '\0') - return G_MAXINT; + isnum_a = FALSE; if (ver_a < 0) - return G_MAXINT; + isnum_a = FALSE; ver_b = g_ascii_strtoll (split_b[i], &endptr, 10); if (endptr != NULL && endptr[0] != '\0') - return G_MAXINT; + isnum_b = FALSE; if (ver_b < 0) + isnum_b = FALSE; + + /* can't compare integer with string */ + if (isnum_a != isnum_b) return G_MAXINT; - if (ver_a < ver_b) - return -1; - if (ver_a > ver_b) - return 1; + + /* compare strings */ + if (!isnum_a) { + gint rc = g_strcmp0 (split_a[i], split_b[i]); + if (rc != 0) + return rc; + + /* compare integers */ + } else { + if (ver_a < ver_b) + return -1; + if (ver_a > ver_b) + return 1; + } } /* we really shouldn't get here */ -- cgit v1.2.1