zbd: change some f->zbd_info conditionals to asserts
[fio.git] / lib / pattern.c
CommitLineData
fada2fcd
TK
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <limits.h>
5#include <errno.h>
6#include <assert.h>
a1554f65
SB
7#include <fcntl.h>
8#include <unistd.h>
fada2fcd 9
634bd210
RP
10#include "strntol.h"
11#include "pattern.h"
fada2fcd 12#include "../minmax.h"
a328eb9a 13#include "../oslib/strcasestr.h"
b29c71c4 14#include "../oslib/strndup.h"
634bd210 15
a1554f65
SB
16/**
17 * parse_file() - parses binary file to fill buffer
18 * @beg - string input, extract filename from this
19 * @out - output buffer where parsed number should be put
20 * @out_len - length of the output buffer
21 * @filled - pointer where number of bytes successfully
22 * parsed will be put
23 *
24 * Returns the end pointer where parsing has been stopped.
25 * In case of parsing error or lack of bytes in output buffer
26 * NULL will be returned.
27 */
28static const char *parse_file(const char *beg, char *out,
29 unsigned int out_len,
30 unsigned int *filled)
31{
32 const char *end;
33 char *file;
34 int fd;
35 ssize_t count;
36
37 if (!out_len)
38 goto err_out;
39
40 assert(*beg == '\'');
41 beg++;
42 end = strchr(beg, '\'');
43 if (!end)
44 goto err_out;
45
46 file = strndup(beg, end - beg);
47 if (file == NULL)
48 goto err_out;
49
50 fd = open(file, O_RDONLY);
51 if (fd < 0)
52 goto err_free_out;
53
54 count = read(fd, out, out_len);
55 if (count == -1)
56 goto err_free_close_out;
57
58 *filled = count;
59 close(fd);
60 free(file);
61
62 /* Catch up quote */
63 return end + 1;
64
65err_free_close_out:
66 close(fd);
67err_free_out:
68 free(file);
69err_out:
70 return NULL;
71
72}
73
634bd210
RP
74/**
75 * parse_string() - parses string in double quotes, like "abc"
76 * @beg - string input
77 * @out - output buffer where parsed number should be put
78 * @out_len - length of the output buffer
79 * @filled - pointer where number of bytes successfully
80 * parsed will be put
81 *
82 * Returns the end pointer where parsing has been stopped.
83 * In case of parsing error or lack of bytes in output buffer
84 * NULL will be returned.
85 */
86static const char *parse_string(const char *beg, char *out,
87 unsigned int out_len,
88 unsigned int *filled)
89{
90 const char *end;
91
92 if (!out_len)
93 return NULL;
94
95 assert(*beg == '"');
96 beg++;
97 end = strchr(beg, '"');
98 if (!end)
99 return NULL;
100 if (end - beg > out_len)
101 return NULL;
102
103 memcpy(out, beg, end - beg);
104 *filled = end - beg;
105
106 /* Catch up quote */
107 return end + 1;
108}
109
110/**
111 * parse_number() - parses numbers
112 * @beg - string input
113 * @out - output buffer where parsed number should be put
114 * @out_len - length of the output buffer
115 * @filled - pointer where number of bytes successfully
116 * parsed will be put
117 *
118 * Supports decimals in the range [INT_MIN, INT_MAX] and
119 * hexidecimals of any size, which should be started with
120 * prefix 0x or 0X.
121 *
122 * Returns the end pointer where parsing has been stopped.
123 * In case of parsing error or lack of bytes in output buffer
124 * NULL will be returned.
125 */
126static const char *parse_number(const char *beg, char *out,
127 unsigned int out_len,
128 unsigned int *filled)
129{
130 const char *end;
131 unsigned int val;
132 long lval;
133 int num, i;
134
135 if (!out_len)
136 return NULL;
137
138 num = 0;
139 sscanf(beg, "0%*[xX]%*[0-9a-fA-F]%n", &num);
140 if (num == 0) {
141 /* Here we are trying to parse decimal */
142
143 char *_end;
144
145 /* Looking ahead */
146 _end = strcasestr(beg, "0x");
147 if (_end)
148 num = _end - beg;
149 if (num)
150 lval = strntol(beg, num, &_end, 10);
151 else
152 lval = strtol(beg, &_end, 10);
153 if (beg == _end || lval > INT_MAX || lval < INT_MIN)
154 return NULL;
155 end = _end;
156 i = 0;
157 if (!lval) {
158 num = 0;
159 out[i] = 0x00;
160 i = 1;
161 } else {
162 val = (unsigned int)lval;
163 for (; val && out_len; out_len--, i++, val >>= 8)
164 out[i] = val & 0xff;
165 if (val)
166 return NULL;
167 }
168 } else {
169 assert(num > 2);
170
171 /* Catch up 0x prefix */
172 num -= 2;
173 beg += 2;
174
175 /* Look back, handle this combined string: 0xff0x14 */
176 if (beg[num] && !strncasecmp(&beg[num - 1], "0x", 2))
177 num--;
178
179 end = beg + num;
180
181 for (i = 0; num && out_len;
182 out_len--, i++, num -= 2, beg += 2) {
183 const char *fmt;
184
185 fmt = (num & 1 ? "%1hhx" : "%2hhx");
186 sscanf(beg, fmt, &out[i]);
187 if (num & 1) {
188 num++;
189 beg--;
190 }
191 }
192 if (num)
193 return NULL;
194 }
195
196 *filled = i;
197 return end;
198
199}
200
201/**
202 * parse_format() - parses formats, like %o, etc
203 * @in - string input
204 * @out - output buffer where space for format should be reserved
205 * @parsed - number of bytes which were already parsed so far
206 * @out_len - length of the output buffer
44031abf 207 * @fmt_desc - format descriptor array, what we expect to find
634bd210
RP
208 * @fmt - format array, the output
209 * @fmt_sz - size of format array
210 *
211 * This function tries to find formats, e.g.:
212 * %o - offset of the block
213 *
214 * In case of successfull parsing it fills the format param
215 * with proper offset and the size of the expected value, which
216 * should be pasted into buffer using the format 'func' callback.
217 *
218 * Returns the end pointer where parsing has been stopped.
219 * In case of parsing error or lack of bytes in output buffer
220 * NULL will be returned.
221 */
222static const char *parse_format(const char *in, char *out, unsigned int parsed,
223 unsigned int out_len, unsigned int *filled,
224 const struct pattern_fmt_desc *fmt_desc,
634bd210
RP
225 struct pattern_fmt *fmt, unsigned int fmt_sz)
226{
227 int i;
228 struct pattern_fmt *f = NULL;
229 unsigned int len = 0;
230
969b9fbb 231 if (!out_len || !fmt_desc || !fmt || !fmt_sz)
634bd210
RP
232 return NULL;
233
234 assert(*in == '%');
235
969b9fbb 236 for (i = 0; fmt_desc[i].fmt; i++) {
634bd210
RP
237 const struct pattern_fmt_desc *desc;
238
239 desc = &fmt_desc[i];
240 len = strlen(desc->fmt);
241 if (0 == strncmp(in, desc->fmt, len)) {
242 fmt->desc = desc;
243 fmt->off = parsed;
244 f = fmt;
245 break;
246 }
247 }
248
249 if (!f)
250 return NULL;
251 if (f->desc->len > out_len)
252 return NULL;
253
254 memset(out, '\0', f->desc->len);
255 *filled = f->desc->len;
256
257 return in + len;
258}
259
260/**
261 * parse_and_fill_pattern() - Parses combined input, which consists of strings,
262 * numbers and pattern formats.
263 * @in - string input
264 * @in_len - size of the input string
265 * @out - output buffer where parsed result will be put
266 * @out_len - lengths of the output buffer
267 * @fmt_desc - array of pattern format descriptors [input]
634bd210
RP
268 * @fmt - array of pattern formats [output]
269 * @fmt_sz - pointer where the size of pattern formats array stored [input],
270 * after successfull parsing this pointer will contain the number
271 * of parsed formats if any [output].
272 *
273 * strings:
274 * bytes sequence in double quotes, e.g. "123".
275 * NOTE: there is no way to escape quote, so "123\"abc" does not work.
276 *
277 * numbers:
278 * hexidecimal - sequence of hex bytes starting from 0x or 0X prefix,
279 * e.g. 0xff12ceff1100ff
280 * decimal - decimal number in range [INT_MIN, INT_MAX]
281 *
282 * formats:
283 * %o - offset of block, reserved 8 bytes.
284 *
285 * Explicit examples of combined string:
286 * #1 #2 #3 #4
287 * in="abcd" in=-1024 in=66 in=0xFF0X1
288 * out=61 62 63 64 out=00 fc ff ff out=42 out=ff 01
289 *
290 * #5 #6
291 * in=%o in="123"0xFFeeCC
292 * out=00 00 00 00 00 00 00 00 out=31 32 33 ff ec cc
293 *
294 * #7
295 * in=-100xab"1"%o"2"
296 * out=f6 ff ff ff ab 31 00 00 00 00 00 00 00 00 32
297 *
298 * #9
299 * in=%o0xdeadbeef%o
300 * out=00 00 00 00 00 00 00 00 de ad be ef 00 00 00 00 00 00 00 00
301 *
302 * #10
303 * in=0xfefefefefefefefefefefefefefefefefefefefefe
304 * out=fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
305 *
306 * Returns number of bytes filled or err < 0 in case of failure.
307 */
308int parse_and_fill_pattern(const char *in, unsigned int in_len,
309 char *out, unsigned int out_len,
310 const struct pattern_fmt_desc *fmt_desc,
634bd210
RP
311 struct pattern_fmt *fmt,
312 unsigned int *fmt_sz_out)
313{
314 const char *beg, *end, *out_beg = out;
315 unsigned int total = 0, fmt_rem = 0;
316
317 if (!in || !in_len || !out || !out_len)
318 return -EINVAL;
319 if (fmt_sz_out)
320 fmt_rem = *fmt_sz_out;
321
322 beg = in;
323 do {
324 unsigned int filled;
325 int parsed_fmt;
326
327 filled = 0;
328 parsed_fmt = 0;
329
330 switch (*beg) {
a1554f65
SB
331 case '\'':
332 end = parse_file(beg, out, out_len, &filled);
333 break;
634bd210
RP
334 case '"':
335 end = parse_string(beg, out, out_len, &filled);
336 break;
337 case '%':
338 end = parse_format(beg, out, out - out_beg, out_len,
969b9fbb 339 &filled, fmt_desc, fmt, fmt_rem);
634bd210
RP
340 parsed_fmt = 1;
341 break;
342 default:
343 end = parse_number(beg, out, out_len, &filled);
344 break;
345 }
346
347 if (!end)
348 return -EINVAL;
349
350 if (parsed_fmt) {
351 assert(fmt_rem);
352 fmt_rem--;
353 fmt++;
354 }
355
356 assert(end - beg <= in_len);
357 in_len -= end - beg;
358 beg = end;
359
360 assert(filled);
361 assert(filled <= out_len);
362 out_len -= filled;
363 out += filled;
364 total += filled;
365
366 } while (in_len);
367
368 if (fmt_sz_out)
369 *fmt_sz_out -= fmt_rem;
370 return total;
371}
372
373/**
374 * dup_pattern() - Duplicates part of the pattern all over the buffer.
375 *
376 * Returns 0 in case of success or errno < 0 in case of failure.
377 */
378static int dup_pattern(char *out, unsigned int out_len, unsigned int pattern_len)
379{
380 unsigned int left, len, off;
381
382 if (out_len <= pattern_len)
383 /* Normal case */
384 return 0;
385
386 off = pattern_len;
387 left = (out_len - off);
388 len = min(left, off);
389
390 /* Duplicate leftover */
391 while (left) {
392 memcpy(out + off, out, len);
393 left -= len;
394 off <<= 1;
395 len = min(left, off);
396 }
397
398 return 0;
399}
400
401/**
402 * cpy_pattern() - Copies pattern to the buffer.
403 *
404 * Function copies pattern along the whole buffer.
405 *
406 * Returns 0 in case of success or errno < 0 in case of failure.
407 */
408int cpy_pattern(const char *pattern, unsigned int pattern_len,
409 char *out, unsigned int out_len)
410{
411 unsigned int len;
412
413 if (!pattern || !pattern_len || !out || !out_len)
414 return -EINVAL;
415
416 /* Copy pattern */
417 len = min(pattern_len, out_len);
418 memcpy(out, pattern, len);
419
420 /* Spread filled chunk all over the buffer */
421 return dup_pattern(out, out_len, pattern_len);
422}
423
424/**
425 * cmp_pattern() - Compares pattern and buffer.
426 *
427 * For the sake of performance this function avoids any loops.
428 * Firstly it tries to compare the buffer itself, checking that
429 * buffer consists of repeating patterns along the buffer size.
430 *
431 * If the difference is not found then the function tries to compare
432 * buffer and pattern.
433 *
434 * Returns 0 in case of success or errno < 0 in case of failure.
435 */
436int cmp_pattern(const char *pattern, unsigned int pattern_size,
437 unsigned int off, const char *buf, unsigned int len)
438{
439 int rc;
440 unsigned int size;
441
442 /* Find the difference in buffer */
443 if (len > pattern_size) {
444 rc = memcmp(buf, buf + pattern_size, len - pattern_size);
445 if (rc)
446 return -EILSEQ;
447 }
448 /* Compare second part of the pattern with buffer */
449 if (off) {
450 size = min(len, pattern_size - off);
451 rc = memcmp(buf, pattern + off, size);
452 if (rc)
453 return -EILSEQ;
454 buf += size;
455 len -= size;
456 }
457 /* Compare first part of the pattern or the whole pattern
458 * with buffer */
459 if (len) {
460 size = min(len, (off ? off : pattern_size));
461 rc = memcmp(buf, pattern, size);
462 if (rc)
463 return -EILSEQ;
464 }
465
466 return 0;
467}
468
469/**
470 * paste_format_inplace() - Pastes parsed formats to the pattern.
471 *
472 * This function pastes formats to the pattern. If @fmt_sz is 0
473 * function does nothing and pattern buffer is left untouched.
474 *
475 * Returns 0 in case of success or errno < 0 in case of failure.
476 */
477int paste_format_inplace(char *pattern, unsigned int pattern_len,
478 struct pattern_fmt *fmt, unsigned int fmt_sz,
479 void *priv)
480{
481 int i, rc;
482 unsigned int len;
483
484 if (!pattern || !pattern_len || !fmt)
485 return -EINVAL;
486
487 /* Paste formats for first pattern chunk */
488 for (i = 0; i < fmt_sz; i++) {
489 struct pattern_fmt *f;
490
491 f = &fmt[i];
492 if (pattern_len <= f->off)
493 break;
494 len = min(pattern_len - f->off, f->desc->len);
495 rc = f->desc->paste(pattern + f->off, len, priv);
496 if (rc)
497 return rc;
498 }
499
500 return 0;
501}
502
503/**
504 * paste_format() - Pastes parsed formats to the buffer.
505 *
506 * This function copies pattern to the buffer, pastes format
507 * into it and then duplicates pattern all over the buffer size.
508 *
509 * Returns 0 in case of success or errno < 0 in case of failure.
510 */
511int paste_format(const char *pattern, unsigned int pattern_len,
512 struct pattern_fmt *fmt, unsigned int fmt_sz,
513 char *out, unsigned int out_len, void *priv)
514{
515 int rc;
516 unsigned int len;
517
518 if (!pattern || !pattern_len || !out || !out_len)
519 return -EINVAL;
520
521 /* Copy pattern */
522 len = min(pattern_len, out_len);
523 memcpy(out, pattern, len);
524
525 rc = paste_format_inplace(out, len, fmt, fmt_sz, priv);
526 if (rc)
527 return rc;
528
529 /* Spread filled chunk all over the buffer */
530 return dup_pattern(out, out_len, pattern_len);
531}