xref: /linux/lib/cmdline.c (revision 8bc7c5e525584903ea83332e18a2118ed3b1985e)
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 values:
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 
150 unsigned long long memparse(const char *ptr, char **retptr)
151 {
152 	char *endptr;	/* local pointer to end of parsed string */
153 
154 	unsigned long long ret = simple_strtoull(ptr, &endptr, 0);
155 
156 	switch (*endptr) {
157 	case 'E':
158 	case 'e':
159 		ret <<= 10;
160 		fallthrough;
161 	case 'P':
162 	case 'p':
163 		ret <<= 10;
164 		fallthrough;
165 	case 'T':
166 	case 't':
167 		ret <<= 10;
168 		fallthrough;
169 	case 'G':
170 	case 'g':
171 		ret <<= 10;
172 		fallthrough;
173 	case 'M':
174 	case 'm':
175 		ret <<= 10;
176 		fallthrough;
177 	case 'K':
178 	case 'k':
179 		ret <<= 10;
180 		endptr++;
181 		fallthrough;
182 	default:
183 		break;
184 	}
185 
186 	if (retptr)
187 		*retptr = endptr;
188 
189 	return ret;
190 }
191 EXPORT_SYMBOL(memparse);
192 
193 /**
194  *	parse_option_str - Parse a string and check an option is set or not
195  *	@str: String to be parsed
196  *	@option: option name
197  *
198  *	This function parses a string containing a comma-separated list of
199  *	strings like a=b,c.
200  *
201  *	Return true if there's such option in the string, or return false.
202  */
203 bool parse_option_str(const char *str, const char *option)
204 {
205 	while (*str) {
206 		if (!strncmp(str, option, strlen(option))) {
207 			str += strlen(option);
208 			if (!*str || *str == ',')
209 				return true;
210 		}
211 
212 		while (*str && *str != ',')
213 			str++;
214 
215 		if (*str == ',')
216 			str++;
217 	}
218 
219 	return false;
220 }
221 
222 /*
223  * Parse a string to get a param value pair.
224  * You can use " around spaces, but can't escape ".
225  * Hyphens and underscores equivalent in parameter names.
226  */
227 char *next_arg(char *args, char **param, char **val)
228 {
229 	unsigned int i, equals = 0;
230 	int in_quote = 0, quoted = 0;
231 
232 	if (*args == '"') {
233 		args++;
234 		in_quote = 1;
235 		quoted = 1;
236 	}
237 
238 	for (i = 0; args[i]; i++) {
239 		if (isspace(args[i]) && !in_quote)
240 			break;
241 		if (equals == 0) {
242 			if (args[i] == '=')
243 				equals = i;
244 		}
245 		if (args[i] == '"')
246 			in_quote = !in_quote;
247 	}
248 
249 	*param = args;
250 	if (!equals)
251 		*val = NULL;
252 	else {
253 		args[equals] = '\0';
254 		*val = args + equals + 1;
255 
256 		/* Don't include quotes in value. */
257 		if (**val == '"') {
258 			(*val)++;
259 			if (args[i-1] == '"')
260 				args[i-1] = '\0';
261 		}
262 	}
263 	if (quoted && i > 0 && args[i-1] == '"')
264 		args[i-1] = '\0';
265 
266 	if (args[i]) {
267 		args[i] = '\0';
268 		args += i + 1;
269 	} else
270 		args += i;
271 
272 	/* Chew up trailing spaces. */
273 	return skip_spaces(args);
274 }
275 EXPORT_SYMBOL(next_arg);
276