summaryrefslogtreecommitdiff
path: root/src/lib/quoted_spawn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/quoted_spawn.c')
-rw-r--r--src/lib/quoted_spawn.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/lib/quoted_spawn.c b/src/lib/quoted_spawn.c
new file mode 100644
index 0000000..648a3a9
--- /dev/null
+++ b/src/lib/quoted_spawn.c
@@ -0,0 +1,276 @@
+/* $Id: quoted_spawn.c 2413 2010-09-11 17:43:04Z bird $ */
+/** @file
+ * quote_spawn - Correctly Quote The _spawnvp arguments, windows specific.
+ */
+
+/*
+ * Copyright (c) 2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "quoted_spawn.h"
+
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+
+/**
+ * Tests if a strings needs quoting.
+ *
+ * @returns 1 if needs, 0 if it doesn't.
+ * @param pszArg The string in question.
+ */
+static int quoted_spawn_need_quoting(const char *pszArg)
+{
+ for (;;)
+ switch (*pszArg++)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '"':
+ case '&':
+ case '>':
+ case '<':
+ case '|':
+ case '%':
+ /* Quote the control chars (tab is included). */
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ return 1;
+ }
+}
+
+/**
+ * Frees any quoted arguments.
+ *
+ * @returns NULL.
+ * @param papszArgsOrg The original argument vector.
+ * @param papszArgsQuoted The quoted argument vector.
+ * @param cArgs The number of arguments in the vector.
+ */
+static const char * const *
+quoted_spawn_free(const char * const *papszArgsOrg, const char * const *papszArgsQuoted, unsigned cArgs)
+{
+ if ( papszArgsOrg != papszArgsQuoted
+ && papszArgsQuoted != NULL)
+ {
+ int iSavedErrno = errno; /* A bit of paranoia. */
+ unsigned i = cArgs;
+ while (i-- > 0)
+ if (papszArgsQuoted[i] != papszArgsOrg[i])
+ free((char *)papszArgsQuoted[i]);
+ free((void *)papszArgsQuoted);
+ errno = iSavedErrno;
+ }
+ return NULL;
+}
+
+/**
+ * Quote an argument string.
+ *
+ * @returns Quoted argument string (new).
+ * @param pszArgOrg The original string.
+ */
+static const char *quoted_spawn_quote_arg(const char *pszArgOrg)
+{
+ size_t cchArgOrg = strlen(pszArgOrg);
+ size_t cchArgNew = 1 + cchArgOrg * 2 + 1 + 1;
+ char *pszArgNew = malloc(cchArgNew);
+ if (pszArgNew)
+ {
+ char ch;
+ char *pszDst = pszArgNew;
+ *pszDst++ = '"';
+ while ((ch = *pszArgOrg++))
+ {
+ if (ch == '\\')
+ {
+ size_t cSlashes = 1;
+ for (;;)
+ {
+ *pszDst++ = '\\';
+ ch = *pszArgOrg;
+ if (ch != '\\')
+ break;
+ pszArgOrg++;
+ cSlashes++;
+ }
+ if (ch == '"' || ch == '\0')
+ {
+ while (cSlashes-- > 0)
+ *pszDst++ = '\\';
+ if (ch == '\0')
+ break;
+ *pszDst++ = '\\';
+ *pszDst++ = '"';
+ }
+ }
+ else if (ch == '"')
+ {
+ *pszDst++ = '\\';
+ *pszDst++ = '"';
+ }
+ else
+ *pszDst++ = ch;
+ }
+ *pszDst++ = '"';
+ *pszDst = '\0';
+ assert((size_t)(pszDst - pszArgNew) < cchArgNew - 1);
+ }
+ return pszArgNew;
+}
+
+/**
+ * Quotes the arguments in an argument vector, producing a new vector.
+ *
+ * @returns The quoted argument vector.
+ * @param papszArgsOrg The vector which arguments to quote.
+ * @param iFirstArg The first argument that needs quoting.
+ * @param pcArgs Where to return the argument count.
+ */
+static const char * const *
+quoted_spawn_quote_vector(const char * const *papszArgsOrg, unsigned iFirstArg, unsigned *pcArgs)
+{
+ const char **papszArgsQuoted;
+ unsigned cArgs;
+ unsigned iArg;
+
+ /* finish counting them and allocate the result array. */
+ cArgs = iFirstArg;
+ while (papszArgsOrg[cArgs])
+ cArgs++;
+ *pcArgs = cArgs;
+
+ papszArgsQuoted = (const char **)calloc(sizeof(const char *), cArgs + 1);
+ if (!papszArgsQuoted)
+ return NULL;
+
+ /* Process the arguments up to the first quoted one (no need to
+ re-examine them). */
+ for (iArg = 0; iArg < iFirstArg; iArg++)
+ papszArgsQuoted[iArg] = papszArgsOrg[iArg];
+
+ papszArgsQuoted[iArg] = quoted_spawn_quote_arg(papszArgsOrg[iArg]);
+ if (!papszArgsQuoted[iArg])
+ return quoted_spawn_free(papszArgsOrg, papszArgsQuoted, cArgs);
+
+ /* Process the remaining arguments. */
+ while (iArg < cArgs)
+ {
+ if (!quoted_spawn_need_quoting(papszArgsOrg[iArg]))
+ papszArgsQuoted[iArg] = papszArgsOrg[iArg];
+ else
+ {
+ papszArgsQuoted[iArg] = quoted_spawn_quote_arg(papszArgsOrg[iArg]);
+ if (!papszArgsQuoted[iArg])
+ return quoted_spawn_free(papszArgsOrg, papszArgsQuoted, cArgs);
+ }
+ iArg++;
+ }
+
+ return papszArgsQuoted;
+}
+
+/**
+ * Checks if any of the arguments in the vector needs quoting and does the job.
+ *
+ * @returns If anything needs quoting a new vector is returned, otherwise the
+ * original is returned.
+ * @param papszArgsOrg The argument vector to check.
+ * @param pcArgs Where to return the argument count.
+ */
+static const char * const *
+quoted_spawn_maybe_quote(const char * const *papszArgsOrg, unsigned *pcArgs)
+{
+ unsigned iArg;
+ for (iArg = 0; papszArgsOrg[iArg]; iArg++)
+ if (quoted_spawn_need_quoting(papszArgsOrg[iArg]))
+ return quoted_spawn_quote_vector(papszArgsOrg, iArg, pcArgs);
+ *pcArgs = iArg;
+ return papszArgsOrg;
+}
+
+/**
+ * Wrapper for _spawnvp.
+ *
+ * @returns The process handle, see _spawnvp for details.
+ * @param fMode The spawn mode, see _spawnvp for details.
+ * @param pszExecPath The path to the executable, or just the name
+ * if a PATH search is desired.
+ * @param papszArgs The arguments to pass to the new process.
+ */
+intptr_t quoted_spawnvp(int fMode, const char *pszExecPath, const char * const *papszArgs)
+{
+ intptr_t hProcess;
+ unsigned cArgs;
+ const char * const *papszArgsQuoted = quoted_spawn_maybe_quote(papszArgs, &cArgs);
+ if (papszArgsQuoted)
+ {
+//unsigned i;
+//fprintf(stderr, "debug: spawning '%s'\n", pszExecPath);
+//for (i = 0; i < cArgs; i++)
+// fprintf(stderr, "debug: #%02u: '%s'\n", i, papszArgsQuoted[i]);
+ hProcess = _spawnvp(fMode, pszExecPath, papszArgsQuoted);
+ quoted_spawn_free(papszArgs, papszArgsQuoted, cArgs);
+ }
+ else
+ {
+ errno = ENOMEM;
+ hProcess = -1;
+ }
+
+ return hProcess;
+}
+