root/lib/cmdline.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/lib/cmdline.c
 * Helper functions generally used for parsing kernel command line
 * and module options.
 *
 * Code and copyrights come from init/main.c and arch/i386/kernel/setup.c.
 *
 * GNU Indent formatting options for this file: -kr -i8 -npsl -pcs
 */

#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ctype.h>

/*
 *      If a hyphen was found in get_option, this will handle the
 *      range of numbers, M-N.  This will expand the range and insert
 *      the values[M, M+1, ..., N] into the ints array in get_options.
 */

static int get_range(char **str, int *pint, int n)
{
        int x, inc_counter, upper_range;

        (*str)++;
        upper_range = simple_strtol((*str), NULL, 0);
        inc_counter = upper_range - *pint;
        for (x = *pint; n && x < upper_range; x++, n--)
                *pint++ = x;
        return inc_counter;
}

/**
 *      get_option - Parse integer from an option string
 *      @str: option string
 *      @pint: (optional output) integer value parsed from @str
 *
 *      Read an int from an option string; if available accept a subsequent
 *      comma as well.
 *
 *      When @pint is NULL the function can be used as a validator of
 *      the current option in the string.
 *
 *      Return values:
 *      0 - no int in string
 *      1 - int found, no subsequent comma
 *      2 - int found including a subsequent comma
 *      3 - hyphen found to denote a range
 *
 *      Leading hyphen without integer is no integer case, but we consume it
 *      for the sake of simplification.
 */

int get_option(char **str, int *pint)
{
        char *cur = *str;
        int value;

        if (!cur || !(*cur))
                return 0;
        if (*cur == '-')
                value = -simple_strtoull(++cur, str, 0);
        else
                value = simple_strtoull(cur, str, 0);
        if (pint)
                *pint = value;
        if (cur == *str)
                return 0;
        if (**str == ',') {
                (*str)++;
                return 2;
        }
        if (**str == '-')
                return 3;

        return 1;
}
EXPORT_SYMBOL(get_option);

/**
 *      get_options - Parse a string into a list of integers
 *      @str: String to be parsed
 *      @nints: size of integer array
 *      @ints: integer array (must have room for at least one element)
 *
 *      This function parses a string containing a comma-separated
 *      list of integers, a hyphen-separated range of _positive_ integers,
 *      or a combination of both.  The parse halts when the array is
 *      full, or when no more numbers can be retrieved from the
 *      string.
 *
 *      When @nints is 0, the function just validates the given @str and
 *      returns the amount of parseable integers as described below.
 *
 *      Returns:
 *
 *      The first element is filled by the number of collected integers
 *      in the range. The rest is what was parsed from the @str.
 *
 *      Return value is the character in the string which caused
 *      the parse to end (typically a null terminator, if @str is
 *      completely parseable).
 */

char *get_options(const char *str, int nints, int *ints)
{
        bool validate = (nints == 0);
        int res, i = 1;

        while (i < nints || validate) {
                int *pint = validate ? ints : ints + i;

                res = get_option((char **)&str, pint);
                if (res == 0)
                        break;
                if (res == 3) {
                        int n = validate ? 0 : nints - i;
                        int range_nums;

                        range_nums = get_range((char **)&str, pint, n);
                        if (range_nums < 0)
                                break;
                        /*
                         * Decrement the result by one to leave out the
                         * last number in the range.  The next iteration
                         * will handle the upper number in the range
                         */
                        i += (range_nums - 1);
                }
                i++;
                if (res == 1)
                        break;
        }
        ints[0] = i - 1;
        return (char *)str;
}
EXPORT_SYMBOL(get_options);

/**
 *      memparse - parse a string with mem suffixes into a number
 *      @ptr: Where parse begins
 *      @retptr: (output) Optional pointer to next char after parse completes
 *
 *      Parses a string into a number.  The number stored at @ptr is
 *      potentially suffixed with K, M, G, T, P, E.
 */

unsigned long long memparse(const char *ptr, char **retptr)
{
        char *endptr;   /* local pointer to end of parsed string */

        unsigned long long ret = simple_strtoull(ptr, &endptr, 0);

        switch (*endptr) {
        case 'E':
        case 'e':
                ret <<= 10;
                fallthrough;
        case 'P':
        case 'p':
                ret <<= 10;
                fallthrough;
        case 'T':
        case 't':
                ret <<= 10;
                fallthrough;
        case 'G':
        case 'g':
                ret <<= 10;
                fallthrough;
        case 'M':
        case 'm':
                ret <<= 10;
                fallthrough;
        case 'K':
        case 'k':
                ret <<= 10;
                endptr++;
                fallthrough;
        default:
                break;
        }

        if (retptr)
                *retptr = endptr;

        return ret;
}
EXPORT_SYMBOL(memparse);

/**
 *      parse_option_str - Parse a string and check an option is set or not
 *      @str: String to be parsed
 *      @option: option name
 *
 *      This function parses a string containing a comma-separated list of
 *      strings like a=b,c.
 *
 *      Return true if there's such option in the string, or return false.
 */
bool parse_option_str(const char *str, const char *option)
{
        while (*str) {
                if (!strncmp(str, option, strlen(option))) {
                        str += strlen(option);
                        if (!*str || *str == ',')
                                return true;
                }

                while (*str && *str != ',')
                        str++;

                if (*str == ',')
                        str++;
        }

        return false;
}

/*
 * Parse a string to get a param value pair.
 * You can use " around spaces, but can't escape ".
 * Hyphens and underscores equivalent in parameter names.
 */
char *next_arg(char *args, char **param, char **val)
{
        unsigned int i, equals = 0;
        int in_quote = 0, quoted = 0;

        if (*args == '"') {
                args++;
                in_quote = 1;
                quoted = 1;
        }

        for (i = 0; args[i]; i++) {
                if (isspace(args[i]) && !in_quote)
                        break;
                if (equals == 0) {
                        if (args[i] == '=')
                                equals = i;
                }
                if (args[i] == '"')
                        in_quote = !in_quote;
        }

        *param = args;
        if (!equals)
                *val = NULL;
        else {
                args[equals] = '\0';
                *val = args + equals + 1;

                /* Don't include quotes in value. */
                if (**val == '"') {
                        (*val)++;
                        if (args[i-1] == '"')
                                args[i-1] = '\0';
                }
        }
        if (quoted && i > 0 && args[i-1] == '"')
                args[i-1] = '\0';

        if (args[i]) {
                args[i] = '\0';
                args += i + 1;
        } else
                args += i;

        /* Chew up trailing spaces. */
        return skip_spaces(args);
}
EXPORT_SYMBOL(next_arg);