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