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