Coverage Report

Created: 2023-06-07 06:15

/src/neomutt/mutt/atoi.c
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
}