1
1

Add functions to transform string to unsigned integer:

* (xstrtoumax): from gnulib.
 * (parse_integer): from coreutils (dd.c).

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
Этот коммит содержится в:
Andrew Borodin 2013-02-28 16:55:51 +04:00
родитель 25924c77d9
Коммит ccf82ada12
6 изменённых файлов: 476 добавлений и 2 удалений

Просмотреть файл

@ -4,6 +4,7 @@
#include "lib/global.h" /* include glib.h */ #include "lib/global.h" /* include glib.h */
#include <sys/types.h> #include <sys/types.h>
#include <inttypes.h>
#include <string.h> #include <string.h>
#ifdef HAVE_ASSERT_H #ifdef HAVE_ASSERT_H
#include <assert.h> /* assert() */ #include <assert.h> /* assert() */
@ -86,6 +87,20 @@ typedef enum
J_CENTER_LEFT_FIT = 0x14 J_CENTER_LEFT_FIT = 0x14
} align_crt_t; } align_crt_t;
/* string-to-integer parsing results
*/
typedef enum
{
LONGINT_OK = 0,
/* These two values can be ORed together, to indicate that both errors occurred. */
LONGINT_OVERFLOW = 1,
LONGINT_INVALID_SUFFIX_CHAR = 2,
LONGINT_INVALID_SUFFIX_CHAR_WITH_OVERFLOW = (LONGINT_INVALID_SUFFIX_CHAR | LONGINT_OVERFLOW),
LONGINT_INVALID = 4
} strtol_error_t;
/*** structures declarations (and typedefs of structures)*****************************************/ /*** structures declarations (and typedefs of structures)*****************************************/
/* all functions in str_class must be defined for every encoding */ /* all functions in str_class must be defined for every encoding */
@ -540,7 +555,13 @@ char *strrstr_skip_count (const char *haystack, const char *needle, size_t skip_
char *str_replace_all (const char *haystack, const char *needle, const char *replacement); char *str_replace_all (const char *haystack, const char *needle, const char *replacement);
strtol_error_t xstrtoumax (const char *s, char **ptr, int base, uintmax_t * val,
const char *valid_suffixes);
uintmax_t parse_integer (const char *str, gboolean * invalid);
/* --------------------------------------------------------------------------------------------- */
/*** inline functions ****************************************************************************/ /*** inline functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
static inline void static inline void
str_replace (char *s, char from, char to) str_replace (char *s, char from, char to)
@ -552,6 +573,7 @@ str_replace (char *s, char from, char to)
} }
} }
/* --------------------------------------------------------------------------------------------- */
/* /*
* strcpy is unsafe on overlapping memory areas, so define memmove-alike * strcpy is unsafe on overlapping memory areas, so define memmove-alike
* string function. * string function.
@ -584,4 +606,6 @@ str_move (char *dest, const char *src)
return (char *) memmove (dest, src, n); return (char *) memmove (dest, src, n);
} }
/* --------------------------------------------------------------------------------------------- */
#endif /* MC_STRUTIL_H */ #endif /* MC_STRUTIL_H */

Просмотреть файл

@ -7,6 +7,7 @@ libmcstrutil_la_SOURCES = \
strutilascii.c \ strutilascii.c \
strutil.c \ strutil.c \
strutilutf8.c \ strutilutf8.c \
strverscmp.c strverscmp.c \
xstrtol.c
AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir)

Просмотреть файл

@ -832,3 +832,42 @@ strrstr_skip_count (const char *haystack, const char *needle, size_t skip_count)
} }
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
/* Interprete string as a non-negative decimal integer, optionally multiplied by various values.
*
* @param str input value
* @param invalid set to TRUE if "str" does not represent a number in this format
*
* @return non-integer representation of "str", 0 in case of error.
*/
uintmax_t
parse_integer (const char *str, gboolean * invalid)
{
uintmax_t n;
char *suffix;
strtol_error_t e;
e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x')
{
uintmax_t multiplier;
multiplier = parse_integer (suffix + 1, invalid);
if (multiplier != 0 && n * multiplier / multiplier != n)
{
*invalid = TRUE;
return 0;
}
n *= multiplier;
}
else if (e != LONGINT_OK)
{
*invalid = TRUE;
n = 0;
}
return n;
}
/* --------------------------------------------------------------------------------------------- */

238
lib/strutil/xstrtol.c Обычный файл
Просмотреть файл

@ -0,0 +1,238 @@
/* A more useful interface to strtol.
Copyright (C) 1995-1996, 1998-2001, 2003-2007, 2009-2012 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 <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering. */
#include <config.h>
/* Some pre-ANSI implementations (e.g. SunOS 4)
need stderr defined if assertion checking is enabled. */
#include <stdio.h>
#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "lib/strutil.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
static strtol_error_t
bkm_scale (uintmax_t * x, int scale_factor)
{
if (UINTMAX_MAX / scale_factor < *x)
{
*x = UINTMAX_MAX;
return LONGINT_OVERFLOW;
}
*x *= scale_factor;
return LONGINT_OK;
}
/* --------------------------------------------------------------------------------------------- */
static strtol_error_t
bkm_scale_by_power (uintmax_t * x, int base, int power)
{
strtol_error_t err = LONGINT_OK;
while (power-- != 0)
err |= bkm_scale (x, base);
return err;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
strtol_error_t
xstrtoumax (const char *s, char **ptr, int base, uintmax_t * val, const char *valid_suffixes)
{
char *t_ptr;
char **p;
uintmax_t tmp;
strtol_error_t err = LONGINT_OK;
#ifdef HAVE_ASSERT_H
assert (0 <= base && base <= 36);
#endif
p = (ptr != NULL ? ptr : &t_ptr);
{
const char *q = s;
unsigned char ch = *q;
while (isspace (ch))
ch = *++q;
if (ch == '-')
return LONGINT_INVALID;
}
errno = 0;
tmp = strtol (s, p, base);
if (*p == s)
{
/* If there is no number but there is a valid suffix, assume the
number is 1. The string is invalid otherwise. */
if (valid_suffixes != NULL && **p != '\0' && strchr (valid_suffixes, **p) != NULL)
tmp = 1;
else
return LONGINT_INVALID;
}
else if (errno != 0)
{
if (errno != ERANGE)
return LONGINT_INVALID;
err = LONGINT_OVERFLOW;
}
/* Let valid_suffixes == NULL mean "allow any suffix". */
/* FIXME: update all callers except the ones that allow suffixes
after the number, changing last parameter NULL to "". */
if (valid_suffixes == NULL)
{
*val = tmp;
return err;
}
if (**p != '\0')
{
int suffixes = 1;
strtol_error_t overflow;
if (strchr (valid_suffixes, **p) == NULL)
{
*val = tmp;
return err | LONGINT_INVALID_SUFFIX_CHAR;
}
base = 1024;
if (strchr (valid_suffixes, '0') != NULL)
{
/* The "valid suffix" '0' is a special flag meaning that
an optional second suffix is allowed, which can change
the base. A suffix "B" (e.g. "100MB") stands for a power
of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
a power of 1024. If no suffix (e.g. "100M"), assume
power-of-1024. */
switch (p[0][1])
{
case 'i':
if (p[0][2] == 'B')
suffixes += 2;
break;
case 'B':
case 'D': /* 'D' is obsolescent */
base = 1000;
suffixes++;
break;
}
}
switch (**p)
{
case 'b':
overflow = bkm_scale (&tmp, 512);
break;
case 'B':
overflow = bkm_scale (&tmp, 1024);
break;
case 'c':
overflow = 0;
break;
case 'E': /* exa or exbi */
overflow = bkm_scale_by_power (&tmp, base, 6);
break;
case 'G': /* giga or gibi */
case 'g': /* 'g' is undocumented; for compatibility only */
overflow = bkm_scale_by_power (&tmp, base, 3);
break;
case 'k': /* kilo */
case 'K': /* kibi */
overflow = bkm_scale_by_power (&tmp, base, 1);
break;
case 'M': /* mega or mebi */
case 'm': /* 'm' is undocumented; for compatibility only */
overflow = bkm_scale_by_power (&tmp, base, 2);
break;
case 'P': /* peta or pebi */
overflow = bkm_scale_by_power (&tmp, base, 5);
break;
case 'T': /* tera or tebi */
case 't': /* 't' is undocumented; for compatibility only */
overflow = bkm_scale_by_power (&tmp, base, 4);
break;
case 'w':
overflow = bkm_scale (&tmp, 2);
break;
case 'Y': /* yotta or 2**80 */
overflow = bkm_scale_by_power (&tmp, base, 8);
break;
case 'Z': /* zetta or 2**70 */
overflow = bkm_scale_by_power (&tmp, base, 7);
break;
default:
*val = tmp;
return err | LONGINT_INVALID_SUFFIX_CHAR;
}
err |= overflow;
*p += suffixes;
if (**p != '\0')
err |= LONGINT_INVALID_SUFFIX_CHAR;
}
*val = tmp;
return err;
}
/* --------------------------------------------------------------------------------------------- */

Просмотреть файл

@ -8,9 +8,13 @@ AM_CPPFLAGS = \
LIBS = @CHECK_LIBS@ $(top_builddir)/lib/libmc.la LIBS = @CHECK_LIBS@ $(top_builddir)/lib/libmc.la
TESTS = \ TESTS = \
replace__str_replace_all replace__str_replace_all \
parse_integer
check_PROGRAMS = $(TESTS) check_PROGRAMS = $(TESTS)
replace__str_replace_all_SOURCES = \ replace__str_replace_all_SOURCES = \
replace__str_replace_all.c replace__str_replace_all.c
parse_integer_SOURCES = \
parse_integer.c

168
tests/lib/strutil/parse_integer.c Обычный файл
Просмотреть файл

@ -0,0 +1,168 @@
/*
lib/strutil - tests for lib/strutil/parse_integer function.
Copyright (C) 2013
The Free Software Foundation, Inc.
Written by:
Andrew Borodin <aborodin@vmail.ru>, 2013
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 <inttypes.h>
#include "lib/strutil.h"
/* --------------------------------------------------------------------------------------------- */
/* @Before */
static void
setup (void)
{
}
/* --------------------------------------------------------------------------------------------- */
/* @After */
static void
teardown (void)
{
}
/* --------------------------------------------------------------------------------------------- */
/* @DataSource("str_replace_all_test_ds") */
/* *INDENT-OFF* */
static const struct parse_integer_test_ds
{
const char *haystack;
uintmax_t expected_result;
gboolean invalid;
} parse_integer_test_ds[] =
{
{
/* too big */
"99999999999999999999999999999999999999999999999999999999999999999999",
0,
TRUE
},
{
"x",
0,
TRUE
},
{
"9x",
0,
TRUE
},
{
"1",
1,
FALSE
},
{
"-1",
0,
TRUE
},
{
"1k",
1024,
FALSE
},
{
"1K",
1024,
FALSE
},
{
"1M",
1024 * 1024,
FALSE
},
{
"1m",
0,
TRUE
},
{
"64M",
64 * 1024 * 1024,
FALSE
},
{
"1G",
1 * 1024 * 1024 * 1024,
FALSE
}
};
/* *INDENT-ON* */
/* @Test(dataSource = "str_replace_all_test_ds") */
/* *INDENT-OFF* */
START_TEST (parse_integer_test)
/* *INDENT-ON* */
{
/* given */
uintmax_t actual_result;
gboolean invalid = FALSE;
const struct parse_integer_test_ds *data = &parse_integer_test_ds[_i];
/* when */
actual_result = parse_integer (data->haystack, &invalid);
/* then */
fail_unless (invalid == data->invalid && actual_result == data->expected_result,
"actial ( %" PRIuMAX ") not equal to\nexpected (%" PRIuMAX ")",
actual_result, 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: *************** */
tcase_add_loop_test (tc_core, parse_integer_test, 0, G_N_ELEMENTS (parse_integer_test_ds));
/* *********************************** */
suite_add_tcase (s, tc_core);
sr = srunner_create (s);
srunner_set_log (sr, "parse_integer.log");
srunner_run_all (sr, CK_NOFORK);
number_failed = srunner_ntests_failed (sr);
srunner_free (sr);
return (number_failed == 0) ? 0 : 1;
}
/* --------------------------------------------------------------------------------------------- */