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