From 2163009270c8dd1d0e93d0f91e3673c69ded7b25 Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Wed, 20 Feb 2019 11:31:05 +0300 Subject: [PATCH 1/2] Ticket #3905, 3956: fix file version sort. * Update str_verscmp implementation: sync with Gnulib. * Add tests. Testcases are taken from Gnulib. Signed-off-by: Andrew Borodin --- lib/strutil/strverscmp.c | 76 ++++++++++------- tests/lib/strutil/Makefile.am | 6 +- tests/lib/strutil/str_verscmp.c | 143 ++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 31 deletions(-) create mode 100644 tests/lib/strutil/str_verscmp.c diff --git a/lib/strutil/strverscmp.c b/lib/strutil/strverscmp.c index 92871700c..5a00fa4c0 100644 --- a/lib/strutil/strverscmp.c +++ b/lib/strutil/strverscmp.c @@ -5,7 +5,21 @@ Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Jean-Fran�ois Bignolles , 1997. + Contributed by Jean-François Bignolles , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . This file is part of the Midnight Commander. @@ -39,11 +53,11 @@ #ifndef HAVE_STRVERSCMP /* states: S_N: normal, S_I: comparing integral part, S_F: comparing - fractionnal parts, S_Z: idem but with leading Zeroes only */ + fractional parts, S_Z: idem but with leading Zeroes only */ #define S_N 0x0 -#define S_I 0x4 -#define S_F 0x8 -#define S_Z 0xC +#define S_I 0x3 +#define S_F 0x6 +#define S_Z 0x9 /* result_type: CMP: return diff; LEN: compare using len_diff/diff */ #define CMP 2 @@ -73,35 +87,34 @@ str_verscmp (const char *s1, const char *s2) return strverscmp (s1, s2); #else /* HAVE_STRVERSCMP */ - unsigned char *p1 = (unsigned char *) s1; - unsigned char *p2 = (unsigned char *) s2; + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; unsigned char c1, c2; int state; int diff; - /* Symbol(s) 0 [1-9] others (padding) - Transition (10) 0 (01) d (00) x (11) - */ - static const unsigned int next_state[] = { - /* state x d 0 - */ - /* S_N */ S_N, S_I, S_Z, S_N, - /* S_I */ S_N, S_I, S_I, S_I, - /* S_F */ S_N, S_F, S_F, S_F, - /* S_Z */ S_N, S_F, S_Z, S_Z + /* *INDENT-OFF* */ + /* Symbol(s) 0 [1-9] others + Transition (10) 0 (01) d (00) x */ + static const unsigned char next_state[] = + { + /* state x d 0 */ + /* S_N */ S_N, S_I, S_Z, + /* S_I */ S_N, S_I, S_I, + /* S_F */ S_N, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z }; - static const int result_type[] = { - /* state x/x x/d x/0 x/- d/x d/d d/0 d/- - 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + static const signed char result_type[] = + { + /* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */ - /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, - CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, - /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP, - +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, - /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, - CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, - /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP, - -1, CMP, CMP, CMP + /* S_N */ CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, +1, LEN, LEN, +1, LEN, LEN, + /* S_F */ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, -1, CMP, CMP, -1, CMP, CMP }; + /* *INDENT-ON* */ if (p1 == p2) return 0; @@ -109,17 +122,20 @@ str_verscmp (const char *s1, const char *s2) c1 = *p1++; c2 = *p2++; /* Hint: '0' is a digit too. */ - state = S_N | ((c1 == '0') + (isdigit (c1) != 0)); + state = S_N + ((c1 == '0') + (isdigit (c1) != 0)); - while ((diff = c1 - c2) == 0 && c1 != '\0') + while ((diff = c1 - c2) == 0) { + if (c1 == '\0') + return diff; + state = next_state[state]; c1 = *p1++; c2 = *p2++; - state |= (c1 == '0') + (isdigit (c1) != 0); + state += (c1 == '0') + (isdigit (c1) != 0); } - state = result_type[state << 2 | (((c2 == '0') + (isdigit (c2) != 0)))]; + state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))]; switch (state) { diff --git a/tests/lib/strutil/Makefile.am b/tests/lib/strutil/Makefile.am index 29b2f5219..a8ec2dd30 100644 --- a/tests/lib/strutil/Makefile.am +++ b/tests/lib/strutil/Makefile.am @@ -15,7 +15,8 @@ endif TESTS = \ replace__str_replace_all \ - parse_integer + parse_integer \ + str_verscmp check_PROGRAMS = $(TESTS) @@ -24,3 +25,6 @@ replace__str_replace_all_SOURCES = \ parse_integer_SOURCES = \ parse_integer.c + +str_verscmp_SOURCES = \ + str_verscmp.c diff --git a/tests/lib/strutil/str_verscmp.c b/tests/lib/strutil/str_verscmp.c new file mode 100644 index 000000000..f9a437bc2 --- /dev/null +++ b/tests/lib/strutil/str_verscmp.c @@ -0,0 +1,143 @@ +/* + lib/strutil - tests for lib/strutil/str_verscmp function. + Testcases are taken from Gnulib. + + Copyright (C) 2019 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin , 2019 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#define TEST_SUITE_NAME "/lib/strutil" + +#include "tests/mctest.h" + +#include "lib/strutil.h" + +/* --------------------------------------------------------------------------------------------- */ + +/* From glibc bug 9913 */ +static char const a[] = "B0075022800016.gbp.corp.com"; +static char const b[] = "B007502280067.gbp.corp.com"; +static char const c[] = "B007502357019.GBP.CORP.COM"; + +/* --------------------------------------------------------------------------------------------- */ + +/* @Before */ +static void +setup (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @After */ +static void +teardown (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sign (int n) +{ + return ((n < 0) ? -1 : (n == 0) ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @DataSource("str_verscmp_test_ds") */ +/* *INDENT-OFF* */ +static const struct str_verscmp_test_struct +{ + const char *s1; + const char *s2; + int expected_result; +} str_verscmp_test_ds[] = +{ + { "", "", 0 }, + { "a", "a", 0 }, + { "a", "b", -1 }, + { "b", "a", 1 }, + { "000", "00", -1 }, + { "00", "000", 1 }, + { "a0", "a", 1 }, + { "00", "01", -1 }, + { "01", "010", -1 }, + { "010", "09", -1 }, + { "09", "0", -1 }, + { "9", "10", -1 }, + { "0a", "0", 1 }, + /* From glibc bug 9913 */ + { a, b, -1 }, + { b, c, -1 }, + { a, c, -1 }, + { b, a, 1 }, + { c, b, 1 }, + { c, a, 1 } +}; +/* *INDENT-ON* */ + +/* @Test(dataSource = "str_verscmp_test_ds") */ +/* *INDENT-OFF* */ +START_TEST (str_verscmp_test) +/* *INDENT-ON* */ +{ + /* given */ + int actual_result; + const struct str_verscmp_test_struct *data = &str_verscmp_test_ds[_i]; + + /* when */ + actual_result = str_verscmp (data->s1, data->s2); + + /* then */ + mctest_assert_int_eq (sign (actual_result), sign (data->expected_result)); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* --------------------------------------------------------------------------------------------- */ + +int +main (void) +{ + int number_failed; + + Suite *s = suite_create (TEST_SUITE_NAME); + TCase *tc_core = tcase_create ("Core"); + SRunner *sr; + + tcase_add_checked_fixture (tc_core, setup, teardown); + + /* Add new tests here: *************** */ + mctest_add_parameterized_test (tc_core, str_verscmp_test, str_verscmp_test_ds); + /* *********************************** */ + + suite_add_tcase (s, tc_core); + sr = srunner_create (s); + srunner_set_log (sr, "str_verscmp.log"); + srunner_run_all (sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* --------------------------------------------------------------------------------------------- */ From a2a5fa017efced2c9fc4f8c97ba90e88a613466f Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Wed, 20 Feb 2019 15:39:13 +0300 Subject: [PATCH 2/2] Fix file version sort. Use filevercmp() instead of str_verscmp(). Source code of filevercmp() is taken from Gnulib. Signed-off-by: Andrew Borodin --- lib/strutil.h | 22 +++ lib/strutil/Makefile.am | 1 + lib/strutil/filevercmp.c | 232 ++++++++++++++++++++++++++ src/filemanager/dir.c | 2 +- tests/lib/strutil/Makefile.am | 6 +- tests/lib/strutil/filevercmp.c | 296 +++++++++++++++++++++++++++++++++ 6 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 lib/strutil/filevercmp.c create mode 100644 tests/lib/strutil/filevercmp.c diff --git a/lib/strutil.h b/lib/strutil.h index 9bbff6ca3..a091c25aa 100644 --- a/lib/strutil.h +++ b/lib/strutil.h @@ -538,6 +538,28 @@ const char *str_detect_termencoding (void); int str_verscmp (const char *s1, const char *s2); +/* Compare version strings: + + This function compares strings s1 and s2: + 1) By PREFIX in the same way as strcmp. + 2) Then by VERSION (most similarly to version compare of Debian's dpkg). + Leading zeros in version numbers are ignored. + 3) If both (PREFIX and VERSION) are equal, strcmp function is used for + comparison. So this function can return 0 if (and only if) strings s1 + and s2 are identical. + + It returns number > 0 for s1 > s2, 0 for s1 == s2 and number < 0 for s1 < s2. + + This function compares strings, in a way that if VER1 and VER2 are version + numbers and PREFIX and SUFFIX (SUFFIX defined as (\.[A-Za-z~][A-Za-z0-9~]*)*) + are strings then VER1 < VER2 implies filevercmp (PREFIX VER1 SUFFIX, + PREFIX VER2 SUFFIX) < 0. + + This function is intended to be a replacement for strverscmp. + */ +int filevercmp (const char *s1, const char *s2); + + /* return how many lines and columns will text occupy on terminal */ void str_msg_term_size (const char *text, int *lines, int *columns); diff --git a/lib/strutil/Makefile.am b/lib/strutil/Makefile.am index 07973c61b..5936a3664 100644 --- a/lib/strutil/Makefile.am +++ b/lib/strutil/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libmcstrutil.la libmcstrutil_la_SOURCES = \ + filevercmp.c \ replace.c \ strescape.c \ strutil8bit.c \ diff --git a/lib/strutil/filevercmp.c b/lib/strutil/filevercmp.c new file mode 100644 index 000000000..a94fb6625 --- /dev/null +++ b/lib/strutil/filevercmp.c @@ -0,0 +1,232 @@ +/* + Copyright (C) 1995 Ian Jackson + Copyright (C) 2001 Anthony Towns + Copyright (C) 2008-2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include + +#include "lib/strutil.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* Match a file suffix defined by this regular expression: /(\.[A-Za-z~][A-Za-z0-9~]*)*$/ + * + * @str pointer to string to scan. + * + * @return pointer to the matching suffix, or NULL if not found. + * Upon return, @str points to terminating NUL. + */ +static const char * +match_suffix (const char **str) +{ + const char *match = NULL; + gboolean read_alpha = FALSE; + + while (**str != '\0') + { + if (read_alpha) + { + read_alpha = FALSE; + if (!g_ascii_isalpha (**str) && **str != '~') + match = NULL; + } + else if (**str == '.') + { + read_alpha = TRUE; + if (match == NULL) + match = *str; + } + else if (!g_ascii_isalnum (**str) && **str != '~') + match = NULL; + (*str)++; + } + + return match; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* verrevcmp helper function */ +static int +order (unsigned char c) +{ + if (g_ascii_isdigit (c)) + return 0; + if (g_ascii_isalpha (c)) + return c; + if (c == '~') + return -1; + return (int) c + UCHAR_MAX + 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Slightly modified verrevcmp function from dpkg + * + * This implements the algorithm for comparison of version strings + * specified by Debian and now widely adopted. The detailed + * specification can be found in the Debian Policy Manual in the + * section on the 'Version' control field. This version of the code + * implements that from s5.6.12 of Debian Policy v3.8.0.1 + * https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version + * + * @s1 first string to compare + * @s1_len length of @s1 + * @s2 second string to compare + * @s2_len length of @s2 + * + * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2. + */ +static int +verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len) +{ + size_t s1_pos = 0; + size_t s2_pos = 0; + + while (s1_pos < s1_len || s2_pos < s2_len) + { + int first_diff = 0; + + while ((s1_pos < s1_len && !g_ascii_isdigit (s1[s1_pos])) + || (s2_pos < s2_len && !g_ascii_isdigit (s2[s2_pos]))) + { + int s1_c = 0; + int s2_c = 0; + + if (s1_pos != s1_len) + s1_c = order (s1[s1_pos]); + if (s2_pos != s2_len) + s2_c = order (s2[s2_pos]); + + if (s1_c != s2_c) + return (s1_c - s2_c); + + s1_pos++; + s2_pos++; + } + + while (s1[s1_pos] == '0') + s1_pos++; + while (s2[s2_pos] == '0') + s2_pos++; + + while (g_ascii_isdigit (s1[s1_pos]) && g_ascii_isdigit (s2[s2_pos])) + { + if (first_diff == 0) + first_diff = s1[s1_pos] - s2[s2_pos]; + + s1_pos++; + s2_pos++; + } + + if (g_ascii_isdigit (s1[s1_pos])) + return 1; + if (g_ascii_isdigit (s2[s2_pos])) + return -1; + if (first_diff != 0) + return first_diff; + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* Compare version strings. + * + * @s1 first string to compare + * @s2 second string to compare + * + * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2. + */ +int +filevercmp (const char *s1, const char *s2) +{ + const char *s1_pos, *s2_pos; + const char *s1_suffix, *s2_suffix; + size_t s1_len, s2_len; + int simple_cmp, result; + + /* easy comparison to see if strings are identical */ + simple_cmp = strcmp (s1, s2); + if (simple_cmp == 0) + return 0; + + /* special handle for "", "." and ".." */ + if (*s1 == '\0') + return -1; + if (*s2 == '\0') + return 1; + if (DIR_IS_DOT (s1)) + return -1; + if (DIR_IS_DOT (s2)) + return 1; + if (DIR_IS_DOTDOT (s1)) + return -1; + if (DIR_IS_DOTDOT (s2)) + return 1; + + /* special handle for other hidden files */ + if (*s1 == '.' && *s2 != '.') + return -1; + if (*s1 != '.' && *s2 == '.') + return 1; + if (*s1 == '.' && *s2 == '.') + { + s1++; + s2++; + } + + /* "cut" file suffixes */ + s1_pos = s1; + s2_pos = s2; + s1_suffix = match_suffix (&s1_pos); + s2_suffix = match_suffix (&s2_pos); + s1_len = (s1_suffix != NULL ? s1_suffix : s1_pos) - s1; + s2_len = (s2_suffix != NULL ? s2_suffix : s2_pos) - s2; + + /* restore file suffixes if strings are identical after "cut" */ + if ((s1_suffix != NULL || s2_suffix != NULL) && (s1_len == s2_len) + && strncmp (s1, s2, s1_len) == 0) + { + s1_len = s1_pos - s1; + s2_len = s2_pos - s2; + } + + result = verrevcmp (s1, s1_len, s2, s2_len); + + return result == 0 ? simple_cmp : result; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/filemanager/dir.c b/src/filemanager/dir.c index 925942fc9..6474aa2f2 100644 --- a/src/filemanager/dir.c +++ b/src/filemanager/dir.c @@ -354,7 +354,7 @@ sort_vers (file_entry_t * a, file_entry_t * b) if (ad == bd || panels_options.mix_all_files) { - return str_verscmp (a->fname, b->fname) * reverse; + return filevercmp (a->fname, b->fname) * reverse; } else { diff --git a/tests/lib/strutil/Makefile.am b/tests/lib/strutil/Makefile.am index a8ec2dd30..52c1f1198 100644 --- a/tests/lib/strutil/Makefile.am +++ b/tests/lib/strutil/Makefile.am @@ -16,7 +16,8 @@ endif TESTS = \ replace__str_replace_all \ parse_integer \ - str_verscmp + str_verscmp \ + filevercmp check_PROGRAMS = $(TESTS) @@ -28,3 +29,6 @@ parse_integer_SOURCES = \ str_verscmp_SOURCES = \ str_verscmp.c + +filevercmp_SOURCES = \ + filevercmp.c diff --git a/tests/lib/strutil/filevercmp.c b/tests/lib/strutil/filevercmp.c new file mode 100644 index 000000000..9faeb3e19 --- /dev/null +++ b/tests/lib/strutil/filevercmp.c @@ -0,0 +1,296 @@ +/* + lib/strutil - tests for lib/strutil/fileverscmp function. + + Copyright (C) 2019 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin , 2019 + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#define TEST_SUITE_NAME "/lib/strutil" + +#include "tests/mctest.h" + +#include "lib/strutil.h" + +/* --------------------------------------------------------------------------------------------- */ + +/* @Before */ +static void +setup (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @After */ +static void +teardown (void) +{ +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sign (int n) +{ + return ((n < 0) ? -1 : (n == 0) ? 0 : 1); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* @DataSource("filevercmp_test_ds1") */ +/* Testcases are taken from Gnulib */ +/* *INDENT-OFF* */ +static const struct filevercmp_test_struct +{ + const char *s1; + const char *s2; + int expected_result; +} filevercmp_test_ds1[] = +{ + { "", "", 0 }, + { "a", "a", 0 }, + { "a", "b", -1 }, + { "b", "a", 1 }, + { "a0", "a", 1 }, + { "00", "01", -1 }, + { "01", "010", -1 }, + { "9", "10", -1 }, + { "0a", "0", 1 } +}; +/* *INDENT-ON* */ + + +/* @Test(dataSource = "filevercmp_test_ds1") */ +/* *INDENT-OFF* */ +START_TEST (filevercmp_test1) +/* *INDENT-ON* */ +{ + /* given */ + int actual_result; + const struct filevercmp_test_struct *data = &filevercmp_test_ds1[_i]; + + /* when */ + actual_result = filevercmp (data->s1, data->s2); + + /* then */ + mctest_assert_int_eq (sign (actual_result), sign (data->expected_result)); +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* --------------------------------------------------------------------------------------------- */ + +/* @DataSource("filevercmp_test_ds2") */ +/* Testcases are taken from Gnulib */ +static const char *filevercmp_test_ds2[] = { + "", + ".", + "..", + ".0", + ".9", + ".A", + ".Z", + ".a~", + ".a", + ".b~", + ".b", + ".z", + ".zz~", + ".zz", + ".zz.~1~", + ".zz.0", + "0", + "9", + "A", + "Z", + "a~", + "a", + "a.b~", + "a.b", + "a.bc~", + "a.bc", + "b~", + "b", + "gcc-c++-10.fc9.tar.gz", + "gcc-c++-10.fc9.tar.gz.~1~", + "gcc-c++-10.fc9.tar.gz.~2~", + "gcc-c++-10.8.12-0.7rc2.fc9.tar.bz2", + "gcc-c++-10.8.12-0.7rc2.fc9.tar.bz2.~1~", + "glibc-2-0.1.beta1.fc10.rpm", + "glibc-common-5-0.2.beta2.fc9.ebuild", + "glibc-common-5-0.2b.deb", + "glibc-common-11b.ebuild", + "glibc-common-11-0.6rc2.ebuild", + "libstdc++-0.5.8.11-0.7rc2.fc10.tar.gz", + "libstdc++-4a.fc8.tar.gz", + "libstdc++-4.10.4.20040204svn.rpm", + "libstdc++-devel-3.fc8.ebuild", + "libstdc++-devel-3a.fc9.tar.gz", + "libstdc++-devel-8.fc8.deb", + "libstdc++-devel-8.6.2-0.4b.fc8", + "nss_ldap-1-0.2b.fc9.tar.bz2", + "nss_ldap-1-0.6rc2.fc8.tar.gz", + "nss_ldap-1.0-0.1a.tar.gz", + "nss_ldap-10beta1.fc8.tar.gz", + "nss_ldap-10.11.8.6.20040204cvs.fc10.ebuild", + "z", + "zz~", + "zz", + "zz.~1~", + "zz.0", + "#.b#" +}; + +const size_t filevercmp_test_ds2_len = G_N_ELEMENTS (filevercmp_test_ds2); + +/* @Test(dataSource = "filevercmp_test_ds2") */ +/* *INDENT-OFF* */ +START_TEST (filevercmp_test2) +/* *INDENT-ON* */ +{ + const char *i = filevercmp_test_ds2[_i]; + size_t _j; + + for (_j = 0; _j < filevercmp_test_ds2_len; _j++) + { + const char *j = filevercmp_test_ds2[_j]; + int result; + + result = filevercmp (i, j); + + if (result < 0) + ck_assert_int_eq (!!((size_t) _i < _j), 1); + else if (0 < result) + ck_assert_int_eq (!!(_j < (size_t) _i), 1); + else + ck_assert_int_eq (!!(_j == (size_t) _i), 1); + } +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + + +/* @DataSource("filevercmp_test_ds3") */ +/* Ticket #3959 */ +static const char *filevercmp_test_ds3[] = { + "application-1.10.tar.gz", + "application-1.10.1.tar.gz" +}; + +const size_t filevercmp_test_ds3_len = G_N_ELEMENTS (filevercmp_test_ds3); + +/* @Test(dataSource = "filevercmp_test_ds3") */ +/* *INDENT-OFF* */ +START_TEST (filevercmp_test3) +/* *INDENT-ON* */ +{ + const char *i = filevercmp_test_ds3[_i]; + size_t _j; + + for (_j = 0; _j < filevercmp_test_ds3_len; _j++) + { + const char *j = filevercmp_test_ds3[_j]; + int result; + + result = filevercmp (i, j); + + if (result < 0) + ck_assert_int_eq (!!((size_t) _i < _j), 1); + else if (0 < result) + ck_assert_int_eq (!!(_j < (size_t) _i), 1); + else + ck_assert_int_eq (!!(_j == (size_t) _i), 1); + } +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + + +/* @DataSource("filevercmp_test_ds4") */ +/* Ticket #3905 */ +static const char *filevercmp_test_ds4[] = { + "firefox-58.0.1+build1.tar.gz", + "firefox-59.0~b14+build1.tar.gz", + "firefox-59.0.1+build1.tar.gz" +}; + +const size_t filevercmp_test_ds4_len = G_N_ELEMENTS (filevercmp_test_ds4); + +/* @Test(dataSource = "filevercmp_test_ds4") */ +/* *INDENT-OFF* */ +START_TEST (filevercmp_test4) +/* *INDENT-ON* */ +{ + const char *i = filevercmp_test_ds4[_i]; + size_t _j; + + for (_j = 0; _j < filevercmp_test_ds4_len; _j++) + { + const char *j = filevercmp_test_ds4[_j]; + int result; + + result = filevercmp (i, j); + + if (result < 0) + ck_assert_int_eq (!!((size_t) _i < _j), 1); + else if (0 < result) + ck_assert_int_eq (!!(_j < (size_t) _i), 1); + else + ck_assert_int_eq (!!(_j == (size_t) _i), 1); + } +} +/* *INDENT-OFF* */ +END_TEST +/* *INDENT-ON* */ + +/* --------------------------------------------------------------------------------------------- */ + +int +main (void) +{ + int number_failed; + + Suite *s = suite_create (TEST_SUITE_NAME); + TCase *tc_core = tcase_create ("Core"); + SRunner *sr; + + tcase_add_checked_fixture (tc_core, setup, teardown); + + /* Add new tests here: *************** */ + mctest_add_parameterized_test (tc_core, filevercmp_test1, filevercmp_test_ds1); + tcase_add_loop_test (tc_core, filevercmp_test2, 0, filevercmp_test_ds2_len); + tcase_add_loop_test (tc_core, filevercmp_test3, 0, filevercmp_test_ds3_len); + tcase_add_loop_test (tc_core, filevercmp_test4, 0, filevercmp_test_ds4_len); + /* *********************************** */ + + suite_add_tcase (s, tc_core); + sr = srunner_create (s); + srunner_set_log (sr, "filevercmp.log"); + srunner_run_all (sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* --------------------------------------------------------------------------------------------- */