| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Helper function for splitting a string into an argv-like array. |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/ctype.h> |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <linux/export.h> |
| |
| static int count_argc(const char *str) |
| { |
| int count = 0; |
| bool was_space; |
| |
| for (was_space = true; *str; str++) { |
| if (isspace(*str)) { |
| was_space = true; |
| } else if (was_space) { |
| was_space = false; |
| count++; |
| } |
| } |
| |
| return count; |
| } |
| |
| /** |
| * argv_free - free an argv |
| * @argv: the argument vector to be freed |
| * |
| * Frees an argv and the strings it points to. |
| */ |
| void argv_free(char **argv) |
| { |
| argv--; |
| kfree(argv[0]); |
| kfree(argv); |
| } |
| EXPORT_SYMBOL(argv_free); |
| |
| /** |
| * argv_split - split a string at whitespace, returning an argv |
| * @gfp: the GFP mask used to allocate memory |
| * @str: the string to be split |
| * @argcp: returned argument count |
| * |
| * Returns: an array of pointers to strings which are split out from |
| * @str. This is performed by strictly splitting on white-space; no |
| * quote processing is performed. Multiple whitespace characters are |
| * considered to be a single argument separator. The returned array |
| * is always NULL-terminated. Returns NULL on memory allocation |
| * failure. |
| * |
| * The source string at `str' may be undergoing concurrent alteration via |
| * userspace sysctl activity (at least). The argv_split() implementation |
| * attempts to handle this gracefully by taking a local copy to work on. |
| */ |
| char **argv_split(gfp_t gfp, const char *str, int *argcp) |
| { |
| char *argv_str; |
| bool was_space; |
| char **argv, **argv_ret; |
| int argc; |
| |
| argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp); |
| if (!argv_str) |
| return NULL; |
| |
| argc = count_argc(argv_str); |
| argv = kmalloc_array(argc + 2, sizeof(*argv), gfp); |
| if (!argv) { |
| kfree(argv_str); |
| return NULL; |
| } |
| |
| *argv = argv_str; |
| argv_ret = ++argv; |
| for (was_space = true; *argv_str; argv_str++) { |
| if (isspace(*argv_str)) { |
| was_space = true; |
| *argv_str = 0; |
| } else if (was_space) { |
| was_space = false; |
| *argv++ = argv_str; |
| } |
| } |
| *argv = NULL; |
| |
| if (argcp) |
| *argcp = argc; |
| return argv_ret; |
| } |
| EXPORT_SYMBOL(argv_split); |