summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/stringhelp.c85
-rw-r--r--common/stringhelp.h3
-rw-r--r--common/t-stringhelp.c63
3 files changed, 150 insertions, 1 deletions
diff --git a/common/stringhelp.c b/common/stringhelp.c
index e8b990a13..8b47a1c7b 100644
--- a/common/stringhelp.c
+++ b/common/stringhelp.c
@@ -1329,6 +1329,91 @@ strtokenize (const char *string, const char *delim)
}
+
+/* Version number parsing. */
+
+/* This function parses the first portion of the version number S and
+ stores it in *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)
+{
+ int val = 0;
+
+ if (*s == '0' && digitp (s+1))
+ return NULL; /* Leading zeros are not allowed. */
+ for (; digitp (s); s++)
+ {
+ val *= 10;
+ val += *s - '0';
+ }
+ *number = val;
+ return val < 0 ? NULL : s;
+}
+
+
+/* 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.
+
+ 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, int *micro)
+{
+ s = parse_version_number (s, major);
+ if (!s || *s != '.')
+ return NULL;
+ s++;
+ s = parse_version_number (s, minor);
+ if (!s)
+ return NULL;
+ if (*s == '.')
+ {
+ s++;
+ s = parse_version_number (s, micro);
+ if (!s)
+ return NULL;
+ }
+ else
+ *micro = 0;
+ return s; /* Patchlevel. */
+}
+
+
+/* Check that the version string MY_VERSION is greater or equal than
+ REQ_VERSION. Returns true if the condition is satisfied or false
+ if not. This works with 3 part and two part version strings; for a
+ two part version string the micor part is assumed to be 0. */
+int
+compare_version_strings (const char *my_version, const char *req_version)
+{
+ int my_major, my_minor, my_micro;
+ int rq_major, rq_minor, rq_micro;
+
+ if (!my_version || !req_version)
+ return 0;
+
+ if (!parse_version_string (my_version, &my_major, &my_minor, &my_micro))
+ return 0;
+ if (!parse_version_string(req_version, &rq_major, &rq_minor, &rq_micro))
+ return 0;
+
+ if (my_major > rq_major
+ || (my_major == rq_major && my_minor > rq_minor)
+ || (my_major == rq_major && my_minor == rq_minor
+ && my_micro >= rq_micro))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+
/* Format a string so that it fits within about TARGET_COLS columns.
If IN_PLACE is 0, then TEXT is copied to a new buffer, which is
returned. Otherwise, TEXT is modified in place and returned.
diff --git a/common/stringhelp.h b/common/stringhelp.h
index c813662c7..d9225a3bb 100644
--- a/common/stringhelp.h
+++ b/common/stringhelp.h
@@ -148,6 +148,9 @@ char **strsplit (char *string, char delim, char replacement, int *count);
/* Tokenize STRING using the set of delimiters in DELIM. */
char **strtokenize (const char *string, const char *delim);
+/* Return True if MYVERSION is greater or equal than REQ_VERSION. */
+int compare_version_strings (const char *my_version, const char *req_version);
+
/* Format a string so that it fits within about TARGET_COLS columns. */
char *format_text (char *text, int in_place, int target_cols, int max_cols);
diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c
index af79cb5cd..b4a41ac39 100644
--- a/common/t-stringhelp.c
+++ b/common/t-stringhelp.c
@@ -705,6 +705,7 @@ stresc (char *s)
return p;
}
+
static void
test_format_text (void)
{
@@ -813,6 +814,65 @@ test_format_text (void)
fail(0);
}
+
+static void
+test_compare_version_strings (void)
+{
+ struct { const char *a; const char *b; int okay; } tests[] = {
+ { "1.0.0", "1.0.0", 1 },
+ { "1.0.0-", "1.0.0", 1 },
+ { "1.0.0-1", "1.0.0", 1 },
+ { "1.0.0.1", "1.0.0", 1 },
+ { "1.0.0", "1.0.1", 0 },
+ { "1.0.0-", "1.0.1", 0 },
+ { "1.0.0-1", "1.0.1", 0 },
+ { "1.0.0.1", "1.0.1", 0 },
+ { "1.0.0", "1.1.0", 0 },
+ { "1.0.0-", "1.1.0", 0 },
+ { "1.0.0-1", "1.1.0", 0 },
+ { "1.0.0.1", "1.1.0", 0 },
+
+ { "1.0.0", "1.0.0-", 1 },
+ { "1.0.0", "1.0.0-1", 1 },
+ { "1.0.0", "1.0.0.1", 1 },
+ { "1.1.0", "1.0.0", 1 },
+ { "1.1.1", "1.1.0", 1 },
+ { "1.1.2", "1.1.2", 1 },
+ { "1.1.2", "1.0.2", 1 },
+ { "1.1.2", "0.0.2", 1 },
+ { "1.1.2", "1.1.3", 0 },
+
+ { "0.99.1", "0.9.9", 1 },
+ { "0.9.1", "0.91.0", 0 },
+
+ { "1.5.3", "1.5", 1 },
+ { "1.5.0", "1.5", 1 },
+ { "1.4.99", "1.5", 0 },
+ { "1.5", "1.4.99", 1 },
+ { "1.5", "1.5.0", 1 },
+ { "1.5", "1.5.1", 0 },
+
+ { "1.5.3-x17", "1.5-23", 1 },
+
+ { "1.5.3a", "1.5.3", 1 },
+ { "1.5.3a", "1.5.3b", 1 },
+
+ { NULL, NULL, 0 }
+ };
+ int idx;
+ int res;
+
+ for (idx=0; idx < DIM(tests); idx++)
+ {
+ res = compare_version_strings (tests[idx].a, tests[idx].b);
+ /* printf ("test %d: '%s' '%s' %d -> %d\n", */
+ /* idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */
+ if (res != tests[idx].okay)
+ fail (idx);
+ }
+}
+
+
int
main (int argc, char **argv)
{
@@ -827,8 +887,9 @@ main (int argc, char **argv)
test_make_absfilename_try ();
test_strsplit ();
test_strtokenize ();
+ test_compare_version_strings ();
test_format_text ();
xfree (home_buffer);
- return 0;
+ return !!errcount;
}