xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/safe_ultostr.c (revision aceb213538ec08a74028e213127af18aa17bf1cf)
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