aboutsummaryrefslogtreecommitdiffstats
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
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]>
-rw-r--r--Makefile.am1
-rw-r--r--NEWS5
-rw-r--r--src/gpg-error.def.in2
-rw-r--r--src/gpg-error.h.in9
-rw-r--r--src/gpg-error.vers1
-rw-r--r--src/gpgrt-int.h6
-rw-r--r--src/version.c179
-rw-r--r--src/visibility.c9
-rw-r--r--src/visibility.h4
-rw-r--r--tests/t-version.c93
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,
diff --git a/NEWS b/NEWS
index 8c605b2..f1facf4 100644
--- a/NEWS
+++ b/NEWS
@@ -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);