diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | src/gpg-error.def.in | 2 | ||||
-rw-r--r-- | src/gpg-error.h.in | 9 | ||||
-rw-r--r-- | src/gpg-error.vers | 1 | ||||
-rw-r--r-- | src/gpgrt-int.h | 6 | ||||
-rw-r--r-- | src/version.c | 179 | ||||
-rw-r--r-- | src/visibility.c | 9 | ||||
-rw-r--r-- | src/visibility.h | 4 | ||||
-rw-r--r-- | tests/t-version.c | 93 |
10 files changed, 275 insertions, 34 deletions
diff --git a/Makefile.am b/Makefile.am index b602c7f..4991911 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see <https://www.gnu.org/licenses/>. +# SPDX-License-Identifier: LGPL-2.1-or-later # Location of the released tarball archives. Note that this is an # internal archive and before uploading this to the public server, @@ -2,6 +2,11 @@ Noteworthy changes in version 1.33 (unreleased) [C24/A24/R_] ----------------------------------------------- + * Interface changes relative to the 1.28 release: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + gpgrt_cmp_version New. + + Noteworthy changes in version 1.32 (2018-07-12) [C24/A24/R3] ----------------------------------------------- diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in index 67bb12e..c2fabb0 100644 --- a/src/gpg-error.def.in +++ b/src/gpg-error.def.in @@ -218,5 +218,7 @@ EXPORTS gpgrt_b64enc_write @167 gpgrt_b64enc_finish @168 + gpgrt_cmp_version @169 + ;; end of file with public symbols for Windows. diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in index 8bcafcc..2bf6a6f 100644 --- a/src/gpg-error.h.in +++ b/src/gpg-error.h.in @@ -1271,6 +1271,15 @@ void gpgrt_set_strusage (const char *(*f)(int)); void gpgrt_set_usage_outfnc (int (*f)(int, const char *)); void gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)); + +/* + * Various helper functions + */ + +/* Compare arbitrary version strings. For the standard m.n.o version + * numbering scheme a LEVEL of 3 is suitable; see the manual. */ +int gpgrt_cmp_version (const char *a, const char *b, int level); + #ifdef __cplusplus diff --git a/src/gpg-error.vers b/src/gpg-error.vers index 201b784..8c50a15 100644 --- a/src/gpg-error.vers +++ b/src/gpg-error.vers @@ -190,6 +190,7 @@ GPG_ERROR_1.0 { gpgrt_b64enc_write; gpgrt_b64enc_finish; + gpgrt_cmp_version; local: *; diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h index 34e494c..b5a4dd1 100644 --- a/src/gpgrt-int.h +++ b/src/gpgrt-int.h @@ -742,6 +742,12 @@ void _gpgrt_set_usage_outfnc (int (*fnc)(int, const char *)); void _gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)); +/* + * Various helper functions + */ +int _gpgrt_cmp_version (const char *a, const char *b, int level); + + /* * Internal platform abstraction functions (sysutils.c) 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; } diff --git a/src/visibility.c b/src/visibility.c index 6f8bb24..ab5e383 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -1123,6 +1123,15 @@ gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)) +/* Compare program versions. */ +int +gpgrt_cmp_version (const char *a, const char *b, int level) +{ + return _gpgrt_cmp_version (a, b, level); +} + + + /* For consistency reasons we use function wrappers also for Windows * specific function despite that they are technically not needed. */ #ifdef HAVE_W32_SYSTEM diff --git a/src/visibility.h b/src/visibility.h index cfa32e5..d6933df 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -209,6 +209,8 @@ MARK_VISIBLE (gpgrt_set_strusage) MARK_VISIBLE (gpgrt_set_fixed_string_mapper); MARK_VISIBLE (gpgrt_set_usage_outfnc); +MARK_VISIBLE (gpgrt_cmp_version); + #undef MARK_VISIBLE #else /*!_GPGRT_INCL_BY_VISIBILITY_C*/ @@ -379,6 +381,8 @@ MARK_VISIBLE (gpgrt_set_usage_outfnc); #define gpgrt_set_usage_outfnc _gpgrt_USE_UNDERSCORED_FUNCTION #define gpgrt_set_fixed_string_mapper _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_cmp_version _gpgrt_USE_UNDERSCORED_FUNCTION + /* Windows specific functions. */ #define gpgrt_w32_reg_query_string _gpgrt_USE_UNDERSCORED_FUNCTION diff --git a/tests/t-version.c b/tests/t-version.c index 4606dbc..240d4b4 100644 --- a/tests/t-version.c +++ b/tests/t-version.c @@ -26,12 +26,90 @@ #include <string.h> #include <assert.h> -#include "../src/gpg-error.h" +#define PGM "t-version" +#include "t-common.h" + +static const char *logpfx = PGM; + + +static void +t_gpgrt_cmp_version (void) +{ + struct { int result; int level; const char *a; const char *b; } t[] = { + { 0, 1, "0", "0" }, + { -1, 1, "0", "1" }, + { 1, 1, "1", "0" }, + { -1, 1, "0.0", "0.1" }, + { -1, 1, "0.1", "1.2" }, + { 1, 1, "1.0", "0.9" }, + { -1, 1, "-1.0", "0.9" }, /* A is invalid */ + { 0, 1, "0rc0", "0rc0" }, + { 1, 1, "0rc1", "0rc0" }, + { -1, 1, "0rc1", "0rc2" }, + { 0, 1, "0.rc0", "0.rc0" }, + { 1, 1, "0.rc1", "0.rc0" }, + { -1, 1, "0.rc1", "0.rc2" }, + { 0, 1, "0.rc1", "0.rc1" }, + { -1, 1, "0qc1", "0rc0" }, + { -1, 1, "0.qc1", "0.rc0" }, + { 0, 2, "0.0", "0.0" }, + { -1, 2, "0.1", "0.2" }, + { -1, 2, "3.1", "3.2" }, + { -1, 2, "3.1", "4.0" }, + { 0, 2, "1.1rc0", "1.1rc0" }, + { 1, 2, "1.1rc1", "1.1rc0" }, + { -1, 2, "1.1rc0", "1.1rc1" }, + { 0, 3, "7.0.0", "7.0.0" }, + { -1, 3, "7.0.1", "7.0.2" }, + { -1, 3, "7.3.1", "7.3.2" }, + { -1, 3, "7.3.1", "7.4.0" }, + { 0, 3, "7.1.1rc0", "7.1.1rc0" }, + { 1, 3, "7.1.1rc1", "7.1.1rc0" }, + { -1, 3, "7.1.1rc0", "7.1.1rc1" }, + { 1, 3, "6.0.0", "5.0.0" }, + { 0, 3, "6.0.0", "6.0.0" }, + { 1, 3, "6.0.1", "6.0.0" }, + { 1, 3, "6.1.0", "6.0.0" }, + { 1, 3, "6.2.1", "6.2.0" }, + { -1, 3, "6.2.1", "6.2.2" }, + { -1, 3, "6.0.0", "6.0.2" }, + { -1, 3, "6.0.0", "6.1.0" }, + { -1, 3, "6.2.0", "6.2.1" }, + { 1, 3, "6.0.0-beta1", "6.0.0-beta0" }, + { 0, 3, "6.0.0-beta2", "6.0.0-beta2" }, + { 1, 3, "6.0.0-beta20", "6.0.0-beta19" }, + { -1, 3, "6.0.0-beta1", "6.0.0-beta2" }, + { 1, 3, "6.0.0-beta2", "6.0.0-beta1" }, + { -1, 3, "6.0.0-beta20", "6.0.0-beta21" }, + { 0,13, "6.0.0-beta1", "6.0.0-beta0" }, + { 0,13, "6.0.0-beta2", "6.0.0-beta2" }, + { 0,13, "6.0.0-beta20", "6.0.0-beta19" }, + { 0,13, "6.0.0-beta1", "6.0.0-beta2" }, + { 0,13, "6.0.0-beta2", "6.0.0-beta1" }, + { 0,13, "6.0.0-beta20", "6.0.0-beta21" } + }; + int i; + int result, expected; + + for (i=0; i < DIM (t); i++) + { + expected = t[i].result; + result = gpgrt_cmp_version (t[i].a, t[i].b, t[i].level); + if (result != expected) + fail ("test %d failed: cmp('%s','%s',%d) = %d expected %d", + i, t[i].a, t[i].b, t[i].level, result, expected); + } + for (i=0; i < DIM (t); i++) + { + expected = 0 - t[i].result; + result = gpgrt_cmp_version (t[i].a, t[i].b, -t[i].level); + if (result != expected) + fail ("test %d-rev failed: cmp('%s','%s',%d) = %d expected %d", + i, t[i].a, t[i].b, -t[i].level, result, expected); + } +} + -static const char *logpfx = ""; -static int verbose; -static int debug; -static int errorcount; int main (int argc, char **argv) @@ -40,7 +118,6 @@ main (int argc, char **argv) if (argc) { - logpfx = *argv; argc--; argv++; } while (argc && last_argc != argc ) @@ -68,6 +145,8 @@ main (int argc, char **argv) } } + t_gpgrt_cmp_version (); + if (!gpg_error_check_version (GPG_ERROR_VERSION)) { fprintf (stderr, "%s: gpg_error_check_version returned an error\n", @@ -80,7 +159,7 @@ main (int argc, char **argv) "error for an old version\n", logpfx); errorcount++; } - if (gpg_error_check_version ("15")) + if (gpg_error_check_version ("15.0")) { fprintf (stderr, "%s: gpg_error_check_version did not return an error" " for a newer version\n", logpfx); |