contrib/chg/chg.c
changeset 28167 66f6dad20c19
parent 28086 65d24ca35496
child 28194 7623ba92af72
--- a/contrib/chg/chg.c	Wed Feb 17 15:08:09 2016 +0000
+++ b/contrib/chg/chg.c	Wed Feb 17 15:00:47 2016 +0000
@@ -32,8 +32,80 @@
 	char sockname[UNIX_PATH_MAX];
 	char lockfile[UNIX_PATH_MAX];
 	char pidfile[UNIX_PATH_MAX];
+	size_t argsize;
+	const char **args;
 };
 
+static void initcmdserveropts(struct cmdserveropts *opts) {
+	memset(opts, 0, sizeof(struct cmdserveropts));
+}
+
+static void freecmdserveropts(struct cmdserveropts *opts) {
+	free(opts->args);
+	opts->args = NULL;
+	opts->argsize = 0;
+}
+
+/*
+ * Test if an argument is a sensitive flag that should be passed to the server.
+ * Return 0 if not, otherwise the number of arguments starting from the current
+ * one that should be passed to the server.
+ */
+static size_t testsensitiveflag(const char *arg)
+{
+	static const struct {
+		const char *name;
+		size_t narg;
+	} flags[] = {
+		{"--config", 1},
+		{"--cwd", 1},
+		{"--repo", 1},
+		{"--repository", 1},
+		{"--traceback", 0},
+		{"-R", 1},
+	};
+	size_t i;
+	for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
+		size_t len = strlen(flags[i].name);
+		size_t narg = flags[i].narg;
+		if (memcmp(arg, flags[i].name, len) == 0) {
+			if (arg[len] == '\0') {  /* --flag (value) */
+				return narg + 1;
+			} else if (arg[len] == '=' && narg > 0) {  /* --flag=value */
+				return 1;
+			} else if (flags[i].name[1] != '-') {  /* short flag */
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+/*
+ * Parse argv[] and put sensitive flags to opts->args
+ */
+static void setcmdserverargs(struct cmdserveropts *opts,
+			     int argc, const char *argv[])
+{
+	size_t i, step;
+	opts->argsize = 0;
+	for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
+		if (!argv[i])
+			continue;  /* pass clang-analyse */
+		if (strcmp(argv[i], "--") == 0)
+			break;
+		size_t n = testsensitiveflag(argv[i]);
+		if (n == 0 || i + n > (size_t)argc)
+			continue;
+		opts->args = reallocx(opts->args,
+				      (n + opts->argsize) * sizeof(char *));
+		memcpy(opts->args + opts->argsize, argv + i,
+		       sizeof(char *) * n);
+		opts->argsize += n;
+		step = n;
+	}
+}
+
 static void preparesockdir(const char *sockdir)
 {
 	int r;
@@ -111,7 +183,7 @@
 	if (!hgcmd || hgcmd[0] == '\0')
 		hgcmd = "hg";
 
-	const char *argv[] = {
+	const char *baseargv[] = {
 		hgcmd,
 		"serve",
 		"--cwd", "/",
@@ -122,10 +194,18 @@
 		"--config", "extensions.chgserver=",
 		/* wrap root ui so that it can be disabled/enabled by config */
 		"--config", "progress.assume-tty=1",
-		NULL,
 	};
+	size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
+	size_t argsize = baseargvsize + opts->argsize + 1;
+
+	const char **argv = mallocx(sizeof(char *) * argsize);
+	memcpy(argv, baseargv, sizeof(baseargv));
+	memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
+	argv[argsize - 1] = NULL;
+
 	if (execvp(hgcmd, (char **)argv) < 0)
 		abortmsg("failed to exec cmdserver (errno = %d)", errno);
+	free(argv);
 }
 
 /*
@@ -352,7 +432,9 @@
 		enabledebugmsg();
 
 	struct cmdserveropts opts;
+	initcmdserveropts(&opts);
 	setcmdserveropts(&opts);
+	setcmdserverargs(&opts, argc, argv);
 
 	if (argc == 2) {
 		int sig = 0;
@@ -379,5 +461,6 @@
 	setuppager(hgc, argv + 1, argc - 1);
 	int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
 	hgc_close(hgc);
+	freecmdserveropts(&opts);
 	return exitcode;
 }