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 <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2019-02-20 11:31:05 +03:00
parent e2b8a50cea
commit 2163009270
3 changed files with 194 additions and 31 deletions

View File

@ -5,7 +5,21 @@
Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Jean-Fran<EFBFBD>ois Bignolles <bignolle ecoledoc ibp fr>, 1997.
Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 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
<https://www.gnu.org/licenses/>.
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)
{

View File

@ -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

View File

@ -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 <aborodin@vmail.ru>, 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 <http://www.gnu.org/licenses/>.
*/
#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;
}
/* --------------------------------------------------------------------------------------------- */