aboutsummaryrefslogtreecommitdiffstats
path: root/src/version.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2018-11-15 18:18:53 +0000
committerWerner Koch <[email protected]>2018-11-15 18:28:20 +0000
commita5d4a4b32b11814d673241d62624ecec1d577571 (patch)
treef276ffcc2ae1522ee83666dc6da4774bb602a26e /src/version.c
parentgpgrt-config: Prepend PKG_CONFIG_LIBDIR to PKG_CONFIG_PATH. (diff)
downloadlibgpg-error-a5d4a4b32b11814d673241d62624ecec1d577571.tar.gz
libgpg-error-a5d4a4b32b11814d673241d62624ecec1d577571.zip
core: New API gpgrt_cmp_version
* src/gpg-error.h.in: New API gpgrt_cmp_version. * src/visibility.c (gpgrt_cmp_version): New wrapper. * src/version.c (parse_version_string): Revamped. (do_cmp_version): New. (_gpgrt_cmp_version): New. (_gpg_error_check_version): Re-implemented using the new func. * tests/t-version.c: Include t-common.h. (t_gpgrt_cmp_version): New test. (main): Run new test. Change test for new version number to require a 2 level number. -- We have implementations of very similar functions in all out libs. Thus it makes sense to provide a generic version. This version is actually derived from the ftp-indexer.c we use for the gnupg website (see the gnupg-doc repo). Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'src/version.c')
-rw-r--r--src/version.c179
1 files changed, 152 insertions, 27 deletions
diff --git a/src/version.c b/src/version.c
index 94b75af..ac2f057 100644
--- a/src/version.c
+++ b/src/version.c
@@ -47,6 +47,10 @@ cright_blurb (void)
}
+/* This function parses the first portion of the version number S and
+ * stores it at NUMBER. On success, this function returns a pointer
+ * into S starting with the first character, which is not part of the
+ * initial number portion; on failure, NULL is returned. */
static const char*
parse_version_number (const char *s, int *number)
{
@@ -64,45 +68,161 @@ parse_version_number (const char *s, int *number)
}
+/* This function breaks up the complete string-representation of the
+ * version number S, which is of the following struture: <major
+ * number>.<minor number>.<micro number><patch level>. The major,
+ * minor and micro number components will be stored in *MAJOR, *MINOR
+ * and *MICRO. If MINOR or MICRO is NULL the version number is
+ * assumed to have just 1 respective 2 parts.
+ *
+ * On success, the last component, the patch level, will be returned;
+ * in failure, NULL will be returned. */
static const char *
-parse_version_string (const char *s, int *major, int *minor)
+parse_version_string (const char *s, int *major, int *minor, int *micro)
{
s = parse_version_number (s, major);
- if (!s || *s != '.')
- return NULL;
- s++;
- s = parse_version_number (s, minor);
if (!s)
return NULL;
- return s; /* Patchlevel. */
+ if (!minor)
+ {
+ if (*s == '.')
+ s++;
+ }
+ else
+ {
+ if (*s != '.')
+ return NULL;
+ s++;
+ s = parse_version_number (s, minor);
+ if (!s)
+ return NULL;
+ if (!micro)
+ {
+ if (*s == '.')
+ s++;
+ }
+ else
+ {
+ if (*s != '.')
+ return NULL;
+ s++;
+ s = parse_version_number (s, micro);
+ if (!s)
+ return NULL;
+ }
+ }
+ return s; /* patchlevel */
}
-static const char *
-compare_versions (const char *my_version, const char *req_version)
+/* Helper for _gpgrt_cmp_version. */
+static int
+do_cmp_version (const char *a, const char *b, int level)
{
- int my_major, my_minor;
- int rq_major, rq_minor;
- const char *my_plvl, *rq_plvl;
+ int a_major, a_minor, a_micro;
+ int b_major, b_minor, b_micro;
+ const char *a_plvl, *b_plvl;
+ int r;
+ int ignore_plvl;
+ int positive, negative;
+
+ if (level < 0)
+ {
+ positive = -1;
+ negative = 1;
+ level = 0 - level;
+ }
+ else
+ {
+ positive = 1;
+ negative = -1;
+ }
+ if ((ignore_plvl = (level > 9)))
+ level %= 10;
+
+ a_major = a_minor = a_micro = 0;
+ a_plvl = parse_version_string (a, &a_major,
+ level > 1? &a_minor : NULL,
+ level > 2? &a_micro : NULL);
+ if (!a_plvl)
+ a_major = a_minor = a_micro = 0; /* Error. */
+
+ b_major = b_minor = b_micro = 0;
+ b_plvl = parse_version_string (b, &b_major,
+ level > 1? &b_minor : NULL,
+ level > 2? &b_micro : NULL);
+ if (!b_plvl)
+ b_major = b_minor = b_micro = 0;
+
+ if (!ignore_plvl)
+ {
+ if (!a_plvl && !b_plvl)
+ return negative; /* Put invalid strings at the end. */
+ if (a_plvl && !b_plvl)
+ return positive;
+ if (!a_plvl && b_plvl)
+ return negative;
+ }
- if (!req_version)
- return my_version;
- if (!my_version)
- return NULL;
+ if (a_major > b_major)
+ return positive;
+ if (a_major < b_major)
+ return negative;
+
+ if (a_minor > b_minor)
+ return positive;
+ if (a_minor < b_minor)
+ return negative;
+
+ if (a_micro > b_micro)
+ return positive;
+ if (a_micro < b_micro)
+ return negative;
- my_plvl = parse_version_string (my_version, &my_major, &my_minor);
- if (!my_plvl)
- return NULL; /* Very strange: our own version is bogus. */
- rq_plvl = parse_version_string(req_version, &rq_major, &rq_minor);
- if (!rq_plvl)
- return NULL; /* Requested version string is invalid. */
+ if (ignore_plvl)
+ return 0;
- if (my_major > rq_major
- || (my_major == rq_major && my_minor >= rq_minor))
+ for (; *a_plvl && *b_plvl; a_plvl++, b_plvl++)
{
- return my_version;
- }
- return NULL;
+ if (*a_plvl == '.' && *b_plvl == '.')
+ {
+ r = strcmp (a_plvl, b_plvl);
+ if (!r)
+ return 0;
+ else if ( r > 0 )
+ return positive;
+ else
+ return negative;
+ }
+ else if (*a_plvl == '.')
+ return negative; /* B is larger. */
+ else if (*b_plvl == '.')
+ return positive; /* A is larger. */
+ else if (*a_plvl != *b_plvl)
+ break;
+ }
+ if (*a_plvl == *b_plvl)
+ return 0;
+ else if ((*(signed char *)a_plvl - *(signed char *)b_plvl) > 0)
+ return positive;
+ else
+ return negative;
+}
+
+
+/* Compare function for version strings. The return value is
+ * like strcmp(). LEVEL may be
+ * 0 - reserved
+ * 1 - format is "<major><patchlevel>".
+ * 2 - format is "<major>.<minor><patchlevel>".
+ * 3 - format is "<major>.<minor>.<micro><patchlevel>".
+ * To ignore the patchlevel in the comparison add 10 to LEVEL. To get
+ * a reverse sorting order use a negative number.
+ */
+int
+_gpgrt_cmp_version (const char *a, const char *b, int level)
+{
+ return do_cmp_version (a, b, level);
}
@@ -115,7 +235,12 @@ compare_versions (const char *my_version, const char *req_version)
const char *
_gpg_error_check_version (const char *req_version)
{
+ const char *my_version = PACKAGE_VERSION;
+
if (req_version && req_version[0] == 1 && req_version[1] == 1)
return cright_blurb ();
- return compare_versions (PACKAGE_VERSION, req_version);
+ if (!req_version)
+ return my_version;
+ return _gpgrt_cmp_version
+ (my_version, req_version, 12) >= 0 ? my_version : NULL;
}