162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Misc librarized functions for cmdline poking.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/string.h>
862306a36Sopenharmony_ci#include <linux/ctype.h>
962306a36Sopenharmony_ci#include <asm/setup.h>
1062306a36Sopenharmony_ci#include <asm/cmdline.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic inline int myisspace(u8 c)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	return c <= ' ';	/* Close enough approximation */
1562306a36Sopenharmony_ci}
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Find a boolean option (like quiet,noapic,nosmp....)
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * @cmdline: the cmdline string
2162306a36Sopenharmony_ci * @max_cmdline_size: the maximum size of cmdline
2262306a36Sopenharmony_ci * @option: option string to look for
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Returns the position of that @option (starts counting with 1)
2562306a36Sopenharmony_ci * or 0 on not found.  @option will only be found if it is found
2662306a36Sopenharmony_ci * as an entire word in @cmdline.  For instance, if @option="car"
2762306a36Sopenharmony_ci * then a cmdline which contains "cart" will not match.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic int
3062306a36Sopenharmony_ci__cmdline_find_option_bool(const char *cmdline, int max_cmdline_size,
3162306a36Sopenharmony_ci			   const char *option)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	char c;
3462306a36Sopenharmony_ci	int pos = 0, wstart = 0;
3562306a36Sopenharmony_ci	const char *opptr = NULL;
3662306a36Sopenharmony_ci	enum {
3762306a36Sopenharmony_ci		st_wordstart = 0,	/* Start of word/after whitespace */
3862306a36Sopenharmony_ci		st_wordcmp,	/* Comparing this word */
3962306a36Sopenharmony_ci		st_wordskip,	/* Miscompare, skip */
4062306a36Sopenharmony_ci	} state = st_wordstart;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!cmdline)
4362306a36Sopenharmony_ci		return -1;      /* No command line */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * This 'pos' check ensures we do not overrun
4762306a36Sopenharmony_ci	 * a non-NULL-terminated 'cmdline'
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	while (pos < max_cmdline_size) {
5062306a36Sopenharmony_ci		c = *(char *)cmdline++;
5162306a36Sopenharmony_ci		pos++;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		switch (state) {
5462306a36Sopenharmony_ci		case st_wordstart:
5562306a36Sopenharmony_ci			if (!c)
5662306a36Sopenharmony_ci				return 0;
5762306a36Sopenharmony_ci			else if (myisspace(c))
5862306a36Sopenharmony_ci				break;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci			state = st_wordcmp;
6162306a36Sopenharmony_ci			opptr = option;
6262306a36Sopenharmony_ci			wstart = pos;
6362306a36Sopenharmony_ci			fallthrough;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		case st_wordcmp:
6662306a36Sopenharmony_ci			if (!*opptr) {
6762306a36Sopenharmony_ci				/*
6862306a36Sopenharmony_ci				 * We matched all the way to the end of the
6962306a36Sopenharmony_ci				 * option we were looking for.  If the
7062306a36Sopenharmony_ci				 * command-line has a space _or_ ends, then
7162306a36Sopenharmony_ci				 * we matched!
7262306a36Sopenharmony_ci				 */
7362306a36Sopenharmony_ci				if (!c || myisspace(c))
7462306a36Sopenharmony_ci					return wstart;
7562306a36Sopenharmony_ci				/*
7662306a36Sopenharmony_ci				 * We hit the end of the option, but _not_
7762306a36Sopenharmony_ci				 * the end of a word on the cmdline.  Not
7862306a36Sopenharmony_ci				 * a match.
7962306a36Sopenharmony_ci				 */
8062306a36Sopenharmony_ci			} else if (!c) {
8162306a36Sopenharmony_ci				/*
8262306a36Sopenharmony_ci				 * Hit the NULL terminator on the end of
8362306a36Sopenharmony_ci				 * cmdline.
8462306a36Sopenharmony_ci				 */
8562306a36Sopenharmony_ci				return 0;
8662306a36Sopenharmony_ci			} else if (c == *opptr++) {
8762306a36Sopenharmony_ci				/*
8862306a36Sopenharmony_ci				 * We are currently matching, so continue
8962306a36Sopenharmony_ci				 * to the next character on the cmdline.
9062306a36Sopenharmony_ci				 */
9162306a36Sopenharmony_ci				break;
9262306a36Sopenharmony_ci			}
9362306a36Sopenharmony_ci			state = st_wordskip;
9462306a36Sopenharmony_ci			fallthrough;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		case st_wordskip:
9762306a36Sopenharmony_ci			if (!c)
9862306a36Sopenharmony_ci				return 0;
9962306a36Sopenharmony_ci			else if (myisspace(c))
10062306a36Sopenharmony_ci				state = st_wordstart;
10162306a36Sopenharmony_ci			break;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;	/* Buffer overrun */
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Find a non-boolean option (i.e. option=argument). In accordance with
11062306a36Sopenharmony_ci * standard Linux practice, if this option is repeated, this returns the
11162306a36Sopenharmony_ci * last instance on the command line.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * @cmdline: the cmdline string
11462306a36Sopenharmony_ci * @max_cmdline_size: the maximum size of cmdline
11562306a36Sopenharmony_ci * @option: option string to look for
11662306a36Sopenharmony_ci * @buffer: memory buffer to return the option argument
11762306a36Sopenharmony_ci * @bufsize: size of the supplied memory buffer
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Returns the length of the argument (regardless of if it was
12062306a36Sopenharmony_ci * truncated to fit in the buffer), or -1 on not found.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic int
12362306a36Sopenharmony_ci__cmdline_find_option(const char *cmdline, int max_cmdline_size,
12462306a36Sopenharmony_ci		      const char *option, char *buffer, int bufsize)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	char c;
12762306a36Sopenharmony_ci	int pos = 0, len = -1;
12862306a36Sopenharmony_ci	const char *opptr = NULL;
12962306a36Sopenharmony_ci	char *bufptr = buffer;
13062306a36Sopenharmony_ci	enum {
13162306a36Sopenharmony_ci		st_wordstart = 0,	/* Start of word/after whitespace */
13262306a36Sopenharmony_ci		st_wordcmp,	/* Comparing this word */
13362306a36Sopenharmony_ci		st_wordskip,	/* Miscompare, skip */
13462306a36Sopenharmony_ci		st_bufcpy,	/* Copying this to buffer */
13562306a36Sopenharmony_ci	} state = st_wordstart;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!cmdline)
13862306a36Sopenharmony_ci		return -1;      /* No command line */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * This 'pos' check ensures we do not overrun
14262306a36Sopenharmony_ci	 * a non-NULL-terminated 'cmdline'
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	while (pos++ < max_cmdline_size) {
14562306a36Sopenharmony_ci		c = *(char *)cmdline++;
14662306a36Sopenharmony_ci		if (!c)
14762306a36Sopenharmony_ci			break;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		switch (state) {
15062306a36Sopenharmony_ci		case st_wordstart:
15162306a36Sopenharmony_ci			if (myisspace(c))
15262306a36Sopenharmony_ci				break;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			state = st_wordcmp;
15562306a36Sopenharmony_ci			opptr = option;
15662306a36Sopenharmony_ci			fallthrough;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		case st_wordcmp:
15962306a36Sopenharmony_ci			if ((c == '=') && !*opptr) {
16062306a36Sopenharmony_ci				/*
16162306a36Sopenharmony_ci				 * We matched all the way to the end of the
16262306a36Sopenharmony_ci				 * option we were looking for, prepare to
16362306a36Sopenharmony_ci				 * copy the argument.
16462306a36Sopenharmony_ci				 */
16562306a36Sopenharmony_ci				len = 0;
16662306a36Sopenharmony_ci				bufptr = buffer;
16762306a36Sopenharmony_ci				state = st_bufcpy;
16862306a36Sopenharmony_ci				break;
16962306a36Sopenharmony_ci			} else if (c == *opptr++) {
17062306a36Sopenharmony_ci				/*
17162306a36Sopenharmony_ci				 * We are currently matching, so continue
17262306a36Sopenharmony_ci				 * to the next character on the cmdline.
17362306a36Sopenharmony_ci				 */
17462306a36Sopenharmony_ci				break;
17562306a36Sopenharmony_ci			}
17662306a36Sopenharmony_ci			state = st_wordskip;
17762306a36Sopenharmony_ci			fallthrough;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		case st_wordskip:
18062306a36Sopenharmony_ci			if (myisspace(c))
18162306a36Sopenharmony_ci				state = st_wordstart;
18262306a36Sopenharmony_ci			break;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		case st_bufcpy:
18562306a36Sopenharmony_ci			if (myisspace(c)) {
18662306a36Sopenharmony_ci				state = st_wordstart;
18762306a36Sopenharmony_ci			} else {
18862306a36Sopenharmony_ci				/*
18962306a36Sopenharmony_ci				 * Increment len, but don't overrun the
19062306a36Sopenharmony_ci				 * supplied buffer and leave room for the
19162306a36Sopenharmony_ci				 * NULL terminator.
19262306a36Sopenharmony_ci				 */
19362306a36Sopenharmony_ci				if (++len < bufsize)
19462306a36Sopenharmony_ci					*bufptr++ = c;
19562306a36Sopenharmony_ci			}
19662306a36Sopenharmony_ci			break;
19762306a36Sopenharmony_ci		}
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (bufsize)
20162306a36Sopenharmony_ci		*bufptr = '\0';
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return len;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciint cmdline_find_option_bool(const char *cmdline, const char *option)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciint cmdline_find_option(const char *cmdline, const char *option, char *buffer,
21262306a36Sopenharmony_ci			int bufsize)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option,
21562306a36Sopenharmony_ci				     buffer, bufsize);
21662306a36Sopenharmony_ci}
217