xref: /openbmc/linux/lib/parser.c (revision 359d6255)
140b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * lib/parser.c - simple parser for mount, etc. options.
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include <linux/ctype.h>
78bc3bcc9SPaul Gortmaker #include <linux/types.h>
88bc3bcc9SPaul Gortmaker #include <linux/export.h>
94c527293SAndy Shevchenko #include <linux/kstrtox.h>
101da177e4SLinus Torvalds #include <linux/parser.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/string.h>
131da177e4SLinus Torvalds 
1467222c4bSLi Lingfeng /*
1567222c4bSLi Lingfeng  * max size needed by different bases to express U64
1667222c4bSLi Lingfeng  * HEX: "0xFFFFFFFFFFFFFFFF" --> 18
1767222c4bSLi Lingfeng  * DEC: "18446744073709551615" --> 20
1867222c4bSLi Lingfeng  * OCT: "01777777777777777777777" --> 23
1967222c4bSLi Lingfeng  * pick the max one to define NUMBER_BUF_LEN
2067222c4bSLi Lingfeng  */
2167222c4bSLi Lingfeng #define NUMBER_BUF_LEN 24
2267222c4bSLi Lingfeng 
231da177e4SLinus Torvalds /**
24b9bffa10SBingJing Chang  * match_one - Determines if a string matches a simple pattern
2525985edcSLucas De Marchi  * @s: the string to examine for presence of the pattern
261da177e4SLinus Torvalds  * @p: the string containing the pattern
271da177e4SLinus Torvalds  * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
281da177e4SLinus Torvalds  * locations.
291da177e4SLinus Torvalds  *
301da177e4SLinus Torvalds  * Description: Determines if the pattern @p is present in string @s. Can only
311da177e4SLinus Torvalds  * match extremely simple token=arg style patterns. If the pattern is found,
321da177e4SLinus Torvalds  * the location(s) of the arguments will be returned in the @args array.
331da177e4SLinus Torvalds  */
match_one(char * s,const char * p,substring_t args[])34ef4533f8SDavid Howells static int match_one(char *s, const char *p, substring_t args[])
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds 	char *meta;
371da177e4SLinus Torvalds 	int argc = 0;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	if (!p)
401da177e4SLinus Torvalds 		return 1;
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	while(1) {
431da177e4SLinus Torvalds 		int len = -1;
441da177e4SLinus Torvalds 		meta = strchr(p, '%');
451da177e4SLinus Torvalds 		if (!meta)
461da177e4SLinus Torvalds 			return strcmp(p, s) == 0;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 		if (strncmp(p, s, meta-p))
491da177e4SLinus Torvalds 			return 0;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 		s += meta - p;
521da177e4SLinus Torvalds 		p = meta + 1;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 		if (isdigit(*p))
55ef4533f8SDavid Howells 			len = simple_strtoul(p, (char **) &p, 10);
561da177e4SLinus Torvalds 		else if (*p == '%') {
571da177e4SLinus Torvalds 			if (*s++ != '%')
581da177e4SLinus Torvalds 				return 0;
591da177e4SLinus Torvalds 			p++;
601da177e4SLinus Torvalds 			continue;
611da177e4SLinus Torvalds 		}
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 		if (argc >= MAX_OPT_ARGS)
641da177e4SLinus Torvalds 			return 0;
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 		args[argc].from = s;
671da177e4SLinus Torvalds 		switch (*p++) {
68b5f54b07SAndré Goddard Rosa 		case 's': {
69b5f54b07SAndré Goddard Rosa 			size_t str_len = strlen(s);
70b5f54b07SAndré Goddard Rosa 
71b5f54b07SAndré Goddard Rosa 			if (str_len == 0)
721da177e4SLinus Torvalds 				return 0;
73b5f54b07SAndré Goddard Rosa 			if (len == -1 || len > str_len)
74b5f54b07SAndré Goddard Rosa 				len = str_len;
751da177e4SLinus Torvalds 			args[argc].to = s + len;
761da177e4SLinus Torvalds 			break;
77b5f54b07SAndré Goddard Rosa 		}
781da177e4SLinus Torvalds 		case 'd':
791da177e4SLinus Torvalds 			simple_strtol(s, &args[argc].to, 0);
801da177e4SLinus Torvalds 			goto num;
811da177e4SLinus Torvalds 		case 'u':
821da177e4SLinus Torvalds 			simple_strtoul(s, &args[argc].to, 0);
831da177e4SLinus Torvalds 			goto num;
841da177e4SLinus Torvalds 		case 'o':
851da177e4SLinus Torvalds 			simple_strtoul(s, &args[argc].to, 8);
861da177e4SLinus Torvalds 			goto num;
871da177e4SLinus Torvalds 		case 'x':
881da177e4SLinus Torvalds 			simple_strtoul(s, &args[argc].to, 16);
891da177e4SLinus Torvalds 		num:
901da177e4SLinus Torvalds 			if (args[argc].to == args[argc].from)
911da177e4SLinus Torvalds 				return 0;
921da177e4SLinus Torvalds 			break;
931da177e4SLinus Torvalds 		default:
941da177e4SLinus Torvalds 			return 0;
951da177e4SLinus Torvalds 		}
961da177e4SLinus Torvalds 		s = args[argc].to;
971da177e4SLinus Torvalds 		argc++;
981da177e4SLinus Torvalds 	}
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds /**
102b9bffa10SBingJing Chang  * match_token - Find a token (and optional args) in a string
1031da177e4SLinus Torvalds  * @s: the string to examine for token/argument pairs
1041da177e4SLinus Torvalds  * @table: match_table_t describing the set of allowed option tokens and the
1051da177e4SLinus Torvalds  * arguments that may be associated with them. Must be terminated with a
1061da177e4SLinus Torvalds  * &struct match_token whose pattern is set to the NULL pointer.
1071da177e4SLinus Torvalds  * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
1081da177e4SLinus Torvalds  * locations.
1091da177e4SLinus Torvalds  *
1101da177e4SLinus Torvalds  * Description: Detects which if any of a set of token strings has been passed
111edd9334cSRandy Dunlap  * to it. Tokens can include up to %MAX_OPT_ARGS instances of basic c-style
1121da177e4SLinus Torvalds  * format identifiers which will be taken into account when matching the
1131da177e4SLinus Torvalds  * tokens, and whose locations will be returned in the @args array.
1141da177e4SLinus Torvalds  */
match_token(char * s,const match_table_t table,substring_t args[])115a447c093SSteven Whitehouse int match_token(char *s, const match_table_t table, substring_t args[])
1161da177e4SLinus Torvalds {
117ef4533f8SDavid Howells 	const struct match_token *p;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	for (p = table; !match_one(s, p->pattern, args) ; p++)
1201da177e4SLinus Torvalds 		;
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 	return p->token;
1231da177e4SLinus Torvalds }
124a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_token);
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds /**
127b9bffa10SBingJing Chang  * match_number - scan a number in the given base from a substring_t
1281da177e4SLinus Torvalds  * @s: substring to be scanned
1291da177e4SLinus Torvalds  * @result: resulting integer on success
1301da177e4SLinus Torvalds  * @base: base to use when converting string
1311da177e4SLinus Torvalds  *
1321da177e4SLinus Torvalds  * Description: Given a &substring_t and a base, attempts to parse the substring
133edd9334cSRandy Dunlap  * as a number in that base.
134edd9334cSRandy Dunlap  *
135edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the
136*359d6255SEric Biggers  * string and returns 0. Returns -EINVAL or -ERANGE on failure.
1371da177e4SLinus Torvalds  */
match_number(substring_t * s,int * result,int base)1381da177e4SLinus Torvalds static int match_number(substring_t *s, int *result, int base)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	char *endp;
14167222c4bSLi Lingfeng 	char buf[NUMBER_BUF_LEN];
1421da177e4SLinus Torvalds 	int ret;
14377dd3b0bSAlex Elder 	long val;
1441da177e4SLinus Torvalds 
14567222c4bSLi Lingfeng 	if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
14667222c4bSLi Lingfeng 		return -ERANGE;
1471da177e4SLinus Torvalds 	ret = 0;
14877dd3b0bSAlex Elder 	val = simple_strtol(buf, &endp, base);
1491da177e4SLinus Torvalds 	if (endp == buf)
1501da177e4SLinus Torvalds 		ret = -EINVAL;
15177dd3b0bSAlex Elder 	else if (val < (long)INT_MIN || val > (long)INT_MAX)
15277dd3b0bSAlex Elder 		ret = -ERANGE;
15377dd3b0bSAlex Elder 	else
15477dd3b0bSAlex Elder 		*result = (int) val;
1551da177e4SLinus Torvalds 	return ret;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /**
159b9bffa10SBingJing Chang  * match_u64int - scan a number in the given base from a substring_t
160a317178eSJames Smart  * @s: substring to be scanned
161a317178eSJames Smart  * @result: resulting u64 on success
162a317178eSJames Smart  * @base: base to use when converting string
163a317178eSJames Smart  *
164a317178eSJames Smart  * Description: Given a &substring_t and a base, attempts to parse the substring
165edd9334cSRandy Dunlap  * as a number in that base.
166edd9334cSRandy Dunlap  *
167edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the
168*359d6255SEric Biggers  * string and returns 0. Returns -EINVAL or -ERANGE on failure.
169a317178eSJames Smart  */
match_u64int(substring_t * s,u64 * result,int base)170a317178eSJames Smart static int match_u64int(substring_t *s, u64 *result, int base)
171a317178eSJames Smart {
17267222c4bSLi Lingfeng 	char buf[NUMBER_BUF_LEN];
173a317178eSJames Smart 	int ret;
174a317178eSJames Smart 	u64 val;
175a317178eSJames Smart 
17667222c4bSLi Lingfeng 	if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
17767222c4bSLi Lingfeng 		return -ERANGE;
178a317178eSJames Smart 	ret = kstrtoull(buf, base, &val);
179a317178eSJames Smart 	if (!ret)
180a317178eSJames Smart 		*result = val;
181a317178eSJames Smart 	return ret;
182a317178eSJames Smart }
183a317178eSJames Smart 
184a317178eSJames Smart /**
185b9bffa10SBingJing Chang  * match_int - scan a decimal representation of an integer from a substring_t
1861da177e4SLinus Torvalds  * @s: substring_t to be scanned
1871da177e4SLinus Torvalds  * @result: resulting integer on success
1881da177e4SLinus Torvalds  *
189edd9334cSRandy Dunlap  * Description: Attempts to parse the &substring_t @s as a decimal integer.
190edd9334cSRandy Dunlap  *
191edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the string
192*359d6255SEric Biggers  * and returns 0. Returns -EINVAL or -ERANGE on failure.
1931da177e4SLinus Torvalds  */
match_int(substring_t * s,int * result)1941da177e4SLinus Torvalds int match_int(substring_t *s, int *result)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds 	return match_number(s, result, 0);
1971da177e4SLinus Torvalds }
198a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_int);
1991da177e4SLinus Torvalds 
200edd9334cSRandy Dunlap /**
20101531ac3SBingJing Chang  * match_uint - scan a decimal representation of an integer from a substring_t
20201531ac3SBingJing Chang  * @s: substring_t to be scanned
20301531ac3SBingJing Chang  * @result: resulting integer on success
20401531ac3SBingJing Chang  *
205edd9334cSRandy Dunlap  * Description: Attempts to parse the &substring_t @s as a decimal integer.
206edd9334cSRandy Dunlap  *
207edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the string
208*359d6255SEric Biggers  * and returns 0. Returns -EINVAL or -ERANGE on failure.
20901531ac3SBingJing Chang  */
match_uint(substring_t * s,unsigned int * result)21001531ac3SBingJing Chang int match_uint(substring_t *s, unsigned int *result)
21101531ac3SBingJing Chang {
21267222c4bSLi Lingfeng 	char buf[NUMBER_BUF_LEN];
21301531ac3SBingJing Chang 
21467222c4bSLi Lingfeng 	if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
21567222c4bSLi Lingfeng 		return -ERANGE;
21667222c4bSLi Lingfeng 
21767222c4bSLi Lingfeng 	return kstrtouint(buf, 10, result);
21801531ac3SBingJing Chang }
21901531ac3SBingJing Chang EXPORT_SYMBOL(match_uint);
22001531ac3SBingJing Chang 
2211da177e4SLinus Torvalds /**
222b9bffa10SBingJing Chang  * match_u64 - scan a decimal representation of a u64 from
223a317178eSJames Smart  *                  a substring_t
224a317178eSJames Smart  * @s: substring_t to be scanned
225a317178eSJames Smart  * @result: resulting unsigned long long on success
226a317178eSJames Smart  *
227a317178eSJames Smart  * Description: Attempts to parse the &substring_t @s as a long decimal
228edd9334cSRandy Dunlap  * integer.
229edd9334cSRandy Dunlap  *
230edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the string
231*359d6255SEric Biggers  * and returns 0. Returns -EINVAL or -ERANGE on failure.
232a317178eSJames Smart  */
match_u64(substring_t * s,u64 * result)233a317178eSJames Smart int match_u64(substring_t *s, u64 *result)
234a317178eSJames Smart {
235a317178eSJames Smart 	return match_u64int(s, result, 0);
236a317178eSJames Smart }
237a317178eSJames Smart EXPORT_SYMBOL(match_u64);
238a317178eSJames Smart 
239a317178eSJames Smart /**
240b9bffa10SBingJing Chang  * match_octal - scan an octal representation of an integer from a substring_t
2411da177e4SLinus Torvalds  * @s: substring_t to be scanned
2421da177e4SLinus Torvalds  * @result: resulting integer on success
2431da177e4SLinus Torvalds  *
244edd9334cSRandy Dunlap  * Description: Attempts to parse the &substring_t @s as an octal integer.
245edd9334cSRandy Dunlap  *
246edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the string
247*359d6255SEric Biggers  * and returns 0. Returns -EINVAL or -ERANGE on failure.
2481da177e4SLinus Torvalds  */
match_octal(substring_t * s,int * result)2491da177e4SLinus Torvalds int match_octal(substring_t *s, int *result)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	return match_number(s, result, 8);
2521da177e4SLinus Torvalds }
253a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_octal);
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds /**
256b9bffa10SBingJing Chang  * match_hex - scan a hex representation of an integer from a substring_t
2571da177e4SLinus Torvalds  * @s: substring_t to be scanned
2581da177e4SLinus Torvalds  * @result: resulting integer on success
2591da177e4SLinus Torvalds  *
2601da177e4SLinus Torvalds  * Description: Attempts to parse the &substring_t @s as a hexadecimal integer.
261edd9334cSRandy Dunlap  *
262edd9334cSRandy Dunlap  * Return: On success, sets @result to the integer represented by the string
263*359d6255SEric Biggers  * and returns 0. Returns -EINVAL or -ERANGE on failure.
2641da177e4SLinus Torvalds  */
match_hex(substring_t * s,int * result)2651da177e4SLinus Torvalds int match_hex(substring_t *s, int *result)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds 	return match_number(s, result, 16);
2681da177e4SLinus Torvalds }
269a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_hex);
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds /**
272b9bffa10SBingJing Chang  * match_wildcard - parse if a string matches given wildcard pattern
273aace0509SDu, Changbin  * @pattern: wildcard pattern
274aace0509SDu, Changbin  * @str: the string to be parsed
275aace0509SDu, Changbin  *
276aace0509SDu, Changbin  * Description: Parse the string @str to check if matches wildcard
277edd9334cSRandy Dunlap  * pattern @pattern. The pattern may contain two types of wildcards:
278aace0509SDu, Changbin  *   '*' - matches zero or more characters
279aace0509SDu, Changbin  *   '?' - matches one character
280edd9334cSRandy Dunlap  *
281edd9334cSRandy Dunlap  * Return: If the @str matches the @pattern, return true, else return false.
282aace0509SDu, Changbin  */
match_wildcard(const char * pattern,const char * str)283aace0509SDu, Changbin bool match_wildcard(const char *pattern, const char *str)
284aace0509SDu, Changbin {
285aace0509SDu, Changbin 	const char *s = str;
286aace0509SDu, Changbin 	const char *p = pattern;
287aace0509SDu, Changbin 	bool star = false;
288aace0509SDu, Changbin 
289aace0509SDu, Changbin 	while (*s) {
290aace0509SDu, Changbin 		switch (*p) {
291aace0509SDu, Changbin 		case '?':
292aace0509SDu, Changbin 			s++;
293aace0509SDu, Changbin 			p++;
294aace0509SDu, Changbin 			break;
295aace0509SDu, Changbin 		case '*':
296aace0509SDu, Changbin 			star = true;
297aace0509SDu, Changbin 			str = s;
298aace0509SDu, Changbin 			if (!*++p)
299aace0509SDu, Changbin 				return true;
300aace0509SDu, Changbin 			pattern = p;
301aace0509SDu, Changbin 			break;
302aace0509SDu, Changbin 		default:
303aace0509SDu, Changbin 			if (*s == *p) {
304aace0509SDu, Changbin 				s++;
305aace0509SDu, Changbin 				p++;
306aace0509SDu, Changbin 			} else {
307aace0509SDu, Changbin 				if (!star)
308aace0509SDu, Changbin 					return false;
309aace0509SDu, Changbin 				str++;
310aace0509SDu, Changbin 				s = str;
311aace0509SDu, Changbin 				p = pattern;
312aace0509SDu, Changbin 			}
313aace0509SDu, Changbin 			break;
314aace0509SDu, Changbin 		}
315aace0509SDu, Changbin 	}
316aace0509SDu, Changbin 
317aace0509SDu, Changbin 	if (*p == '*')
318aace0509SDu, Changbin 		++p;
319aace0509SDu, Changbin 	return !*p;
320aace0509SDu, Changbin }
321a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_wildcard);
322aace0509SDu, Changbin 
323aace0509SDu, Changbin /**
324b9bffa10SBingJing Chang  * match_strlcpy - Copy the characters from a substring_t to a sized buffer
325b32a09dbSMarkus Armbruster  * @dest: where to copy to
326b32a09dbSMarkus Armbruster  * @src: &substring_t to copy
327b32a09dbSMarkus Armbruster  * @size: size of destination buffer
3281da177e4SLinus Torvalds  *
329b32a09dbSMarkus Armbruster  * Description: Copy the characters in &substring_t @src to the
330b32a09dbSMarkus Armbruster  * c-style string @dest.  Copy no more than @size - 1 characters, plus
331edd9334cSRandy Dunlap  * the terminating NUL.
332edd9334cSRandy Dunlap  *
333edd9334cSRandy Dunlap  * Return: length of @src.
3341da177e4SLinus Torvalds  */
match_strlcpy(char * dest,const substring_t * src,size_t size)335b32a09dbSMarkus Armbruster size_t match_strlcpy(char *dest, const substring_t *src, size_t size)
3361da177e4SLinus Torvalds {
337b32a09dbSMarkus Armbruster 	size_t ret = src->to - src->from;
338b32a09dbSMarkus Armbruster 
339b32a09dbSMarkus Armbruster 	if (size) {
340b32a09dbSMarkus Armbruster 		size_t len = ret >= size ? size - 1 : ret;
341b32a09dbSMarkus Armbruster 		memcpy(dest, src->from, len);
342b32a09dbSMarkus Armbruster 		dest[len] = '\0';
343b32a09dbSMarkus Armbruster 	}
344b32a09dbSMarkus Armbruster 	return ret;
3451da177e4SLinus Torvalds }
346a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_strlcpy);
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds /**
349b9bffa10SBingJing Chang  * match_strdup - allocate a new string with the contents of a substring_t
3501da177e4SLinus Torvalds  * @s: &substring_t to copy
3511da177e4SLinus Torvalds  *
3521da177e4SLinus Torvalds  * Description: Allocates and returns a string filled with the contents of
3531da177e4SLinus Torvalds  * the &substring_t @s. The caller is responsible for freeing the returned
3541da177e4SLinus Torvalds  * string with kfree().
355edd9334cSRandy Dunlap  *
356edd9334cSRandy Dunlap  * Return: the address of the newly allocated NUL-terminated string or
357edd9334cSRandy Dunlap  * %NULL on error.
3581da177e4SLinus Torvalds  */
match_strdup(const substring_t * s)359ef4533f8SDavid Howells char *match_strdup(const substring_t *s)
3601da177e4SLinus Torvalds {
36130f7bc99SEric Biggers 	return kmemdup_nul(s->from, s->to - s->from, GFP_KERNEL);
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds EXPORT_SYMBOL(match_strdup);
364