0ae10c9b6e3de886829618746676a037ca247fc2
[reactos.git] / lib / msvcrt / stdlib / strtol.c
1 /* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
2 #include <limits.h>
3 #include <msvcrt/ctype.h>
4 #include <msvcrt/errno.h>
5 #include <msvcrt/stdlib.h>
6
7
8 /*
9  * @implemented
10  */
11 long
12 strtol(const char *nptr, char **endptr, int base)
13 {
14   const char *s = nptr;
15   unsigned long acc;
16   int c;
17   unsigned long cutoff;
18   int neg = 0, any, cutlim;
19
20   /*
21    * Skip white space and pick up leading +/- sign if any.
22    * If base is 0, allow 0x for hex and 0 for octal, else
23    * assume decimal; if base is already 16, allow 0x.
24    */
25   do {
26     c = *s++;
27   } while (isspace(c));
28   if (c == '-')
29   {
30     neg = 1;
31     c = *s++;
32   }
33   else if (c == '+')
34     c = *s++;
35   if ((base == 0 || base == 16) &&
36       c == '0' && (*s == 'x' || *s == 'X'))
37   {
38     c = s[1];
39     s += 2;
40     base = 16;
41   }
42   if (base == 0)
43     base = c == '0' ? 8 : 10;
44
45   /*
46    * Compute the cutoff value between legal numbers and illegal
47    * numbers.  That is the largest legal value, divided by the
48    * base.  An input number that is greater than this value, if
49    * followed by a legal input character, is too big.  One that
50    * is equal to this value may be valid or not; the limit
51    * between valid and invalid numbers is then based on the last
52    * digit.  For instance, if the range for longs is
53    * [-2147483648..2147483647] and the input base is 10,
54    * cutoff will be set to 214748364 and cutlim to either
55    * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
56    * a value > 214748364, or equal but the next digit is > 7 (or 8),
57    * the number is too big, and we will return a range error.
58    *
59    * Set any if any `digits' consumed; make it negative to indicate
60    * overflow.
61    */
62   cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
63   cutlim = cutoff % (unsigned long)base;
64   cutoff /= (unsigned long)base;
65   for (acc = 0, any = 0;; c = *s++)
66   {
67     if (isdigit(c))
68       c -= '0';
69     else if (isalpha(c))
70       c -= isupper(c) ? 'A' - 10 : 'a' - 10;
71     else
72       break;
73     if (c >= base)
74       break;
75     if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
76       any = -1;
77     else
78     {
79       any = 1;
80       acc *= base;
81       acc += c;
82     }
83   }
84   if (any < 0)
85   {
86     acc = neg ? LONG_MIN : LONG_MAX;
87     __set_errno(ERANGE);
88   }
89   else if (neg)
90     acc = -acc;
91   if (endptr != 0)
92     *endptr = any ? (char *)s - 1 : (char *)nptr;
93   return acc;
94 }