summaryrefslogtreecommitdiffstats
path: root/private/crt32/convert/wcstol.c
blob: 6d3c3d20194dab6030aced45b51d798ff458256a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/***
*wcstol.c - Contains C runtimes wcstol and wcstoul
*
*	Copyright (c) 1989-1993, Microsoft Corporation. All rights reserved.
*
*Purpose:
*	wcstol - convert wchar_t string to long signed integer
*	wcstoul - convert wchar_t string to long unsigned integer
*
*Revision History:
*	06-15-92  KRS	Module created, based on strtol.c
*	04-21-93  GJF	Removed assumption that LONG_MIN == -LONG_MAX.
*	05-10-93  GJF	Fixed error check.
*	05-20-93  GJF	Nothing like taking ugly code and making prettier...
*			and wrong. Fixed bug introduced on 5-10.
*
*******************************************************************************/

#include <cruntime.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <ctype.h>

/***
*wcstol, wcstoul(nptr,endptr,ibase) - Convert ascii string to long un/signed
*	int.
*
*Purpose:
*	Convert an ascii string to a long 32-bit value.  The base
*	used for the caculations is supplied by the caller.  The base
*	must be in the range 0, 2-36.  If a base of 0 is supplied, the
*	ascii string must be examined to determine the base of the
*	number:
*		(a) First char = '0', second char = 'x' or 'X',
*		    use base 16.
*		(b) First char = '0', use base 8
*		(c) First char in range '1' - '9', use base 10.
*
*	If the 'endptr' value is non-NULL, then wcstol/wcstoul places
*	a pointer to the terminating character in this value.
*	See ANSI standard for details
*
*Entry:
*	nptr == NEAR/FAR pointer to the start of string.
*	endptr == NEAR/FAR pointer to the end of the string.
*	ibase == integer base to use for the calculations.
*
*	string format: [whitespace] [sign] [0] [x] [digits/letters]
*
*Exit:
*	Good return:
*		result
*
*	Overflow return:
*		wcstol -- LONG_MAX or LONG_MIN
*		wcstoul -- ULONG_MAX
*		wcstol/wcstoul -- errno == ERANGE
*
*	No digits or bad base return:
*		0
*		endptr = nptr*
*
*Exceptions:
*	None.
*******************************************************************************/

/* flag values */
#define FL_UNSIGNED   1       /* wcstoul called */
#define FL_NEG	      2       /* negative sign found */
#define FL_OVERFLOW   4       /* overflow occured */
#define FL_READDIGIT  8       /* we've read at least one correct digit */


static unsigned long _CRTAPI3 wcstoxl (
	const wchar_t *nptr,
	const wchar_t **endptr,
	int ibase,
	int flags
	)
{
	const wchar_t *p;
	wchar_t c;
	unsigned long number;
	unsigned digval;
	unsigned long maxval;

	p = nptr;			/* p is our scanning pointer */
	number = 0;			/* start with zero */

	c = *p++;			/* read char */
	while (iswspace(c))
		c = *p++;		/* skip whitespace */

	if (c == '-') {
		flags |= FL_NEG;	/* remember minus sign */
		c = *p++;
	}
	else if (c == '+')
		c = *p++;		/* skip sign */

	if (ibase < 0 || ibase == 1 || ibase > 36) {
		/* bad base! */
		if (endptr)
			/* store beginning of string in endptr */
			*endptr = nptr;
		return 0L;		/* return 0 */
	}
	else if (ibase == 0) {
		/* determine base free-lance, based on first two chars of
		   string */
		if (c != L'0')
			ibase = 10;
		else if (*p == L'x' || *p == L'X')
			ibase = 16;
		else
			ibase = 8;
	}

	if (ibase == 16) {
		/* we might have 0x in front of number; remove if there */
		if (c == L'0' && (*p == L'x' || *p == L'X')) {
			++p;
			c = *p++;	/* advance past prefix */
		}
	}

	/* if our number exceeds this, we will overflow on multiply */
	maxval = ULONG_MAX / ibase;


	for (;;) {	/* exit in middle of loop */
		/* convert c to value */
		if (iswdigit(c))
			digval = c - L'0';
		else if (iswalpha(c))
			digval = towupper(c) - L'A' + 10;
		else
			break;
		if (digval >= (unsigned)ibase)
			break;		/* exit loop if bad digit found */

		/* record the fact we have read one digit */
		flags |= FL_READDIGIT;

		/* we now need to compute number = number * base + digval,
		   but we need to know if overflow occured.  This requires
		   a tricky pre-check. */

		if (number < maxval || (number == maxval &&
		(unsigned long)digval <= ULONG_MAX % ibase)) {
			/* we won't overflow, go ahead and multiply */
			number = number * ibase + digval;
		}
		else {
			/* we would have overflowed -- set the overflow flag */
			flags |= FL_OVERFLOW;
		}

		c = *p++;		/* read next digit */
	}

	--p;				/* point to place that stopped scan */

	if (!(flags & FL_READDIGIT)) {
		/* no number there; return 0 and point to beginning of
		   string */
		if (endptr)
			/* store beginning of string in endptr later on */
			p = nptr;
		number = 0L;		/* return 0 */
	}
	else if ( (flags & FL_OVERFLOW) ||
		  ( !(flags & FL_UNSIGNED) &&
		    ( ( (flags & FL_NEG) && (number > -LONG_MIN) ) ||
		      ( !(flags & FL_NEG) && (number > LONG_MAX) ) ) ) )
	{
		/* overflow or signed overflow occurred */
		errno = ERANGE;
		if ( flags & FL_UNSIGNED )
			number = ULONG_MAX;
		else if ( flags & FL_NEG )
			number = (unsigned long)(-LONG_MIN);
		else
			number = LONG_MAX;
	}

	if (endptr != NULL)
		/* store pointer to char that stopped the scan */
		*endptr = p;

	if (flags & FL_NEG)
		/* negate result if there was a neg sign */
		number = (unsigned long)(-(long)number);

	return number;			/* done. */
}

long _CRTAPI1 wcstol (
	const wchar_t *nptr,
	wchar_t **endptr,
	int ibase
	)
{
	return (long) wcstoxl(nptr, endptr, ibase, 0);
}

unsigned long _CRTAPI1 wcstoul (
	const wchar_t *nptr,
	wchar_t **endptr,
	int ibase
	)
{
	return wcstoxl(nptr, endptr, ibase, FL_UNSIGNED);
}