After 3 months...
Merge xargs(1) with that of xMach. Bring in xargs(1) changes to add -L and -I as per the Single Unix Specification version 3. Proper exit status numbers are implemented, and the manual page has been updated to reflect reality. The code has been ANSIfied, and a new file has been added to xargs(1) to do the substring substitution as SUSv3 requires. Traditional behaviour should not be affected, use of -J should be deprecated in favor of the more portable -I (though -J has been left, for now). Submitted by: me, tjr (the exit status stuff) Obtained from: xMach
This commit is contained in:
parent
4326e8b1d3
commit
fc17b349c8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=95080
@ -1,5 +1,7 @@
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/6/93
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= xargs
|
||||
SRCS= xargs.c strnsubst.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
78
usr.bin/xargs/strnsubst.c
Normal file
78
usr.bin/xargs/strnsubst.c
Normal file
@ -0,0 +1,78 @@
|
||||
/* $xMach: strnsubst.c,v 1.3 2002/02/23 02:10:24 jmallett Exp $ */
|
||||
|
||||
/*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void strnsubst(char **, const char *, const char *, size_t);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* No value is returned.
|
||||
*/
|
||||
void
|
||||
strnsubst(char **str, const char *match, const char *replstr, size_t maxsize)
|
||||
{
|
||||
char *s1, *s2;
|
||||
|
||||
s1 = *str;
|
||||
if (s1 == NULL)
|
||||
return;
|
||||
s2 = calloc(maxsize, 1);
|
||||
if (s2 == NULL)
|
||||
err(1, "calloc");
|
||||
|
||||
for (;;) {
|
||||
char *this;
|
||||
|
||||
this = strstr(s1, match);
|
||||
if (this == NULL)
|
||||
break;
|
||||
if ((strlen(s2) + ((uintptr_t)this - (uintptr_t)s1) +
|
||||
(strlen(replstr) - 1)) > maxsize) {
|
||||
strlcat(s2, s1, maxsize);
|
||||
goto done;
|
||||
}
|
||||
strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1);
|
||||
strcat(s2, replstr);
|
||||
s1 = this + strlen(match);
|
||||
}
|
||||
strcat(s2, s1);
|
||||
done:
|
||||
*str = s2;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char *x, *y;
|
||||
|
||||
y = x = "{}{}{}";
|
||||
strnsubst(&x, "{}", "v ybir whyv! ", 12);
|
||||
if (strcmp(x, "v ybir whyv! ") == 0)
|
||||
printf("strnsubst() seems to work as expected.\n");
|
||||
printf("x: %s\ny: %s\n", x, y);
|
||||
free(x);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -35,6 +35,7 @@
|
||||
.\"
|
||||
.\" @(#)xargs.1 8.1 (Berkeley) 6/6/93
|
||||
.\" $FreeBSD$
|
||||
.\" $xMach: xargs.1,v 1.2 2002/02/23 05:23:37 tim Exp $
|
||||
.\"
|
||||
.Dd May 7, 2001
|
||||
.Dt XARGS 1
|
||||
@ -44,14 +45,16 @@
|
||||
.Nd "construct argument list(s) and execute utility"
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl 0
|
||||
.Op Fl 0pt
|
||||
.Op Fl E Ar eofstr
|
||||
.Op Fl I Ar replstr
|
||||
.Op Fl J Ar replstr
|
||||
.Op Fl L Ar number
|
||||
.Oo
|
||||
.Fl n Ar number
|
||||
.Op Fl x
|
||||
.Oc
|
||||
.Op Fl s Ar size
|
||||
.Op Fl t
|
||||
.Op Ar utility Op Ar argument ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
@ -92,6 +95,34 @@ This is expected to be used in concert with the
|
||||
.Fl print0
|
||||
function in
|
||||
.Xr find 1 .
|
||||
.It Fl E Ar eofstr
|
||||
Use
|
||||
.Ar eofstr
|
||||
as a logical EOF marker.
|
||||
.It Fl I Ar replstr
|
||||
Execute
|
||||
.Ar utility
|
||||
for each input line, replacing one or more occurences of
|
||||
.Ar replstr
|
||||
in up to 5 arguments to
|
||||
.Ar utility
|
||||
with the entire line of input.
|
||||
The resulting arguments after replacement is done will not be allowed to grow
|
||||
beyond 255 bytes, this is implemented by concatenating as much of the argument
|
||||
containing
|
||||
.Ar replstr
|
||||
as possible to the constructed arguments to
|
||||
.Ar utility
|
||||
up to 255 bytes.
|
||||
The 255 byte limit does not apply to arguments to
|
||||
.Ar utility
|
||||
which do not contain
|
||||
.Ar replstr ,
|
||||
and furthermore no replacement will be done on
|
||||
.Ar utility
|
||||
itself.
|
||||
Implies
|
||||
.Fl x .
|
||||
.It Fl J Ar replstr
|
||||
If this option is specified,
|
||||
.Nm
|
||||
@ -124,6 +155,17 @@ directory to
|
||||
.Pp
|
||||
.Dl /bin/ls -1d [A-Z]* | xargs -J [] cp -rp [] destdir
|
||||
.Pp
|
||||
.It Fl L Ar number
|
||||
Calls
|
||||
.Ar utility
|
||||
for every
|
||||
.Ar number
|
||||
lines read.
|
||||
If EOF is reached and fewer lines have been read than
|
||||
.Ar number
|
||||
then
|
||||
.Ar utility
|
||||
will be called with the available lines.
|
||||
.It Fl n Ar number
|
||||
Set the maximum number of arguments taken from standard input for each
|
||||
invocation of the utility.
|
||||
@ -142,6 +184,13 @@ arguments remaining for the last invocation of
|
||||
The current default value for
|
||||
.Ar number
|
||||
is 5000.
|
||||
.It Fl p
|
||||
Echo each command to be executed and ask the user whether it should be
|
||||
executed.
|
||||
A response of
|
||||
.Ql y
|
||||
causes the command to be executed, any other response causes it to be
|
||||
skipped.
|
||||
.It Fl s Ar size
|
||||
Set the maximum number of bytes for the command line length provided to
|
||||
.Ar utility .
|
||||
@ -184,15 +233,19 @@ command line cannot be assembled,
|
||||
.Ar utility
|
||||
cannot be invoked, an invocation of the utility is terminated by a signal
|
||||
or an invocation of the utility exits with a value of 255.
|
||||
.Pp
|
||||
.Sh DIAGNOSTICS
|
||||
The
|
||||
.Nm
|
||||
utility exits with a value of 0 if no error occurs.
|
||||
If
|
||||
.Ar utility
|
||||
cannot be invoked,
|
||||
cannot be found,
|
||||
.Nm
|
||||
exits with a value of 127.
|
||||
exits with a value of 127, otherwise if
|
||||
.Ar utility
|
||||
cannot be executed,
|
||||
.Nm
|
||||
exits with a value of 126.
|
||||
If any other error occurs,
|
||||
.Nm
|
||||
exits with a value of 1.
|
||||
|
@ -32,6 +32,8 @@
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
@ -61,29 +63,31 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "pathnames.h"
|
||||
|
||||
int tflag, rval;
|
||||
int zflag;
|
||||
|
||||
void run(char **);
|
||||
static void run(char **);
|
||||
static void usage(void);
|
||||
void strnsubst(char **, const char *, const char *, size_t);
|
||||
|
||||
static char echo[] = _PATH_ECHO;
|
||||
static int pflag, tflag, rval, zflag;
|
||||
|
||||
extern char *environ[];
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
char *p, *bbp, **bxp, *ebp, **exp, **xp;
|
||||
int cnt, jfound, indouble, insingle;
|
||||
int nargs, nflag, nline, xflag, wasquoted;
|
||||
char **av, **avj, *argp, **ep, *replstr;
|
||||
long arg_max;
|
||||
int argumentc, ch, cnt, count, Iflag, indouble, insingle, jfound, lflag;
|
||||
int nargs, nflag, nline, wasquoted, foundeof, xflag;
|
||||
size_t linelen;
|
||||
const char *eofstr;
|
||||
char **av, **avj, **bxp, **ep, **exp, **xp;
|
||||
char *argp, *bbp, *ebp, *inpline, *p, *replstr;
|
||||
|
||||
ep = environ;
|
||||
jfound = 0;
|
||||
replstr = NULL; /* set if user requests -J */
|
||||
inpline = replstr = NULL;
|
||||
eofstr = "";
|
||||
argumentc = cnt = count = Iflag = jfound = lflag = nflag = xflag =
|
||||
wasquoted = 0;
|
||||
|
||||
/*
|
||||
* POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
|
||||
@ -106,17 +110,30 @@ main(int argc, char **argv)
|
||||
/* 1 byte for each '\0' */
|
||||
nline -= strlen(*ep++) + 1 + sizeof(*ep);
|
||||
}
|
||||
nflag = xflag = wasquoted = 0;
|
||||
while ((ch = getopt(argc, argv, "0J:n:s:tx")) != -1)
|
||||
while ((ch = getopt(argc, argv, "0E:I:J:L:n:ps:tx")) != -1)
|
||||
switch(ch) {
|
||||
case 'E':
|
||||
eofstr = optarg;
|
||||
break;
|
||||
case 'I':
|
||||
Iflag = 1;
|
||||
lflag = 1;
|
||||
replstr = optarg;
|
||||
break;
|
||||
case 'J':
|
||||
replstr = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
lflag = atoi(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
if ((nargs = atoi(optarg)) <= 0)
|
||||
errx(1, "illegal argument count");
|
||||
break;
|
||||
case 'p':
|
||||
pflag = 1;
|
||||
break;
|
||||
case 's':
|
||||
nline = atoi(optarg);
|
||||
break;
|
||||
@ -138,15 +155,19 @@ main(int argc, char **argv)
|
||||
|
||||
if (xflag && !nflag)
|
||||
usage();
|
||||
if (Iflag || lflag)
|
||||
xflag = 1;
|
||||
if (replstr != NULL && *replstr == '\0')
|
||||
errx(1, "replstr may not be empty");
|
||||
|
||||
/*
|
||||
* Allocate pointers for the utility name, the utility arguments,
|
||||
* the maximum arguments to be read from stdin and the trailing
|
||||
* NULL.
|
||||
*/
|
||||
if (!(av = bxp =
|
||||
malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
|
||||
errx(1, "malloc");
|
||||
linelen = 1 + argc + nargs + 1;
|
||||
if ((av = bxp = calloc(linelen * sizeof(char **), 1)) == NULL)
|
||||
err(1, "calloc");
|
||||
|
||||
/*
|
||||
* Use the user's name for the utility as argv[0], just like the
|
||||
@ -156,9 +177,8 @@ main(int argc, char **argv)
|
||||
if (!*argv)
|
||||
cnt = strlen((*bxp++ = echo));
|
||||
else {
|
||||
cnt = 0;
|
||||
do {
|
||||
if (replstr && strcmp(*argv, replstr) == 0) {
|
||||
if (!Iflag && replstr && strcmp(*argv, replstr) == 0) {
|
||||
jfound = 1;
|
||||
argv++;
|
||||
for (avj = argv; *avj; avj++)
|
||||
@ -188,8 +208,8 @@ main(int argc, char **argv)
|
||||
if (nline <= 0)
|
||||
errx(1, "insufficient space for command");
|
||||
|
||||
if (!(bbp = malloc((u_int)nline + 1)))
|
||||
errx(1, "malloc");
|
||||
if ((bbp = malloc((u_int)nline + 1)) == NULL)
|
||||
err(1, "malloc");
|
||||
ebp = (argp = p = bbp) + nline - 1;
|
||||
|
||||
for (insingle = indouble = 0;;)
|
||||
@ -210,6 +230,7 @@ main(int argc, char **argv)
|
||||
goto arg2;
|
||||
goto addch;
|
||||
case '\n':
|
||||
count++;
|
||||
if (zflag)
|
||||
goto addch;
|
||||
|
||||
@ -218,10 +239,35 @@ arg1: if (insingle || indouble)
|
||||
errx(1, "unterminated quote");
|
||||
|
||||
arg2:
|
||||
foundeof = *eofstr != '\0' &&
|
||||
strcmp(argp, eofstr) == 0;
|
||||
|
||||
/* Do not make empty args unless they are quoted */
|
||||
if (argp != p || wasquoted) {
|
||||
if ((argp != p || wasquoted) && !foundeof) {
|
||||
*p++ = '\0';
|
||||
*xp++ = argp;
|
||||
if (Iflag) {
|
||||
char *realloc_holder;
|
||||
size_t curlen;
|
||||
realloc_holder = inpline;
|
||||
if (realloc_holder == NULL)
|
||||
curlen = 0;
|
||||
else {
|
||||
curlen = strlen(realloc_holder);
|
||||
if (curlen)
|
||||
strcat(inpline, " ");
|
||||
}
|
||||
curlen++;
|
||||
argumentc++;
|
||||
inpline = realloc(realloc_holder, strlen(argp) +
|
||||
curlen);
|
||||
if (inpline == NULL)
|
||||
err(1, "realloc");
|
||||
if (curlen == 1)
|
||||
strcpy(inpline, argp);
|
||||
else
|
||||
strcat(inpline, argp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -229,19 +275,58 @@ arg1: if (insingle || indouble)
|
||||
* run the command. If xflag and max'd out on buffer
|
||||
* but not on args, object.
|
||||
*/
|
||||
if (xp == exp || p > ebp || ch == EOF) {
|
||||
if (xp == exp || p > ebp || ch == EOF || (lflag <= count && xflag) || foundeof) {
|
||||
if (xflag && xp != exp && p > ebp)
|
||||
errx(1, "insufficient space for arguments");
|
||||
if (jfound) {
|
||||
for (avj = argv; *avj; avj++)
|
||||
*xp++ = *avj;
|
||||
}
|
||||
*xp = NULL;
|
||||
run(av);
|
||||
if (ch == EOF)
|
||||
if (Iflag) {
|
||||
char **tmp, **tmp2;
|
||||
size_t repls;
|
||||
|
||||
tmp = calloc(linelen * sizeof(char **), 1);
|
||||
if (tmp == NULL)
|
||||
err(1, "malloc");
|
||||
tmp2 = tmp;
|
||||
for (repls = 5, avj = av; *avj != NULL; avj++) {
|
||||
*tmp = *avj;
|
||||
if (avj != av && repls > 0 &&
|
||||
strstr(*tmp, replstr) != NULL) {
|
||||
strnsubst(tmp, replstr,
|
||||
inpline, 255);
|
||||
repls--;
|
||||
} else {
|
||||
*tmp = strdup(*avj);
|
||||
if (*tmp == NULL)
|
||||
err(1,
|
||||
"strdup");
|
||||
}
|
||||
tmp++;
|
||||
}
|
||||
do {
|
||||
if (*tmp != NULL)
|
||||
free(*tmp);
|
||||
tmp--;
|
||||
} while (--argumentc);
|
||||
*tmp = *xp = NULL;
|
||||
run(tmp2);
|
||||
for (; tmp2 != tmp; tmp--)
|
||||
free(*tmp);
|
||||
free(tmp2);
|
||||
free(inpline);
|
||||
inpline = strdup("");
|
||||
argumentc = 0;
|
||||
} else {
|
||||
*xp = NULL;
|
||||
run(av);
|
||||
}
|
||||
if (ch == EOF || foundeof)
|
||||
exit(rval);
|
||||
p = bbp;
|
||||
xp = bxp;
|
||||
count = 0;
|
||||
}
|
||||
argp = p;
|
||||
wasquoted = 0;
|
||||
@ -286,7 +371,7 @@ addch: if (p < ebp) {
|
||||
run(av);
|
||||
xp = bxp;
|
||||
cnt = ebp - argp;
|
||||
bcopy(argp, bbp, cnt);
|
||||
memcpy(bbp, argp, (size_t)cnt);
|
||||
p = (argp = bbp) + cnt;
|
||||
*p++ = ch;
|
||||
break;
|
||||
@ -294,20 +379,32 @@ addch: if (p < ebp) {
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
run(char **argv)
|
||||
{
|
||||
volatile int childerr;
|
||||
char **p;
|
||||
FILE *ttyfp;
|
||||
pid_t pid;
|
||||
int status;
|
||||
int ch, status;
|
||||
|
||||
if (tflag) {
|
||||
if (tflag || pflag) {
|
||||
(void)fprintf(stderr, "%s", *argv);
|
||||
for (p = argv + 1; *p; ++p)
|
||||
(void)fprintf(stderr, " %s", *p);
|
||||
(void)fprintf(stderr, "\n");
|
||||
(void)fflush(stderr);
|
||||
if (pflag) {
|
||||
if ((ttyfp = fopen("/dev/tty", "r")) != NULL) {
|
||||
(void)fprintf(stderr, "?");
|
||||
(void)fflush(stderr);
|
||||
ch = getc(ttyfp);
|
||||
fclose(ttyfp);
|
||||
if (ch != 'y')
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
(void)fprintf(stderr, "\n");
|
||||
(void)fflush(stderr);
|
||||
}
|
||||
}
|
||||
childerr = 0;
|
||||
switch(pid = vfork()) {
|
||||
@ -321,12 +418,9 @@ run(char **argv)
|
||||
pid = waitpid(pid, &status, 0);
|
||||
if (pid == -1)
|
||||
err(1, "waitpid");
|
||||
/* If we couldn't invoke the utility, exit 127. */
|
||||
if (childerr != 0) {
|
||||
errno = childerr;
|
||||
warn("%s", argv[0]);
|
||||
exit(127);
|
||||
}
|
||||
/* If we couldn't invoke the utility, exit. */
|
||||
if (childerr != 0)
|
||||
err(childerr == ENOENT ? 127 : 126, "%s", *argv);
|
||||
/* If utility signaled or exited with a value of 255, exit 1-125. */
|
||||
if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
|
||||
exit(1);
|
||||
@ -338,7 +432,8 @@ static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: xargs [-0t] [-J replstr] [-n number [-x]] [-s size]\n"
|
||||
" [utility [argument ...]]\n");
|
||||
"usage: xargs [-0pt] [-E eofstr] [-I replstr] [-J replstr] [-L number]\n");
|
||||
fprintf(stderr,
|
||||
" [-n number [-x] [-s size] [utility [argument ...]]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user