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