From cf8de26afe0db5c330f4c913d736dc1f840add88 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 30 Mar 2010 13:47:33 +0200 Subject: [PATCH] lib: add strtosize() function This function int strtosize(const char *str, uintmax_t *res) supports {K,M,G,T,E,P}iB and {K,M,G,T,E,P}B suffixes. Signed-off-by: Karel Zak --- include/Makefile.am | 1 + include/strtosize.h | 8 ++ lib/Makefile.am | 4 +- lib/strtosize.c | 148 ++++++++++++++++++++++++++++++++++ tests/commands.sh.in | 1 + tests/expected/misc/strtosize | 23 ++++++ tests/ts/misc/strtosize | 51 ++++++++++++ 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 include/strtosize.h create mode 100644 lib/strtosize.c create mode 100644 tests/expected/misc/strtosize create mode 100755 tests/ts/misc/strtosize diff --git a/include/Makefile.am b/include/Makefile.am index ccae85d7..65e447d5 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -22,4 +22,5 @@ dist_noinst_HEADERS = \ widechar.h \ crc32.h \ mangle.h \ + strtosize.h \ xstrncpy.h diff --git a/include/strtosize.h b/include/strtosize.h new file mode 100644 index 00000000..c789df93 --- /dev/null +++ b/include/strtosize.h @@ -0,0 +1,8 @@ +#ifndef UTIL_LINUX_STRTOSIZE +#define UTIL_LINUX_STRTOSIZE + +#include + +extern int strtosize(const char *str, uintmax_t *res); + +#endif /* UTIL_LINUX_STRING_TO_NUMBE */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 0fddfb5e..0f008b30 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,12 +2,14 @@ include $(top_srcdir)/config/include-Makefile.am AM_CPPFLAGS += -DTEST_PROGRAM -noinst_PROGRAMS = test_blkdev test_ismounted test_wholedisk test_mangle +noinst_PROGRAMS = test_blkdev test_ismounted test_wholedisk test_mangle \ + test_strtosize test_blkdev_SOURCES = blkdev.c test_ismounted_SOURCES = ismounted.c test_wholedisk_SOURCES = wholedisk.c test_mangle_SOURCES = mangle.c +test_strtosize_SOURCES = strtosize.c if LINUX test_blkdev_SOURCES += linux_version.c diff --git a/lib/strtosize.c b/lib/strtosize.c new file mode 100644 index 00000000..068c5429 --- /dev/null +++ b/lib/strtosize.c @@ -0,0 +1,148 @@ +/* + * strtosize() - convert string to size (uintmax_t). + * + * Supported suffixes: + * + * XiB or X for 2^N + * where X = {K,M,G,T,P,E,Y,Z} + * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only) + * for example: + * 10KiB = 10240 + * 10K = 10240 + * + * XB for 10^N + * where X = {K,M,G,T,P,E,Y,Z} + * for example: + * 10KB = 10000 + * + * Note that the function does not accept numbers with '-' (negative sign) + * prefix. + * + * Returns 0 on success, -1 in case of error, -2 in case of overflow. + * + * Copyright (C) 2010 Karel Zak + */ +#include +#include +#include +#include + +static int do_scale_by_power (uintmax_t *x, int base, int power) +{ + while (power--) { + if (UINTMAX_MAX / base < *x) + return -2; + *x *= base; + } + return 0; +} + +int strtosize(const char *str, uintmax_t *res) +{ + char *p; + uintmax_t x; + int base = 1024, rc = 0; + + *res = 0; + + if (!str || !*str) + goto err; + + /* Only positive numbers are acceptable + * + * Note that this check is not perfect, it would be better to + * use lconv->negative_sign. But coreutils use the same solution, + * so it's probably good enough... + */ + p = (char *) str; + while (isspace((unsigned char) *p)) + p++; + if (*p == '-') + goto err; + p = NULL; + + errno = 0; + x = strtoumax(str, &p, 0); + + if (p == str || + (errno != 0 && (x == UINTMAX_MAX || x == 0))) + goto err; + + if (!p || !*p) + goto done; /* without suffix */ + + /* + * Check size suffixes + */ + if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3)) + base = 1024; /* XiB, 2^N */ + else if (*(p + 1) == 'B' && !*(p + 2)) + base = 1000; /* XB, 10^N */ + else if (*(p + 1)) + goto err; /* unexpected suffix */ + + switch(*p) { + case 'K': + case 'k': + rc = do_scale_by_power(&x, base, 1); + break; + case 'M': + case 'm': + rc = do_scale_by_power(&x, base, 2); + break; + case 'G': + case 'g': + rc = do_scale_by_power(&x, base, 3); + break; + case 'T': + case 't': + rc = do_scale_by_power(&x, base, 4); + break; + case 'P': + case 'p': + rc = do_scale_by_power(&x, base, 5); + break; + case 'E': + case 'e': + rc = do_scale_by_power(&x, base, 6); + break; + case 'Z': + rc = do_scale_by_power(&x, base, 7); + break; + case 'Y': + rc = do_scale_by_power(&x, base, 8); + break; + default: + goto err; + } + +done: + *res = x; + return rc; +err: + return -1; +} + +#ifdef TEST_PROGRAM + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + uintmax_t size = 0; + + if (argc < 2) { + fprintf(stderr, "usage: %s [suffix]\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (strtosize(argv[1], &size)) + errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]); + + printf("%25s : %20ju\n", argv[1], size); + return EXIT_FAILURE; +} +#endif /* TEST_PROGRAM */ + diff --git a/tests/commands.sh.in b/tests/commands.sh.in index 8399738c..f52a6868 100644 --- a/tests/commands.sh.in +++ b/tests/commands.sh.in @@ -10,6 +10,7 @@ TS_HELPER_BYTESWAP="$TS_TOPDIR/helpers/test_byteswap" TS_HELPER_MD5="$TS_TOPDIR/helpers/test_md5" TS_HELPER_ISMOUNTED="$TOPDIR/lib/test_ismounted" +TS_HELPER_STRTOSIZE="$TOPDIR/lib/test_strtosize" # TODO: use partx TS_HELPER_PARTITIONS="$TOPDIR/shlibs/blkid/samples/partitions" diff --git a/tests/expected/misc/strtosize b/tests/expected/misc/strtosize new file mode 100644 index 00000000..b7cb2461 --- /dev/null +++ b/tests/expected/misc/strtosize @@ -0,0 +1,23 @@ +test_strtosize: invalid size '-1' value + 0 : 0 + 1 : 1 + 123 : 123 + 18446744073709551615 : 18446744073709551615 + 1K : 1024 + 1KiB : 1024 + 1M : 1048576 + 1MiB : 1048576 + 1G : 1073741824 + 1GiB : 1073741824 + 1T : 1099511627776 + 1TiB : 1099511627776 + 1P : 1125899906842624 + 1PiB : 1125899906842624 + 1E : 1152921504606846976 + 1EiB : 1152921504606846976 + 1KB : 1000 + 1MB : 1000000 + 1GB : 1000000000 + 1TB : 1000000000000 + 1PB : 1000000000000000 + 1EB : 1000000000000000000 diff --git a/tests/ts/misc/strtosize b/tests/ts/misc/strtosize new file mode 100755 index 00000000..4668823c --- /dev/null +++ b/tests/ts/misc/strtosize @@ -0,0 +1,51 @@ +#!/bin/bash + +# +# Copyright (C) 2010 Karel Zak +# +# This file is part of util-linux-ng. +# +# This file 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 2 of the License, or +# (at your option) any later version. +# +# This file 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. +# +TS_TOPDIR="$(dirname $0)/../.." +TS_DESC="strtosize" + +. $TS_TOPDIR/functions.sh +ts_init "$*" + +$TS_HELPER_STRTOSIZE -1 >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 0 >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1 >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 123 >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 18446744073709551615 >> $TS_OUTPUT 2>&1 + +$TS_HELPER_STRTOSIZE 1K >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1KiB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1M >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1MiB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1G >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1GiB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1T >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1TiB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1P >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1PiB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1E >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1EiB >> $TS_OUTPUT 2>&1 + +$TS_HELPER_STRTOSIZE 1KB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1MB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1GB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1TB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1PB >> $TS_OUTPUT 2>&1 +$TS_HELPER_STRTOSIZE 1EB >> $TS_OUTPUT 2>&1 + +ts_finalize + -- 2.39.5