xref: /DragonStub/apps/lib/cmdline.c (revision 78b790fa8bc8298f8bdfd5bd1945781650dbf361)
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 <dragonstub/linux/const.h>
13 #include <dragonstub/dragonstub.h>
14 #include <dragonstub/linux/ctype.h>
15 
16 /*
17  *	If a hyphen was found in get_option, this will handle the
18  *	range of numbers, M-N.  This will expand the range and insert
19  *	the values[M, M+1, ..., N] into the ints array in get_options.
20  */
21 
get_range(char ** str,int * pint,int n)22 static int get_range(char **str, int *pint, int n)
23 {
24 	int x, inc_counter, upper_range;
25 
26 	(*str)++;
27 	upper_range = simple_strtol((*str), NULL, 0);
28 	inc_counter = upper_range - *pint;
29 	for (x = *pint; n && x < upper_range; x++, n--)
30 		*pint++ = x;
31 	return inc_counter;
32 }
33 
34 /**
35  *	get_option - Parse integer from an option string
36  *	@str: option string
37  *	@pint: (optional output) integer value parsed from @str
38  *
39  *	Read an int from an option string; if available accept a subsequent
40  *	comma as well.
41  *
42  *	When @pint is NULL the function can be used as a validator of
43  *	the current option in the string.
44  *
45  *	Return values:
46  *	0 - no int in string
47  *	1 - int found, no subsequent comma
48  *	2 - int found including a subsequent comma
49  *	3 - hyphen found to denote a range
50  *
51  *	Leading hyphen without integer is no integer case, but we consume it
52  *	for the sake of simplification.
53  */
54 
get_option(char ** str,int * pint)55 int get_option(char **str, int *pint)
56 {
57 	char *cur = *str;
58 	int value;
59 
60 	if (!cur || !(*cur))
61 		return 0;
62 	if (*cur == '-')
63 		value = -simple_strtoull(++cur, str, 0);
64 	else
65 		value = simple_strtoull(cur, str, 0);
66 	if (pint)
67 		*pint = value;
68 	if (cur == *str)
69 		return 0;
70 	if (**str == ',') {
71 		(*str)++;
72 		return 2;
73 	}
74 	if (**str == '-')
75 		return 3;
76 
77 	return 1;
78 }
79 
80 /**
81  *	get_options - Parse a string into a list of integers
82  *	@str: String to be parsed
83  *	@nints: size of integer array
84  *	@ints: integer array (must have room for at least one element)
85  *
86  *	This function parses a string containing a comma-separated
87  *	list of integers, a hyphen-separated range of _positive_ integers,
88  *	or a combination of both.  The parse halts when the array is
89  *	full, or when no more numbers can be retrieved from the
90  *	string.
91  *
92  *	When @nints is 0, the function just validates the given @str and
93  *	returns the amount of parseable integers as described below.
94  *
95  *	Returns:
96  *
97  *	The first element is filled by the number of collected integers
98  *	in the range. The rest is what was parsed from the @str.
99  *
100  *	Return value is the character in the string which caused
101  *	the parse to end (typically a null terminator, if @str is
102  *	completely parseable).
103  */
104 
get_options(const char * str,int nints,int * ints)105 char *get_options(const char *str, int nints, int *ints)
106 {
107 	bool validate = (nints == 0);
108 	int res, i = 1;
109 
110 	while (i < nints || validate) {
111 		int *pint = validate ? ints : ints + i;
112 
113 		res = get_option((char **)&str, pint);
114 		if (res == 0)
115 			break;
116 		if (res == 3) {
117 			int n = validate ? 0 : nints - i;
118 			int range_nums;
119 
120 			range_nums = get_range((char **)&str, pint, n);
121 			if (range_nums < 0)
122 				break;
123 			/*
124 			 * Decrement the result by one to leave out the
125 			 * last number in the range.  The next iteration
126 			 * will handle the upper number in the range
127 			 */
128 			i += (range_nums - 1);
129 		}
130 		i++;
131 		if (res == 1)
132 			break;
133 	}
134 	ints[0] = i - 1;
135 	return (char *)str;
136 }
137 
138 /**
139  *	memparse - parse a string with mem suffixes into a number
140  *	@ptr: Where parse begins
141  *	@retptr: (output) Optional pointer to next char after parse completes
142  *
143  *	Parses a string into a number.  The number stored at @ptr is
144  *	potentially suffixed with K, M, G, T, P, E.
145  */
146 
memparse(const char * ptr,char ** retptr)147 unsigned long long memparse(const char *ptr, char **retptr)
148 {
149 	char *endptr; /* local pointer to end of parsed string */
150 
151 	unsigned long long ret = simple_strtoull(ptr, &endptr, 0);
152 
153 	switch (*endptr) {
154 	case 'E':
155 	case 'e':
156 		ret <<= 10;
157 		fallthrough;
158 	case 'P':
159 	case 'p':
160 		ret <<= 10;
161 		fallthrough;
162 	case 'T':
163 	case 't':
164 		ret <<= 10;
165 		fallthrough;
166 	case 'G':
167 	case 'g':
168 		ret <<= 10;
169 		fallthrough;
170 	case 'M':
171 	case 'm':
172 		ret <<= 10;
173 		fallthrough;
174 	case 'K':
175 	case 'k':
176 		ret <<= 10;
177 		endptr++;
178 		fallthrough;
179 	default:
180 		break;
181 	}
182 
183 	if (retptr)
184 		*retptr = endptr;
185 
186 	return ret;
187 }
188 
189 
190 /*
191  * Parse a string to get a param value pair.
192  * You can use " around spaces, but can't escape ".
193  * Hyphens and underscores equivalent in parameter names.
194  */
next_arg(char * args,char ** param,char ** val)195 char *next_arg(char *args, char **param, char **val)
196 {
197 	unsigned int i, equals = 0;
198 	int in_quote = 0, quoted = 0;
199 
200 	if (*args == '"') {
201 		args++;
202 		in_quote = 1;
203 		quoted = 1;
204 	}
205 
206 	for (i = 0; args[i]; i++) {
207 		if (isspace(args[i]) && !in_quote)
208 			break;
209 		if (equals == 0) {
210 			if (args[i] == '=')
211 				equals = i;
212 		}
213 		if (args[i] == '"')
214 			in_quote = !in_quote;
215 	}
216 
217 	*param = args;
218 	if (!equals)
219 		*val = NULL;
220 	else {
221 		args[equals] = '\0';
222 		*val = args + equals + 1;
223 
224 		/* Don't include quotes in value. */
225 		if (**val == '"') {
226 			(*val)++;
227 			if (args[i - 1] == '"')
228 				args[i - 1] = '\0';
229 		}
230 	}
231 	if (quoted && i > 0 && args[i - 1] == '"')
232 		args[i - 1] = '\0';
233 
234 	if (args[i]) {
235 		args[i] = '\0';
236 		args += i + 1;
237 	} else
238 		args += i;
239 
240 	/* Chew up trailing spaces. */
241 	return skip_spaces(args);
242 }
243