1/*++ 2/* NAME 3/* safe_ultostr 3 4/* SUMMARY 5/* convert unsigned long to safe string 6/* SYNOPSIS 7/* #include <safe_ultostr.h> 8/* 9/* char *safe_ultostr(result, ulval, base, padlen, padchar) 10/* VSTRING *result; 11/* unsigned long ulval; 12/* int base; 13/* int padlen; 14/* int padchar; 15/* 16/* unsigned long safe_strtoul(start, end, base) 17/* const char *start; 18/* char **end; 19/* int base; 20/* DESCRIPTION 21/* The functions in this module perform conversions between 22/* unsigned long values and "safe" alphanumerical strings 23/* (strings with digits, uppercase letters and lowercase 24/* letters, but without the vowels AEIOUaeiou). Specifically, 25/* the characters B-Z represent the numbers 10-30, and b-z 26/* represent 31-51. 27/* 28/* safe_ultostr() converts an unsigned long value to a safe 29/* alphanumerical string. This is the reverse of safe_strtoul(). 30/* 31/* safe_strtoul() implements similar functionality as strtoul() 32/* except that it uses a safe alphanumerical string as input, 33/* and that it supports no signs or 0/0x prefixes. 34/* 35/* Arguments: 36/* .IP result 37/* Buffer for storage of the result of conversion to string. 38/* .IP ulval 39/* Unsigned long value. 40/* .IP base 41/* Value between 2 and 52 inclusive. 42/* .IP padlen 43/* .IP padchar 44/* Left-pad a short result with padchar characters to the 45/* specified length. Specify padlen=0 to disable padding. 46/* .IP start 47/* Pointer to the first character of the string to be converted. 48/* .IP end 49/* On return, pointer to the first character not in the input 50/* alphabet, or to the string terminator. 51/* DIAGNOSTICS 52/* Fatal: out of memory. 53/* 54/* safe_strtoul() returns (0, EINVAL) when no conversion could 55/* be performed, and (ULONG_MAX, ERANGE) in case of overflow. 56/* LICENSE 57/* .ad 58/* .fi 59/* The Secure Mailer license must be distributed with this software. 60/* AUTHOR(S) 61/* Wietse Venema 62/* IBM T.J. Watson Research 63/* P.O. Box 704 64/* Yorktown Heights, NY 10598, USA 65/*--*/ 66 67/* System library. */ 68 69#include <sys_defs.h> 70#include <stdlib.h> 71#include <limits.h> 72#include <errno.h> 73#include <ctype.h> 74 75/* Utility library. */ 76 77#include <msg.h> 78#include <vstring.h> 79#include <mymalloc.h> 80 81/* Global library. */ 82 83#include <safe_ultostr.h> 84 85/* Application-specific. */ 86 87#define STR vstring_str 88#define END vstring_end 89#define SWAP(type, a, b) { type temp; temp = a; a = b; b = temp; } 90 91static unsigned char safe_chars[] = 92"0123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; 93 94#define SAFE_MAX_BASE (sizeof(safe_chars) - 1) 95#define SAFE_MIN_BASE (2) 96 97/* safe_ultostr - convert unsigned long to safe alphanumerical string */ 98 99char *safe_ultostr(VSTRING *buf, unsigned long ulval, int base, 100 int padlen, int padchar) 101{ 102 const char *myname = "safe_ultostr"; 103 char *start; 104 char *last; 105 int i; 106 107 /* 108 * Sanity check. 109 */ 110 if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) 111 msg_panic("%s: bad base: %d", myname, base); 112 113 /* 114 * First accumulate the result, backwards. 115 */ 116 VSTRING_RESET(buf); 117 while (ulval != 0) { 118 VSTRING_ADDCH(buf, safe_chars[ulval % base]); 119 ulval /= base; 120 } 121 while (VSTRING_LEN(buf) < padlen) 122 VSTRING_ADDCH(buf, padchar); 123 VSTRING_TERMINATE(buf); 124 125 /* 126 * Then, reverse the result. 127 */ 128 start = STR(buf); 129 last = END(buf) - 1; 130 for (i = 0; i < VSTRING_LEN(buf) / 2; i++) 131 SWAP(int, start[i], last[-i]); 132 return (STR(buf)); 133} 134 135/* safe_strtoul - convert safe alphanumerical string to unsigned long */ 136 137unsigned long safe_strtoul(const char *start, char **end, int base) 138{ 139 const char *myname = "safe_strtoul"; 140 static unsigned char *char_map = 0; 141 unsigned char *cp; 142 unsigned long sum; 143 unsigned long div_limit; 144 unsigned long mod_limit; 145 int char_val; 146 147 /* 148 * Sanity check. 149 */ 150 if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) 151 msg_panic("%s: bad base: %d", myname, base); 152 153 /* 154 * One-time initialization. Assume 8-bit bytes. 155 */ 156 if (char_map == 0) { 157 char_map = (unsigned char *) mymalloc(256); 158 for (char_val = 0; char_val < 256; char_val++) 159 char_map[char_val] = SAFE_MAX_BASE; 160 for (char_val = 0; char_val < SAFE_MAX_BASE; char_val++) 161 char_map[safe_chars[char_val]] = char_val; 162 } 163 164 /* 165 * Per-call initialization. 166 */ 167 sum = 0; 168 div_limit = ULONG_MAX / base; 169 mod_limit = ULONG_MAX % base; 170 171 /* 172 * Skip leading whitespace. We don't implement sign/base prefixes. 173 */ 174 while (ISSPACE(*start)) 175 ++start; 176 177 /* 178 * Start the conversion. 179 */ 180 errno = 0; 181 for (cp = (unsigned char *) start; *cp; cp++) { 182 /* Return (0, EINVAL) if no conversion was made. */ 183 if ((char_val = char_map[*cp]) >= base) { 184 if (cp == (unsigned char *) start) 185 errno = EINVAL; 186 break; 187 } 188 /* Return (ULONG_MAX, ERANGE) if the result is too large. */ 189 if (sum > div_limit 190 || (sum == div_limit && char_val > mod_limit)) { 191 sum = ULONG_MAX; 192 errno = ERANGE; 193 /* Skip "valid" characters, per the strtoul() spec. */ 194 while (char_map[*++cp] < base) 195 /* void */ ; 196 break; 197 } 198 sum = sum * base + char_val; 199 } 200 if (end) 201 *end = (char *) cp; 202 return (sum); 203} 204 205#ifdef TEST 206 207 /* 208 * Proof-of-concept test program. Read a number from stdin, convert to 209 * string, and print the result. 210 */ 211#include <stdio.h> /* sscanf */ 212#include <vstream.h> 213#include <vstring_vstream.h> 214 215int main(int unused_argc, char **unused_argv) 216{ 217 VSTRING *buf = vstring_alloc(100); 218 char *junk; 219 unsigned long ulval; 220 int base; 221 char ch; 222 unsigned long ulval2; 223 224#ifdef MISSING_STRTOUL 225#define strtoul strtol 226#endif 227 228 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { 229 ch = 0; 230 if (sscanf(STR(buf), "%lu %d%c", &ulval, &base, &ch) != 2 || ch) { 231 msg_warn("bad input %s", STR(buf)); 232 } else { 233 (void) safe_ultostr(buf, ulval, base, 5, '0'); 234 vstream_printf("%lu = %s\n", ulval, STR(buf)); 235 ulval2 = safe_strtoul(STR(buf), &junk, base); 236 if (*junk || (ulval2 == ULONG_MAX && errno == ERANGE)) 237 msg_warn("%s: %m", STR(buf)); 238 if (ulval2 != ulval) 239 msg_warn("%lu != %lu", ulval2, ulval); 240 } 241 vstream_fflush(VSTREAM_OUT); 242 } 243 vstring_free(buf); 244 return (0); 245} 246 247#endif 248