Merge tag 'nfsd-6.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
[platform/kernel/linux-rpi.git] / lib / cmdline.c
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);