xargs: add replace string flag (-I)

This commit is contained in:
sewn 2023-07-28 18:58:37 +03:00 committed by Roberto E. Vargas Caballero
parent f14887c765
commit 8d97acc135
5 changed files with 86 additions and 3 deletions

View File

@ -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

62
libutil/strnsubst.c Normal file
View File

@ -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;
}

2
util.h
View File

@ -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);

View File

@ -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

16
xargs.c
View File

@ -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++;