[PATCH] Abstract option handling
[fio.git] / parse.c
CommitLineData
cb2c86fd
JA
1/*
2 * This file contains the ini and command liner parser main.
3 */
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <ctype.h>
8#include <string.h>
9#include <errno.h>
10#include <limits.h>
11
12#include "parse.h"
13
14static unsigned long get_mult_time(char c)
15{
16 switch (c) {
17 case 'm':
18 case 'M':
19 return 60;
20 case 'h':
21 case 'H':
22 return 60 * 60;
23 case 'd':
24 case 'D':
25 return 24 * 60 * 60;
26 default:
27 return 1;
28 }
29}
30
31static unsigned long get_mult_bytes(char c)
32{
33 switch (c) {
34 case 'k':
35 case 'K':
36 return 1024;
37 case 'm':
38 case 'M':
39 return 1024 * 1024;
40 case 'g':
41 case 'G':
42 return 1024 * 1024 * 1024;
43 default:
44 return 1;
45 }
46}
47
48/*
e1f36503 49 * convert string into decimal value, noting any size suffix
cb2c86fd
JA
50 */
51static int str_to_decimal(char *p, unsigned long long *val, int kilo)
52{
e1f36503 53 char *str = p;
cb2c86fd
JA
54 int len;
55
cb2c86fd
JA
56 len = strlen(str);
57
58 *val = strtoul(str, NULL, 10);
59 if (*val == ULONG_MAX && errno == ERANGE)
60 return 1;
61
62 if (kilo)
63 *val *= get_mult_bytes(str[len - 1]);
64 else
65 *val *= get_mult_time(str[len - 1]);
66 return 0;
67}
68
e1f36503 69static int check_str_bytes(char *p, unsigned long long *val)
cb2c86fd 70{
cb2c86fd
JA
71 return str_to_decimal(p, val, 1);
72}
73
e1f36503 74static int check_str_time(char *p, unsigned long long *val)
cb2c86fd 75{
cb2c86fd
JA
76 return str_to_decimal(p, val, 0);
77}
78
79void strip_blank_front(char **p)
80{
81 char *s = *p;
82
83 while (isspace(*s))
84 s++;
85}
86
87void strip_blank_end(char *p)
88{
89 char *s = p + strlen(p) - 1;
90
91 while (isspace(*s) || iscntrl(*s))
92 s--;
93
94 *(s + 1) = '\0';
95}
96
e1f36503 97static int check_range_bytes(char *str, unsigned long *val)
cb2c86fd
JA
98{
99 char suffix;
100
101 if (sscanf(str, "%lu%c", val, &suffix) == 2) {
102 *val *= get_mult_bytes(suffix);
103 return 0;
104 }
105
106 if (sscanf(str, "%lu", val) == 1)
107 return 0;
108
109 return 1;
110}
111
e1f36503 112int check_int(char *p, unsigned int *val)
cb2c86fd 113{
e1f36503
JA
114 if (sscanf(p, "%u", val) == 1)
115 return 0;
cb2c86fd 116
e1f36503
JA
117 return 1;
118}
cb2c86fd 119
e1f36503
JA
120int check_strset(char *p, char *name)
121{
122 return strncmp(p, name, strlen(name));
123}
cb2c86fd 124
e1f36503
JA
125static struct fio_option *find_option(struct fio_option *options,
126 const char *opt)
127{
128 struct fio_option *o;
129 int i = 0;
cb2c86fd 130
e1f36503
JA
131 do {
132 o = &options[i];
133 if (!o->name)
134 break;
cb2c86fd 135
e1f36503
JA
136 if (!strcmp(o->name, opt))
137 return o;
cb2c86fd 138
e1f36503
JA
139 i++;
140 } while (1);
cb2c86fd 141
e1f36503 142 return NULL;
cb2c86fd
JA
143}
144
e1f36503 145static int handle_option(struct fio_option *o, char *ptr, void *data)
cb2c86fd 146{
e1f36503
JA
147 unsigned int il, *ilp;
148 unsigned long long ull, *ullp;
149 unsigned long ul1, ul2, *ulp1, *ulp2;
150 char *tmpbuf, **cp;
151 int ret = 0, is_time = 0;
cb2c86fd 152
e1f36503 153 tmpbuf = malloc(4096);
cb2c86fd 154
e1f36503
JA
155 switch (o->type) {
156 case FIO_OPT_STR: {
157 fio_opt_str_fn *fn = o->cb;
cb2c86fd 158
e1f36503
JA
159 ret = fn(data, ptr);
160 break;
161 }
162 case FIO_OPT_STR_VAL_TIME:
163 is_time = 1;
164 case FIO_OPT_STR_VAL: {
165 fio_opt_str_val_fn *fn = o->cb;
166
167 if (is_time)
168 ret = check_str_time(ptr, &ull);
169 else
170 ret = check_str_bytes(ptr, &ull);
171
172 if (ret)
173 break;
174
175 if (o->max_val && ull > o->max_val)
176 ull = o->max_val;
177
178 if (fn)
179 ret = fn(data, &ull);
180 else {
181 ullp = td_var(data, o->off1);
182 *ullp = ull;
183 }
184 break;
185 }
186 case FIO_OPT_STR_STORE:
187 cp = td_var(data, o->off1);
188 *cp = strdup(ptr);
189 break;
190 case FIO_OPT_RANGE: {
191 char *p1, *p2;
192
193 p1 = strchr(ptr, '-');
194 if (!p1) {
195 ret = 1;
196 break;
197 }
198
199 p2 = p1 + 1;
200 *p1 = '\0';
201
202 ret = 1;
203 if (!check_range_bytes(p1, &ul1) && !check_range_bytes(p2, &ul2)) {
204 ret = 0;
205 ulp1 = td_var(data, o->off1);
206 ulp2 = td_var(data, o->off2);
207 if (ul1 > ul2) {
208 *ulp1 = ul2;
209 *ulp2 = ul1;
210 } else {
211 *ulp2 = ul2;
212 *ulp1 = ul1;
213 }
214 }
215
216 break;
217 }
218 case FIO_OPT_INT: {
219 fio_opt_int_fn *fn = o->cb;
220
221 ret = check_int(ptr, &il);
222 if (ret)
223 break;
224
225 if (o->max_val && il > o->max_val)
226 il = o->max_val;
227
228 if (fn)
229 ret = fn(data, &il);
230 else {
231 ilp = td_var(data, o->off1);
232 *ilp = il;
233 }
234 break;
235 }
236 case FIO_OPT_STR_SET: {
237 fio_opt_str_set_fn *fn = o->cb;
238
239 if (fn)
240 ret = fn(data);
241 else {
242 ilp = td_var(data, o->off1);
243 *ilp = 1;
244 }
245 break;
246 }
247 default:
248 fprintf(stderr, "Bad option type %d\n", o->type);
249 ret = 1;
250 }
cb2c86fd 251
e1f36503
JA
252 free(tmpbuf);
253 return ret;
cb2c86fd
JA
254}
255
e1f36503 256int parse_option(const char *opt, struct fio_option *options, void *data)
cb2c86fd 257{
e1f36503
JA
258 struct fio_option *o = find_option(options, opt);
259 char *pre, *post;
260 char tmp[64];
261
262 strcpy(tmp, opt);
263
264 pre = strchr(tmp, '=');
265 if (pre) {
266 post = pre;
267 *pre = '\0';
268 pre = tmp;
269 post++;
270 o = find_option(options, pre);
271 } else {
272 o = find_option(options, tmp);
273 post = NULL;
274 }
cb2c86fd 275
e1f36503
JA
276 if (!o) {
277 fprintf(stderr, "Bad option %s\n", tmp);
278 return 1;
279 }
280
281 return handle_option(o, post, data);
282}