zbd: change some f->zbd_info conditionals to asserts
[fio.git] / lib / pattern.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <limits.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9
10 #include "strntol.h"
11 #include "pattern.h"
12 #include "../minmax.h"
13 #include "../oslib/strcasestr.h"
14 #include "../oslib/strndup.h"
15
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  */
28 static 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
65 err_free_close_out:
66         close(fd);
67 err_free_out:
68         free(file);
69 err_out:
70         return NULL;
71
72 }
73
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  */
86 static 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  */
126 static 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
207  * @fmt_desc - format descriptor array, what we expect to find
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  */
222 static 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,
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
231         if (!out_len || !fmt_desc || !fmt || !fmt_sz)
232                 return NULL;
233
234         assert(*in == '%');
235
236         for (i = 0; fmt_desc[i].fmt; i++) {
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]
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  */
308 int 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,
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) {
331                 case '\'':
332                         end = parse_file(beg, out, out_len, &filled);
333                         break;
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,
339                                            &filled, fmt_desc, fmt, fmt_rem);
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  */
378 static 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  */
408 int 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  */
436 int 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  */
477 int 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  */
511 int 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 }