diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-15 09:31:11 +0100 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-15 09:31:11 +0100 |
commit | d0dc3f5c30ca0b8350b48ba032a65681bfa20bdb (patch) | |
tree | 17ada9ef2482441523576855dce14785e40d96a3 /src/pv/number.c | |
download | pv-d0dc3f5c30ca0b8350b48ba032a65681bfa20bdb.tar.gz |
Tarball conversion
Diffstat (limited to 'src/pv/number.c')
-rw-r--r-- | src/pv/number.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/pv/number.c b/src/pv/number.c new file mode 100644 index 0000000..0495b13 --- /dev/null +++ b/src/pv/number.c @@ -0,0 +1,206 @@ +/* + * Functions for converting strings to numbers. + * + * Copyright 2012 Andrew Wood, distributed under the Artistic License 2.0. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +/* + * This function is used instead of the macro from <ctype.h> because + * including <ctype.h> causes weird versioned glibc dependencies on certain + * Red Hat systems, complicating package management. + */ +static int pv__isdigit(char c) +{ + return ((c >= '0') && (c <= '9')); +} + + +/* + * Return the numeric value of "str", as a long long. + */ +long long pv_getnum_ll(char *str) +{ + long long n = 0; + long long decimal = 0; + int decdivisor = 1; + int shift = 0; + + while (str[0] != 0 && (!pv__isdigit(str[0]))) + str++; + + for (; pv__isdigit(str[0]); str++) { + n = n * 10; + n += (str[0] - '0'); + } + + /* + * If a decimal value was given, skip the decimal part. + */ + if ((str[0] == '.') || (str[0] == ',')) { + str++; + for (; pv__isdigit(str[0]); str++) { + if (decdivisor < 10000) { + decimal = decimal * 10; + decimal += (str[0] - '0'); + decdivisor = decdivisor * 10; + } + } + } + + /* + * Parse any units given (K=KiB=*1024, M=MiB=1024KiB, G=GiB=1024MiB, + * T=TiB=1024GiB). + */ + if (str[0]) { + while ((str[0] == ' ') || (str[0] == '\t')) + str++; + switch (str[0]) { + case 'k': + case 'K': + shift = 10; + break; + case 'm': + case 'M': + shift = 20; + break; + case 'g': + case 'G': + shift = 30; + break; + case 't': + case 'T': + shift = 40; + break; + default: + break; + } + } + + /* + * Binary left-shift the supplied number by "shift" times, i.e. + * apply the given units (KiB, MiB, etc) to it, but never shift left + * more than 30 at a time to avoid overflows. + */ + while (shift > 0) { + int shiftby; + + shiftby = shift; + if (shiftby > 30) + shiftby = 30; + + n = n << shiftby; + decimal = decimal << shiftby; + shift -= shiftby; + } + + /* + * Add any decimal component. + */ + decimal = decimal / decdivisor; + n += decimal; + + return n; +} + + +/* + * Return the numeric value of "str", as a double. + */ +double pv_getnum_d(char *str) +{ + double n = 0.0; + double step = 1; + + while (str[0] != 0 && (!pv__isdigit(str[0]))) + str++; + + for (; pv__isdigit(str[0]); str++) { + n = n * 10; + n += (str[0] - '0'); + } + + if ((str[0] != '.') && (str[0] != ',')) + return n; + + str++; + + for (; pv__isdigit(str[0]) && step < 1000000; str++) { + step = step * 10; + n += (str[0] - '0') / step; + } + + return n; +} + + +/* + * Return the numeric value of "str", as an int. + */ +int pv_getnum_i(char *str) +{ + return (int) pv_getnum_ll(str); +} + + +/* + * Return nonzero if the given string is not a valid integer (type=0) or + * double (type=1). + */ +int pv_getnum_check(char *str, int type) +{ + if (!str) + return 1; + + while ((str[0] == ' ') || (str[0] == '\t')) + str++; + + if (!pv__isdigit(str[0])) + return 1; + + for (; pv__isdigit(str[0]); str++); + + if (str[0] == '.') { + if (type == 0) + return 1; + str++; + for (; pv__isdigit(str[0]); str++); + } + + if (str[0] == 0) + return 0; + + /* + * Suffixes are not allowed for doubles, only for integers. + */ + if (type == 1) + return 1; + + while ((str[0] == ' ') || (str[0] == '\t')) + str++; + switch (str[0]) { + case 'k': + case 'K': + case 'm': + case 'M': + case 'g': + case 'G': + case 't': + case 'T': + str++; + break; + default: + return 1; + } + + if (str[0]) + return 1; + + return 0; +} + +/* EOF */ |