Tor 0.4.9.0-alpha-dev
unitparse.c
Go to the documentation of this file.
1/* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5/* See LICENSE for licensing information */
6
7/**
8 * @file unitparse.c
9 * @brief Functions for parsing values with units from a configuration file.
10 **/
11
12#include "orconfig.h"
14#include "lib/log/log.h"
15#include "lib/log/util_bug.h"
16#include "lib/malloc/malloc.h"
18#include "lib/string/printf.h"
20#include "lib/intmath/muldiv.h"
21
22#include <string.h>
23
24/** Table to map the names of memory units to the number of bytes they
25 * contain. */
26// clang-format off
27const struct unit_table_t memory_units[] = {
28 { "", 1 },
29 { "b", 1<< 0 },
30 { "byte", 1<< 0 },
31 { "bytes", 1<< 0 },
32 { "kb", 1<<10 },
33 { "kbyte", 1<<10 },
34 { "kbytes", 1<<10 },
35 { "kilobyte", 1<<10 },
36 { "kilobytes", 1<<10 },
37 { "kilobits", 1<<7 },
38 { "kilobit", 1<<7 },
39 { "kbits", 1<<7 },
40 { "kbit", 1<<7 },
41 { "m", 1<<20 },
42 { "mb", 1<<20 },
43 { "mbyte", 1<<20 },
44 { "mbytes", 1<<20 },
45 { "megabyte", 1<<20 },
46 { "megabytes", 1<<20 },
47 { "megabits", 1<<17 },
48 { "megabit", 1<<17 },
49 { "mbits", 1<<17 },
50 { "mbit", 1<<17 },
51 { "gb", 1<<30 },
52 { "gbyte", 1<<30 },
53 { "gbytes", 1<<30 },
54 { "gigabyte", 1<<30 },
55 { "gigabytes", 1<<30 },
56 { "gigabits", 1<<27 },
57 { "gigabit", 1<<27 },
58 { "gbits", 1<<27 },
59 { "gbit", 1<<27 },
60 { "tb", UINT64_C(1)<<40 },
61 { "tbyte", UINT64_C(1)<<40 },
62 { "tbytes", UINT64_C(1)<<40 },
63 { "terabyte", UINT64_C(1)<<40 },
64 { "terabytes", UINT64_C(1)<<40 },
65 { "terabits", UINT64_C(1)<<37 },
66 { "terabit", UINT64_C(1)<<37 },
67 { "tbits", UINT64_C(1)<<37 },
68 { "tbit", UINT64_C(1)<<37 },
69 { NULL, 0 },
70};
71// clang-format on
72
73/** Table to map the names of time units to the number of seconds they
74 * contain. */
75// clang-format off
76const struct unit_table_t time_units[] = {
77 { "", 1 },
78 { "second", 1 },
79 { "seconds", 1 },
80 { "minute", 60 },
81 { "minutes", 60 },
82 { "hour", 60*60 },
83 { "hours", 60*60 },
84 { "day", 24*60*60 },
85 { "days", 24*60*60 },
86 { "week", 7*24*60*60 },
87 { "weeks", 7*24*60*60 },
88 { "month", 2629728, }, /* about 30.437 days */
89 { "months", 2629728, },
90 { NULL, 0 },
91};
92// clang-format on
93
94/** Table to map the names of time units to the number of milliseconds
95 * they contain. */
96// clang-format off
97const struct unit_table_t time_msec_units[] = {
98 { "", 1 },
99 { "msec", 1 },
100 { "millisecond", 1 },
101 { "milliseconds", 1 },
102 { "second", 1000 },
103 { "seconds", 1000 },
104 { "minute", 60*1000 },
105 { "minutes", 60*1000 },
106 { "hour", 60*60*1000 },
107 { "hours", 60*60*1000 },
108 { "day", 24*60*60*1000 },
109 { "days", 24*60*60*1000 },
110 { "week", 7*24*60*60*1000 },
111 { "weeks", 7*24*60*60*1000 },
112 { NULL, 0 },
113};
114// clang-format on
115
116/** Parse a string <b>val</b> containing a number, zero or more
117 * spaces, and an optional unit string. If the unit appears in the
118 * table <b>u</b>, then multiply the number by the unit multiplier.
119 * On success, set *<b>ok</b> to 1 and return this product.
120 * Otherwise, set *<b>ok</b> to 0.
121 *
122 * If an error (like overflow or a negative value is detected), put an error
123 * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise
124 * log a warning.
125 */
126uint64_t
127config_parse_units(const char *val, const unit_table_t *u, int *ok,
128 char **errmsg_out)
129{
130 uint64_t v = 0;
131 double d = 0;
132 int use_float = 0;
133 char *cp;
134 char *errmsg = NULL;
135
136 tor_assert(ok);
137
138 v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
139 if (!*ok || (cp && *cp == '.')) {
140 d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
141 if (!*ok) {
142 tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
143 goto done;
144 }
145 use_float = 1;
146 }
147
148 if (BUG(!cp)) {
149 // cp should always be non-NULL if the parse operation succeeds.
150
151 // LCOV_EXCL_START
152 *ok = 1;
153 v = use_float ? ((uint64_t)d) : v;
154 goto done;
155 // LCOV_EXCL_STOP
156 }
157
158 cp = (char*) eat_whitespace(cp);
159
160 for ( ;u->unit;++u) {
161 if (!strcasecmp(u->unit, cp)) {
162 if (use_float) {
163 d = u->multiplier * d;
164
165 if (d < 0) {
166 tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
167 val, u->unit);
168 *ok = 0;
169 goto done;
170 }
171
172 // Some compilers may warn about casting a double to an unsigned type
173 // because they don't know if d is >= 0
174 if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
175 tor_asprintf(&errmsg, "Overflow while parsing %s %s",
176 val, u->unit);
177 *ok = 0;
178 goto done;
179 }
180
181 v = (uint64_t) d;
182 } else {
184
185 if (v > INT64_MAX) {
186 tor_asprintf(&errmsg, "Overflow while parsing %s %s",
187 val, u->unit);
188 *ok = 0;
189 goto done;
190 }
191 }
192
193 *ok = 1;
194 goto done;
195 }
196 }
197 tor_asprintf(&errmsg, "Unknown unit in %s", val);
198 *ok = 0;
199 done:
200
201 if (errmsg) {
202 tor_assert_nonfatal(!*ok);
203 if (errmsg_out) {
204 *errmsg_out = errmsg;
205 } else {
206 log_warn(LD_CONFIG, "%s", errmsg);
207 tor_free(errmsg);
208 }
209 }
210
211 if (*ok)
212 return v;
213 else
214 return 0;
215}
216
217/** Parse a string in the format "number unit", where unit is a unit of
218 * information (byte, KB, M, etc). On success, set *<b>ok</b> to true
219 * and return the number of bytes specified. Otherwise, set
220 * *<b>ok</b> to false and return 0. */
221uint64_t
222config_parse_memunit(const char *s, int *ok)
223{
224 uint64_t u = config_parse_units(s, memory_units, ok, NULL);
225 return u;
226}
227
228/** Parse a string in the format "number unit", where unit is a unit of
229 * time in milliseconds. On success, set *<b>ok</b> to true and return
230 * the number of milliseconds in the provided interval. Otherwise, set
231 * *<b>ok</b> to 0 and return -1. */
232int
233config_parse_msec_interval(const char *s, int *ok)
234{
235 uint64_t r;
236 r = config_parse_units(s, time_msec_units, ok, NULL);
237 if (r > INT_MAX) {
238 log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
239 *ok = 0;
240 return -1;
241 }
242 return (int)r;
243}
244
245/** Parse a string in the format "number unit", where unit is a unit of time.
246 * On success, set *<b>ok</b> to true and return the number of seconds in
247 * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1.
248 */
249int
250config_parse_interval(const char *s, int *ok)
251{
252 uint64_t r;
253 r = config_parse_units(s, time_units, ok, NULL);
254 if (r > INT_MAX) {
255 log_warn(LD_CONFIG, "Interval '%s' is too long", s);
256 *ok = 0;
257 return -1;
258 }
259 return (int)r;
260}
Headers for log.c.
#define LD_CONFIG
Definition: log.h:68
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:56
uint64_t tor_mul_u64_nowrap(uint64_t a, uint64_t b)
Definition: muldiv.c:75
Header for muldiv.c.
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next)
Definition: parse_int.c:110
double tor_parse_double(const char *s, double min, double max, int *ok, char **next)
Definition: parse_int.c:97
Header for parse_int.c.
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
Header for printf.c.
uint64_t multiplier
Definition: unitparse.h:21
const char * unit
Definition: unitparse.h:20
const struct unit_table_t time_units[]
Definition: unitparse.c:76
const struct unit_table_t time_msec_units[]
Definition: unitparse.c:97
int config_parse_interval(const char *s, int *ok)
Definition: unitparse.c:250
const struct unit_table_t memory_units[]
Definition: unitparse.c:27
uint64_t config_parse_memunit(const char *s, int *ok)
Definition: unitparse.c:222
uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok, char **errmsg_out)
Definition: unitparse.c:127
int config_parse_msec_interval(const char *s, int *ok)
Definition: unitparse.c:233
Header for lib/confmgt/unitparse.c.
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:103
const char * eat_whitespace(const char *s)
Definition: util_string.c:279
Header for util_string.c.