1 /* $NetBSD: safe_ultostr.c,v 1.1.1.2 2014/07/06 19:27:51 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
safe_ultostr(VSTRING * buf,unsigned long ulval,int base,int padlen,int padchar)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
safe_strtoul(const char * start,char ** end,int base)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 if (end)
177 *end = (char *) start;
178 while (ISSPACE(*start))
179 ++start;
180
181 /*
182 * Start the conversion.
183 */
184 errno = 0;
185 for (cp = (unsigned char *) start; (char_val = char_map[*cp]) < base; cp++) {
186 /* Return (ULONG_MAX, ERANGE) if the result is too large. */
187 if (sum > div_limit
188 || (sum == div_limit && char_val > mod_limit)) {
189 sum = ULONG_MAX;
190 errno = ERANGE;
191 /* Skip "valid" characters, per the strtoul() spec. */
192 while (char_map[*++cp] < base)
193 /* void */ ;
194 break;
195 }
196 sum = sum * base + char_val;
197 }
198 /* Return (0, EINVAL) after no conversion. Test moved here 20131209. */
199 if (cp == (unsigned char *) start)
200 errno = EINVAL;
201 else if (end)
202 *end = (char *) cp;
203 return (sum);
204 }
205
206 #ifdef TEST
207
208 /*
209 * Proof-of-concept test program. Read a number from stdin, convert to
210 * string, and print the result.
211 */
212 #include <stdio.h> /* sscanf */
213 #include <vstream.h>
214 #include <vstring_vstream.h>
215
main(int unused_argc,char ** unused_argv)216 int main(int unused_argc, char **unused_argv)
217 {
218 VSTRING *buf = vstring_alloc(100);
219 char *junk;
220 unsigned long ulval;
221 int base;
222 char ch;
223 unsigned long ulval2;
224
225 #ifdef MISSING_STRTOUL
226 #define strtoul strtol
227 #endif
228
229 /*
230 * Hard-coded string-to-number test.
231 */
232 ulval2 = safe_strtoul(" ", &junk, 10);
233 if (*junk == 0 || errno != EINVAL)
234 msg_warn("input=' ' result=%lu errno=%m", ulval2);
235
236 /*
237 * Configurable number-to-string-to-number test.
238 */
239 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
240 ch = 0;
241 if (sscanf(STR(buf), "%lu %d%c", &ulval, &base, &ch) != 2 || ch) {
242 msg_warn("bad input %s", STR(buf));
243 } else {
244 (void) safe_ultostr(buf, ulval, base, 5, '0');
245 vstream_printf("%lu = %s\n", ulval, STR(buf));
246 ulval2 = safe_strtoul(STR(buf), &junk, base);
247 if (*junk || (ulval2 == ULONG_MAX && errno == ERANGE))
248 msg_warn("%s: %m", STR(buf));
249 if (ulval2 != ulval)
250 msg_warn("%lu != %lu", ulval2, ulval);
251 }
252 vstream_fflush(VSTREAM_OUT);
253 }
254 vstring_free(buf);
255 return (0);
256 }
257
258 #endif
259