Line | Count | Source (jump to first uncovered line) |
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 | | #include "atoi.h" |
35 | | |
36 | | /** |
37 | | * str_atol_clamp - Convert ASCII string to a long and clamp |
38 | | * @param[in] str String to read |
39 | | * @param[in] lmin Lower bound |
40 | | * @param[in] lmax Upper bound |
41 | | * @param[out] dst Store the result |
42 | | * @retval ptr endptr |
43 | | * |
44 | | * - endptr == NULL -> no conversion happened, or overflow |
45 | | * - endptr[0] == '\0' -> str was fully converted |
46 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
47 | | * |
48 | | * This is a strtol() wrapper with range checking. |
49 | | * errno may be set on error, e.g. ERANGE |
50 | | */ |
51 | | static const char *str_atol_clamp(const char *str, long *dst, long lmin, long lmax) |
52 | 17.8k | { |
53 | 17.8k | if (dst) |
54 | 17.8k | { |
55 | 17.8k | *dst = 0; |
56 | 17.8k | } |
57 | | |
58 | 17.8k | if (!str || (*str == '\0')) |
59 | 6.08k | { |
60 | 6.08k | return NULL; |
61 | 6.08k | } |
62 | | |
63 | 11.7k | char *e = NULL; |
64 | 11.7k | errno = 0; |
65 | 11.7k | long res = strtol(str, &e, 10); |
66 | 11.7k | if ((e == str) || (((res == LONG_MIN) || (res == LONG_MAX)) && (errno == ERANGE)) || |
67 | 11.7k | (res < lmin) || (res > lmax)) |
68 | 1.81k | { |
69 | 1.81k | return NULL; |
70 | 1.81k | } |
71 | | |
72 | 9.96k | if (dst) |
73 | 9.96k | { |
74 | 9.96k | *dst = res; |
75 | 9.96k | } |
76 | | |
77 | 9.96k | return e; |
78 | 11.7k | } |
79 | | |
80 | | /** |
81 | | * str_atoull_clamp - Convert ASCII string to an unsigned long long and clamp |
82 | | * @param[in] str String to read |
83 | | * @param[in] ullmax Upper bound |
84 | | * @param[out] dst Store the result |
85 | | * @retval ptr endptr |
86 | | * |
87 | | * - endptr == NULL -> no conversion happened, or overflow |
88 | | * - endptr[0] == '\0' -> str was fully converted |
89 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
90 | | * |
91 | | * This is a strtoull() wrapper with range checking. |
92 | | * errno may be set on error, e.g. ERANGE |
93 | | */ |
94 | | static const char *str_atoull_clamp(const char *str, unsigned long long *dst, |
95 | | unsigned long long ullmax) |
96 | 5.99k | { |
97 | 5.99k | if (dst) |
98 | 5.99k | { |
99 | 5.99k | *dst = 0; |
100 | 5.99k | } |
101 | | |
102 | 5.99k | if (!str || (*str == '\0')) |
103 | 0 | { |
104 | 0 | return str; |
105 | 0 | } |
106 | | |
107 | 5.99k | char *e = NULL; |
108 | 5.99k | errno = 0; |
109 | 5.99k | unsigned long long res = strtoull(str, &e, 10); |
110 | 5.99k | if ((e == str) || ((res == ULLONG_MAX) && (errno == ERANGE)) || (res > ullmax)) |
111 | 2.21k | { |
112 | 2.21k | return NULL; |
113 | 2.21k | } |
114 | | |
115 | 3.78k | if (dst) |
116 | 3.78k | { |
117 | 3.78k | *dst = res; |
118 | 3.78k | } |
119 | | |
120 | 3.78k | return e; |
121 | 5.99k | } |
122 | | |
123 | | /** |
124 | | * mutt_str_atol - Convert ASCII string to a long |
125 | | * @param[in] str String to read |
126 | | * @param[out] dst Store the result |
127 | | * @retval ptr endptr |
128 | | * |
129 | | * - endptr == NULL -> no conversion happened, or overflow |
130 | | * - endptr[0] == '\0' -> str was fully converted |
131 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
132 | | * |
133 | | * This is a strtol() wrapper with range checking. |
134 | | * errno may be set on error, e.g. ERANGE |
135 | | */ |
136 | | const char *mutt_str_atol(const char *str, long *dst) |
137 | 0 | { |
138 | 0 | return str_atol_clamp(str, dst, LONG_MIN, LONG_MAX); |
139 | 0 | } |
140 | | |
141 | | /** |
142 | | * mutt_str_atos - Convert ASCII string to a short |
143 | | * @param[in] str String to read |
144 | | * @param[out] dst Store the result |
145 | | * @retval 0 Success |
146 | | * @retval -1 Error |
147 | | * @retval -2 Error, overflow |
148 | | * |
149 | | * This is a strtol() wrapper with range checking. |
150 | | * If @a dst is NULL, the string will be tested only (without conversion). |
151 | | * |
152 | | * errno may be set on error, e.g. ERANGE |
153 | | */ |
154 | | const char *mutt_str_atos(const char *str, short *dst) |
155 | 0 | { |
156 | 0 | long l; |
157 | 0 | const char *res = str_atol_clamp(str, &l, SHRT_MIN, SHRT_MAX); |
158 | 0 | if (dst) |
159 | 0 | { |
160 | 0 | *dst = res ? l : 0; |
161 | 0 | } |
162 | 0 | return res; |
163 | 0 | } |
164 | | |
165 | | /** |
166 | | * mutt_str_atoi - Convert ASCII string to an integer |
167 | | * @param[in] str String to read |
168 | | * @param[out] dst Store the result |
169 | | * @retval ptr endptr |
170 | | * |
171 | | * - endptr == NULL -> no conversion happened, or overflow |
172 | | * - endptr[0] == '\0' -> str was fully converted |
173 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
174 | | * |
175 | | * This is a strtol() wrapper with range checking. |
176 | | * If @a dst is NULL, the string will be tested only (without conversion). |
177 | | * errno may be set on error, e.g. ERANGE |
178 | | */ |
179 | | const char *mutt_str_atoi(const char *str, int *dst) |
180 | 17.8k | { |
181 | 17.8k | long l; |
182 | 17.8k | const char *res = str_atol_clamp(str, &l, INT_MIN, INT_MAX); |
183 | 17.8k | if (dst) |
184 | 17.8k | { |
185 | 17.8k | *dst = res ? l : 0; |
186 | 17.8k | } |
187 | 17.8k | return res; |
188 | 17.8k | } |
189 | | |
190 | | /** |
191 | | * mutt_str_atoui - Convert ASCII string to an unsigned integer |
192 | | * @param[in] str String to read |
193 | | * @param[out] dst Store the result |
194 | | * @retval ptr endptr |
195 | | * |
196 | | * - endptr == NULL -> no conversion happened, or overflow |
197 | | * - endptr[0] == '\0' -> str was fully converted |
198 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
199 | | * |
200 | | * @note This function's return value differs from the other functions. |
201 | | * They return -1 if there is input beyond the number. |
202 | | */ |
203 | | const char *mutt_str_atoui(const char *str, unsigned int *dst) |
204 | 2.55k | { |
205 | 2.55k | unsigned long long l; |
206 | 2.55k | const char *res = str_atoull_clamp(str, &l, UINT_MAX); |
207 | 2.55k | if (dst) |
208 | 2.55k | { |
209 | 2.55k | *dst = res ? l : 0; |
210 | 2.55k | } |
211 | 2.55k | return res; |
212 | 2.55k | } |
213 | | |
214 | | /** |
215 | | * mutt_str_atoul - Convert ASCII string to an unsigned long |
216 | | * @param[in] str String to read |
217 | | * @param[out] dst Store the result |
218 | | * @retval ptr endptr |
219 | | * |
220 | | * - endptr == NULL -> no conversion happened, or overflow |
221 | | * - endptr[0] == '\0' -> str was fully converted |
222 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
223 | | * |
224 | | * @note This function's return value differs from the other functions. |
225 | | * They return -1 if there is input beyond the number. |
226 | | */ |
227 | | const char *mutt_str_atoul(const char *str, unsigned long *dst) |
228 | 3.44k | { |
229 | 3.44k | unsigned long long l; |
230 | 3.44k | const char *res = str_atoull_clamp(str, &l, ULONG_MAX); |
231 | 3.44k | if (dst) |
232 | 3.44k | { |
233 | 3.44k | *dst = res ? l : 0; |
234 | 3.44k | } |
235 | 3.44k | return res; |
236 | 3.44k | } |
237 | | |
238 | | /** |
239 | | * mutt_str_atous - Convert ASCII string to an unsigned short |
240 | | * @param[in] str String to read |
241 | | * @param[out] dst Store the result |
242 | | * @retval ptr endptr |
243 | | * |
244 | | * - endptr == NULL -> no conversion happened, or overflow |
245 | | * - endptr[0] == '\0' -> str was fully converted |
246 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
247 | | * |
248 | | * @note This function's return value differs from the other functions. |
249 | | * They return -1 if there is input beyond the number. |
250 | | */ |
251 | | const char *mutt_str_atous(const char *str, unsigned short *dst) |
252 | 0 | { |
253 | 0 | unsigned long long l; |
254 | 0 | const char *res = str_atoull_clamp(str, &l, USHRT_MAX); |
255 | 0 | if (dst) |
256 | 0 | { |
257 | 0 | *dst = res ? l : 0; |
258 | 0 | } |
259 | 0 | return res; |
260 | 0 | } |
261 | | |
262 | | /** |
263 | | * mutt_str_atoull - Convert ASCII string to an unsigned long long |
264 | | * @param[in] str String to read |
265 | | * @param[out] dst Store the result |
266 | | * @retval ptr endptr |
267 | | * |
268 | | * - endptr == NULL -> no conversion happened, or overflow |
269 | | * - endptr[0] == '\0' -> str was fully converted |
270 | | * - endptr[0] != '\0' -> endptr points to first non converted char in str |
271 | | * |
272 | | * @note This function's return value differs from the other functions. |
273 | | * They return -1 if there is input beyond the number. |
274 | | */ |
275 | | const char *mutt_str_atoull(const char *str, unsigned long long *dst) |
276 | 0 | { |
277 | 0 | return str_atoull_clamp(str, dst, ULLONG_MAX); |
278 | 0 | } |