xref: /linux/lib/cmdline.c (revision 117d2bfa0ab1bec88d600c50884000334f034338)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * linux/lib/cmdline.c
4  * Helper functions generally used for parsing kernel command line
5  * and module options.
6  *
7  * Code and copyrights come from init/main.c and arch/i386/kernel/setup.c.
8  *
9  * GNU Indent formatting options for this file: -kr -i8 -npsl -pcs
10  */
11 
12 #include <linux/export.h>
13 #include <linux/kernel.h>
14 #include <linux/string.h>
15 #include <linux/ctype.h>
16 
17 /*
18  *	If a hyphen was found in get_option, this will handle the
19  *	range of numbers, M-N.  This will expand the range and insert
20  *	the values[M, M+1, ..., N] into the ints array in get_options.
21  */
22 
23 static int get_range(char **str, int *pint, int n)
24 {
25 	int x, inc_counter, upper_range;
26 
27 	(*str)++;
28 	upper_range = simple_strtol((*str), NULL, 0);
29 	inc_counter = upper_range - *pint;
30 	for (x = *pint; n && x < upper_range; x++, n--)
31 		*pint++ = x;
32 	return inc_counter;
33 }
34 
35 /**
36  *	get_option - Parse integer from an option string
37  *	@str: option string
38  *	@pint: (optional output) integer value parsed from @str
39  *
40  *	Read an int from an option string; if available accept a subsequent
41  *	comma as well.
42  *
43  *	When @pint is NULL the function can be used as a validator of
44  *	the current option in the string.
45  *
46  *	Return:
47  *	0 - no int in string
48  *	1 - int found, no subsequent comma
49  *	2 - int found including a subsequent comma
50  *	3 - hyphen found to denote a range
51  *
52  *	Leading hyphen without integer is no integer case, but we consume it
53  *	for the sake of simplification.
54  */
55 
56 int get_option(char **str, int *pint)
57 {
58 	char *cur = *str;
59 	int value;
60 
61 	if (!cur || !(*cur))
62 		return 0;
63 	if (*cur == '-')
64 		value = -simple_strtoull(++cur, str, 0);
65 	else
66 		value = simple_strtoull(cur, str, 0);
67 	if (pint)
68 		*pint = value;
69 	if (cur == *str)
70 		return 0;
71 	if (**str == ',') {
72 		(*str)++;
73 		return 2;
74 	}
75 	if (**str == '-')
76 		return 3;
77 
78 	return 1;
79 }
80 EXPORT_SYMBOL(get_option);
81 
82 /**
83  *	get_options - Parse a string into a list of integers
84  *	@str: String to be parsed
85  *	@nints: size of integer array
86  *	@ints: integer array (must have room for at least one element)
87  *
88  *	This function parses a string containing a comma-separated
89  *	list of integers, a hyphen-separated range of _positive_ integers,
90  *	or a combination of both.  The parse halts when the array is
91  *	full, or when no more numbers can be retrieved from the
92  *	string.
93  *
94  *	When @nints is 0, the function just validates the given @str and
95  *	returns the amount of parseable integers as described below.
96  *
97  *	Returns:
98  *
99  *	The first element is filled by the number of collected integers
100  *	in the range. The rest is what was parsed from the @str.
101  *
102  *	Return value is the character in the string which caused
103  *	the parse to end (typically a null terminator, if @str is
104  *	completely parseable).
105  */
106 
107 char *get_options(const char *str, int nints, int *ints)
108 {
109 	bool validate = (nints == 0);
110 	int res, i = 1;
111 
112 	while (i < nints || validate) {
113 		int *pint = validate ? ints : ints + i;
114 
115 		res = get_option((char **)&str, pint);
116 		if (res == 0)
117 			break;
118 		if (res == 3) {
119 			int n = validate ? 0 : nints - i;
120 			int range_nums;
121 
122 			range_nums = get_range((char **)&str, pint, n);
123 			if (range_nums < 0)
124 				break;
125 			/*
126 			 * Decrement the result by one to leave out the
127 			 * last number in the range.  The next iteration
128 			 * will handle the upper number in the range
129 			 */
130 			i += (range_nums - 1);
131 		}
132 		i++;
133 		if (res == 1)
134 			break;
135 	}
136 	ints[0] = i - 1;
137 	return (char *)str;
138 }
139 EXPORT_SYMBOL(get_options);
140 
141 /**
142  *	memparse - parse a string with mem suffixes into a number
143  *	@ptr: Where parse begins
144  *	@retptr: (output) Optional pointer to next char after parse completes
145  *
146  *	Parses a string into a number.  The number stored at @ptr is
147  *	potentially suffixed with K, M, G, T, P, E.
148  *
149  *	Return: The value as recognized by simple_strtoull() multiplied
150  *	by the value as specified by suffix, if any.
151  */
152 
153 unsigned long long memparse(const char *ptr, char **retptr)
154 {
155 	char *endptr;	/* local pointer to end of parsed string */
156 	unsigned long long ret = simple_strtoull(ptr, &endptr, 0);
157 	unsigned int shl = 0;
158 
159 	/* Consume valid suffix even in case of overflow. */
160 	switch (*endptr) {
161 	case 'E':
162 	case 'e':
163 		shl += 10;
164 		fallthrough;
165 	case 'P':
166 	case 'p':
167 		shl += 10;
168 		fallthrough;
169 	case 'T':
170 	case 't':
171 		shl += 10;
172 		fallthrough;
173 	case 'G':
174 	case 'g':
175 		shl += 10;
176 		fallthrough;
177 	case 'M':
178 	case 'm':
179 		shl += 10;
180 		fallthrough;
181 	case 'K':
182 	case 'k':
183 		shl += 10;
184 		fallthrough;
185 	default:
186 		break;
187 	}
188 
189 	if (shl && likely(ptr != endptr)) {
190 		/* Have valid suffix with preceding number. */
191 		if (unlikely(check_shl_overflow(ret, shl, &ret)))
192 			ret = ULLONG_MAX;
193 		endptr++;
194 	}
195 
196 	if (retptr)
197 		*retptr = endptr;
198 
199 	return ret;
200 }
201 EXPORT_SYMBOL(memparse);
202 
203 /**
204  *	parse_option_str - Parse a string and check an option is set or not
205  *	@str: String to be parsed
206  *	@option: option name
207  *
208  *	This function parses a string containing a comma-separated list of
209  *	strings like a=b,c.
210  *
211  *	Return: True if there's such option in the string or false otherwise.
212  */
213 bool parse_option_str(const char *str, const char *option)
214 {
215 	while (*str) {
216 		if (!strncmp(str, option, strlen(option))) {
217 			str += strlen(option);
218 			if (!*str || *str == ',')
219 				return true;
220 		}
221 
222 		while (*str && *str != ',')
223 			str++;
224 
225 		if (*str == ',')
226 			str++;
227 	}
228 
229 	return false;
230 }
231 
232 /*
233  * Parse a string to get a param value pair.
234  * You can use " around spaces, but can't escape ".
235  * Hyphens and underscores equivalent in parameter names.
236  */
237 char *next_arg(char *args, char **param, char **val)
238 {
239 	unsigned int i, equals = 0;
240 	int in_quote = 0, quoted = 0;
241 
242 	if (*args == '"') {
243 		args++;
244 		in_quote = 1;
245 		quoted = 1;
246 	}
247 
248 	for (i = 0; args[i]; i++) {
249 		if (isspace(args[i]) && !in_quote)
250 			break;
251 		if (equals == 0) {
252 			if (args[i] == '=')
253 				equals = i;
254 		}
255 		if (args[i] == '"')
256 			in_quote = !in_quote;
257 	}
258 
259 	*param = args;
260 	if (!equals)
261 		*val = NULL;
262 	else {
263 		args[equals] = '\0';
264 		*val = args + equals + 1;
265 
266 		/* Don't include quotes in value. */
267 		if (**val == '"') {
268 			(*val)++;
269 			if (args[i-1] == '"')
270 				args[i-1] = '\0';
271 		}
272 	}
273 	if (quoted && i > 0 && args[i-1] == '"')
274 		args[i-1] = '\0';
275 
276 	if (args[i]) {
277 		args[i] = '\0';
278 		args += i + 1;
279 	} else
280 		args += i;
281 
282 	/* Chew up trailing spaces. */
283 	return skip_spaces(args);
284 }
285 EXPORT_SYMBOL(next_arg);
286