18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Helper function for splitting a string into an argv-like array. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/ctype.h> 88c2ecf20Sopenharmony_ci#include <linux/string.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int count_argc(const char *str) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci int count = 0; 158c2ecf20Sopenharmony_ci bool was_space; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci for (was_space = true; *str; str++) { 188c2ecf20Sopenharmony_ci if (isspace(*str)) { 198c2ecf20Sopenharmony_ci was_space = true; 208c2ecf20Sopenharmony_ci } else if (was_space) { 218c2ecf20Sopenharmony_ci was_space = false; 228c2ecf20Sopenharmony_ci count++; 238c2ecf20Sopenharmony_ci } 248c2ecf20Sopenharmony_ci } 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return count; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/** 308c2ecf20Sopenharmony_ci * argv_free - free an argv 318c2ecf20Sopenharmony_ci * @argv - the argument vector to be freed 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Frees an argv and the strings it points to. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_civoid argv_free(char **argv) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci argv--; 388c2ecf20Sopenharmony_ci kfree(argv[0]); 398c2ecf20Sopenharmony_ci kfree(argv); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(argv_free); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * argv_split - split a string at whitespace, returning an argv 458c2ecf20Sopenharmony_ci * @gfp: the GFP mask used to allocate memory 468c2ecf20Sopenharmony_ci * @str: the string to be split 478c2ecf20Sopenharmony_ci * @argcp: returned argument count 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Returns an array of pointers to strings which are split out from 508c2ecf20Sopenharmony_ci * @str. This is performed by strictly splitting on white-space; no 518c2ecf20Sopenharmony_ci * quote processing is performed. Multiple whitespace characters are 528c2ecf20Sopenharmony_ci * considered to be a single argument separator. The returned array 538c2ecf20Sopenharmony_ci * is always NULL-terminated. Returns NULL on memory allocation 548c2ecf20Sopenharmony_ci * failure. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * The source string at `str' may be undergoing concurrent alteration via 578c2ecf20Sopenharmony_ci * userspace sysctl activity (at least). The argv_split() implementation 588c2ecf20Sopenharmony_ci * attempts to handle this gracefully by taking a local copy to work on. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cichar **argv_split(gfp_t gfp, const char *str, int *argcp) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci char *argv_str; 638c2ecf20Sopenharmony_ci bool was_space; 648c2ecf20Sopenharmony_ci char **argv, **argv_ret; 658c2ecf20Sopenharmony_ci int argc; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp); 688c2ecf20Sopenharmony_ci if (!argv_str) 698c2ecf20Sopenharmony_ci return NULL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci argc = count_argc(argv_str); 728c2ecf20Sopenharmony_ci argv = kmalloc_array(argc + 2, sizeof(*argv), gfp); 738c2ecf20Sopenharmony_ci if (!argv) { 748c2ecf20Sopenharmony_ci kfree(argv_str); 758c2ecf20Sopenharmony_ci return NULL; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci *argv = argv_str; 798c2ecf20Sopenharmony_ci argv_ret = ++argv; 808c2ecf20Sopenharmony_ci for (was_space = true; *argv_str; argv_str++) { 818c2ecf20Sopenharmony_ci if (isspace(*argv_str)) { 828c2ecf20Sopenharmony_ci was_space = true; 838c2ecf20Sopenharmony_ci *argv_str = 0; 848c2ecf20Sopenharmony_ci } else if (was_space) { 858c2ecf20Sopenharmony_ci was_space = false; 868c2ecf20Sopenharmony_ci *argv++ = argv_str; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci *argv = NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (argcp) 928c2ecf20Sopenharmony_ci *argcp = argc; 938c2ecf20Sopenharmony_ci return argv_ret; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(argv_split); 96