commit 8d97acc135239e7fae906e5fd3c479af240b3dea
parent f14887c76522b3a5d375351b6aa8432430447f6a
Author: sewn <sewn@disroot.org>
Date: Fri, 28 Jul 2023 18:58:37 +0300
xargs: add replace string flag (-I)
Diffstat:
5 files changed, 86 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -78,6 +78,7 @@ LIBUTILSRC =\
libutil/strlcat.c\
libutil/strlcpy.c\
libutil/strsep.c\
+ libutil/strnsubst.c\
libutil/strtonum.c\
libutil/unescape.c\
libutil/writeall.c
diff --git a/libutil/strnsubst.c b/libutil/strnsubst.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2002 J. Mallett. All rights reserved.
+ * You may do whatever you want with this file as long as
+ * the above copyright and this notice remain intact, along
+ * with the following statement:
+ * For the man who taught me vi, and who got too old, too young.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../util.h"
+
+/*
+ * Replaces str with a string consisting of str with match replaced with
+ * replstr as many times as can be done before the constructed string is
+ * maxsize bytes large. It does not free the string pointed to by str, it
+ * is up to the calling program to be sure that the original contents of
+ * str as well as the new contents are handled in an appropriate manner.
+ * If replstr is NULL, then that internally is changed to a nil-string, so
+ * that we can still pretend to do somewhat meaningful substitution.
+ * No value is returned.
+ */
+void
+strnsubst(char **str, const char *match, const char *replstr, size_t maxsize)
+{
+ char *s1, *s2, *this;
+ size_t matchlen, s2len;
+ int n;
+
+ if ((s1 = *str) == NULL)
+ return;
+ s2 = emalloc(maxsize);
+
+ if (replstr == NULL)
+ replstr = "";
+
+ if (match == NULL || *match == '\0' || strlen(s1) >= maxsize) {
+ strlcpy(s2, s1, maxsize);
+ goto done;
+ }
+
+ *s2 = '\0';
+ s2len = 0;
+ matchlen = strlen(match);
+ for (;;) {
+ if ((this = strstr(s1, match)) == NULL)
+ break;
+ n = snprintf(s2 + s2len, maxsize - s2len, "%.*s%s",
+ (int)(this - s1), s1, replstr);
+ if (n == -1 || n + s2len + strlen(this + matchlen) >= maxsize)
+ break; /* out of room */
+ s2len += n;
+ s1 = this + matchlen;
+ }
+ strlcpy(s2 + s2len, s1, maxsize - s2len);
+done:
+ free(*str);
+ *str = s2;
+ return;
+}
diff --git a/util.h b/util.h
@@ -63,6 +63,8 @@ size_t estrlcpy(char *, const char *, size_t);
#define strsep xstrsep
char *strsep(char **, const char *);
+void strnsubst(char **, const char *, const char *, size_t);
+
/* regex */
int enregcomp(int, regex_t *, const char *, int);
int eregcomp(regex_t *, const char *, int);
diff --git a/xargs.1 b/xargs.1
@@ -1,4 +1,4 @@
-.Dd 2015-10-08
+.Dd 2023-07-30
.Dt XARGS 1
.Os sbase
.Sh NAME
@@ -8,6 +8,7 @@
.Nm
.Op Fl rtx
.Op Fl E Ar eofstr
+.Op Fl I Ar replstr
.Op Fl n Ar num
.Op Fl s Ar num
.Op Ar cmd Op Ar arg ...
@@ -44,6 +45,11 @@ Normally the command is executed at least once even if there are no arguments.
Use
.Ar eofstr
as a logical EOF marker.
+.It Fl I Ar replstr
+Use
+.Ar replstr
+as the placeholder for the argument.
+Sets the arguments count to 1 per command line.
.It Fl s Ar num
Use at most
.Ar num
diff --git a/xargs.c b/xargs.c
@@ -195,10 +195,11 @@ usage(void)
int
main(int argc, char *argv[])
{
- int ret = 0, leftover = 0, i;
+ int ret = 0, leftover = 0, ri = 0, i;
size_t argsz, argmaxsz;
size_t arglen, a;
char *arg = "";
+ char *replstr;
if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1)
argmaxsz = _POSIX_ARG_MAX;
@@ -225,6 +226,11 @@ main(int argc, char *argv[])
case 'E':
eofstr = EARGF(usage());
break;
+ case 'I':
+ nflag = 1;
+ maxargs = 1;
+ replstr = EARGF(usage());
+ break;
default:
usage();
} ARGEND
@@ -235,6 +241,8 @@ main(int argc, char *argv[])
for (; i < argc; i++) {
cmd[i] = estrdup(argv[i]);
argsz += strlen(cmd[i]) + 1;
+ if (!strcmp(cmd[i], replstr))
+ ri = i;
}
} else {
cmd[i] = estrdup("/bin/echo");
@@ -252,7 +260,11 @@ main(int argc, char *argv[])
leftover = 1;
break;
}
- cmd[i] = estrdup(arg);
+ if (ri > 0)
+ strnsubst(&cmd[ri], replstr, arg, 255);
+ else
+ cmd[i] = estrdup(arg);
+
argsz += arglen + 1;
i++;
a++;