diff options
author | Werner Koch <[email protected]> | 2018-11-15 18:18:53 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2018-11-15 18:28:20 +0000 |
commit | a5d4a4b32b11814d673241d62624ecec1d577571 (patch) | |
tree | f276ffcc2ae1522ee83666dc6da4774bb602a26e /src/version.c | |
parent | gpgrt-config: Prepend PKG_CONFIG_LIBDIR to PKG_CONFIG_PATH. (diff) | |
download | libgpg-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.c | 179 |
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; } |