]> git.sur5r.net Git - cc65/blob - libsrc/common/strtol.c
CMOS optimisation
[cc65] / libsrc / common / strtol.c
1 #include <limits.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <stdlib.h>
5
6
7
8 long __fastcall__ strtol (const char* nptr, char** endptr, int base)
9 /* Convert a string to a long 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     if (Minus) {
58         MaxVal = LONG_MIN;
59     } else {
60         MaxVal = LONG_MAX;
61     }
62     MaxDigit = MaxVal % base;
63     MaxVal  /= base;
64
65     /* Convert the number */
66     while (1) {
67
68         /* Convert the digit into a numeric value */
69         if (isdigit (*S)) {
70             DigitVal = *S - '0';
71         } else if (isupper (*S)) {
72             DigitVal = *S - ('A' - 10);
73         } else if (islower (*S)) {
74             DigitVal = *S - ('a' - 10);
75         } else {
76             /* Unknown character */
77             break;
78         }
79
80         /* Don't accept a character that doesn't match base */
81         if (DigitVal >= base) {
82             break;
83         }
84
85         /* Don't accept anything that makes the final value invalid */
86         if (Val > MaxVal || (Val == MaxVal && DigitVal > MaxDigit)) {
87             Ovf = 1;
88         }
89
90         /* Calculate the next value if digit is not invalid */
91         if (Ovf == 0) {
92             Val = (Val * base) + DigitVal;
93             ++CvtCount;
94         }
95
96         /* Next character from input */
97         ++S;
98     }
99
100     /* Store the end pointer. If no conversion was performed, the value of
101     ** nptr is returned in endptr.
102     */
103     if (endptr) {
104         if (CvtCount > 0) {
105             *endptr = (char*) S;
106         } else {
107             *endptr = (char*) nptr;
108         }
109     }
110
111     /* Handle overflow */
112     if (Ovf) {
113         _seterrno (ERANGE);
114         if (Minus) {
115             return LONG_MIN;
116         } else {
117             return LONG_MAX;
118         }
119     }
120
121     /* Return the result */
122     if (Minus) {
123         return -(long)Val;
124     } else {
125         return Val;
126     }
127 }
128