1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d84d1cc7SJeremy Fitzhardinge /*
3d84d1cc7SJeremy Fitzhardinge * Helper function for splitting a string into an argv-like array.
4d84d1cc7SJeremy Fitzhardinge */
5d84d1cc7SJeremy Fitzhardinge
6d84d1cc7SJeremy Fitzhardinge #include <linux/kernel.h>
7d84d1cc7SJeremy Fitzhardinge #include <linux/ctype.h>
8e7d2860bSAndré Goddard Rosa #include <linux/string.h>
95a56db1cSRobert P. J. Day #include <linux/slab.h>
108bc3bcc9SPaul Gortmaker #include <linux/export.h>
11d84d1cc7SJeremy Fitzhardinge
count_argc(const char * str)12d84d1cc7SJeremy Fitzhardinge static int count_argc(const char *str)
13d84d1cc7SJeremy Fitzhardinge {
14d84d1cc7SJeremy Fitzhardinge int count = 0;
15095d141bSOleg Nesterov bool was_space;
16d84d1cc7SJeremy Fitzhardinge
17095d141bSOleg Nesterov for (was_space = true; *str; str++) {
18095d141bSOleg Nesterov if (isspace(*str)) {
19095d141bSOleg Nesterov was_space = true;
20095d141bSOleg Nesterov } else if (was_space) {
21095d141bSOleg Nesterov was_space = false;
22d84d1cc7SJeremy Fitzhardinge count++;
23d84d1cc7SJeremy Fitzhardinge }
24d84d1cc7SJeremy Fitzhardinge }
25d84d1cc7SJeremy Fitzhardinge
26d84d1cc7SJeremy Fitzhardinge return count;
27d84d1cc7SJeremy Fitzhardinge }
28d84d1cc7SJeremy Fitzhardinge
29d84d1cc7SJeremy Fitzhardinge /**
30d84d1cc7SJeremy Fitzhardinge * argv_free - free an argv
31*36ee98b5SRandy Dunlap * @argv: the argument vector to be freed
32d84d1cc7SJeremy Fitzhardinge *
33d84d1cc7SJeremy Fitzhardinge * Frees an argv and the strings it points to.
34d84d1cc7SJeremy Fitzhardinge */
argv_free(char ** argv)35d84d1cc7SJeremy Fitzhardinge void argv_free(char **argv)
36d84d1cc7SJeremy Fitzhardinge {
37095d141bSOleg Nesterov argv--;
38095d141bSOleg Nesterov kfree(argv[0]);
39d84d1cc7SJeremy Fitzhardinge kfree(argv);
40d84d1cc7SJeremy Fitzhardinge }
41d84d1cc7SJeremy Fitzhardinge EXPORT_SYMBOL(argv_free);
42d84d1cc7SJeremy Fitzhardinge
43d84d1cc7SJeremy Fitzhardinge /**
44d84d1cc7SJeremy Fitzhardinge * argv_split - split a string at whitespace, returning an argv
45d84d1cc7SJeremy Fitzhardinge * @gfp: the GFP mask used to allocate memory
46d84d1cc7SJeremy Fitzhardinge * @str: the string to be split
47d84d1cc7SJeremy Fitzhardinge * @argcp: returned argument count
48d84d1cc7SJeremy Fitzhardinge *
49*36ee98b5SRandy Dunlap * Returns: an array of pointers to strings which are split out from
50d84d1cc7SJeremy Fitzhardinge * @str. This is performed by strictly splitting on white-space; no
51d84d1cc7SJeremy Fitzhardinge * quote processing is performed. Multiple whitespace characters are
52d84d1cc7SJeremy Fitzhardinge * considered to be a single argument separator. The returned array
53d84d1cc7SJeremy Fitzhardinge * is always NULL-terminated. Returns NULL on memory allocation
54d84d1cc7SJeremy Fitzhardinge * failure.
55095d141bSOleg Nesterov *
56095d141bSOleg Nesterov * The source string at `str' may be undergoing concurrent alteration via
57095d141bSOleg Nesterov * userspace sysctl activity (at least). The argv_split() implementation
58095d141bSOleg Nesterov * attempts to handle this gracefully by taking a local copy to work on.
59d84d1cc7SJeremy Fitzhardinge */
argv_split(gfp_t gfp,const char * str,int * argcp)60d84d1cc7SJeremy Fitzhardinge char **argv_split(gfp_t gfp, const char *str, int *argcp)
61d84d1cc7SJeremy Fitzhardinge {
62095d141bSOleg Nesterov char *argv_str;
63095d141bSOleg Nesterov bool was_space;
64095d141bSOleg Nesterov char **argv, **argv_ret;
65095d141bSOleg Nesterov int argc;
66d84d1cc7SJeremy Fitzhardinge
67095d141bSOleg Nesterov argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp);
68095d141bSOleg Nesterov if (!argv_str)
69095d141bSOleg Nesterov return NULL;
70095d141bSOleg Nesterov
71095d141bSOleg Nesterov argc = count_argc(argv_str);
726da2ec56SKees Cook argv = kmalloc_array(argc + 2, sizeof(*argv), gfp);
73095d141bSOleg Nesterov if (!argv) {
74095d141bSOleg Nesterov kfree(argv_str);
75095d141bSOleg Nesterov return NULL;
76095d141bSOleg Nesterov }
77095d141bSOleg Nesterov
78095d141bSOleg Nesterov *argv = argv_str;
79095d141bSOleg Nesterov argv_ret = ++argv;
80095d141bSOleg Nesterov for (was_space = true; *argv_str; argv_str++) {
81095d141bSOleg Nesterov if (isspace(*argv_str)) {
82095d141bSOleg Nesterov was_space = true;
83095d141bSOleg Nesterov *argv_str = 0;
84095d141bSOleg Nesterov } else if (was_space) {
85095d141bSOleg Nesterov was_space = false;
86095d141bSOleg Nesterov *argv++ = argv_str;
87095d141bSOleg Nesterov }
88095d141bSOleg Nesterov }
89095d141bSOleg Nesterov *argv = NULL;
90d84d1cc7SJeremy Fitzhardinge
918e2b7056SNeil Horman if (argcp)
92d84d1cc7SJeremy Fitzhardinge *argcp = argc;
93095d141bSOleg Nesterov return argv_ret;
94d84d1cc7SJeremy Fitzhardinge }
95d84d1cc7SJeremy Fitzhardinge EXPORT_SYMBOL(argv_split);
96