>From 9f4be567ff25fee986976c6afa193223496013a6 Mon Sep 17 00:00:00 2001
From: sewn <s...@disroot.org>
Date: Fri, 28 Jul 2023 18:58:37 +0300
Subject: [PATCH] xargs: add replace string flag (-I)

---
 Makefile            |  1 +
 libutil/strnsubst.c | 63 +++++++++++++++++++++++++++++++++++++++++++++
 util.h              |  2 ++
 xargs.1             |  8 +++++-
 xargs.c             | 16 ++++++++++--
 5 files changed, 87 insertions(+), 3 deletions(-)
 create mode 100644 libutil/strnsubst.c

diff --git a/Makefile b/Makefile
index 3243b1c..226ed52 100644
--- 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
new file mode 100644
index 0000000..a76d484
--- /dev/null
+++ b/libutil/strnsubst.c
@@ -0,0 +1,63 @@
+/*
+ * 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 <unistd.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, repllen, 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);
+       repllen = strlen(replstr);
+       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:
+       *str = s2;
+       return;
+}
diff --git a/util.h b/util.h
index 8d5004b..d6911bd 100644
--- 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 **str, const char *match, const char *replstr, size_t 
maxsize);
+
 /* regex */
 int enregcomp(int, regex_t *, const char *, int);
 int eregcomp(regex_t *, const char *, int);
diff --git a/xargs.1 b/xargs.1
index e74a608..881cc94 100644
--- 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
index 0d5dd53..d31f065 100644
--- 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, (size_t)255);
+                       else
+                               cmd[i] = estrdup(arg);
+
                        argsz += arglen + 1;
                        i++;
                        a++;
-- 
2.41.0



Reply via email to