Line data Source code
1 : /**
2 : * @file
3 : * Parse a number in a string
4 : *
5 : * @authors
6 : * Copyright (C) 2021 Pietro Cerutti <gahr@gahr.ch>
7 : *
8 : * @copyright
9 : * This program is free software: you can redistribute it and/or modify it under
10 : * the terms of the GNU General Public License as published by the Free Software
11 : * Foundation, either version 2 of the License, or (at your option) any later
12 : * version.
13 : *
14 : * This program is distributed in the hope that it will be useful, but WITHOUT
15 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 : * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 : * details.
18 : *
19 : * You should have received a copy of the GNU General Public License along with
20 : * this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : /**
24 : * @page mutt_atoi Parse a number in a string
25 : *
26 : * Parse a number in a string.
27 : */
28 :
29 : #include "config.h"
30 : #include <errno.h>
31 : #include <limits.h>
32 : #include <stdio.h>
33 : #include <stdlib.h>
34 :
35 : /**
36 : * str_atol_clamp - Convert ASCII string to a long and clamp
37 : * @param[in] str String to read
38 : * @param[in] lmin Lower bound
39 : * @param[in] lmax Upper bound
40 : * @param[out] dst Store the result
41 : * @retval endptr
42 : *
43 : * endptr == NULL -> no conversion happened, or overflow
44 : * endptr[0] == '\0' -> str was fully converted
45 : * endptr[0] != '\0' -> endptr points to first non converted char in str
46 : *
47 : * This is a strtol() wrapper with range checking.
48 : * errno may be set on error, e.g. ERANGE
49 : */
50 424 : static const char *str_atol_clamp(const char *str, long *dst, long lmin, long lmax)
51 : {
52 424 : if (dst)
53 : {
54 422 : *dst = 0;
55 : }
56 :
57 424 : if (!str || (*str == '\0'))
58 : {
59 22 : return NULL;
60 : }
61 :
62 402 : char *e = NULL;
63 402 : errno = 0;
64 402 : long res = strtol(str, &e, 10);
65 402 : if ((e == str) || (((res == LONG_MIN) || (res == LONG_MAX)) && (errno == ERANGE)) ||
66 326 : (res < lmin) || (res > lmax))
67 : {
68 88 : return NULL;
69 : }
70 :
71 314 : if (dst)
72 : {
73 312 : *dst = res;
74 : }
75 :
76 314 : return e;
77 : }
78 :
79 : /**
80 : * str_atoull_clamp - Convert ASCII string to an unsigned long long and clamp
81 : * @param[in] str String to read
82 : * @param[in] ullmax Upper bound
83 : * @param[out] dst Store the result
84 : * @retval endptr
85 : *
86 : * endptr == NULL -> no conversion happened, or overflow
87 : * endptr[0] == '\0' -> str was fully converted
88 : * endptr[0] != '\0' -> endptr points to first non converted char in str
89 : *
90 : * This is a strtoull() wrapper with range checking.
91 : * errno may be set on error, e.g. ERANGE
92 : */
93 406 : static const char *str_atoull_clamp(const char *str, unsigned long long *dst,
94 : unsigned long long ullmax)
95 : {
96 406 : if (dst)
97 : {
98 404 : *dst = 0;
99 : }
100 :
101 406 : if (!str || (*str == '\0'))
102 : {
103 6 : return str;
104 : }
105 :
106 400 : char *e = NULL;
107 400 : errno = 0;
108 400 : unsigned long long res = strtoull(str, &e, 10);
109 400 : if ((e == str) || ((res == ULLONG_MAX) && (errno == ERANGE)) || (res > ullmax))
110 : {
111 50 : return NULL;
112 : }
113 :
114 350 : if (dst)
115 : {
116 348 : *dst = res;
117 : }
118 :
119 350 : return e;
120 : }
121 :
122 : /**
123 : * mutt_str_atol - Convert ASCII string to a long
124 : * @param[in] str String to read
125 : * @param[out] dst Store the result
126 : * @retval endptr
127 : *
128 : * endptr == NULL -> no conversion happened, or overflow
129 : * endptr[0] == '\0' -> str was fully converted
130 : * endptr[0] != '\0' -> endptr points to first non converted char in str
131 : *
132 : * This is a strtol() wrapper with range checking.
133 : * errno may be set on error, e.g. ERANGE
134 : */
135 158 : const char *mutt_str_atol(const char *str, long *dst)
136 : {
137 158 : return str_atol_clamp(str, dst, LONG_MIN, LONG_MAX);
138 : }
139 :
140 : /**
141 : * mutt_str_atos - Convert ASCII string to a short
142 : * @param[in] str String to read
143 : * @param[out] dst Store the result
144 : * @retval 0 Success
145 : * @retval -1 Error
146 : * @retval -2 Error, overflow
147 : *
148 : * This is a strtol() wrapper with range checking.
149 : * If @a dst is NULL, the string will be tested only (without conversion).
150 : *
151 : * errno may be set on error, e.g. ERANGE
152 : */
153 78 : const char *mutt_str_atos(const char *str, short *dst)
154 : {
155 : long l;
156 78 : const char *res = str_atol_clamp(str, &l, SHRT_MIN, SHRT_MAX);
157 78 : if (dst)
158 : {
159 76 : *dst = res ? l : 0;
160 : }
161 78 : return res;
162 : }
163 :
164 : /**
165 : * mutt_str_atoi - Convert ASCII string to an integer
166 : * @param[in] str String to read
167 : * @param[out] dst Store the result
168 : * @retval endptr
169 : *
170 : * endptr == NULL -> no conversion happened, or overflow
171 : * endptr[0] == '\0' -> str was fully converted
172 : * endptr[0] != '\0' -> endptr points to first non converted char in str
173 : *
174 : * This is a strtol() wrapper with range checking.
175 : * If @a dst is NULL, the string will be tested only (without conversion).
176 : * errno may be set on error, e.g. ERANGE
177 : */
178 188 : const char *mutt_str_atoi(const char *str, int *dst)
179 : {
180 : long l;
181 188 : const char *res = str_atol_clamp(str, &l, INT_MIN, INT_MAX);
182 188 : if (dst)
183 : {
184 186 : *dst = res ? l : 0;
185 : }
186 188 : return res;
187 : }
188 :
189 : /**
190 : * mutt_str_atoui - Convert ASCII string to an unsigned integer
191 : * @param[in] str String to read
192 : * @param[out] dst Store the result
193 : * @retval endptr
194 : *
195 : * endptr == NULL -> no conversion happened, or overflow
196 : * endptr[0] == '\0' -> str was fully converted
197 : * endptr[0] != '\0' -> endptr points to first non converted char in str
198 : *
199 : * @note This function's return value differs from the other functions.
200 : * They return -1 if there is input beyond the number.
201 : */
202 54 : const char *mutt_str_atoui(const char *str, unsigned int *dst)
203 : {
204 : unsigned long long l;
205 54 : const char *res = str_atoull_clamp(str, &l, UINT_MAX);
206 54 : if (dst)
207 : {
208 52 : *dst = res ? l : 0;
209 : }
210 54 : return res;
211 : }
212 :
213 : /**
214 : * mutt_str_atoul - Convert ASCII string to an unsigned long
215 : * @param[in] str String to read
216 : * @param[out] dst Store the result
217 : * @retval endptr
218 : *
219 : * endptr == NULL -> no conversion happened, or overflow
220 : * endptr[0] == '\0' -> str was fully converted
221 : * endptr[0] != '\0' -> endptr points to first non converted char in str
222 : *
223 : * @note This function's return value differs from the other functions.
224 : * They return -1 if there is input beyond the number.
225 : */
226 52 : const char *mutt_str_atoul(const char *str, unsigned long *dst)
227 : {
228 : unsigned long long l;
229 52 : const char *res = str_atoull_clamp(str, &l, ULONG_MAX);
230 52 : if (dst)
231 : {
232 50 : *dst = res ? l : 0;
233 : }
234 52 : return res;
235 : }
236 :
237 : /**
238 : * mutt_str_atous - Convert ASCII string to an unsigned short
239 : * @param[in] str String to read
240 : * @param[out] dst Store the result
241 : * @retval endptr
242 : *
243 : * endptr == NULL -> no conversion happened, or overflow
244 : * endptr[0] == '\0' -> str was fully converted
245 : * endptr[0] != '\0' -> endptr points to first non converted char in str
246 : *
247 : * @note This function's return value differs from the other functions.
248 : * They return -1 if there is input beyond the number.
249 : */
250 248 : const char *mutt_str_atous(const char *str, unsigned short *dst)
251 : {
252 : unsigned long long l;
253 248 : const char *res = str_atoull_clamp(str, &l, USHRT_MAX);
254 248 : if (dst)
255 : {
256 248 : *dst = res ? l : 0;
257 : }
258 248 : return res;
259 : }
260 :
261 : /**
262 : * mutt_str_atoull - Convert ASCII string to an unsigned long long
263 : * @param[in] str String to read
264 : * @param[out] dst Store the result
265 : * @retval endptr
266 : *
267 : * endptr == NULL -> no conversion happened, or overflow
268 : * endptr[0] == '\0' -> str was fully converted
269 : * endptr[0] != '\0' -> endptr points to first non converted char in str
270 : *
271 : * @note This function's return value differs from the other functions.
272 : * They return -1 if there is input beyond the number.
273 : */
274 52 : const char *mutt_str_atoull(const char *str, unsigned long long *dst)
275 : {
276 52 : return str_atoull_clamp(str, dst, ULLONG_MAX);
277 : }
|