about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-01-16 22:16:18 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2016-01-16 22:16:18 +0100
commit65fc8164e4a7b4f9b4254ac0a561082ea1682be0 (patch)
treed2006995d46ce583a76d1a4328374da60b201f97
parent821697dbee3e2d0acafb26b6567ff9690d2a9b29 (diff)
downloadxe-65fc8164e4a7b4f9b4254ac0a561082ea1682be0.tar.gz
xe-65fc8164e4a7b4f9b4254ac0a561082ea1682be0.tar.xz
xe-65fc8164e4a7b4f9b4254ac0a561082ea1682be0.zip
expand {} also in the case of passing multiple arguments
-rw-r--r--README.md4
-rw-r--r--xe.18
-rw-r--r--xe.c73
3 files changed, 46 insertions, 39 deletions
diff --git a/README.md b/README.md
index 55e2d88..84ec599 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ Over xargs:
 * No weird parsing, arguments are seperated linewise or by NUL byte.
 * Can also take arguments from command-line.
 * No shell involved unless `-s` is used.
+* `{}` replacing possible with multiple arguments.
 
 Over apply:
 * Parallel mode.
@@ -31,7 +32,8 @@ Over apply:
 * `-k`: keep going: don't stop when a command failed to execute.
 * `-n`: don't run the commands, just print them.
 * `-v`: print commands to standard error before running them.
-* `-I`: replace occurences of *arg* with the argument (default: `{}`).
+* `-I`: replace occurences of *arg* with the argument(s) (default: `{}`).
+  Use an empty *arg* to disable the replace function.
 * `-N`: pass upto *maxargs* arguments to each COMMAND (default: 1).
   `-N0` will pass as many arguments as possible.
 * `-j`: run up to *maxjobs* processes concurrently.
diff --git a/xe.1 b/xe.1
index 2ebfc67..86c07c0 100644
--- a/xe.1
+++ b/xe.1
@@ -81,9 +81,15 @@ Verbose: print commands to standard error before running them.
 .It Fl I Ar arg
 Replace occurences of
 .Ar arg
-in the command with the argument.
 (default:
 .Cm {} )
+in the command with the argument(s).
+Pass an empty
+.Ar arg
+to disable the replace function.
+Contrary to
+.Xr xargs 1
+this also works when multiple arguments are to be inserted.
 .It Fl N Ar maxargs
 Pass up to
 .Ar maxargs
diff --git a/xe.c b/xe.c
index bd781a3..c134e4d 100644
--- a/xe.c
+++ b/xe.c
@@ -33,7 +33,6 @@ static long iterations = 0;
 static FILE *traceout;
 
 static size_t argmax;
-static int push_overflowed;
 
 static char *buf;
 static size_t buflen;
@@ -42,6 +41,7 @@ static size_t bufcap;
 static char **args;
 static size_t argslen;
 static size_t argscap;
+static size_t argsresv;
 
 static char **inargs;
 
@@ -157,10 +157,8 @@ pusharg(const char *a)
 {
 	size_t l = strlen(a) + 1;   // including nul
 
-	if (buflen >= argmax - l || argslen + 1 >= argscap) {
-		push_overflowed = 1;
+	if (buflen >= argmax - l - argsresv || argslen + 1 >= argscap)
 		return 0;
-	}
 
 	if (buflen + l > bufcap) {
 		while (buflen + l > bufcap)
@@ -220,11 +218,18 @@ run()
 	return 0;
 }
 
+void
+toolong()
+{
+	fprintf(stderr, "xe: fixed argument list too long\n");
+	exit(1);
+}
+
 int
 main(int argc, char *argv[], char *envp[])
 {
 	char c;
-	int i, cmdend;
+	int i, j, cmdend;
 	char *arg;
 
 	bufcap = 4096;
@@ -307,41 +312,35 @@ main(int argc, char *argv[], char *envp[])
 			pusharg("%s\\n");
 		}
 
-		if (maxatonce == 1) {
-			// substitute {}
-			int substituted = 0;
-			for (i = optind; i < cmdend; i++) {
-				if (strcmp(argv[i], replace) == 0) {
-					pusharg(arg);
-					substituted = 1;
-				} else {
-					pusharg(argv[i]);
-				}
-			}
-			if (!substituted)
-				pusharg(arg);
-		} else {
-			// just append to cmd
-			for (i = optind; i < cmdend; i++)
-				pusharg(argv[i]);
-			pusharg(arg);
-			if (!push_overflowed) {
-				for (i = 0; maxatonce < 1 || i < maxatonce - 1; i++) {
-					arg = getarg();
-					if (!arg)
-						break;
-					if (!pusharg(arg)) {
-						run();
-						goto keeparg;
-					}
-				}
-			}
+		for (i = optind; i < cmdend; i++) {
+			if (*replace && strcmp(argv[i], replace) == 0)
+				break;
+			if (!pusharg(argv[i]))
+				toolong();
 		}
 
-		if (push_overflowed) {
-			fprintf(stderr, "xe: fixed argument list too long\n");
-			exit(1);
+		if (!pusharg(arg))
+			toolong();
+		arg = getarg();
+		i++;
+
+		// reserve space for final arguments
+		for (argsresv = 0, j = i; j < cmdend; j++)
+			argsresv += 1 + strlen(argv[j]);
+
+		// fill with up to maxatonce arguments
+		for (j = 0; maxatonce < 1 || j < maxatonce-1; j++) {
+			if (!arg)
+				break;
+			if (!pusharg(arg))
+				break;
+			arg = getarg();
 		}
+
+		for (argsresv = 0, j = i; j < cmdend; j++)
+			if (!pusharg(argv[i]))
+				toolong();
+
 		run();
 	}