xref: /openbmc/linux/lib/argv_split.c (revision 36ee98b5)
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