opt: add integer helpers that accept k, M, G, T, P, E suffixes
authorDouglas Bagnall <douglas@paradise.net.nz>
Sat, 13 Aug 2011 12:19:59 +0000 (21:49 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Sat, 13 Aug 2011 12:19:59 +0000 (21:49 +0930)
These functions come in two flavours: those ending with "_si", which
have 1000-based interpretations of the suffixes; and those ending with
"_bi", which use base 1024.  There are versions for signed and
unsigned int, long, and long long destinations, with tests for all 12
new functions.  The tests get a bit repetitive, I am afraid.

As an example, if the -x option were using the opt_set_intval_bi
function, then all of these would do the same thing:

$ foo -x 5M
$ foo -x $((5 * 1024 * 1024))
$ foo -x 5242880
$ foo -x 5120k

quite what that thing is depends on the size of your int -- people
with 16 bit ints would see an "out of range" error message.

The arithmetic for unsigned variations is actually done using signed
long long integers, so the maximum possible value is LLONG_MAX, not
ULLONG_MAX.  This follows the practice of existing functions, and
avoids tedious work.

ccan/opt/helpers.c
ccan/opt/opt.h
ccan/opt/test/run-helpers.c

index 9c181946d6122122c2d89cf3b137129b4bd2f1d9..7022afbbf090a53346388bdbd6a3f5d2b00ce290 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <stdio.h>
+#include <limits.h>
 #include "private.h"
 
 /* Upper bound to sprintf this simple type?  Each 3 bits < 1 digit. */
@@ -78,7 +79,7 @@ char *opt_set_uintval(const char *arg, unsigned int *ui)
        if (err)
                return err;
        if (i < 0)
-               return arg_bad("'%s' is negative", arg);
+               return arg_bad("'%s' is negative but destination is unsigned", arg);
        *ui = i;
        return NULL;
 }
@@ -107,7 +108,7 @@ char *opt_set_ulongval(const char *arg, unsigned long *ul)
                return err;
        *ul = l;
        if (l < 0)
-               return arg_bad("'%s' is negative", arg);
+               return arg_bad("'%s' is negative but destination is unsigned", arg);
        return NULL;
 }
 
@@ -172,3 +173,199 @@ void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul)
 {
        snprintf(buf, OPT_SHOW_LEN, "%lu", *ul);
 }
+
+/* a helper function that multiplies out an argument's kMGTPE suffix in the
+ * long long int range, and perform checks common to all integer destinations.
+ *
+ * The base will be either 1000 or 1024, corresponding with the '_si' and
+ * '_bi' functions.
+ */
+
+static char *set_llong_with_suffix(const char *arg, long long *ll,
+                                  const long long base)
+{
+       char *endp;
+       if (!arg[0])
+               return arg_bad("'%s' (an empty string) is not a number", arg);
+
+       errno = 0;
+       *ll = strtoll(arg, &endp, 0);
+       if (errno)
+               return arg_bad("'%s' is out of range", arg);
+       if (*endp){
+               /*The string continues with non-digits.  If there is just one
+                 letter and it is a known multiplier suffix, use it.*/
+               if (endp[1])
+                       return arg_bad("'%s' is not a number (suffix too long)", arg);
+               long long mul;
+               switch(*endp){
+               case 'K':
+               case 'k':
+                       mul = base;
+                       break;
+               case 'M':
+               case 'm':
+                       mul = base * base;
+                       break;
+               case 'G':
+               case 'g':
+                       mul = base * base * base;
+                       break;
+               case 'T':
+               case 't':
+                       mul = base * base * base * base;
+                       break;
+               case 'P':
+                       mul = base * base * base * base * base;
+                       break;
+               case 'E':
+                       mul = base * base * base * base * base * base;
+                       break;
+               /* This is as far as we can go in 64 bits ('E' is 2 ^ 60) */
+               default:
+                       return arg_bad("'%s' is not a number (unknown suffix)",
+                                      arg);
+               }
+               if (*ll > LLONG_MAX / mul || *ll < LLONG_MIN / mul)
+                       return arg_bad("'%s' is out of range", arg);
+               *ll *= mul;
+       }
+       return NULL;
+}
+
+/* Middle layer helpers that perform bounds checks for specific target sizes
+ * and signednesses.
+ */
+static char * set_ulonglong_with_suffix(const char *arg, unsigned long long *ull,
+                                       const long base)
+{
+       long long ll;
+       char *err = set_llong_with_suffix(arg, &ll, base);
+       if (err != NULL)
+               return err;
+       if (ll < 0)
+               return arg_bad("'%s' is negative but destination is unsigned", arg);
+       *ull = ll;
+       return NULL;
+}
+
+static char * set_long_with_suffix(const char *arg, long *l, const long base)
+{
+       long long ll;
+       char *err = set_llong_with_suffix(arg, &ll, base);
+       if (err != NULL) /*an error*/
+               return err;
+
+       *l = ll;
+       if (*l != ll)
+               return arg_bad("value '%s' does not fit into a long", arg);
+       return NULL;
+}
+
+static char * set_ulong_with_suffix(const char *arg, unsigned long *ul, const long base)
+{
+       long long ll;
+       char *err = set_llong_with_suffix(arg, &ll, base);
+       if (err != NULL)
+               return err;
+       if (ll < 0)
+               return arg_bad("'%s' is negative but destination is unsigned", arg);
+       *ul = ll;
+       if (*ul != ll)
+               return arg_bad("value '%s' does not fit into an unsigned long", arg);
+       return NULL;
+}
+
+static char * set_int_with_suffix(const char *arg, int *i, const long base)
+{
+       long long ll;
+       char *err = set_llong_with_suffix(arg, &ll, base);
+       if (err != NULL) /*an error*/
+               return err;
+
+       *i = ll;
+       if (*i != ll)
+               return arg_bad("value '%s' does not fit into an int", arg);
+       return NULL;
+}
+
+static char * set_uint_with_suffix(const char *arg, unsigned int *u, const long base)
+{
+       long long ll;
+       char *err = set_llong_with_suffix(arg, &ll, base);
+       if (err != NULL)
+               return err;
+       if (ll < 0)
+               return arg_bad("'%s' is negative but destination is unsigned", arg);
+       *u = ll;
+       if (*u != ll)
+               return arg_bad("value '%s' does not fit into an unsigned int", arg);
+       return NULL;
+}
+
+/*Set an integer, with decimal or binary suffixes.
+  The accepted suffixes are k/K, M/m, G/g, T, P, E.
+
+  The *_bi functions multiply the numeric value by a power of 1024, while the
+  *_si functions multiply by a power of 1000.
+ */
+
+char * opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll)
+{
+       return set_ulonglong_with_suffix(arg, ll, 1024);
+}
+
+char * opt_set_ulonglongval_si(const char *arg, unsigned long long *ll)
+{
+       return set_ulonglong_with_suffix(arg, ll, 1000);
+}
+
+char * opt_set_longlongval_bi(const char *arg, long long *ll)
+{
+       return set_llong_with_suffix(arg, ll, 1024);
+}
+
+char * opt_set_longlongval_si(const char *arg, long long *ll)
+{
+       return set_llong_with_suffix(arg, ll, 1000);
+}
+
+char * opt_set_longval_bi(const char *arg, long *l)
+{
+       return set_long_with_suffix(arg, l, 1024);
+}
+
+char * opt_set_longval_si(const char *arg, long *l)
+{
+       return set_long_with_suffix(arg, l, 1000);
+}
+
+char * opt_set_ulongval_bi(const char *arg, unsigned long *ul)
+{
+       return set_ulong_with_suffix(arg, ul, 1024);
+}
+
+char * opt_set_ulongval_si(const char *arg, unsigned long *ul)
+{
+       return set_ulong_with_suffix(arg, ul, 1000);
+}
+
+char * opt_set_intval_bi(const char *arg, int *i)
+{
+       return set_int_with_suffix(arg, i, 1024);
+}
+
+char * opt_set_intval_si(const char *arg, int *i)
+{
+       return set_int_with_suffix(arg, i, 1000);
+}
+
+char * opt_set_uintval_bi(const char *arg, unsigned int *u)
+{
+       return set_uint_with_suffix(arg, u, 1024);
+}
+
+char * opt_set_uintval_si(const char *arg, unsigned int *u)
+{
+       return set_uint_with_suffix(arg, u, 1000);
+}
index 643c18de336ba17cdcec3f3f7161dee6b3b513ae..7049fffc1c7fa57b8e3b1296da56fc8dc183addd 100644 (file)
@@ -280,6 +280,23 @@ void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l);
 char *opt_set_ulongval(const char *arg, unsigned long *ul);
 void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul);
 
+/* the following setting functions accept k, M, G, T, P, or E suffixes, which
+   multiplies the numeric value by the corresponding power of 1000 or 1024
+   (for the _si and _bi versions, respectively).
+ */
+char *opt_set_intval_bi(const char *arg, int *i);
+char *opt_set_intval_si(const char *arg, int *i);
+char *opt_set_uintval_bi(const char *arg, unsigned int *u);
+char *opt_set_uintval_si(const char *arg, unsigned int *u);
+char *opt_set_longval_bi(const char *arg, long *l);
+char *opt_set_longval_si(const char *arg, long *l);
+char *opt_set_ulongval_bi(const char *arg, unsigned long *ul);
+char *opt_set_ulongval_si(const char *arg, unsigned long *ul);
+char *opt_set_longlongval_bi(const char *arg, long long *ll);
+char *opt_set_longlongval_si(const char *arg, long long *ll);
+char *opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll);
+char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll);
+
 /* Increment. */
 char *opt_inc_intval(int *i);
 
index a58e4d91797d2c56befa958b40fb08a14fac3594..a71fe40c33fc1958389823084f338997f779aa6b 100644 (file)
@@ -82,7 +82,7 @@ static void *saved_malloc(size_t size)
 /* Test helpers. */
 int main(int argc, char *argv[])
 {
-       plan_tests(100);
+       plan_tests(334);
 
        /* opt_set_bool */
        {
@@ -211,6 +211,443 @@ int main(int argc, char *argv[])
                else
                        fail("FIXME: Handle other long sizes");
        }
+
+       {
+               const long long k = 1024;
+               const long long M = k * k;
+               const long long G = k * k * k;
+               const long long T = k * k * k * k;
+               const long long P = k * k * k * k * k;
+               const long long E = k * k * k * k * k * k;
+
+               /* opt_set_uintval_bi */
+               {
+                       unsigned int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_uintval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0k", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "3Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "30k", NULL));
+                       ok1(arg == 30 * k);
+                       ok1(!parse_args(&argc, &argv, "-a", "-1K", NULL));
+       }
+
+               /* opt_set_intval_bi */
+               {
+                       int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_intval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0k", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "3Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "30k", NULL));
+                       ok1(arg == 30 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-1K", NULL));
+                       ok1(arg == -1 * k);
+               }
+
+
+               /* opt_set_ulongval_bi */
+               {
+                       unsigned long int arg = 1000;
+
+                       reset_options();
+                       opt_register_arg("-a", opt_set_ulongval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "99M", NULL));
+                       ok1(arg == 99 * M);
+                       /*note, 2999M > max signed 32 bit long, 1 << 31*/
+                       ok1(parse_args(&argc, &argv, "-a", "2999m", NULL));
+                       ok1(arg == 2999 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(!parse_args(&argc, &argv, "-a", "-1G", NULL));
+                       if (sizeof(long) == 4){
+                               ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1T", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                       }
+                       else if (sizeof(long) == 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "18446744073709551616", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "8E", NULL));
+                               ok1(parse_args(&argc, &argv, "-a", "3E", NULL));
+                       }
+                       else
+                               fail("FIXME: Handle other long sizes");
+               }
+
+               /* opt_set_longval_bi */
+               {
+                       long int arg = 1000;
+
+                       reset_options();
+                       opt_register_arg("-a", opt_set_longval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-100k", NULL));
+                       ok1(arg == -100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "99M", NULL));
+                       ok1(arg == 99 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(parse_args(&argc, &argv, "-a", "-1G", NULL));
+                       ok1(arg == -1 * G);
+                       if (sizeof(long) == 4){
+                               ok1(!parse_args(&argc, &argv, "-a", "2147483648", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "2G", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "2048m", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1T", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                       }
+                       else if (sizeof(long) == 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "9223372036854775808", NULL));
+                               ok1(parse_args(&argc, &argv, "-a", "3E", NULL));
+                               ok1(arg == 3 * E);
+                               ok1(parse_args(&argc, &argv, "-a", "123T", NULL));
+                               ok1(arg == 123 * T);
+                       }
+                       else
+                               fail("FIXME: Handle other long sizes");
+               }
+
+
+               /* opt_set_longlongval_bi */
+               {
+                       long long int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_longlongval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1kk", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-100k", NULL));
+                       ok1(arg == -100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-333333M", NULL));
+                       ok1(arg == -333333 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(parse_args(&argc, &argv, "-a", "1024t", NULL));
+                       ok1(arg == 1024 * T);
+                       ok1(parse_args(&argc, &argv, "-a", "123P", NULL));
+                       ok1(arg == 123 * P);
+                       ok1(parse_args(&argc, &argv, "-a", "-3E", NULL));
+                       ok1(arg == -3 * E);
+
+                       if (sizeof(long long) == 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "9223372036854775808", NULL));
+                               /*8E and 922337.. are both 1 << 63*/
+                               ok1(!parse_args(&argc, &argv, "-a", "8E", NULL));
+                       }
+                       else
+                               fail("FIXME: Handle other long long int"
+                                    " sizes (specifically %lu bytes)",
+                                    sizeof(long long));
+               }
+               /* opt_set_ulonglongval_bi */
+               {
+                       unsigned long long int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_ulonglongval_bi, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1kk", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100G", NULL));
+                       ok1(arg == 100 * G);
+                       ok1(!parse_args(&argc, &argv, "-a", "-100G", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "8191P", NULL));
+                       ok1(arg == 8191 * P);
+               }
+       }
+
+       {
+               const long long k = 1000;
+               const long long M = k * k;
+               const long long G = k * k * k;
+               const long long T = k * k * k * k;
+               const long long P = k * k * k * k * k;
+               const long long E = k * k * k * k * k * k;
+
+               /* opt_set_uintval_si */
+               {
+                       unsigned int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_uintval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0k", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "3Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "30k", NULL));
+                       ok1(arg == 30 * k);
+                       ok1(!parse_args(&argc, &argv, "-a", "-1K", NULL));
+                       if (sizeof(unsigned int) < 8)
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                       else
+                               pass("can't test int truncation when int is so huge");
+       }
+
+               /* opt_set_intval_si */
+               {
+                       int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_intval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0k", NULL));
+                       ok1(arg == 0);
+                       arg = 1;
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "3Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "30k", NULL));
+                       ok1(arg == 30 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-1K", NULL));
+                       ok1(arg == -1 * k);
+                       if (sizeof(int) < 8)
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                       else
+                               pass("can't test int truncation when int is so huge");
+               }
+
+
+               /* opt_set_ulongval_si */
+               {
+                       unsigned long long int arg = 1000;
+
+                       reset_options();
+                       opt_register_arg("-a", opt_set_ulongval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "99M", NULL));
+                       ok1(arg == 99 * M);
+                       /*note, 2999M > max signed 32 bit long, 1 << 31*/
+                       ok1(parse_args(&argc, &argv, "-a", "2999m", NULL));
+                       ok1(arg == 2999 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(!parse_args(&argc, &argv, "-a", "-1G", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "4G", NULL));
+                       ok1(arg == 4000000000);
+                       if (sizeof(long) == 4){
+                               ok1(!parse_args(&argc, &argv, "-a", "4294967296", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "4295M", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1T", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                       }
+                       else if (sizeof(long)== 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "18446744073709551616", NULL));
+                               ok1(parse_args(&argc, &argv, "-a", "9E", NULL));
+                               ok1(arg == 9000000000000000000ULL);
+                               ok1(!parse_args(&argc, &argv, "-a", "19E", NULL));
+                       }
+                       else
+                               fail("FIXME: Handle other long sizes");
+               }
+
+               /* opt_set_longval_si */
+               {
+                       long int arg = 1000;
+
+                       reset_options();
+                       opt_register_arg("-a", opt_set_longval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0P", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-100k", NULL));
+                       ok1(arg == -100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "99M", NULL));
+                       ok1(arg == 99 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(parse_args(&argc, &argv, "-a", "-1G", NULL));
+                       ok1(arg == -1 * G);
+                       if (sizeof(long) == 4){
+                               ok1(!parse_args(&argc, &argv, "-a", "2147483648", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "4G", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1T", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "1E", NULL));
+                               ok1(parse_args(&argc, &argv, "-a", "1999m", NULL));
+                               ok1(arg == 1999 * M);
+                       }
+                       else if (sizeof(long)== 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "9223372036854775808", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a", "9224P", NULL));
+                               ok1(parse_args(&argc, &argv, "-a", "9E", NULL));
+                               ok1(arg == 9 * E);
+                               ok1(parse_args(&argc, &argv, "-a", "123T", NULL));
+                               ok1(arg == 123 * T);
+                       }
+                       else
+                               fail("FIXME: Handle other long sizes");
+               }
+
+
+               /* opt_set_longlongval_si */
+               {
+                       long long int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_longlongval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(arg == -9999);
+                       ok1(parse_args(&argc, &argv, "-a", "0T", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "100crap", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1kk", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100k", NULL));
+                       ok1(arg == 100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-100k", NULL));
+                       ok1(arg == -100 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "1K", NULL));
+                       ok1(arg == 1 * k);
+                       ok1(parse_args(&argc, &argv, "-a", "-333333M", NULL));
+                       ok1(arg == -333333 * M);
+                       ok1(parse_args(&argc, &argv, "-a", "1G", NULL));
+                       ok1(arg == 1 * G);
+                       ok1(parse_args(&argc, &argv, "-a", "1024t", NULL));
+                       ok1(arg == 1024 * T);
+                       ok1(parse_args(&argc, &argv, "-a", "123P", NULL));
+                       ok1(arg == 123 * P);
+                       ok1(parse_args(&argc, &argv, "-a", "-3E", NULL));
+                       ok1(arg == -3 * E);
+                       ok1(parse_args(&argc, &argv, "-a", "8E", NULL));
+                       if (sizeof(long long) == 8){
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "9223372036854775808", NULL));
+                               ok1(!parse_args(&argc, &argv, "-a",
+                                               "10E", NULL));
+                       }
+                       else
+                               fail("FIXME: Handle other long long int"
+                                    " sizes (specifically %lu bytes)",
+                                    sizeof(long long));
+
+               }
+               /* opt_set_ulonglongval_si */
+               {
+                       unsigned long long int arg = 1000;
+                       reset_options();
+                       opt_register_arg("-a", opt_set_ulonglongval_si, NULL,
+                                        &arg, "All");
+                       ok1(parse_args(&argc, &argv, "-a", "9999", NULL));
+                       ok1(arg == 9999);
+                       ok1(!parse_args(&argc, &argv, "-a", "-9999", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "0", NULL));
+                       ok1(arg == 0);
+                       ok1(!parse_args(&argc, &argv, "-a", "1Q", NULL));
+                       ok1(!parse_args(&argc, &argv, "-a", "1kk", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "100G", NULL));
+                       ok1(arg == 100 * G);
+                       ok1(!parse_args(&argc, &argv, "-a", "-100G", NULL));
+                       ok1(parse_args(&argc, &argv, "-a", "8E", NULL));
+               }
+       }
+
+
        /* opt_inc_intval */
        {
                int arg = 1000;
@@ -436,5 +873,6 @@ int main(int argc, char *argv[])
                output = NULL;
        }
 
+       //diag("%s\n", err_output);
        return exit_status();
 }