summaryrefslogtreecommitdiffstats
path: root/src/global/safe_ultostr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/safe_ultostr.c')
-rw-r--r--src/global/safe_ultostr.c256
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