diff options
Diffstat (limited to 'src/global/safe_ultostr.c')
-rw-r--r-- | src/global/safe_ultostr.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/global/safe_ultostr.c b/src/global/safe_ultostr.c new file mode 100644 index 0000000..910c2ee --- /dev/null +++ b/src/global/safe_ultostr.c @@ -0,0 +1,256 @@ +/*++ +/* NAME +/* safe_ultostr 3 +/* SUMMARY +/* convert unsigned long to safe string +/* SYNOPSIS +/* #include <safe_ultostr.h> +/* +/* char *safe_ultostr(result, ulval, base, padlen, padchar) +/* VSTRING *result; +/* unsigned long ulval; +/* int base; +/* int padlen; +/* int padchar; +/* +/* unsigned long safe_strtoul(start, end, base) +/* const char *start; +/* char **end; +/* int base; +/* DESCRIPTION +/* The functions in this module perform conversions between +/* unsigned long values and "safe" alphanumerical strings +/* (strings with digits, uppercase letters and lowercase +/* letters, but without the vowels AEIOUaeiou). Specifically, +/* the characters B-Z represent the numbers 10-30, and b-z +/* represent 31-51. +/* +/* safe_ultostr() converts an unsigned long value to a safe +/* alphanumerical string. This is the reverse of safe_strtoul(). +/* +/* safe_strtoul() implements similar functionality as strtoul() +/* except that it uses a safe alphanumerical string as input, +/* and that it supports no signs or 0/0x prefixes. +/* +/* Arguments: +/* .IP result +/* Buffer for storage of the result of conversion to string. +/* .IP ulval +/* Unsigned long value. +/* .IP base +/* Value between 2 and 52 inclusive. +/* .IP padlen +/* .IP padchar +/* Left-pad a short result with padchar characters to the +/* specified length. Specify padlen=0 to disable padding. +/* .IP start +/* Pointer to the first character of the string to be converted. +/* .IP end +/* On return, pointer to the first character not in the input +/* alphabet, or to the string terminator. +/* DIAGNOSTICS +/* Fatal: out of memory. +/* +/* safe_strtoul() returns (0, EINVAL) when no conversion could +/* be performed, and (ULONG_MAX, ERANGE) in case of overflow. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <safe_ultostr.h> + +/* Application-specific. */ + +#define STR vstring_str +#define END vstring_end +#define SWAP(type, a, b) { type temp; temp = a; a = b; b = temp; } + +static unsigned char safe_chars[] = +"0123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; + +#define SAFE_MAX_BASE (sizeof(safe_chars) - 1) +#define SAFE_MIN_BASE (2) + +/* safe_ultostr - convert unsigned long to safe alphanumerical string */ + +char *safe_ultostr(VSTRING *buf, unsigned long ulval, int base, + int padlen, int padchar) +{ + const char *myname = "safe_ultostr"; + char *start; + char *last; + int i; + + /* + * Sanity check. + */ + if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) + msg_panic("%s: bad base: %d", myname, base); + + /* + * First accumulate the result, backwards. + */ + VSTRING_RESET(buf); + while (ulval != 0) { + VSTRING_ADDCH(buf, safe_chars[ulval % base]); + ulval /= base; + } + while (VSTRING_LEN(buf) < padlen) + VSTRING_ADDCH(buf, padchar); + VSTRING_TERMINATE(buf); + + /* + * Then, reverse the result. + */ + start = STR(buf); + last = END(buf) - 1; + for (i = 0; i < VSTRING_LEN(buf) / 2; i++) + SWAP(int, start[i], last[-i]); + return (STR(buf)); +} + +/* safe_strtoul - convert safe alphanumerical string to unsigned long */ + +unsigned long safe_strtoul(const char *start, char **end, int base) +{ + const char *myname = "safe_strtoul"; + static unsigned char *char_map = 0; + unsigned char *cp; + unsigned long sum; + unsigned long div_limit; + unsigned long mod_limit; + int char_val; + + /* + * Sanity check. + */ + if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) + msg_panic("%s: bad base: %d", myname, base); + + /* + * One-time initialization. Assume 8-bit bytes. + */ + if (char_map == 0) { + char_map = (unsigned char *) mymalloc(256); + for (char_val = 0; char_val < 256; char_val++) + char_map[char_val] = SAFE_MAX_BASE; + for (char_val = 0; char_val < SAFE_MAX_BASE; char_val++) + char_map[safe_chars[char_val]] = char_val; + } + + /* + * Per-call initialization. + */ + sum = 0; + div_limit = ULONG_MAX / base; + mod_limit = ULONG_MAX % base; + + /* + * Skip leading whitespace. We don't implement sign/base prefixes. + */ + if (end) + *end = (char *) start; + while (ISSPACE(*start)) + ++start; + + /* + * Start the conversion. + */ + errno = 0; + for (cp = (unsigned char *) start; (char_val = char_map[*cp]) < base; cp++) { + /* Return (ULONG_MAX, ERANGE) if the result is too large. */ + if (sum > div_limit + || (sum == div_limit && char_val > mod_limit)) { + sum = ULONG_MAX; + errno = ERANGE; + /* Skip "valid" characters, per the strtoul() spec. */ + while (char_map[*++cp] < base) + /* void */ ; + break; + } + sum = sum * base + char_val; + } + /* Return (0, EINVAL) after no conversion. Test moved here 20131209. */ + if (cp == (unsigned char *) start) + errno = EINVAL; + else if (end) + *end = (char *) cp; + return (sum); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read a number from stdin, convert to + * string, and print the result. + */ +#include <stdio.h> /* sscanf */ +#include <vstream.h> +#include <vstring_vstream.h> + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *buf = vstring_alloc(100); + char *junk; + unsigned long ulval; + int base; + char ch; + unsigned long ulval2; + +#ifdef MISSING_STRTOUL +#define strtoul strtol +#endif + + /* + * Hard-coded string-to-number test. + */ + ulval2 = safe_strtoul(" ", &junk, 10); + if (*junk == 0 || errno != EINVAL) + msg_warn("input=' ' result=%lu errno=%m", ulval2); + + /* + * Configurable number-to-string-to-number test. + */ + while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + ch = 0; + if (sscanf(STR(buf), "%lu %d%c", &ulval, &base, &ch) != 2 || ch) { + msg_warn("bad input %s", STR(buf)); + } else { + (void) safe_ultostr(buf, ulval, base, 5, '0'); + vstream_printf("%lu = %s\n", ulval, STR(buf)); + ulval2 = safe_strtoul(STR(buf), &junk, base); + if (*junk || (ulval2 == ULONG_MAX && errno == ERANGE)) + msg_warn("%s: %m", STR(buf)); + if (ulval2 != ulval) + msg_warn("%lu != %lu", ulval2, ulval); + } + vstream_fflush(VSTREAM_OUT); + } + vstring_free(buf); + return (0); +} + +#endif |