/src/dovecot/src/lib/str-parse.c
Line | Count | Source |
1 | | /* Copyright (c) 2022 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str-parse.h" |
5 | | |
6 | | #include <ctype.h> |
7 | | |
8 | | static int str_parse_get_interval_full( |
9 | | const char *str, unsigned int *interval_r, bool milliseconds, |
10 | | const char **error_r) |
11 | 0 | { |
12 | 0 | uintmax_t num, multiply = milliseconds ? 1000 : 1; |
13 | 0 | const char *p; |
14 | |
|
15 | 0 | if (str_parse_uintmax(str, &num, &p) < 0) { |
16 | 0 | *error_r = t_strconcat("Invalid time interval: ", str, NULL); |
17 | 0 | return -1; |
18 | 0 | } |
19 | 0 | while (*p == ' ') p++; |
20 | 0 | if (*p == '\0' && num != 0) { |
21 | 0 | *error_r = t_strdup_printf("Time interval '%s' is missing units " |
22 | 0 | "(add e.g. 's' for seconds)", str); |
23 | 0 | return -1; |
24 | 0 | } |
25 | 0 | switch (i_toupper(*p)) { |
26 | 0 | case 'S': |
27 | 0 | multiply *= 1; |
28 | 0 | if (str_begins_icase_with("secs", p) || |
29 | 0 | str_begins_icase_with("seconds", p)) |
30 | 0 | p = ""; |
31 | 0 | break; |
32 | 0 | case 'M': |
33 | 0 | multiply *= 60; |
34 | 0 | if (str_begins_icase_with("mins", p) || |
35 | 0 | str_begins_icase_with("minutes", p)) |
36 | 0 | p = ""; |
37 | 0 | else if (str_begins_icase_with("msecs", p) || |
38 | 0 | str_begins_icase_with("mseconds", p) || |
39 | 0 | str_begins_icase_with("millisecs", p) || |
40 | 0 | str_begins_icase_with("milliseconds", p)) { |
41 | 0 | if (milliseconds || (num % 1000) == 0) { |
42 | 0 | if (!milliseconds) { |
43 | | /* allow ms also for seconds, as long |
44 | | as it's divisible by seconds */ |
45 | 0 | num /= 1000; |
46 | 0 | } |
47 | 0 | multiply = 1; |
48 | 0 | p = ""; |
49 | 0 | break; |
50 | 0 | } |
51 | 0 | *error_r = t_strdup_printf( |
52 | 0 | "Milliseconds not supported for this setting: %s", str); |
53 | 0 | return -1; |
54 | 0 | } |
55 | 0 | break; |
56 | 0 | case 'H': |
57 | 0 | multiply *= 60*60; |
58 | 0 | if (str_begins_icase_with("hours", p)) |
59 | 0 | p = ""; |
60 | 0 | break; |
61 | 0 | case 'D': |
62 | 0 | multiply *= 60*60*24; |
63 | 0 | if (str_begins_icase_with("days", p)) |
64 | 0 | p = ""; |
65 | 0 | break; |
66 | 0 | case 'W': |
67 | 0 | multiply *= 60*60*24*7; |
68 | 0 | if (str_begins_icase_with("weeks", p)) |
69 | 0 | p = ""; |
70 | 0 | break; |
71 | 0 | } |
72 | | |
73 | 0 | if (*p != '\0') { |
74 | 0 | *error_r = t_strconcat("Invalid time interval: ", str, NULL); |
75 | 0 | return -1; |
76 | 0 | } |
77 | 0 | if (num > UINT_MAX / multiply) { |
78 | 0 | *error_r = t_strconcat("Time interval is too large: ", |
79 | 0 | str, NULL); |
80 | 0 | return -1; |
81 | 0 | } |
82 | 0 | *interval_r = num * multiply; |
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | | int str_parse_get_interval(const char *str, unsigned int *secs_r, |
87 | | const char **error_r) |
88 | 0 | { |
89 | 0 | return str_parse_get_interval_full(str, secs_r, FALSE, error_r); |
90 | 0 | } |
91 | | |
92 | | int str_parse_get_interval_msecs(const char *str, unsigned int *msecs_r, |
93 | | const char **error_r) |
94 | 0 | { |
95 | 0 | return str_parse_get_interval_full(str, msecs_r, TRUE, error_r); |
96 | 0 | } |
97 | | |
98 | | int str_parse_get_size(const char *str, uoff_t *bytes_r, |
99 | | const char **error_r) |
100 | 0 | { |
101 | 0 | uintmax_t num, multiply = 1; |
102 | 0 | const char *p; |
103 | |
|
104 | 0 | if (str_parse_uintmax(str, &num, &p) < 0) { |
105 | 0 | *error_r = t_strconcat("Invalid size: ", str, NULL); |
106 | 0 | return -1; |
107 | 0 | } |
108 | 0 | while (*p == ' ') p++; |
109 | 0 | switch (i_toupper(*p)) { |
110 | 0 | case 'B': |
111 | 0 | multiply = 1; |
112 | 0 | p += 1; |
113 | 0 | break; |
114 | 0 | case 'K': |
115 | 0 | multiply = 1024; |
116 | 0 | p += 1; |
117 | 0 | break; |
118 | 0 | case 'M': |
119 | 0 | multiply = 1024*1024; |
120 | 0 | p += 1; |
121 | 0 | break; |
122 | 0 | case 'G': |
123 | 0 | multiply = 1024*1024*1024; |
124 | 0 | p += 1; |
125 | 0 | break; |
126 | 0 | case 'T': |
127 | 0 | multiply = 1024ULL*1024*1024*1024; |
128 | 0 | p += 1; |
129 | 0 | break; |
130 | 0 | } |
131 | | |
132 | 0 | if (multiply > 1) { |
133 | | /* Allow: k, ki, kiB */ |
134 | 0 | if (i_toupper(*p) == 'I') |
135 | 0 | p++; |
136 | 0 | if (i_toupper(*p) == 'B') |
137 | 0 | p++; |
138 | 0 | } |
139 | 0 | if (*p != '\0') { |
140 | 0 | *error_r = t_strconcat("Invalid size: ", str, NULL); |
141 | 0 | return -1; |
142 | 0 | } |
143 | 0 | if (num > (UOFF_T_MAX) / multiply) { |
144 | 0 | *error_r = t_strconcat("Size is too large: ", str, NULL); |
145 | 0 | return -1; |
146 | 0 | } |
147 | 0 | *bytes_r = num * multiply; |
148 | 0 | return 0; |
149 | 0 | } |
150 | | |
151 | | int str_parse_get_bool(const char *value, bool *result_r, |
152 | | const char **error_r) |
153 | 0 | { |
154 | | /* FIXME: eventually we'd want to support only yes/no */ |
155 | 0 | if (strcasecmp(value, "yes") == 0 || |
156 | 0 | strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0) |
157 | 0 | *result_r = TRUE; |
158 | 0 | else if (strcasecmp(value, "no") == 0) |
159 | 0 | *result_r = FALSE; |
160 | 0 | else { |
161 | 0 | *error_r = t_strdup_printf("Invalid boolean value: %s (use yes or no)", |
162 | 0 | value); |
163 | 0 | return -1; |
164 | 0 | } |
165 | | |
166 | 0 | return 0; |
167 | 0 | } |