From e8f92b7bdbc1f36e2dc76aae5c3431087c6ca10c Mon Sep 17 00:00:00 2001
From: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date: Wed, 14 Apr 2010 02:17:06 +0200
Subject: win32: add shell script and internal applet execution to spawn* and
 exec*
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Most of this was extracted from commit
e56b799d6ad8afba4168fffa7218d44c041a72d2
in Git repository.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 win32/process.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 347 insertions(+)

diff --git a/win32/process.c b/win32/process.c
index 77d19fb9a..c97474fb9 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -8,3 +8,350 @@ int waitpid(pid_t pid, int *status, unsigned options)
 	errno = EINVAL;
 	return -1;
 }
+
+/*
+ * Splits the PATH into parts.
+ */
+static char **
+get_path_split(void)
+{
+	char *p, **path, *envpath = getenv("PATH");
+	int i, n = 0;
+
+	if (!envpath || !*envpath)
+		return NULL;
+
+	envpath = xstrdup(envpath);
+	p = envpath;
+	while (p) {
+		char *dir = p;
+		p = strchr(p, ';');
+		if (p) *p++ = '\0';
+		if (*dir) {	/* not earlier, catches series of ; */
+			++n;
+		}
+	}
+	if (!n)
+		return NULL;
+
+	path = xmalloc((n+1)*sizeof(char*));
+	p = envpath;
+	i = 0;
+	do {
+		if (*p)
+			path[i++] = xstrdup(p);
+		p = p+strlen(p)+1;
+	} while (i < n);
+	path[i] = NULL;
+
+	free(envpath);
+
+	return path;
+}
+
+static void
+free_path_split(char **path)
+{
+	if (path) {
+		char **p = path;
+
+		while (*p)
+			free(*p++);
+		free(path);
+	}
+}
+
+/*
+ * exe_only means that we only want to detect .exe files, but not scripts
+ * (which do not have an extension)
+ */
+static char *
+lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only)
+{
+	char path[MAX_PATH];
+	snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd);
+
+	if (!isexe && access(path, F_OK) == 0)
+		return xstrdup(path);
+	path[strlen(path)-4] = '\0';
+	if ((!exe_only || isexe) && access(path, F_OK) == 0)
+		if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
+			return xstrdup(path);
+	return NULL;
+}
+
+/*
+ * Determines the absolute path of cmd using the the split path in path.
+ * If cmd contains a slash or backslash, no lookup is performed.
+ */
+static char *
+path_lookup(const char *cmd, char **path, int exe_only)
+{
+	char *prog = NULL;
+	int len = strlen(cmd);
+	int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
+
+	if (strchr(cmd, '/') || strchr(cmd, '\\')) {
+		if (!isexe) {
+			char path_exe[MAX_PATH];
+			sprintf(path_exe, "%s.exe", cmd);
+			if (!access(path_exe, F_OK))
+				return xstrdup(path_exe);
+		}
+		prog = xstrdup(cmd);
+	}
+
+	while (!prog && *path)
+		prog = lookup_prog(*path++, cmd, isexe, exe_only);
+
+	return prog;
+}
+
+static const char *
+parse_interpreter(const char *cmd)
+{
+	static char buf[100];
+	char *p, *opt;
+	int n, fd;
+
+	/* don't even try a .exe */
+	n = strlen(cmd);
+	if (n >= 4 && !strcasecmp(cmd+n-4, ".exe"))
+		return NULL;
+
+	fd = open(cmd, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+	n = read(fd, buf, sizeof(buf)-1);
+	close(fd);
+	if (n < 4)	/* at least '#!/x' and not error */
+		return NULL;
+
+	if (buf[0] != '#' || buf[1] != '!')
+		return NULL;
+	buf[n] = '\0';
+	p = strchr(buf, '\n');
+	if (!p)
+		return NULL;
+
+	*p = '\0';
+	if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\')))
+		return NULL;
+	/* strip options */
+	if ((opt = strchr(p+1, ' ')))
+		*opt = '\0';
+	return p+1;
+}
+
+/*
+ * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
+ * (Parsing C++ Command-Line Arguments)
+ */
+static char *
+quote_arg(const char *arg)
+{
+	/* count chars to quote */
+	int len = 0, n = 0;
+	int force_quotes = 0;
+	char *q, *d;
+	const char *p = arg;
+	if (!*p) force_quotes = 1;
+	while (*p) {
+		if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
+			force_quotes = 1;
+		else if (*p == '"')
+			n++;
+		else if (*p == '\\') {
+			int count = 0;
+			while (*p == '\\') {
+				count++;
+				p++;
+				len++;
+			}
+			if (*p == '"')
+				n += count*2 + 1;
+			continue;
+		}
+		len++;
+		p++;
+	}
+	if (!force_quotes && n == 0)
+		return (char*)arg;
+
+	/* insert \ where necessary */
+	d = q = xmalloc(len+n+3);
+	*d++ = '"';
+	while (*arg) {
+		if (*arg == '"')
+			*d++ = '\\';
+		else if (*arg == '\\') {
+			int count = 0;
+			while (*arg == '\\') {
+				count++;
+				*d++ = *arg++;
+			}
+			if (*arg == '"') {
+				while (count-- > 0)
+					*d++ = '\\';
+				*d++ = '\\';
+			}
+		}
+		*d++ = *arg++;
+	}
+	*d++ = '"';
+	*d++ = 0;
+	return q;
+}
+
+static pid_t
+spawnveq(int mode, const char *path, const char *const *argv, const char *const *env)
+{
+	char **new_argv;
+	int i, argc = 0;
+	pid_t ret;
+
+	if (!argv) {
+		const char *empty_argv[] = { path, NULL };
+		return spawnve(mode, path, empty_argv, env);
+	}
+
+
+	while (argv[argc])
+		argc++;
+
+	new_argv = malloc(sizeof(*argv)*(argc+1));
+	for (i = 0;i < argc;i++)
+		new_argv[i] = quote_arg(argv[i]);
+	new_argv[argc] = NULL;
+	ret = spawnve(mode, path, (const char *const *)new_argv, env);
+	for (i = 0;i < argc;i++)
+		if (new_argv[i] != argv[i])
+			free(new_argv[i]);
+	free(new_argv);
+	return ret;
+}
+
+pid_t
+mingw_spawn_applet(int mode,
+		   const char *applet,
+		   const char *const *argv,
+		   const char *const *envp,
+		   int transfer_fd)
+{
+	char **env = copy_environ(envp);
+	char path[MAX_PATH+20];
+	int ret;
+
+	sprintf(path, "BUSYBOX_APPLET_NAME=%s", applet);
+	env = env_setenv(env, path);
+	if (transfer_fd) {
+		sprintf(path, "BUSYBOX_ASH_TRANSFER=%x", (int)_get_osfhandle(transfer_fd));
+		env = env_setenv(env, path);
+	}
+	ret = spawnveq(mode, get_busybox_exec_path(), argv, (const char *const *)env);
+	free_environ(env);
+	return ret;
+}
+
+static pid_t
+mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, char **path, const char *const *envp)
+{
+	const char *interpr = parse_interpreter(prog);
+
+	if (!interpr)
+		return spawnveq(mode, prog, argv, envp);
+
+	if (ENABLE_FEATURE_PREFER_APPLETS && !strcmp(interpr, "sh")) {
+		int ret;
+		const char **new_argv;
+		int argc = 0;
+
+		while (argv[argc])
+			argc++;
+		new_argv = malloc(sizeof(*argv)*(argc+2));
+		memcpy(new_argv+1, argv, sizeof(*argv)*(argc+1));
+		new_argv[0] = prog; /* pass absolute path */
+		ret = mingw_spawn_applet(mode, "sh", new_argv, envp, 0);
+		free(new_argv);
+		return ret;
+	}
+	else {
+		int ret;
+		char *iprog = path_lookup(interpr, path, 1);
+		if (!iprog) {
+			errno = ENOENT;
+			return -1;
+		}
+		ret = spawnveq(mode, iprog, argv, envp);
+		free(iprog);
+		return ret;
+	}
+}
+
+pid_t
+mingw_spawn_1(int mode, const char *cmd, const char *const *argv, const char *const *envp)
+{
+	int ret;
+
+	if (ENABLE_FEATURE_PREFER_APPLETS &&
+	    find_applet_by_name(cmd) >= 0)
+		return mingw_spawn_applet(mode, cmd, argv++, envp, 0);
+	else {
+		char **path = get_path_split();
+		char *prog = path_lookup(cmd, path, 0);
+
+		if (prog) {
+			ret = mingw_spawn_interpreter(mode, prog, argv, path, envp);
+			free(prog);
+			free_path_split(path);
+			return ret;
+		}
+		else {
+			errno = ENOENT;
+			free_path_split(path);
+			return -1;
+		}
+	}
+	return ret;
+}
+
+pid_t
+mingw_spawn(char **argv)
+{
+	return mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)(argv+1), (const char *const *)environ);
+}
+
+int
+mingw_execvp(const char *cmd, const char *const *argv)
+{
+	int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv,
+				     (const char *const *)environ);
+	if (ret != -1)
+		exit(ret);
+	return ret;
+}
+
+int
+mingw_execve(const char *cmd, const char *const *argv, const char *const *envp)
+{
+	int ret;
+	int mode = P_WAIT;
+
+	if (ENABLE_FEATURE_PREFER_APPLETS &&
+	    find_applet_by_name(cmd) >= 0)
+		ret = mingw_spawn_applet(mode, cmd, argv++, envp, 0);
+	else {
+		char **path = get_path_split();
+		ret = mingw_spawn_interpreter(mode, cmd, argv, path, envp);
+		free_path_split(path);
+	}
+	if (ret != -1)
+		exit(ret);
+	return ret;
+}
+
+int
+mingw_execv(const char *cmd, const char *const *argv)
+{
+	return mingw_execve(cmd, argv, (const char *const *)environ);
+}
-- 
cgit v1.2.3-55-g6feb