]> git.sur5r.net Git - cc65/blob - libsrc/common/strtoul.c
Implemented line wrap.
[cc65] / libsrc / common / strtoul.c
1 #include <limits.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <stdlib.h>
5
6
7
8 unsigned long __fastcall__ strtoul (const char* nptr, char** endptr, int base)
9 /* Convert a string to a long unsigned int */
10 {
11     register const char* S          = nptr;
12     unsigned long        Val        = 0;
13     unsigned char        Minus      = 0;
14     unsigned char        Ovf        = 0;
15     unsigned             CvtCount   = 0;
16     unsigned char        DigitVal;
17     unsigned long        MaxVal;
18     unsigned char        MaxDigit;
19
20
21     /* Skip white space */
22     while (isspace (*S)) {
23         ++S;
24     }
25
26     /* Check for leading + or - sign */
27     switch (*S) {
28         case '-':
29             Minus = 1;
30             /* FALLTHROUGH */
31         case '+':
32             ++S;
33     }
34
35     /* If base is zero, we may have a 0 or 0x prefix. If base is 16, we may
36     ** have a 0x prefix.
37     */
38     if (base == 0) {
39         if (*S == '0') {
40             ++S;
41             if (*S == 'x' || *S == 'X') {
42                 ++S;
43                 base = 16;
44             } else {
45                 base = 8;
46             }
47         } else {
48             base = 10;
49         }
50     } else if (base == 16 && *S == '0' && (S[1] == 'x' || S[1] == 'X')) {
51         S += 2;
52     }
53
54     /* Determine the maximum valid number and (if the number is equal to this
55     ** value) the maximum valid digit.
56     */
57     MaxDigit = ULONG_MAX % base;
58     MaxVal   = ULONG_MAX / base;
59
60     /* Convert the number */
61     while (1) {
62
63         /* Convert the digit into a numeric value */
64         if (isdigit (*S)) {
65             DigitVal = *S - '0';
66         } else if (isupper (*S)) {
67             DigitVal = *S - ('A' - 10);
68         } else if (islower (*S)) {
69             DigitVal = *S - ('a' - 10);
70         } else {
71             /* Unknown character */
72             break;
73         }
74
75         /* Don't accept a character that doesn't match base */
76         if (DigitVal >= base) {
77             break;
78         }
79
80         /* Don't accept anything that makes the final value invalid */
81         if (Val > MaxVal || (Val == MaxVal && DigitVal > MaxDigit)) {
82             Ovf = 1;
83         }
84
85         /* Calculate the next value if digit is not invalid */
86         if (Ovf == 0) {
87             Val = (Val * base) + DigitVal;
88             ++CvtCount;
89         }
90
91         /* Next character from input */
92         ++S;
93     }
94
95     /* Store the end pointer. If no conversion was performed, the value of
96     ** nptr is returned in endptr.
97     */
98     if (endptr) {
99         if (CvtCount > 0) {
100             *endptr = (char*) S;
101         } else {
102             *endptr = (char*) nptr;
103         }
104     }
105
106     /* Handle overflow */
107     if (Ovf) {
108         _seterrno (ERANGE);
109         return ULONG_MAX;
110     }
111
112     /* Return the result */
113     if (Minus) {
114         return (unsigned long) -(long)Val;
115     } else {
116         return Val;
117     }
118 }
119