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:
Juli Mallett 2002-04-19 23:28:54 +00:00
parent 4326e8b1d3
commit fc17b349c8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=95080
4 changed files with 272 additions and 44 deletions

View File

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

View File

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

View File

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