Coverage Report

Created: 2025-07-18 06:24

/src/dovecot/src/lib/strnum.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "strnum.h"
5
6
bool str_is_numeric(const char *str, char end_char)
7
0
{
8
0
  if (*str == '\0' || *str == end_char)
9
0
    return FALSE;
10
11
0
  while (*str != '\0' && *str != end_char) {
12
0
    if (*str < '0' || *str > '9')
13
0
      return FALSE;
14
0
    str++;
15
0
  }
16
17
0
  return TRUE;
18
0
}
19
20
bool str_is_float(const char *str, char end_char)
21
0
{
22
0
  bool dot_seen = FALSE;
23
0
  bool num_seen = FALSE;
24
25
0
  if (*str == '\0' || *str == end_char)
26
0
    return FALSE;
27
28
0
  while (*str != '\0' && *str != end_char) {
29
0
    if (*str == '.') {
30
0
      if (dot_seen || !num_seen) return FALSE;
31
0
      dot_seen = TRUE;
32
0
      num_seen = FALSE;
33
0
      str++;
34
      /* enforce that number follows dot */
35
0
      continue;
36
0
    }
37
0
    if (*str < '0' || *str > '9')
38
0
      return FALSE;
39
0
    num_seen = TRUE;
40
0
    str++;
41
0
  }
42
43
0
  return num_seen;
44
0
}
45
46
/*
47
 * Unsigned decimal
48
 */
49
50
#define STR_PARSE_U__TEMPLATE(name, type)                     \
51
2.40M
int name(const char *str, type *num_r, const char **endp_r)   \
52
2.40M
{                                                             \
53
2.40M
  uintmax_t l;                                                \
54
2.40M
  if (str_parse_uintmax(str, &l, endp_r) < 0 || l > (type)-1) \
55
2.40M
    return -1;                                                \
56
2.40M
  *num_r = (type)l;                                           \
57
1.65M
  return 0;                                                   \
58
2.40M
}
str_parse_uint
Line
Count
Source
51
2.40M
int name(const char *str, type *num_r, const char **endp_r)   \
52
2.40M
{                                                             \
53
2.40M
  uintmax_t l;                                                \
54
2.40M
  if (str_parse_uintmax(str, &l, endp_r) < 0 || l > (type)-1) \
55
2.40M
    return -1;                                                \
56
2.40M
  *num_r = (type)l;                                           \
57
1.65M
  return 0;                                                   \
58
2.40M
}
Unexecuted instantiation: str_parse_ulong
Unexecuted instantiation: str_parse_ullong
Unexecuted instantiation: str_parse_uint32
Unexecuted instantiation: str_parse_uint64
Unexecuted instantiation: str_parse_uoff
59
60
STR_PARSE_U__TEMPLATE(str_parse_uint, unsigned int)
61
STR_PARSE_U__TEMPLATE(str_parse_ulong, unsigned long)
62
STR_PARSE_U__TEMPLATE(str_parse_ullong, unsigned long long)
63
STR_PARSE_U__TEMPLATE(str_parse_uint32, uint32_t)
64
STR_PARSE_U__TEMPLATE(str_parse_uint64, uint64_t)
65
66
#define STR_TO_U__TEMPLATE(name, type)                        \
67
0
int name(const char *str, type *num_r)                        \
68
0
{                                                             \
69
0
  uintmax_t l;                                                \
70
0
  if (str_to_uintmax(str, &l) < 0 || l > (type)-1)            \
71
0
    return -1;                                                \
72
0
  *num_r = (type)l;                                           \
73
0
  return 0;                                                   \
74
0
}
Unexecuted instantiation: str_to_uint
Unexecuted instantiation: str_to_ulong
Unexecuted instantiation: str_to_ullong
Unexecuted instantiation: str_to_uint32
Unexecuted instantiation: str_to_uint64
75
76
STR_TO_U__TEMPLATE(str_to_uint, unsigned int)
77
STR_TO_U__TEMPLATE(str_to_ulong, unsigned long)
78
STR_TO_U__TEMPLATE(str_to_ullong, unsigned long long)
79
STR_TO_U__TEMPLATE(str_to_uint32, uint32_t)
80
STR_TO_U__TEMPLATE(str_to_uint64, uint64_t)
81
82
int str_parse_uintmax(const char *str, uintmax_t *num_r,
83
  const char **endp_r)
84
2.40M
{
85
2.40M
  uintmax_t n = 0;
86
87
2.40M
  if (*str < '0' || *str > '9')
88
742k
    return -1;
89
90
1.81M
  do {
91
1.81M
    if (n >= ((uintmax_t)-1 / 10)) {
92
3.93k
      if (n > (uintmax_t)-1 / 10)
93
2.11k
        return -1;
94
1.82k
      if ((uintmax_t)(*str - '0') > ((uintmax_t)-1 % 10))
95
256
        return -1;
96
1.82k
    }
97
1.81M
    n = n * 10 + (*str - '0');
98
1.81M
    str++;
99
1.81M
  } while (*str >= '0' && *str <= '9');
100
101
1.65M
  if (endp_r != NULL)
102
1.65M
    *endp_r = str;
103
1.65M
  *num_r = n;
104
1.65M
  return 0;
105
1.65M
}
106
int str_to_uintmax(const char *str, uintmax_t *num_r)
107
0
{
108
0
  const char *endp;
109
0
  uintmax_t n;
110
0
  int ret = str_parse_uintmax(str, &n, &endp);
111
0
  if ((ret != 0) || (*endp != '\0'))
112
0
    return -1;
113
0
  *num_r = n;
114
0
  return 0;
115
0
}
116
117
bool str_uint_equals(const char *str, uintmax_t num)
118
0
{
119
0
  uintmax_t l;
120
121
0
  if (str_to_uintmax(str, &l) < 0)
122
0
    return FALSE;
123
0
  return l == num;
124
0
}
125
126
/*
127
 * Unsigned hexadecimal
128
 */
129
130
#define STR_PARSE_UHEX__TEMPLATE(name, type)                       \
131
0
int name(const char *str, type *num_r, const char **endp_r)        \
132
0
{                                                                  \
133
0
  uintmax_t l;                                                     \
134
0
  if (str_parse_uintmax_hex(str, &l, endp_r) < 0 || l > (type)-1)  \
135
0
    return -1;                                                     \
136
0
  *num_r = (type)l;                                                \
137
0
  return 0;                                                        \
138
0
}
Unexecuted instantiation: str_parse_uint_hex
Unexecuted instantiation: str_parse_ulong_hex
Unexecuted instantiation: str_parse_ullong_hex
Unexecuted instantiation: str_parse_uint32_hex
Unexecuted instantiation: str_parse_uint64_hex
139
140
STR_PARSE_UHEX__TEMPLATE(str_parse_uint_hex, unsigned int)
141
STR_PARSE_UHEX__TEMPLATE(str_parse_ulong_hex, unsigned long)
142
STR_PARSE_UHEX__TEMPLATE(str_parse_ullong_hex, unsigned long long)
143
STR_PARSE_UHEX__TEMPLATE(str_parse_uint32_hex, uint32_t)
144
STR_PARSE_UHEX__TEMPLATE(str_parse_uint64_hex, uint64_t)
145
146
#define STR_TO_UHEX__TEMPLATE(name, type)                          \
147
0
int name(const char *str, type *num_r)                             \
148
0
{                                                                  \
149
0
  uintmax_t l;                                                     \
150
0
  if (str_to_uintmax_hex(str, &l) < 0 || l > (type)-1)             \
151
0
    return -1;                                                     \
152
0
  *num_r = (type)l;                                                \
153
0
  return 0;                                                        \
154
0
}
Unexecuted instantiation: str_to_uint_hex
Unexecuted instantiation: str_to_ulong_hex
Unexecuted instantiation: str_to_ullong_hex
Unexecuted instantiation: str_to_uint32_hex
Unexecuted instantiation: str_to_uint64_hex
155
156
STR_TO_UHEX__TEMPLATE(str_to_uint_hex, unsigned int)
157
STR_TO_UHEX__TEMPLATE(str_to_ulong_hex, unsigned long)
158
STR_TO_UHEX__TEMPLATE(str_to_ullong_hex, unsigned long long)
159
STR_TO_UHEX__TEMPLATE(str_to_uint32_hex, uint32_t)
160
STR_TO_UHEX__TEMPLATE(str_to_uint64_hex, uint64_t)
161
162
static inline int _str_parse_hex(const char ch,
163
  unsigned int *hex_r)
164
0
{
165
0
  switch (ch) {
166
0
  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
167
0
    *hex_r = (unsigned int)(ch - 'a' + 10);
168
0
    return 0;
169
0
  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
170
0
    *hex_r = (unsigned int)(ch - 'A' + 10);
171
0
    return 0;
172
0
  case '0': case '1': case '2': case '3': case '4':
173
0
  case '5': case '6': case '7': case '8': case '9':
174
0
    *hex_r = (unsigned int)(ch - '0');
175
0
    return 0;
176
0
  default:
177
0
    break;
178
0
  }
179
0
  return -1;
180
0
}
181
int str_parse_uintmax_hex(const char *str, uintmax_t *num_r,
182
  const char **endp_r)
183
0
{
184
0
  unsigned int hex;
185
0
  uintmax_t n = 0;
186
187
0
  if (_str_parse_hex(*str, &hex) < 0)
188
0
    return -1;
189
190
0
  do {
191
0
    if (n > (uintmax_t)-1 >> 4)
192
0
      return -1;
193
0
    n = (n << 4) + hex;
194
0
    str++;
195
0
  } while (_str_parse_hex(*str, &hex) >= 0);
196
0
  if (endp_r != NULL)
197
0
    *endp_r = str;
198
0
  *num_r = n;
199
0
  return 0;
200
0
}
201
int str_to_uintmax_hex(const char *str, uintmax_t *num_r)
202
0
{
203
0
  const char *endp;
204
0
  uintmax_t n;
205
0
  int ret = str_parse_uintmax_hex(str, &n, &endp);
206
0
  if ((ret != 0) || (*endp != '\0'))
207
0
    return -1;
208
0
  *num_r = n;
209
0
  return 0;
210
0
}
211
212
/*
213
 * Unsigned octal
214
 */
215
216
#define STR_PARSE_UOCT__TEMPLATE(name, type)                       \
217
0
int name(const char *str, type *num_r, const char **endp_r)        \
218
0
{                                                                  \
219
0
  uintmax_t l;                                                     \
220
0
  if (str_parse_uintmax_oct(str, &l, endp_r) < 0 || l > (type)-1)  \
221
0
    return -1;                                                     \
222
0
  *num_r = (type)l;                                                \
223
0
  return 0;                                                        \
224
0
}
Unexecuted instantiation: str_parse_uint_oct
Unexecuted instantiation: str_parse_ulong_oct
Unexecuted instantiation: str_parse_ullong_oct
Unexecuted instantiation: str_parse_uint32_oct
Unexecuted instantiation: str_parse_uint64_oct
225
226
STR_PARSE_UOCT__TEMPLATE(str_parse_uint_oct, unsigned int)
227
STR_PARSE_UOCT__TEMPLATE(str_parse_ulong_oct, unsigned long)
228
STR_PARSE_UOCT__TEMPLATE(str_parse_ullong_oct, unsigned long long)
229
STR_PARSE_UOCT__TEMPLATE(str_parse_uint32_oct, uint32_t)
230
STR_PARSE_UOCT__TEMPLATE(str_parse_uint64_oct, uint64_t)
231
232
#define STR_TO_UOCT__TEMPLATE(name, type)                          \
233
0
int name(const char *str, type *num_r)                             \
234
0
{                                                                  \
235
0
  uintmax_t l;                                                     \
236
0
  if (str_to_uintmax_oct(str, &l) < 0 || l > (type)-1)             \
237
0
    return -1;                                                     \
238
0
  *num_r = (type)l;                                                \
239
0
  return 0;                                                        \
240
0
}
Unexecuted instantiation: str_to_uint_oct
Unexecuted instantiation: str_to_ulong_oct
Unexecuted instantiation: str_to_ullong_oct
Unexecuted instantiation: str_to_uint32_oct
Unexecuted instantiation: str_to_uint64_oct
241
242
STR_TO_UOCT__TEMPLATE(str_to_uint_oct, unsigned int)
243
STR_TO_UOCT__TEMPLATE(str_to_ulong_oct, unsigned long)
244
STR_TO_UOCT__TEMPLATE(str_to_ullong_oct, unsigned long long)
245
STR_TO_UOCT__TEMPLATE(str_to_uint32_oct, uint32_t)
246
STR_TO_UOCT__TEMPLATE(str_to_uint64_oct, uint64_t)
247
248
int str_parse_uintmax_oct(const char *str, uintmax_t *num_r,
249
  const char **endp_r)
250
0
{
251
0
  uintmax_t n = 0;
252
253
0
  if (*str < '0' || *str > '7')
254
0
    return -1;
255
256
0
  for (; *str >= '0' && *str <= '7'; str++) {
257
0
    if (n > (uintmax_t)-1 >> 3)
258
0
      return -1;
259
0
    n = (n << 3) + (*str - '0');
260
0
  }
261
0
  if (endp_r != NULL)
262
0
    *endp_r = str;
263
0
  *num_r = n;
264
0
  return 0;
265
0
}
266
int str_to_uintmax_oct(const char *str, uintmax_t *num_r)
267
0
{
268
0
  const char *endp;
269
0
  uintmax_t n;
270
0
  int ret = str_parse_uintmax_oct(str, &n, &endp);
271
0
  if ((ret != 0) || (*endp != '\0'))
272
0
    return -1;
273
0
  *num_r = n;
274
0
  return 0;
275
0
}
276
277
/*
278
 * Signed
279
 */
280
281
#define STR_PARSE_S__TEMPLATE(name, type, int_min, int_max)   \
282
0
int name(const char *str, type *num_r, const char **endp_r)   \
283
0
{                                                             \
284
0
  intmax_t l;                                                 \
285
0
  if (str_parse_intmax(str, &l, endp_r) < 0)                  \
286
0
    return -1;                                                \
287
0
  if (l < int_min || l > int_max)                             \
288
0
    return -1;                                                \
289
0
  *num_r = (type)l;                                           \
290
0
  return 0;                                                   \
291
0
}
Unexecuted instantiation: str_parse_int
Unexecuted instantiation: str_parse_long
Unexecuted instantiation: str_parse_llong
Unexecuted instantiation: str_parse_int32
Unexecuted instantiation: str_parse_int64
292
293
STR_PARSE_S__TEMPLATE(str_parse_int, int, INT_MIN, INT_MAX)
294
STR_PARSE_S__TEMPLATE(str_parse_long, long, LONG_MIN, LONG_MAX)
295
STR_PARSE_S__TEMPLATE(str_parse_llong, long long, LLONG_MIN, LLONG_MAX)
296
STR_PARSE_S__TEMPLATE(str_parse_int32, int32_t, INT32_MIN, INT32_MAX)
297
STR_PARSE_S__TEMPLATE(str_parse_int64, int64_t, INT64_MIN, INT64_MAX)
298
299
#define STR_TO_S__TEMPLATE(name, type, int_min, int_max)      \
300
0
int name(const char *str, type *num_r)                        \
301
0
{                                                             \
302
0
  intmax_t l;                                                 \
303
0
  if (str_to_intmax(str, &l) < 0)                             \
304
0
    return -1;                                                \
305
0
  if (l < int_min || l > int_max)                             \
306
0
    return -1;                                                \
307
0
  *num_r = (type)l;                                           \
308
0
  return 0;                                                   \
309
0
}
Unexecuted instantiation: str_to_int
Unexecuted instantiation: str_to_long
Unexecuted instantiation: str_to_llong
Unexecuted instantiation: str_to_int32
Unexecuted instantiation: str_to_int64
310
311
STR_TO_S__TEMPLATE(str_to_int, int, INT_MIN, INT_MAX)
312
STR_TO_S__TEMPLATE(str_to_long, long, LONG_MIN, LONG_MAX)
313
STR_TO_S__TEMPLATE(str_to_llong, long long, LLONG_MIN, LLONG_MAX)
314
STR_TO_S__TEMPLATE(str_to_int32, int32_t, INT32_MIN, INT32_MAX)
315
STR_TO_S__TEMPLATE(str_to_int64, int64_t, INT64_MIN, INT64_MAX)
316
317
int ATTR_NO_SANITIZE_IMPLICIT_CONVERSION ATTR_NO_SANITIZE_INTEGER
318
str_parse_intmax(const char *str, intmax_t *num_r, const char **endp_r)
319
0
{
320
0
  bool neg = FALSE;
321
0
  uintmax_t l;
322
323
0
  if (*str == '-') {
324
0
    neg = TRUE;
325
0
    str++;
326
0
  }
327
0
  if (str_parse_uintmax(str, &l, endp_r) < 0)
328
0
    return -1;
329
330
0
  if (!neg) {
331
0
    if (l > INTMAX_MAX)
332
0
      return -1;
333
0
    *num_r = (intmax_t)l;
334
0
  } else {
335
0
    if (l > UINTMAX_MAX - (UINTMAX_MAX + INTMAX_MIN))
336
0
      return -1;
337
0
    *num_r = (intmax_t) UNSIGNED_MINUS(l);
338
0
  }
339
0
  return 0;
340
0
}
341
int str_to_intmax(const char *str, intmax_t *num_r)
342
0
{
343
0
  const char *endp;
344
0
  intmax_t n;
345
0
  int ret = str_parse_intmax(str, &n, &endp);
346
0
  if ((ret != 0) || (*endp != '\0'))
347
0
    return -1;
348
0
  *num_r = n;
349
0
  return 0;
350
0
}
351
352
/*
353
 * Special numeric types
354
 */
355
356
static int verify_xid(uintmax_t l, unsigned int result_size)
357
0
{
358
0
  unsigned int result_bits;
359
360
  /* we assume that result is a signed type,
361
     but that it can never be negative */
362
0
  result_bits = result_size*CHAR_BIT - 1;
363
0
  if ((l >> result_bits) != 0)
364
0
    return -1;
365
0
  return 0;
366
0
}
367
368
int str_to_uid(const char *str, uid_t *num_r)
369
0
{
370
0
  uintmax_t l;
371
372
0
  if (str_to_uintmax(str, &l) < 0)
373
0
    return -1;
374
375
0
  if (verify_xid(l, sizeof(*num_r)) < 0)
376
0
    return -1;
377
0
  *num_r = (uid_t)l;
378
0
  return 0;
379
0
}
380
381
int str_to_gid(const char *str, gid_t *num_r)
382
0
{
383
0
  uintmax_t l;
384
385
0
  if (str_to_uintmax(str, &l) < 0)
386
0
    return -1;
387
388
  /* OS X uses negative GIDs */
389
0
#ifndef __APPLE__
390
0
  if (verify_xid(l, sizeof(*num_r)) < 0)
391
0
    return -1;
392
0
#endif
393
0
  *num_r = (gid_t)l;
394
0
  return 0;
395
0
}
396
397
int str_to_pid(const char *str, pid_t *num_r)
398
0
{
399
0
  uintmax_t l;
400
401
0
  if (str_to_uintmax(str, &l) < 0)
402
0
    return -1;
403
404
0
  if (verify_xid(l, sizeof(*num_r)) < 0)
405
0
    return -1;
406
0
  *num_r = (pid_t)l;
407
0
  return 0;
408
0
}
409
410
int str_to_ino(const char *str, ino_t *num_r)
411
0
{
412
0
  uintmax_t l;
413
414
0
  if (str_to_uintmax(str, &l) < 0)
415
0
    return -1;
416
417
0
  if (verify_xid(l, sizeof(*num_r)) < 0)
418
0
    return -1;
419
0
  *num_r = (ino_t)l;
420
0
  return 0;
421
0
}
422
423
int str_to_uoff(const char *str, uoff_t *num_r)
424
0
{
425
0
  uintmax_t l;
426
427
0
  if (str_to_uintmax(str, &l) < 0)
428
0
    return -1;
429
430
0
  if (l > UOFF_T_MAX)
431
0
    return -1;
432
0
  *num_r = (uoff_t)l;
433
0
  return 0;
434
0
}
435
436
int str_to_time(const char *str, time_t *num_r)
437
0
{
438
0
  intmax_t l;
439
440
0
  if (str_to_intmax(str, &l) < 0)
441
0
    return -1;
442
443
0
  *num_r = (time_t)l;
444
0
  return 0;
445
0
}
446
447
STR_PARSE_U__TEMPLATE(str_parse_uoff, uoff_t)
448
449
/*
450
 * Floating point types
451
 */
452
453
int str_to_float(const char *str, float *num_r)
454
0
{
455
0
  char *endp;
456
0
  float num = strtof(str, &endp);
457
0
  if (*endp != '\0')
458
0
    return -1;
459
460
0
  *num_r = num;
461
0
  return 0;
462
0
}
463
464
int str_to_double(const char *str, double *num_r)
465
0
{
466
0
  char *endp;
467
0
  double num = strtod(str, &endp);
468
0
  if (*endp != '\0')
469
0
    return -1;
470
471
0
  *num_r = num;
472
0
  return 0;
473
0
}
474
475
/*
476
 * Error handling
477
 */
478
479
const char *str_num_error(const char *str)
480
0
{
481
0
  if (*str == '-') {
482
0
    if (!str_is_numeric(str + 1, '\0'))
483
0
      return "Not a valid number";
484
0
    return "Number too small";
485
0
  } else {
486
0
    if (!str_is_numeric(str, '\0'))
487
0
      return "Not a valid number";
488
0
    return "Number too large";
489
0
  }
490
0
}