1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Helper function for splitting a string into an argv-like array. 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/ctype.h> 8 #include <linux/string.h> 9 #include <linux/slab.h> 10 #include <linux/export.h> 11 12 static int count_argc(const char *str) 13 { 14 int count = 0; 15 bool was_space; 16 17 for (was_space = true; *str; str++) { 18 if (isspace(*str)) { 19 was_space = true; 20 } else if (was_space) { 21 was_space = false; 22 count++; 23 } 24 } 25 26 return count; 27 } 28 29 /** 30 * argv_free - free an argv 31 * @argv - the argument vector to be freed 32 * 33 * Frees an argv and the strings it points to. 34 */ 35 void argv_free(char **argv) 36 { 37 argv--; 38 kfree(argv[0]); 39 kfree(argv); 40 } 41 EXPORT_SYMBOL(argv_free); 42 43 /** 44 * argv_split - split a string at whitespace, returning an argv 45 * @gfp: the GFP mask used to allocate memory 46 * @str: the string to be split 47 * @argcp: returned argument count 48 * 49 * Returns an array of pointers to strings which are split out from 50 * @str. This is performed by strictly splitting on white-space; no 51 * quote processing is performed. Multiple whitespace characters are 52 * considered to be a single argument separator. The returned array 53 * is always NULL-terminated. Returns NULL on memory allocation 54 * failure. 55 * 56 * The source string at `str' may be undergoing concurrent alteration via 57 * userspace sysctl activity (at least). The argv_split() implementation 58 * attempts to handle this gracefully by taking a local copy to work on. 59 */ 60 char **argv_split(gfp_t gfp, const char *str, int *argcp) 61 { 62 char *argv_str; 63 bool was_space; 64 char **argv, **argv_ret; 65 int argc; 66 67 argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp); 68 if (!argv_str) 69 return NULL; 70 71 argc = count_argc(argv_str); 72 argv = kmalloc_array(argc + 2, sizeof(*argv), gfp); 73 if (!argv) { 74 kfree(argv_str); 75 return NULL; 76 } 77 78 *argv = argv_str; 79 argv_ret = ++argv; 80 for (was_space = true; *argv_str; argv_str++) { 81 if (isspace(*argv_str)) { 82 was_space = true; 83 *argv_str = 0; 84 } else if (was_space) { 85 was_space = false; 86 *argv++ = argv_str; 87 } 88 } 89 *argv = NULL; 90 91 if (argcp) 92 *argcp = argc; 93 return argv_ret; 94 } 95 EXPORT_SYMBOL(argv_split); 96