18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Helper function for splitting a string into an argv-like array.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/ctype.h>
88c2ecf20Sopenharmony_ci#include <linux/string.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic int count_argc(const char *str)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	int count = 0;
158c2ecf20Sopenharmony_ci	bool was_space;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	for (was_space = true; *str; str++) {
188c2ecf20Sopenharmony_ci		if (isspace(*str)) {
198c2ecf20Sopenharmony_ci			was_space = true;
208c2ecf20Sopenharmony_ci		} else if (was_space) {
218c2ecf20Sopenharmony_ci			was_space = false;
228c2ecf20Sopenharmony_ci			count++;
238c2ecf20Sopenharmony_ci		}
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return count;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/**
308c2ecf20Sopenharmony_ci * argv_free - free an argv
318c2ecf20Sopenharmony_ci * @argv - the argument vector to be freed
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Frees an argv and the strings it points to.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_civoid argv_free(char **argv)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	argv--;
388c2ecf20Sopenharmony_ci	kfree(argv[0]);
398c2ecf20Sopenharmony_ci	kfree(argv);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(argv_free);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/**
448c2ecf20Sopenharmony_ci * argv_split - split a string at whitespace, returning an argv
458c2ecf20Sopenharmony_ci * @gfp: the GFP mask used to allocate memory
468c2ecf20Sopenharmony_ci * @str: the string to be split
478c2ecf20Sopenharmony_ci * @argcp: returned argument count
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * Returns an array of pointers to strings which are split out from
508c2ecf20Sopenharmony_ci * @str.  This is performed by strictly splitting on white-space; no
518c2ecf20Sopenharmony_ci * quote processing is performed.  Multiple whitespace characters are
528c2ecf20Sopenharmony_ci * considered to be a single argument separator.  The returned array
538c2ecf20Sopenharmony_ci * is always NULL-terminated.  Returns NULL on memory allocation
548c2ecf20Sopenharmony_ci * failure.
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * The source string at `str' may be undergoing concurrent alteration via
578c2ecf20Sopenharmony_ci * userspace sysctl activity (at least).  The argv_split() implementation
588c2ecf20Sopenharmony_ci * attempts to handle this gracefully by taking a local copy to work on.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cichar **argv_split(gfp_t gfp, const char *str, int *argcp)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	char *argv_str;
638c2ecf20Sopenharmony_ci	bool was_space;
648c2ecf20Sopenharmony_ci	char **argv, **argv_ret;
658c2ecf20Sopenharmony_ci	int argc;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp);
688c2ecf20Sopenharmony_ci	if (!argv_str)
698c2ecf20Sopenharmony_ci		return NULL;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	argc = count_argc(argv_str);
728c2ecf20Sopenharmony_ci	argv = kmalloc_array(argc + 2, sizeof(*argv), gfp);
738c2ecf20Sopenharmony_ci	if (!argv) {
748c2ecf20Sopenharmony_ci		kfree(argv_str);
758c2ecf20Sopenharmony_ci		return NULL;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	*argv = argv_str;
798c2ecf20Sopenharmony_ci	argv_ret = ++argv;
808c2ecf20Sopenharmony_ci	for (was_space = true; *argv_str; argv_str++) {
818c2ecf20Sopenharmony_ci		if (isspace(*argv_str)) {
828c2ecf20Sopenharmony_ci			was_space = true;
838c2ecf20Sopenharmony_ci			*argv_str = 0;
848c2ecf20Sopenharmony_ci		} else if (was_space) {
858c2ecf20Sopenharmony_ci			was_space = false;
868c2ecf20Sopenharmony_ci			*argv++ = argv_str;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	*argv = NULL;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (argcp)
928c2ecf20Sopenharmony_ci		*argcp = argc;
938c2ecf20Sopenharmony_ci	return argv_ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(argv_split);
96