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