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>
Этот коммит содержится в:
родитель
25924c77d9
Коммит
ccf82ada12
@ -4,6 +4,7 @@
|
||||
#include "lib/global.h" /* include glib.h */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_ASSERT_H
|
||||
#include <assert.h> /* assert() */
|
||||
@ -86,6 +87,20 @@ typedef enum
|
||||
J_CENTER_LEFT_FIT = 0x14
|
||||
} 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)*****************************************/
|
||||
|
||||
/* 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);
|
||||
|
||||
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 ****************************************************************************/
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
static inline void
|
||||
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
|
||||
* string function.
|
||||
@ -584,4 +606,6 @@ str_move (char *dest, const char *src)
|
||||
return (char *) memmove (dest, src, n);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
|
||||
#endif /* MC_STRUTIL_H */
|
||||
|
@ -7,6 +7,7 @@ libmcstrutil_la_SOURCES = \
|
||||
strutilascii.c \
|
||||
strutil.c \
|
||||
strutilutf8.c \
|
||||
strverscmp.c
|
||||
strverscmp.c \
|
||||
xstrtol.c
|
||||
|
||||
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
Обычный файл
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
|
||||
|
||||
TESTS = \
|
||||
replace__str_replace_all
|
||||
replace__str_replace_all \
|
||||
parse_integer
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
replace__str_replace_all_SOURCES = \
|
||||
replace__str_replace_all.c
|
||||
|
||||
parse_integer_SOURCES = \
|
||||
parse_integer.c
|
||||
|
168
tests/lib/strutil/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;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
Загрузка…
x
Ссылка в новой задаче
Block a user