18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/lib/cmdline.c 48c2ecf20Sopenharmony_ci * Helper functions generally used for parsing kernel command line 58c2ecf20Sopenharmony_ci * and module options. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Code and copyrights come from init/main.c and arch/i386/kernel/setup.c. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * GNU Indent formatting options for this file: -kr -i8 -npsl -pcs 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/ctype.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * If a hyphen was found in get_option, this will handle the 198c2ecf20Sopenharmony_ci * range of numbers, M-N. This will expand the range and insert 208c2ecf20Sopenharmony_ci * the values[M, M+1, ..., N] into the ints array in get_options. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int get_range(char **str, int *pint, int n) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci int x, inc_counter, upper_range; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci (*str)++; 288c2ecf20Sopenharmony_ci upper_range = simple_strtol((*str), NULL, 0); 298c2ecf20Sopenharmony_ci inc_counter = upper_range - *pint; 308c2ecf20Sopenharmony_ci for (x = *pint; n && x < upper_range; x++, n--) 318c2ecf20Sopenharmony_ci *pint++ = x; 328c2ecf20Sopenharmony_ci return inc_counter; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/** 368c2ecf20Sopenharmony_ci * get_option - Parse integer from an option string 378c2ecf20Sopenharmony_ci * @str: option string 388c2ecf20Sopenharmony_ci * @pint: (output) integer value parsed from @str 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Read an int from an option string; if available accept a subsequent 418c2ecf20Sopenharmony_ci * comma as well. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Return values: 448c2ecf20Sopenharmony_ci * 0 - no int in string 458c2ecf20Sopenharmony_ci * 1 - int found, no subsequent comma 468c2ecf20Sopenharmony_ci * 2 - int found including a subsequent comma 478c2ecf20Sopenharmony_ci * 3 - hyphen found to denote a range 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciint get_option(char **str, int *pint) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci char *cur = *str; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!cur || !(*cur)) 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci *pint = simple_strtol(cur, str, 0); 578c2ecf20Sopenharmony_ci if (cur == *str) 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci if (**str == ',') { 608c2ecf20Sopenharmony_ci (*str)++; 618c2ecf20Sopenharmony_ci return 2; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci if (**str == '-') 648c2ecf20Sopenharmony_ci return 3; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 1; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_option); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * get_options - Parse a string into a list of integers 728c2ecf20Sopenharmony_ci * @str: String to be parsed 738c2ecf20Sopenharmony_ci * @nints: size of integer array 748c2ecf20Sopenharmony_ci * @ints: integer array 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * This function parses a string containing a comma-separated 778c2ecf20Sopenharmony_ci * list of integers, a hyphen-separated range of _positive_ integers, 788c2ecf20Sopenharmony_ci * or a combination of both. The parse halts when the array is 798c2ecf20Sopenharmony_ci * full, or when no more numbers can be retrieved from the 808c2ecf20Sopenharmony_ci * string. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Return value is the character in the string which caused 838c2ecf20Sopenharmony_ci * the parse to end (typically a null terminator, if @str is 848c2ecf20Sopenharmony_ci * completely parseable). 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cichar *get_options(const char *str, int nints, int *ints) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int res, i = 1; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci while (i < nints) { 928c2ecf20Sopenharmony_ci res = get_option((char **)&str, ints + i); 938c2ecf20Sopenharmony_ci if (res == 0) 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci if (res == 3) { 968c2ecf20Sopenharmony_ci int range_nums; 978c2ecf20Sopenharmony_ci range_nums = get_range((char **)&str, ints + i, nints - i); 988c2ecf20Sopenharmony_ci if (range_nums < 0) 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * Decrement the result by one to leave out the 1028c2ecf20Sopenharmony_ci * last number in the range. The next iteration 1038c2ecf20Sopenharmony_ci * will handle the upper number in the range 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci i += (range_nums - 1); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci i++; 1088c2ecf20Sopenharmony_ci if (res == 1) 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci ints[0] = i - 1; 1128c2ecf20Sopenharmony_ci return (char *)str; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_options); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/** 1178c2ecf20Sopenharmony_ci * memparse - parse a string with mem suffixes into a number 1188c2ecf20Sopenharmony_ci * @ptr: Where parse begins 1198c2ecf20Sopenharmony_ci * @retptr: (output) Optional pointer to next char after parse completes 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Parses a string into a number. The number stored at @ptr is 1228c2ecf20Sopenharmony_ci * potentially suffixed with K, M, G, T, P, E. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciunsigned long long memparse(const char *ptr, char **retptr) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci char *endptr; /* local pointer to end of parsed string */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci unsigned long long ret = simple_strtoull(ptr, &endptr, 0); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci switch (*endptr) { 1328c2ecf20Sopenharmony_ci case 'E': 1338c2ecf20Sopenharmony_ci case 'e': 1348c2ecf20Sopenharmony_ci ret <<= 10; 1358c2ecf20Sopenharmony_ci /* fall through */ 1368c2ecf20Sopenharmony_ci case 'P': 1378c2ecf20Sopenharmony_ci case 'p': 1388c2ecf20Sopenharmony_ci ret <<= 10; 1398c2ecf20Sopenharmony_ci /* fall through */ 1408c2ecf20Sopenharmony_ci case 'T': 1418c2ecf20Sopenharmony_ci case 't': 1428c2ecf20Sopenharmony_ci ret <<= 10; 1438c2ecf20Sopenharmony_ci /* fall through */ 1448c2ecf20Sopenharmony_ci case 'G': 1458c2ecf20Sopenharmony_ci case 'g': 1468c2ecf20Sopenharmony_ci ret <<= 10; 1478c2ecf20Sopenharmony_ci /* fall through */ 1488c2ecf20Sopenharmony_ci case 'M': 1498c2ecf20Sopenharmony_ci case 'm': 1508c2ecf20Sopenharmony_ci ret <<= 10; 1518c2ecf20Sopenharmony_ci /* fall through */ 1528c2ecf20Sopenharmony_ci case 'K': 1538c2ecf20Sopenharmony_ci case 'k': 1548c2ecf20Sopenharmony_ci ret <<= 10; 1558c2ecf20Sopenharmony_ci endptr++; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (retptr) 1618c2ecf20Sopenharmony_ci *retptr = endptr; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memparse); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/** 1688c2ecf20Sopenharmony_ci * parse_option_str - Parse a string and check an option is set or not 1698c2ecf20Sopenharmony_ci * @str: String to be parsed 1708c2ecf20Sopenharmony_ci * @option: option name 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * This function parses a string containing a comma-separated list of 1738c2ecf20Sopenharmony_ci * strings like a=b,c. 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * Return true if there's such option in the string, or return false. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cibool parse_option_str(const char *str, const char *option) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci while (*str) { 1808c2ecf20Sopenharmony_ci if (!strncmp(str, option, strlen(option))) { 1818c2ecf20Sopenharmony_ci str += strlen(option); 1828c2ecf20Sopenharmony_ci if (!*str || *str == ',') 1838c2ecf20Sopenharmony_ci return true; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci while (*str && *str != ',') 1878c2ecf20Sopenharmony_ci str++; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (*str == ',') 1908c2ecf20Sopenharmony_ci str++; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return false; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Parse a string to get a param value pair. 1988c2ecf20Sopenharmony_ci * You can use " around spaces, but can't escape ". 1998c2ecf20Sopenharmony_ci * Hyphens and underscores equivalent in parameter names. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cichar *next_arg(char *args, char **param, char **val) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned int i, equals = 0; 2048c2ecf20Sopenharmony_ci int in_quote = 0, quoted = 0; 2058c2ecf20Sopenharmony_ci char *next; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (*args == '"') { 2088c2ecf20Sopenharmony_ci args++; 2098c2ecf20Sopenharmony_ci in_quote = 1; 2108c2ecf20Sopenharmony_ci quoted = 1; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci for (i = 0; args[i]; i++) { 2148c2ecf20Sopenharmony_ci if (isspace(args[i]) && !in_quote) 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci if (equals == 0) { 2178c2ecf20Sopenharmony_ci if (args[i] == '=') 2188c2ecf20Sopenharmony_ci equals = i; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci if (args[i] == '"') 2218c2ecf20Sopenharmony_ci in_quote = !in_quote; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci *param = args; 2258c2ecf20Sopenharmony_ci if (!equals) 2268c2ecf20Sopenharmony_ci *val = NULL; 2278c2ecf20Sopenharmony_ci else { 2288c2ecf20Sopenharmony_ci args[equals] = '\0'; 2298c2ecf20Sopenharmony_ci *val = args + equals + 1; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Don't include quotes in value. */ 2328c2ecf20Sopenharmony_ci if (**val == '"') { 2338c2ecf20Sopenharmony_ci (*val)++; 2348c2ecf20Sopenharmony_ci if (args[i-1] == '"') 2358c2ecf20Sopenharmony_ci args[i-1] = '\0'; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci if (quoted && args[i-1] == '"') 2398c2ecf20Sopenharmony_ci args[i-1] = '\0'; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (args[i]) { 2428c2ecf20Sopenharmony_ci args[i] = '\0'; 2438c2ecf20Sopenharmony_ci next = args + i + 1; 2448c2ecf20Sopenharmony_ci } else 2458c2ecf20Sopenharmony_ci next = args + i; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Chew up trailing spaces. */ 2488c2ecf20Sopenharmony_ci return skip_spaces(next); 2498c2ecf20Sopenharmony_ci} 250