X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fopt%2Fhelpers.c;h=747a78e9139b875e7f7f6620d194510418c2c300;hp=7554c997b252fcf7ceda1c5102a871b08a8fcb04;hb=b989e06c093fb7a2befae277f684fa75b64b9ef5;hpb=d7d5abe98caeec82d784ce525e0444ff438acd46 diff --git a/ccan/opt/helpers.c b/ccan/opt/helpers.c index 7554c997..747a78e9 100644 --- a/ccan/opt/helpers.c +++ b/ccan/opt/helpers.c @@ -1,10 +1,18 @@ +/* Licensed under GPLv2+ - see LICENSE file for details */ #include +#include +#include #include +#include #include #include #include +#include #include "private.h" +/* Upper bound to sprintf this simple type? Each 3 bits < 1 digit. */ +#define CHAR_SIZE(type) (((sizeof(type)*CHAR_BIT + 2) / 3) + 1) + /* FIXME: asprintf module? */ static char *arg_bad(const char *fmt, const char *arg) { @@ -47,11 +55,12 @@ char *opt_set_invbool_arg(const char *arg, bool *b) /* Set a char *. */ char *opt_set_charp(const char *arg, char **p) { - *p = (char *)arg; + *p = cast_const(char *, arg); return NULL; } -/* Set an integer value, various forms. Sets to 1 on arg == NULL. */ +/* Set an integer value, various forms. + FIXME: set to 1 on arg == NULL ? */ char *opt_set_intval(const char *arg, int *i) { long l; @@ -60,8 +69,8 @@ char *opt_set_intval(const char *arg, int *i) if (err) return err; *i = l; - /* Beware truncation... */ - if (*i != l) + /* Beware truncation, but don't generate untestable code. */ + if (sizeof(*i) != sizeof(l) && *i != l) return arg_bad("value '%s' does not fit into an integer", arg); return err; } @@ -74,7 +83,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; } @@ -88,10 +97,8 @@ char *opt_set_longval(const char *arg, long *l) *l = strtol(arg, &endp, 0); if (*endp || !arg[0]) return arg_bad("'%s' is not a number", arg); - if (errno == ERANGE) - return arg_bad("'%s' is out of range", arg); if (errno) - return opt_invalid_argument(arg); + return arg_bad("'%s' is out of range", arg); return NULL; } @@ -99,16 +106,59 @@ char *opt_set_ulongval(const char *arg, unsigned long *ul) { long int l; char *err; - + err = opt_set_longval(arg, &l); if (err) 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; +} + +char *opt_set_floatval(const char *arg, float *f) +{ + double d; + char *err; + + err = opt_set_doubleval(arg, &d); + if (err) + return err; + + *f = d; + if (*f != d) + return arg_bad("'%s' is out of range", arg); + + return NULL; +} + +void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f) +{ + double d = *f; + opt_show_doubleval(buf, &d); +} + +char *opt_set_doubleval(const char *arg, double *d) +{ + char *endp; + + /* This is how the manpage says to do it. Yech. */ + errno = 0; + /* Don't assume strtof */ + *d = strtod(arg, &endp); + if (*endp || !arg[0]) + return arg_bad("'%s' is not a number", arg); + if (errno) + return arg_bad("'%s' is out of range", arg); + return NULL; } +void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d) +{ + snprintf(buf, OPT_SHOW_LEN, "%f", *d); +} + char *opt_inc_intval(int *i) { (*i)++; @@ -116,14 +166,374 @@ char *opt_inc_intval(int *i) } /* Display version string. */ -char *opt_show_version_and_exit(const char *version) +char *opt_version_and_exit(const char *version) { printf("%s\n", version); + /* Don't have valgrind complain! */ + opt_free_table(); exit(0); } char *opt_usage_and_exit(const char *extra) { - printf("%s", opt_usage(opt_argv0, extra)); + char *usage = opt_usage(opt_argv0, extra); + printf("%s", usage); + /* Don't have valgrind complain! */ + opt_alloc.free(usage); + opt_free_table(); exit(0); } + +void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b) +{ + strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN); +} + +void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b) +{ + strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN); +} + +void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p) +{ + size_t len = strlen(*p); + buf[0] = '"'; + if (len > OPT_SHOW_LEN - 2) + len = OPT_SHOW_LEN - 2; + strncpy(buf+1, *p, len); + buf[1+len] = '"'; + if (len < OPT_SHOW_LEN - 2) + buf[2+len] = '\0'; +} + +/* Show an integer value, various forms. */ +void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i) +{ + snprintf(buf, OPT_SHOW_LEN, "%i", *i); +} + +void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui) +{ + snprintf(buf, OPT_SHOW_LEN, "%u", *ui); +} + +void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l) +{ + snprintf(buf, OPT_SHOW_LEN, "%li", *l); +} + +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; + /* Beware truncation, but don't generate untestable code. */ + if (sizeof(*l) != sizeof(ll) && *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; + /* Beware truncation, but don't generate untestable code. */ + if (sizeof(*ul) != sizeof(ll) && *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); +} + +/*static helpers for showing values with kMGTPE suffixes. In this case there + are separate but essentially identical functions for signed and unsigned + values, so that unsigned values greater than LLONG_MAX get suffixes. + */ +static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll, + const long long base) +{ + const char *suffixes = "kMGTPE"; + int i; + if (ll == 0){ + /*zero is special because everything divides it (you'd get "0E")*/ + snprintf(buf, OPT_SHOW_LEN, "0"); + return; + } + for (i = 0; i < strlen(suffixes); i++){ + long long tmp = ll / base; + if (tmp * base != ll) + break; + ll = tmp; + } + if (i == 0) + snprintf(buf, OPT_SHOW_LEN, "%"PRId64, (int64_t)ll); + else + snprintf(buf, OPT_SHOW_LEN, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]); +} + +static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long ull, + const unsigned base) +{ + const char *suffixes = "kMGTPE"; + int i; + if (ull == 0){ + /*zero is special because everything divides it (you'd get "0E")*/ + snprintf(buf, OPT_SHOW_LEN, "0"); + return; + } + for (i = 0; i < strlen(suffixes); i++){ + unsigned long long tmp = ull / base; + if (tmp * base != ull) + break; + ull = tmp; + } + if (i == 0) + snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, (uint64_t)ull); + else + snprintf(buf, OPT_SHOW_LEN, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]); +} + +/* _bi, signed */ +void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x) +{ + show_llong_with_suffix(buf, *x, 1024); +} + +void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x) +{ + show_llong_with_suffix(buf, *x, 1024); +} + +void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x) +{ + show_llong_with_suffix(buf, *x, 1024); +} + +/* _bi, unsigned */ +void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); +} + +void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); +} + +void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); +} + +/* _si, signed */ +void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x) +{ + show_llong_with_suffix(buf, (long long) *x, 1000); +} + +void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x) +{ + show_llong_with_suffix(buf, (long long) *x, 1000); +} + +void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x) +{ + show_llong_with_suffix(buf, *x, 1000); +} + +/* _si, unsigned */ +void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); +} + +void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); +} + +void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x) +{ + show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); +} +