Home | History | Annotate | Download | only in lib
      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 
      8 #include "strntol.h"
      9 #include "pattern.h"
     10 #include "../minmax.h"
     11 #include "../oslib/strcasestr.h"
     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  */
     25 static 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  */
     65 static 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  */
    162 static 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  */
    250 int 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  */
    319 static 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  */
    349 int 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  */
    377 int 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  */
    418 int 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  */
    452 int 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 }
    473