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