18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Misc librarized functions for cmdline poking.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/string.h>
88c2ecf20Sopenharmony_ci#include <linux/ctype.h>
98c2ecf20Sopenharmony_ci#include <asm/setup.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic inline int myisspace(u8 c)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	return c <= ' ';	/* Close enough approximation */
148c2ecf20Sopenharmony_ci}
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/**
178c2ecf20Sopenharmony_ci * Find a boolean option (like quiet,noapic,nosmp....)
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * @cmdline: the cmdline string
208c2ecf20Sopenharmony_ci * @option: option string to look for
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Returns the position of that @option (starts counting with 1)
238c2ecf20Sopenharmony_ci * or 0 on not found.  @option will only be found if it is found
248c2ecf20Sopenharmony_ci * as an entire word in @cmdline.  For instance, if @option="car"
258c2ecf20Sopenharmony_ci * then a cmdline which contains "cart" will not match.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic int
288c2ecf20Sopenharmony_ci__cmdline_find_option_bool(const char *cmdline, int max_cmdline_size,
298c2ecf20Sopenharmony_ci			   const char *option)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	char c;
328c2ecf20Sopenharmony_ci	int pos = 0, wstart = 0;
338c2ecf20Sopenharmony_ci	const char *opptr = NULL;
348c2ecf20Sopenharmony_ci	enum {
358c2ecf20Sopenharmony_ci		st_wordstart = 0,	/* Start of word/after whitespace */
368c2ecf20Sopenharmony_ci		st_wordcmp,	/* Comparing this word */
378c2ecf20Sopenharmony_ci		st_wordskip,	/* Miscompare, skip */
388c2ecf20Sopenharmony_ci	} state = st_wordstart;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (!cmdline)
418c2ecf20Sopenharmony_ci		return -1;      /* No command line */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/*
448c2ecf20Sopenharmony_ci	 * This 'pos' check ensures we do not overrun
458c2ecf20Sopenharmony_ci	 * a non-NULL-terminated 'cmdline'
468c2ecf20Sopenharmony_ci	 */
478c2ecf20Sopenharmony_ci	while (pos < max_cmdline_size) {
488c2ecf20Sopenharmony_ci		c = *(char *)cmdline++;
498c2ecf20Sopenharmony_ci		pos++;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		switch (state) {
528c2ecf20Sopenharmony_ci		case st_wordstart:
538c2ecf20Sopenharmony_ci			if (!c)
548c2ecf20Sopenharmony_ci				return 0;
558c2ecf20Sopenharmony_ci			else if (myisspace(c))
568c2ecf20Sopenharmony_ci				break;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci			state = st_wordcmp;
598c2ecf20Sopenharmony_ci			opptr = option;
608c2ecf20Sopenharmony_ci			wstart = pos;
618c2ecf20Sopenharmony_ci			fallthrough;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		case st_wordcmp:
648c2ecf20Sopenharmony_ci			if (!*opptr) {
658c2ecf20Sopenharmony_ci				/*
668c2ecf20Sopenharmony_ci				 * We matched all the way to the end of the
678c2ecf20Sopenharmony_ci				 * option we were looking for.  If the
688c2ecf20Sopenharmony_ci				 * command-line has a space _or_ ends, then
698c2ecf20Sopenharmony_ci				 * we matched!
708c2ecf20Sopenharmony_ci				 */
718c2ecf20Sopenharmony_ci				if (!c || myisspace(c))
728c2ecf20Sopenharmony_ci					return wstart;
738c2ecf20Sopenharmony_ci				/*
748c2ecf20Sopenharmony_ci				 * We hit the end of the option, but _not_
758c2ecf20Sopenharmony_ci				 * the end of a word on the cmdline.  Not
768c2ecf20Sopenharmony_ci				 * a match.
778c2ecf20Sopenharmony_ci				 */
788c2ecf20Sopenharmony_ci			} else if (!c) {
798c2ecf20Sopenharmony_ci				/*
808c2ecf20Sopenharmony_ci				 * Hit the NULL terminator on the end of
818c2ecf20Sopenharmony_ci				 * cmdline.
828c2ecf20Sopenharmony_ci				 */
838c2ecf20Sopenharmony_ci				return 0;
848c2ecf20Sopenharmony_ci			} else if (c == *opptr++) {
858c2ecf20Sopenharmony_ci				/*
868c2ecf20Sopenharmony_ci				 * We are currently matching, so continue
878c2ecf20Sopenharmony_ci				 * to the next character on the cmdline.
888c2ecf20Sopenharmony_ci				 */
898c2ecf20Sopenharmony_ci				break;
908c2ecf20Sopenharmony_ci			}
918c2ecf20Sopenharmony_ci			state = st_wordskip;
928c2ecf20Sopenharmony_ci			fallthrough;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		case st_wordskip:
958c2ecf20Sopenharmony_ci			if (!c)
968c2ecf20Sopenharmony_ci				return 0;
978c2ecf20Sopenharmony_ci			else if (myisspace(c))
988c2ecf20Sopenharmony_ci				state = st_wordstart;
998c2ecf20Sopenharmony_ci			break;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;	/* Buffer overrun */
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * Find a non-boolean option (i.e. option=argument). In accordance with
1088c2ecf20Sopenharmony_ci * standard Linux practice, if this option is repeated, this returns the
1098c2ecf20Sopenharmony_ci * last instance on the command line.
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * @cmdline: the cmdline string
1128c2ecf20Sopenharmony_ci * @max_cmdline_size: the maximum size of cmdline
1138c2ecf20Sopenharmony_ci * @option: option string to look for
1148c2ecf20Sopenharmony_ci * @buffer: memory buffer to return the option argument
1158c2ecf20Sopenharmony_ci * @bufsize: size of the supplied memory buffer
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * Returns the length of the argument (regardless of if it was
1188c2ecf20Sopenharmony_ci * truncated to fit in the buffer), or -1 on not found.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic int
1218c2ecf20Sopenharmony_ci__cmdline_find_option(const char *cmdline, int max_cmdline_size,
1228c2ecf20Sopenharmony_ci		      const char *option, char *buffer, int bufsize)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	char c;
1258c2ecf20Sopenharmony_ci	int pos = 0, len = -1;
1268c2ecf20Sopenharmony_ci	const char *opptr = NULL;
1278c2ecf20Sopenharmony_ci	char *bufptr = buffer;
1288c2ecf20Sopenharmony_ci	enum {
1298c2ecf20Sopenharmony_ci		st_wordstart = 0,	/* Start of word/after whitespace */
1308c2ecf20Sopenharmony_ci		st_wordcmp,	/* Comparing this word */
1318c2ecf20Sopenharmony_ci		st_wordskip,	/* Miscompare, skip */
1328c2ecf20Sopenharmony_ci		st_bufcpy,	/* Copying this to buffer */
1338c2ecf20Sopenharmony_ci	} state = st_wordstart;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (!cmdline)
1368c2ecf20Sopenharmony_ci		return -1;      /* No command line */
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * This 'pos' check ensures we do not overrun
1408c2ecf20Sopenharmony_ci	 * a non-NULL-terminated 'cmdline'
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	while (pos++ < max_cmdline_size) {
1438c2ecf20Sopenharmony_ci		c = *(char *)cmdline++;
1448c2ecf20Sopenharmony_ci		if (!c)
1458c2ecf20Sopenharmony_ci			break;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		switch (state) {
1488c2ecf20Sopenharmony_ci		case st_wordstart:
1498c2ecf20Sopenharmony_ci			if (myisspace(c))
1508c2ecf20Sopenharmony_ci				break;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci			state = st_wordcmp;
1538c2ecf20Sopenharmony_ci			opptr = option;
1548c2ecf20Sopenharmony_ci			fallthrough;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		case st_wordcmp:
1578c2ecf20Sopenharmony_ci			if ((c == '=') && !*opptr) {
1588c2ecf20Sopenharmony_ci				/*
1598c2ecf20Sopenharmony_ci				 * We matched all the way to the end of the
1608c2ecf20Sopenharmony_ci				 * option we were looking for, prepare to
1618c2ecf20Sopenharmony_ci				 * copy the argument.
1628c2ecf20Sopenharmony_ci				 */
1638c2ecf20Sopenharmony_ci				len = 0;
1648c2ecf20Sopenharmony_ci				bufptr = buffer;
1658c2ecf20Sopenharmony_ci				state = st_bufcpy;
1668c2ecf20Sopenharmony_ci				break;
1678c2ecf20Sopenharmony_ci			} else if (c == *opptr++) {
1688c2ecf20Sopenharmony_ci				/*
1698c2ecf20Sopenharmony_ci				 * We are currently matching, so continue
1708c2ecf20Sopenharmony_ci				 * to the next character on the cmdline.
1718c2ecf20Sopenharmony_ci				 */
1728c2ecf20Sopenharmony_ci				break;
1738c2ecf20Sopenharmony_ci			}
1748c2ecf20Sopenharmony_ci			state = st_wordskip;
1758c2ecf20Sopenharmony_ci			fallthrough;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		case st_wordskip:
1788c2ecf20Sopenharmony_ci			if (myisspace(c))
1798c2ecf20Sopenharmony_ci				state = st_wordstart;
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		case st_bufcpy:
1838c2ecf20Sopenharmony_ci			if (myisspace(c)) {
1848c2ecf20Sopenharmony_ci				state = st_wordstart;
1858c2ecf20Sopenharmony_ci			} else {
1868c2ecf20Sopenharmony_ci				/*
1878c2ecf20Sopenharmony_ci				 * Increment len, but don't overrun the
1888c2ecf20Sopenharmony_ci				 * supplied buffer and leave room for the
1898c2ecf20Sopenharmony_ci				 * NULL terminator.
1908c2ecf20Sopenharmony_ci				 */
1918c2ecf20Sopenharmony_ci				if (++len < bufsize)
1928c2ecf20Sopenharmony_ci					*bufptr++ = c;
1938c2ecf20Sopenharmony_ci			}
1948c2ecf20Sopenharmony_ci			break;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (bufsize)
1998c2ecf20Sopenharmony_ci		*bufptr = '\0';
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return len;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciint cmdline_find_option_bool(const char *cmdline, const char *option)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciint cmdline_find_option(const char *cmdline, const char *option, char *buffer,
2108c2ecf20Sopenharmony_ci			int bufsize)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option,
2138c2ecf20Sopenharmony_ci				     buffer, bufsize);
2148c2ecf20Sopenharmony_ci}
215