162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Helper function for splitting a string into an argv-like array. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/ctype.h> 862306a36Sopenharmony_ci#include <linux/string.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int count_argc(const char *str) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci int count = 0; 1562306a36Sopenharmony_ci bool was_space; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci for (was_space = true; *str; str++) { 1862306a36Sopenharmony_ci if (isspace(*str)) { 1962306a36Sopenharmony_ci was_space = true; 2062306a36Sopenharmony_ci } else if (was_space) { 2162306a36Sopenharmony_ci was_space = false; 2262306a36Sopenharmony_ci count++; 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci return count; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * argv_free - free an argv 3162306a36Sopenharmony_ci * @argv: the argument vector to be freed 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Frees an argv and the strings it points to. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_civoid argv_free(char **argv) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci argv--; 3862306a36Sopenharmony_ci kfree(argv[0]); 3962306a36Sopenharmony_ci kfree(argv); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL(argv_free); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * argv_split - split a string at whitespace, returning an argv 4562306a36Sopenharmony_ci * @gfp: the GFP mask used to allocate memory 4662306a36Sopenharmony_ci * @str: the string to be split 4762306a36Sopenharmony_ci * @argcp: returned argument count 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Returns: an array of pointers to strings which are split out from 5062306a36Sopenharmony_ci * @str. This is performed by strictly splitting on white-space; no 5162306a36Sopenharmony_ci * quote processing is performed. Multiple whitespace characters are 5262306a36Sopenharmony_ci * considered to be a single argument separator. The returned array 5362306a36Sopenharmony_ci * is always NULL-terminated. Returns NULL on memory allocation 5462306a36Sopenharmony_ci * failure. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * The source string at `str' may be undergoing concurrent alteration via 5762306a36Sopenharmony_ci * userspace sysctl activity (at least). The argv_split() implementation 5862306a36Sopenharmony_ci * attempts to handle this gracefully by taking a local copy to work on. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cichar **argv_split(gfp_t gfp, const char *str, int *argcp) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci char *argv_str; 6362306a36Sopenharmony_ci bool was_space; 6462306a36Sopenharmony_ci char **argv, **argv_ret; 6562306a36Sopenharmony_ci int argc; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp); 6862306a36Sopenharmony_ci if (!argv_str) 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci argc = count_argc(argv_str); 7262306a36Sopenharmony_ci argv = kmalloc_array(argc + 2, sizeof(*argv), gfp); 7362306a36Sopenharmony_ci if (!argv) { 7462306a36Sopenharmony_ci kfree(argv_str); 7562306a36Sopenharmony_ci return NULL; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci *argv = argv_str; 7962306a36Sopenharmony_ci argv_ret = ++argv; 8062306a36Sopenharmony_ci for (was_space = true; *argv_str; argv_str++) { 8162306a36Sopenharmony_ci if (isspace(*argv_str)) { 8262306a36Sopenharmony_ci was_space = true; 8362306a36Sopenharmony_ci *argv_str = 0; 8462306a36Sopenharmony_ci } else if (was_space) { 8562306a36Sopenharmony_ci was_space = false; 8662306a36Sopenharmony_ci *argv++ = argv_str; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci *argv = NULL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (argcp) 9262306a36Sopenharmony_ci *argcp = argc; 9362306a36Sopenharmony_ci return argv_ret; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL(argv_split); 96