This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Re: [perl #26136] localtime(3) calls tzset(3), but localtime_r(3) may not.
[perl5.git] / uts / strtol_wrap.c
1 /* A wrapper around strtol() and strtoul() to correct some
2  * "out of bounds" cases that don't work well on at least UTS.
3  * If a value is Larger than the max, strto[u]l should return
4  * the max value, and set errno to ERANGE
5  *  The same if a value is smaller than the min value (only
6  * relevant for strtol(); not strtoul()), except the minimum
7  * value is returned (and errno == ERANGE).
8  */
9
10 #include        <ctype.h>
11 #include        <string.h>
12 #include        <sys/errno.h>
13 #include        <stdlib.h>
14
15 extern int      errno;
16
17 #undef  I32
18 #undef  U32
19
20 #define I32     int
21 #define U32     unsigned int
22
23 struct  base_info {
24         char    *ValidChars;
25
26         char    *Ulong_max_str;
27         char    *Long_max_str;
28         char    *Long_min_str;  /* Absolute value */
29
30         int     Ulong_max_str_len;
31         int     Long_max_str_len;
32         int     Long_min_str_len;       /* Absolute value */
33
34         U32     Ulong_max;
35         I32     Long_max;
36         I32     Long_min;       /* NOT Absolute value */
37 };
38 static struct   base_info Base_info[37];
39
40 static struct base_info Base_info_16 = {
41         "0123456789abcdefABCDEF",
42         "4294967295", "2147483648" /* <== ABS VAL */ , "2147483647",
43         10, 10, 10,
44         4294967295, 2147483647, - 2147483648,
45 };
46
47 static struct base_info Base_info_10 = {
48         "0123456789",
49         "4294967295", "2147483648" /* <== ABS VAL */ , "2147483647",
50         10, 10, 10,
51         4294967295, 2147483647, - 2147483648,
52 };
53
54  /* Used eventually (if this is fully developed) to hold info
55   * for processing bases 2-36.  So that we can just plug the
56   * base in as a selector for its info, we sacrifice
57   * Base_info[0] and Base_info[1] (unless they are used
58   * at some point for special information).
59   */
60
61 /* This may be replaced later by something more universal */
62 static void
63 init_Base_info()
64 {
65         if(Base_info[10].ValidChars) return;
66         Base_info[10] = Base_info_10;
67         Base_info[16] = Base_info_16;
68 }
69
70 unsigned int
71 strtoul_wrap32(char *s, char **pEnd, int base)
72 {
73         int     Len;
74         int     isNegated = 0;
75         char    *sOrig = s;
76
77         init_Base_info();
78
79         while(*s && isspace(*s)) ++s;
80
81         if(*s == '-') {
82                 ++isNegated;
83                 ++s;
84                 while(*s && isspace(*s)) ++s;
85         }
86         if(base == 0) {
87                 if(*s == '0') {
88                         if(s[1] == 'x' || s[1] == 'X') {
89                                 s += 2;
90                                 base = 16;
91                         } else {
92                                 ++s;
93                                 base = 8;
94                         }
95                 } else if(isdigit(*s)) {
96                         base = 10;
97                 }
98         }
99         if(base != 10) {
100                 return strtoul(sOrig, pEnd, base);
101         }
102         
103         Len = strspn(s, Base_info[base].ValidChars);
104
105         if(Len > Base_info[base].Ulong_max_str_len
106                 ||
107            (Len == Base_info[base].Ulong_max_str_len
108                         &&
109             strncmp(Base_info[base].Ulong_max_str, s, Len) < 0)
110           ) {
111                 /* In case isNegated is set - what to do?? */
112                 /* Mightn't we say a negative number is ERANGE for strtoul? */
113                 errno = ERANGE;
114                 return Base_info[base].Ulong_max;
115         }
116
117         return strtoul(sOrig, pEnd, base);
118 }
119
120 int
121 strtol_wrap32(char *s, char **pEnd, int base)
122 {
123         int     Len;
124         int     isNegated = 0;
125         char    *sOrig = s;
126
127         init_Base_info();
128
129         while(*s && isspace(*s)) ++s;
130
131         if(*s == '-') {
132                 ++isNegated;
133                 ++s;
134                 while(*s && isspace(*s)) ++s;
135         }
136         if(base == 0) {
137                 if(*s == '0') {
138                         if(s[1] == 'x' || s[1] == 'X') {
139                                 s += 2;
140                                 base = 16;
141                         } else {
142                                 ++s;
143                                 base = 8;
144                         }
145                 } else if(isdigit(*s)) {
146                         base = 10;
147                 }
148         }
149         if(base != 10) {
150                 return strtol(sOrig, pEnd, base);
151         }
152         
153         Len = strspn(s, Base_info[base].ValidChars);
154
155         if(Len > Base_info[base].Long_max_str_len
156                                 ||
157            (!isNegated && Len == Base_info[base].Long_max_str_len
158                 &&
159             strncmp(Base_info[base].Long_max_str, s, Len) < 0)
160                                 ||
161            (isNegated && Len == Base_info[base].Long_min_str_len
162                 &&
163             strncmp(Base_info[base].Long_min_str, s, Len) < 0)
164           ) {
165                 /* In case isNegated is set - what to do?? */
166                 /* Mightn't we say a negative number is ERANGE for strtol? */
167                 errno = ERANGE;
168                 return(isNegated ? Base_info[base].Long_min
169                                         :
170                                    Base_info[base].Long_min);
171         }
172
173         return strtol(sOrig, pEnd, base);
174 }