]> err.no Git - util-linux/commitdiff
lib: add strtosize() function
authorKarel Zak <kzak@redhat.com>
Tue, 30 Mar 2010 11:47:33 +0000 (13:47 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 30 Mar 2010 11:51:58 +0000 (13:51 +0200)
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 <kzak@redhat.com>
include/Makefile.am
include/strtosize.h [new file with mode: 0644]
lib/Makefile.am
lib/strtosize.c [new file with mode: 0644]
tests/commands.sh.in
tests/expected/misc/strtosize [new file with mode: 0644]
tests/ts/misc/strtosize [new file with mode: 0755]

index ccae85d7cbd6de1be4e4a2d59c5cdf63870a606c..65e447d51afb19449855a3995e0936a704c9d3f3 100644 (file)
@@ -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 (file)
index 0000000..c789df9
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef UTIL_LINUX_STRTOSIZE
+#define UTIL_LINUX_STRTOSIZE
+
+#include <inttypes.h>
+
+extern int strtosize(const char *str, uintmax_t *res);
+
+#endif /* UTIL_LINUX_STRING_TO_NUMBE */
index 0fddfb5edf26ff21251d8874f8ad4a459c3deaf7..0f008b3028ae506290472120da507d36fdcd278d 100644 (file)
@@ -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 (file)
index 0000000..068c542
--- /dev/null
@@ -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 <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+
+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 <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+
+int main(int argc, char *argv[])
+{
+       uintmax_t size = 0;
+
+       if (argc < 2) {
+               fprintf(stderr, "usage: %s <number>[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 */
+
index 8399738c8e476c80bf323e8973612a82ffae513d..f52a6868785bcbe482558414c61557e9ead734b0 100644 (file)
@@ -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 (file)
index 0000000..b7cb246
--- /dev/null
@@ -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 (executable)
index 0000000..4668823
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+#
+# 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
+