Importing usr.bin/xargs

Change-Id: I6a8ce6a8eee31942abb4db797672da7d00dfa43d
This commit is contained in:
Lionel Sambuc 2013-08-24 00:59:39 +02:00
parent 3dab66925c
commit 00785f561f
11 changed files with 1200 additions and 594 deletions

View file

@ -30,7 +30,7 @@ SUBDIR= add_route arp ash at backup btrace \
truncate tty udpstat umount uname \
unstack update uud uue version vol \
whereis which write writeisofs fetch \
xargs zdump zmodem pkgin_cd pkgin_all \
zdump zmodem pkgin_cd pkgin_all \
worldstone updateboot update_bootcfg
.if ${MACHINE_ARCH} == "i386"

View file

@ -1,4 +0,0 @@
PROG= xargs
MAN=
.include <bsd.prog.mk>

View file

@ -1,426 +0,0 @@
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* John B. Roll Jr.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)xargs.c 5.11 (Berkeley) 6/19/91";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <stdarg.h>
#if __minix
#define _PATH_ECHO "/bin/echo"
#else
#include "pathnames.h"
#endif
#ifndef ARG_MAX
#define ARG_MAX (sizeof(int) == 2 ? 4096 : 128 * 1024)
#endif
int exit_status = 0;
int tflag;
void err(const char *, ...);
void run(char **argv);
void usage(void);
int main(int argc, char **argv)
{
extern int optind;
extern char *optarg;
register int ch;
register char *p, *bbp, *ebp, **bxp, **exp, **xp;
int cnt, indouble, insingle, nargs, nflag, nline, xflag, zflag;
char **av, *argp;
/*
* POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
* caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
* that the smallest argument is 2 bytes in length, this means that
* the number of arguments is limited to:
*
* (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
*
* We arbitrarily limit the number of arguments to 5000. This is
* allowed by POSIX.2 as long as the resulting minimum exec line is
* at least LINE_MAX. Realloc'ing as necessary is possible, but
* probably not worthwhile.
*/
#if !__minix || __minix_vmd
nargs = 5000;
nline = ARG_MAX - 4 * 1024;
#else
/* Things are more cramped under standard Minix. */
nargs = 80 * sizeof(int);
nline = ARG_MAX - 512 * sizeof(int);
#endif
nflag = xflag = zflag = 0;
while ((ch = getopt(argc, argv, "n:s:tx0")) != EOF)
switch(ch) {
case 'n':
nflag = 1;
if ((nargs = atoi(optarg)) <= 0)
err("illegal argument count");
break;
case 's':
nline = atoi(optarg);
break;
case 't':
tflag = 1;
break;
case 'x':
xflag = 1;
break;
case '0':
zflag = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (xflag && !nflag)
usage();
/*
* 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 **))))
err("%s", strerror(errno));
/*
* Use the user's name for the utility as argv[0], just like the
* shell. Echo is the default. Set up pointers for the user's
* arguments.
*/
if (!*argv)
cnt = strlen(*bxp++ = _PATH_ECHO);
else {
cnt = 0;
do {
cnt += strlen(*bxp++ = *argv) + 1;
} while (*++argv);
}
/*
* Set up begin/end/traversing pointers into the array. The -n
* count doesn't include the trailing NULL pointer, so the malloc
* added in an extra slot.
*/
exp = (xp = bxp) + nargs;
/*
* Allocate buffer space for the arguments read from stdin and the
* trailing NULL. Buffer space is defined as the default or specified
* space, minus the length of the utility name and arguments. Set up
* begin/end/traversing pointers into the array. The -s count does
* include the trailing NULL, so the malloc didn't add in an extra
* slot.
*/
nline -= cnt;
if (nline <= 0)
err("insufficient space for command");
if (!(bbp = malloc((u_int)nline + 1)))
err("%s", strerror(errno));
ebp = (argp = p = bbp) + nline - 1;
if (zflag) {
/* Read pathnames terminated by null bytes as produced by
* find ... -print0. No comments in this code, see further
* below.
*/
for (;;)
switch(ch = getchar()) {
case EOF:
if (p == bbp)
exit(exit_status);
if (argp == p) {
*xp = NULL;
run(av);
exit(exit_status);
}
/*FALL THROUGH*/
case '\0':
if (argp == p)
continue;
*p = '\0';
*xp++ = argp;
if (xp == exp || p == ebp || ch == EOF) {
if (xflag && xp != exp && p == ebp)
err(
"insufficient space for arguments");
*xp = NULL;
run(av);
if (ch == EOF)
exit(exit_status);
p = bbp;
xp = bxp;
} else
++p;
argp = p;
break;
default:
if (p < ebp) {
*p++ = ch;
break;
}
if (bxp == xp)
err("insufficient space for argument");
if (xflag)
err("insufficient space for arguments");
*xp = NULL;
run(av);
xp = bxp;
cnt = ebp - argp;
bcopy(argp, bbp, cnt);
p = (argp = bbp) + cnt;
*p++ = ch;
break;
}
/* NOTREACHED */
}
for (insingle = indouble = 0;;)
switch(ch = getchar()) {
case EOF:
/* No arguments since last exec. */
if (p == bbp)
exit(exit_status);
/* Nothing since end of last argument. */
if (argp == p) {
*xp = NULL;
run(av);
exit(exit_status);
}
goto arg1;
case ' ':
case '\t':
/* Quotes escape tabs and spaces. */
if (insingle || indouble)
goto addch;
goto arg2;
case '\n':
/* Empty lines are skipped. */
if (argp == p)
continue;
/* Quotes do not escape newlines. */
arg1: if (insingle || indouble)
err("unterminated quote");
arg2: *p = '\0';
*xp++ = argp;
/*
* If max'd out on args or buffer, or reached EOF,
* run the command. If xflag and max'd out on buffer
* but not on args, object.
*/
if (xp == exp || p == ebp || ch == EOF) {
if (xflag && xp != exp && p == ebp)
err("insufficient space for arguments");
*xp = NULL;
run(av);
if (ch == EOF)
exit(exit_status);
p = bbp;
xp = bxp;
} else
++p;
argp = p;
break;
case '\'':
if (indouble)
goto addch;
insingle = !insingle;
break;
case '"':
if (insingle)
goto addch;
indouble = !indouble;
break;
case '\\':
/* Backslash escapes anything, is escaped by quotes. */
if (!insingle && !indouble && (ch = getchar()) == EOF)
err("backslash at EOF");
/* FALLTHROUGH */
default:
addch: if (p < ebp) {
*p++ = ch;
break;
}
/* If only one argument, not enough buffer space. */
if (bxp == xp)
err("insufficient space for argument");
/* Didn't hit argument limit, so if xflag object. */
if (xflag)
err("insufficient space for arguments");
*xp = NULL;
run(av);
xp = bxp;
cnt = ebp - argp;
bcopy(argp, bbp, cnt);
p = (argp = bbp) + cnt;
*p++ = ch;
break;
}
/* NOTREACHED */
}
void run(char **argv)
{
register char **p;
pid_t pid;
int noinvoke;
int status;
int pfd[2];
if (tflag) {
(void)fprintf(stderr, "%s", *argv);
for (p = argv + 1; *p; ++p)
(void)fprintf(stderr, " %s", *p);
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
}
if (pipe(pfd) < 0) err("pipe: %s", strerror(errno));
switch(pid = fork()) {
case -1:
err("fork: %s", strerror(errno));
case 0:
close(pfd[0]);
fcntl(pfd[1], F_SETFD, fcntl(pfd[1], F_GETFD) | FD_CLOEXEC);
execvp(argv[0], argv);
noinvoke = (errno == ENOENT) ? 127 : 126;
(void)fprintf(stderr,
"xargs: %s exec failed: %s.\n", argv[0], strerror(errno));
/* Modern way of returning noinvoke instead of a dirty vfork()
* trick: (kjb)
*/
write(pfd[1], &noinvoke, sizeof(noinvoke));
_exit(-1);
}
close(pfd[1]);
if (read(pfd[0], &noinvoke, sizeof(noinvoke)) < sizeof(noinvoke))
noinvoke = 0;
close(pfd[0]);
pid = waitpid(pid, &status, 0);
if (pid == -1)
err("waitpid: %s", strerror(errno));
/*
* If we couldn't invoke the utility or the utility didn't exit
* properly, quit with 127 or 126 respectively.
*/
if (noinvoke)
exit(noinvoke);
/*
* According to POSIX, we have to exit if the utility exits with
* a 255 status, or is interrupted by a signal. xargs is allowed
* to return any exit status between 1 and 125 in these cases, but
* we'll use 124 and 125, the same values used by GNU xargs.
*/
if (WIFEXITED(status)) {
if (WEXITSTATUS (status) == 255) {
fprintf (stderr, "xargs: %s exited with status 255\n",
argv[0]);
exit(124);
} else if (WEXITSTATUS (status) != 0) {
exit_status = 123;
}
} else if (WIFSTOPPED (status)) {
fprintf (stderr, "xargs: %s terminated by signal %d\n",
argv[0], WSTOPSIG (status));
exit(125);
} else if (WIFSIGNALED (status)) {
fprintf (stderr, "xargs: %s terminated by signal %d\n",
argv[0], WTERMSIG (status));
exit(125);
}
}
void usage(void)
{
(void)fprintf(stderr,
"usage: xargs [-t0] [[-x] -n number] [-s size] [utility [argument ...]]\n");
exit(1);
}
void err(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void)fprintf(stderr, "xargs: ");
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
exit(1);
/* NOTREACHED */
}

View file

@ -21,7 +21,7 @@ MAN= ash.1 at.1 \
term.1 termcap.1 tget.1 time.1 tr.1 true.1 \
truncate.1 tty.1 umount.1 uname.1 \
uud.1 uue.1 vol.1 whereis.1 which.1 \
write.1 xargs.1 yap.1 linkfarm.1 pkg_view.1
write.1 yap.1 linkfarm.1 pkg_view.1
MLINKS += ash.1 sh.1
MLINKS += ash.1 ..1

View file

@ -1,161 +0,0 @@
.\" Copyright (c) 1990 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" John B. Roll Jr. and the Institute of Electrical and Electronics
.\" Engineers, Inc.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" 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.
.\"
.\" @(#)xargs.1 5.5 (Berkeley) 6/27/91
.\"
.TH XARGS 1 "June 27, 1991"
.UC 7
.SH NAME
xargs \- construct argument list(s) and execute utility.
.SH SYNOPSIS
.B xargs
.RB [ \-ft0 ]
.RB [[ \-x ]
.B \-n
.IR number ]
.RB [ \-s
.IR size ]
.RI [ utility
.RI [ argument " ...]]"
.SH DESCRIPTION
The
.B xargs
utility reads space, tab, newline and end-of-file delimited arguments
from the standard input and executes the specified
.I utility
with them as arguments.
.PP
The utility and any arguments specified on the command line are given
to the
.I utility
upon each invocation, followed by some number of the arguments read
from standard input.
The
.I utility
is repeatedly executed until standard input is exhausted.
.PP
Spaces, tabs and newlines may be embedded in arguments using single (`` ' '')
or double (``"'') quotes or backslashes (``\e'').
Single quotes escape all non-single quote characters, excluding newlines,
up to the matching single quote.
Double quotes escape all non-double quote characters, excluding newlines,
up to the matching double quote.
Any single character, including newlines, may be escaped by a backslash.
.PP
The options are as follows:
.TP
.BI \-n " number"
Set the maximum number of arguments taken from standard input for each
invocation of the utility.
An invocation of
.I utility
will use less than
.I number
standard input arguments if the number of bytes accumulated (see the
.I \-s
option) exceeds the specified
.I size
or there are fewer than
.I number
arguments remaining for the last invocation of
.IR utility .
The current default value for
.I number
is 5000.
.TP
.BI \-s " size"
Set the maximum number of bytes for the command line length provided to
.IR utility .
The sum of the length of the utility name and the arguments passed to
.I utility
(including NULL terminators) will be less than or equal to this number.
The current default value for
.I size
is ARG_MAX - 2048.
.TP
.B \-t
Echo the command to be executed to standard error immediately before it
is executed.
.TP
.B \-x
Force
.B xargs
to terminate immediately if a command line containing
.I number
arguments will not fit in the specified (or default) command line length.
.TP
.B \-0
Read null-byte terminated pathnames from standard input as may have been
produced by the
.B \-print0
option of
.BR find (1).
This is a MINIX 3 specific extension to
.BR xargs .
.PP
If no
.I utility
is specified,
.BR echo (1)
is used.
.PP
Undefined behavior may occur if
.I utility
reads from the standard input.
.PP
.B Xargs
exits with an exit status of 0 if no error occurs.
If
.I utility
cannot be invoked, is terminated by a signal or terminates without
calling
.BR exit (2),
.B xargs
exits with an exit status of 127.
If
.I utility
exits with an exit status other than 0,
.B xargs
exits with that exit status.
Otherwise,
.B xargs
exits with an exit status of 1.
.SH "SEE ALSO"
.BR echo (1),
.BR find (1).
.SH STANDARDS
The
.B xargs
utility is expected to be POSIX 1003.2 compliant.

View file

@ -31,7 +31,7 @@ SUBDIR= \
uniq \
\
wc whatis who \
xinstall yes
xargs xinstall yes
.if !defined(__MINIX)
SUBDIR+= ../external/zlib/pigz/bin/pigz

7
usr.bin/xargs/Makefile Normal file
View file

@ -0,0 +1,7 @@
# $NetBSD: Makefile,v 1.5 2009/04/14 22:15:29 lukem Exp $
# @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= xargs
SRCS= xargs.c strnsubst.c
.include <bsd.prog.mk>

36
usr.bin/xargs/pathnames.h Normal file
View file

@ -0,0 +1,36 @@
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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.
*
* @(#)pathnames.h 8.1 (Berkeley) 6/6/93
*/
#define _PATH_ECHO "/bin/echo"

115
usr.bin/xargs/strnsubst.c Normal file
View file

@ -0,0 +1,115 @@
/* $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>
#ifndef lint
#if 0
__FBSDID("$FreeBSD: src/usr.bin/xargs/strnsubst.c,v 1.8 2005/12/30 23:22:50 jmallett Exp $");
#endif
__RCSID("$NetBSD: strnsubst.c,v 1.1 2007/04/18 15:56:07 christos Exp $");
#endif /* not lint */
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.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.
* 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;
s1 = *str;
if (s1 == NULL)
return;
/*
* If maxsize is 0 then set it to to the length of s1, because we have
* to duplicate s1. XXX we maybe should double-check whether the match
* appears in s1. If it doesn't, then we also have to set the length
* to the length of s1, to avoid modifying the argument. It may make
* sense to check if maxsize is <= strlen(s1), because in that case we
* want to return the unmodified string, too.
*/
if (maxsize == 0) {
match = NULL;
maxsize = strlen(s1) + 1;
}
s2 = calloc(maxsize, 1);
if (s2 == NULL)
err(1, "calloc");
if (replstr == NULL)
replstr = "";
if (match == NULL || replstr == NULL || maxsize == strlen(s1)) {
(void)strlcpy(s2, s1, maxsize);
goto done;
}
for (;;) {
this = strstr(s1, match);
if (this == NULL)
break;
if ((strlen(s2) + strlen(s1) + strlen(replstr) -
strlen(match) + 1) > maxsize) {
(void)strlcat(s2, s1, maxsize);
goto done;
}
(void)strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1);
(void)strcat(s2, replstr);
s1 = this + strlen(match);
}
(void)strcat(s2, s1);
done:
*str = s2;
return;
}
#ifdef TEST
#include <stdio.h>
int
main(void)
{
char *x, *y, *z, *za;
x = "{}%$";
strnsubst(&x, "%$", "{} enpury!", 255);
y = x;
strnsubst(&y, "}{}", "ybir", 255);
z = y;
strnsubst(&z, "{", "v ", 255);
za = z;
strnsubst(&z, NULL, za, 255);
if (strcmp(z, "v ybir enpury!") == 0)
(void)printf("strnsubst() seems to work!\n");
else
(void)printf("strnsubst() is broken.\n");
(void)printf("%s\n", z);
free(x);
free(y);
free(z);
free(za);
return 0;
}
#endif

387
usr.bin/xargs/xargs.1 Normal file
View file

@ -0,0 +1,387 @@
.\" $NetBSD: xargs.1,v 1.23 2012/10/13 14:18:17 njoly Exp $
.\"
.\" Copyright (c) 1990, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" John B. Roll Jr. and the Institute of Electrical and Electronics
.\" Engineers, Inc.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" 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.
.\"
.\" @(#)xargs.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD: src/usr.bin/xargs/xargs.1,v 1.40 2010/12/11 08:32:16 joel Exp $
.\" $xMach: xargs.1,v 1.2 2002/02/23 05:23:37 tim Exp $
.\"
.Dd December 21, 2010
.Dt XARGS 1
.Os
.Sh NAME
.Nm xargs
.Nd "construct argument list(s) and execute utility"
.Sh SYNOPSIS
.Nm
.Op Fl 0oprt
.Op Fl E Ar eofstr
.Oo
.Fl I Ar replstr
.Op Fl R Ar replacements
.Op Fl S Ar replsize
.Oc
.Op Fl J Ar replstr
.Op Fl L Ar number
.Oo
.Fl n Ar number
.Op Fl x
.Oc
.Op Fl P Ar maxprocs
.Op Fl s Ar size
.Op Ar utility Op Ar argument ...
.Sh DESCRIPTION
The
.Nm
utility reads space, tab, newline and end-of-file delimited strings
from the standard input and executes
.Ar utility
with the strings as
arguments.
.Pp
Any arguments specified on the command line are given to
.Ar utility
upon each invocation, followed by some number of the arguments read
from the standard input of
.Nm .
This is repeated until standard input is exhausted.
.Pp
Spaces, tabs and newlines may be embedded in arguments using single
(``\ '\ '')
or double (``"'') quotes or backslashes (``\e'').
Single quotes escape all non-single quote characters, excluding newlines,
up to the matching single quote.
Double quotes escape all non-double quote characters, excluding newlines,
up to the matching double quote.
Any single character, including newlines, may be escaped by a backslash.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl 0
Change
.Nm
to expect NUL
(``\e0'')
characters as separators, instead of spaces and newlines.
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 occurrences of
.Ar replstr
in up to
.Ar replacements
(or 5 if no
.Fl R
flag is specified) arguments to
.Ar utility
with the entire line of input.
The resulting arguments, after replacement is done, will not be allowed to grow
beyond
.Ar replsize
(or 255 if no
.Fl S
flag is specified)
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
.Ar replsize
bytes.
The size 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
will use the data read from standard input to replace the first occurrence of
.Ar replstr
instead of appending that data after all other arguments.
This option will not affect how many arguments will be read from input
.Pq Fl n ,
or the size of the command(s)
.Nm
will generate
.Pq Fl s .
The option just moves where those arguments will be placed in the command(s)
that are executed.
The
.Ar replstr
must show up as a distinct
.Ar argument
to
.Nm .
It will not be recognized if, for instance, it is in the middle of a
quoted string.
Furthermore, only the first occurrence of the
.Ar replstr
will be replaced.
For example, the following command will copy the list of files and
directories which start with an uppercase letter in the current
directory to
.Pa destdir :
.Pp
.Dl /bin/ls -1d [A-Z]* | xargs -J % cp -rp % destdir
.It Fl L Ar number
Call
.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
.Ar utility .
An invocation of
.Ar utility
will use less than
.Ar number
standard input arguments if the number of bytes accumulated (see the
.Fl s
option) exceeds the specified
.Ar size
or there are fewer than
.Ar number
arguments remaining for the last invocation of
.Ar utility .
The current default value for
.Ar number
is 5000.
.It Fl o
Reopen stdin as
.Pa /dev/tty
in the child process before executing the command.
This is useful if you want
.Nm
to run an interactive application.
.It Fl P Ar maxprocs
Parallel mode: run at most
.Ar maxprocs
invocations of
.Ar utility
at once.
.It Fl p
Echo each command to be executed and ask the user whether it should be
executed.
An affirmative response,
.Ql y
in the POSIX locale,
causes the command to be executed, any other response causes it to be
skipped.
No commands are executed if the process is not attached to a terminal.
.It Fl r
Compatibility with GNU
.Nm .
The GNU version of
.Nm
runs the
.Ar utility
argument at least once, even if
.Nm
input is empty, and it supports a
.Fl r
option to inhibit this behavior.
The
.Nx
version of
.Nm
does not run the
.Ar utility
argument on empty input, but it supports the
.Fl r
option for command-line compatibility with GNU
.Nm ;
but the
.Fl r
option does nothing in the
.Nx
version of
.Nm .
.It Fl R Ar replacements
Specify the maximum number of arguments that
.Fl I
will do replacement in.
If
.Ar replacements
is negative, the number of arguments in which to replace is unbounded.
.It Fl S Ar replsize
Specify the amount of space (in bytes) that
.Fl I
can use for replacements.
The default for
.Ar replsize
is 255.
.It Fl s Ar size
Set the maximum number of bytes for the command line length provided to
.Ar utility .
The sum of the length of the utility name, the arguments passed to
.Ar utility
(including
.Dv NULL
terminators) and the current environment will be less than or equal to
this number.
The current default value for
.Ar size
is
.Dv ARG_MAX
- 4096.
.It Fl t
Echo the command to be executed to standard error immediately before it
is executed.
.It Fl x
Force
.Nm
to terminate immediately if a command line containing
.Ar number
arguments will not fit in the specified (or default) command line length.
.El
.Pp
If
.Ar utility
is omitted,
.Xr echo 1
is used.
.Pp
Undefined behavior may occur if
.Ar utility
reads from the standard input.
.Pp
The
.Nm
utility exits immediately (without processing any further input) if a
command line cannot be assembled,
.Ar utility
cannot be invoked, an invocation of
.Ar utility
is terminated by a signal,
or an invocation of
.Ar utility
exits with a value of 255.
.Sh FILES
.Bl -tag -width /dev/tty -compact
.It Pa /dev/tty
used to read responses in prompt mode
.El
.Sh EXIT STATUS
.Nm
exits with one of the following values:
.Bl -tag -width Ds -compact
.It 0
All invocations of
.Ar utility
returned a zero exit status.
.It 123
One or more invocations of
.Ar utility
returned a nonzero exit status.
.It 124
The
.Ar utility
exited with a 255 exit status.
.It 125
The
.Ar utility
was killed or stopped by a signal.
.It 126
The
.Ar utility
was found but could not be invoked.
.It 127
The
.Ar utility
could not be found.
.It 1
Some other error occurred.
.El
.Sh SEE ALSO
.Xr echo 1 ,
.Xr find 1 ,
.Xr execvp 3
.Sh STANDARDS
The
.Nm
utility is expected to be
.St -p1003.2
compliant.
The
.Fl J , o , P , R ,
and
.Fl S
options are non-standard
.Fx
extensions which may not be available on other operating systems.
.Sh HISTORY
The
.Nm
utility appeared in PWB UNIX 1.0.
It made its first BSD appearance in the 4.3 Reno release.
.Pp
The meaning of 123, 124, and 125 exit values and the
.Fl 0
option were taken from GNU xargs.
.Sh BUGS
If
.Ar utility
attempts to invoke another command such that the number of arguments or the
size of the environment is increased, it risks
.Xr execvp 3
failing with
.Er E2BIG .
.Pp
The
.Nm
utility does not take multibyte characters into account when performing
string comparisons for the
.Fl I
and
.Fl J
options, which may lead to incorrect results in some locales.

652
usr.bin/xargs/xargs.c Normal file
View file

@ -0,0 +1,652 @@
/* $NetBSD: xargs.c,v 1.20 2010/12/17 11:32:57 plunky Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* John B. Roll Jr.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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 $
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1990, 1993\
The Regents of the University of California. All rights reserved.");
#if 0
static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
__FBSDID("$FreeBSD: src/usr.bin/xargs/xargs.c,v 1.62 2006/01/01 22:59:54 jmallett Exp $");
#endif
__RCSID("$NetBSD: xargs.c,v 1.20 2010/12/17 11:32:57 plunky Exp $");
#endif /* not lint */
#include <sys/param.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <langinfo.h>
#include <locale.h>
#include <paths.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include "pathnames.h"
static void parse_input(int, char *[]);
static void prerun(int, char *[]);
static int prompt(void);
static void run(char **);
static void usage(void) __dead;
void strnsubst(char **, const char *, const char *, size_t);
static void waitchildren(const char *, int);
static char echo[] = _PATH_ECHO;
static char **av, **bxp, **ep, **endxp, **xp;
static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
static const char *eofstr;
static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
static int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag;
static int curprocs, maxprocs;
static volatile int childerr;
extern char **environ;
int
main(int argc, char *argv[])
{
long arg_max;
int ch, Jflag, nargs, nflag, nline;
size_t linelen;
char *endptr;
setprogname(argv[0]);
inpline = replstr = NULL;
ep = environ;
eofstr = "";
Jflag = nflag = 0;
(void)setlocale(LC_ALL, "");
/*
* SUSv3 says of the exec family of functions:
* The number of bytes available for the new process'
* combined argument and environment lists is {ARG_MAX}. It
* is implementation-defined whether null terminators,
* pointers, and/or any alignment bytes are included in this
* total.
*
* SUSv3 says of xargs:
* ... the combined argument and environment lists ...
* shall not exceed {ARG_MAX}-2048.
*
* To be conservative, we use ARG_MAX - 4K, and we do include
* nul terminators and pointers in the calculation.
*
* Given that the smallest argument is 2 bytes in length, this
* means that the number of arguments is limited to:
*
* (ARG_MAX - 4K - LENGTH(env + utility + arguments)) / 2.
*
* We arbitrarily limit the number of arguments to 5000. This is
* allowed by POSIX.2 as long as the resulting minimum exec line is
* at least LINE_MAX. Realloc'ing as necessary is possible, but
* probably not worthwhile.
*/
nargs = 5000;
if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
errx(1, "sysconf(_SC_ARG_MAX) failed");
nline = arg_max - 4 * 1024;
while (*ep != NULL) {
/* 1 byte for each '\0' */
nline -= strlen(*ep++) + 1 + sizeof(*ep);
}
maxprocs = 1;
while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1)
switch (ch) {
case 'E':
eofstr = optarg;
break;
case 'I':
Jflag = 0;
Iflag = 1;
Lflag = 1;
replstr = optarg;
break;
case 'J':
Iflag = 0;
Jflag = 1;
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 'o':
oflag = 1;
break;
case 'P':
if ((maxprocs = atoi(optarg)) <= 0)
errx(1, "max. processes must be >0");
break;
case 'p':
pflag = 1;
break;
case 'R':
Rflag = strtol(optarg, &endptr, 10);
if (*endptr != '\0')
errx(1, "replacements must be a number");
break;
case 'r':
/* GNU compatibility */
break;
case 'S':
Sflag = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
errx(1, "replsize must be a number");
break;
case 's':
nline = atoi(optarg);
break;
case 't':
tflag = 1;
break;
case 'x':
xflag = 1;
break;
case '0':
zflag = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (!Iflag && Rflag)
usage();
if (!Iflag && Sflag)
usage();
if (Iflag && !Rflag)
Rflag = 5;
if (Iflag && !Sflag)
Sflag = 255;
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.
*/
linelen = 1 + argc + nargs + 1;
if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
errx(1, "malloc failed");
/*
* Use the user's name for the utility as argv[0], just like the
* shell. Echo is the default. Set up pointers for the user's
* arguments.
*/
if (*argv == NULL)
cnt = strlen(*bxp++ = echo);
else {
do {
if (Jflag && strcmp(*argv, replstr) == 0) {
char **avj;
jfound = 1;
argv++;
for (avj = argv; *avj; avj++)
cnt += strlen(*avj) + 1;
break;
}
cnt += strlen(*bxp++ = *argv) + 1;
} while (*++argv != NULL);
}
/*
* Set up begin/end/traversing pointers into the array. The -n
* count doesn't include the trailing NULL pointer, so the malloc
* added in an extra slot.
*/
endxp = (xp = bxp) + nargs;
/*
* Allocate buffer space for the arguments read from stdin and the
* trailing NULL. Buffer space is defined as the default or specified
* space, minus the length of the utility name and arguments. Set up
* begin/end/traversing pointers into the array. The -s count does
* include the trailing NULL, so the malloc didn't add in an extra
* slot.
*/
nline -= cnt;
if (nline <= 0)
errx(1, "insufficient space for command");
if ((bbp = malloc((size_t)(nline + 1))) == NULL)
errx(1, "malloc failed");
ebp = (argp = p = bbp) + nline - 1;
for (;;)
parse_input(argc, argv);
}
static void
parse_input(int argc, char *argv[])
{
int ch, foundeof;
char **avj;
foundeof = 0;
switch (ch = getchar()) {
case EOF:
/* No arguments since last exec. */
if (p == bbp) {
waitchildren(*argv, 1);
exit(rval);
}
goto arg1;
case ' ':
case '\t':
/* Quotes escape tabs and spaces. */
if (insingle || indouble || zflag)
goto addch;
goto arg2;
case '\0':
if (zflag) {
/*
* Increment 'count', so that nulls will be treated
* as end-of-line, as well as end-of-argument. This
* is needed so -0 works properly with -I and -L.
*/
count++;
goto arg2;
}
goto addch;
case '\n':
if (zflag)
goto addch;
count++; /* Indicate end-of-line (used by -L) */
/* Quotes do not escape newlines. */
arg1: if (insingle || indouble)
errx(1, "unterminated quote");
arg2:
foundeof = *eofstr != '\0' &&
strncmp(argp, eofstr, (size_t)(p - argp)) == 0;
/* Do not make empty args unless they are quoted */
if ((argp != p || wasquoted) && !foundeof) {
*p++ = '\0';
*xp++ = argp;
if (Iflag) {
size_t curlen;
if (inpline == NULL)
curlen = 0;
else {
/*
* If this string is not zero
* length, append a space for
* separation before the next
* argument.
*/
if ((curlen = strlen(inpline)) != 0)
(void)strcat(inpline, " ");
}
curlen++;
/*
* Allocate enough to hold what we will
* be holding in a second, and to append
* a space next time through, if we have
* to.
*/
inpline = realloc(inpline, curlen + 2 +
strlen(argp));
if (inpline == NULL)
errx(1, "realloc failed");
if (curlen == 1)
(void)strcpy(inpline, argp);
else
(void)strcat(inpline, argp);
}
}
/*
* If max'd out on args or buffer, or reached EOF,
* run the command. If xflag and max'd out on buffer
* but not on args, object. Having reached the limit
* of input lines, as specified by -L is the same as
* maxing out on arguments.
*/
if (xp == endxp || p > ebp || ch == EOF ||
(Lflag <= count && xflag) || foundeof) {
if (xflag && xp != endxp && p > ebp)
errx(1, "insufficient space for arguments");
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
if (ch == EOF || foundeof) {
waitchildren(*argv, 1);
exit(rval);
}
p = bbp;
xp = bxp;
count = 0;
}
argp = p;
wasquoted = 0;
break;
case '\'':
if (indouble || zflag)
goto addch;
insingle = !insingle;
wasquoted = 1;
break;
case '"':
if (insingle || zflag)
goto addch;
indouble = !indouble;
wasquoted = 1;
break;
case '\\':
if (zflag)
goto addch;
/* Backslash escapes anything, is escaped by quotes. */
if (!insingle && !indouble && (ch = getchar()) == EOF)
errx(1, "backslash at EOF");
/* FALLTHROUGH */
default:
addch: if (p < ebp) {
*p++ = ch;
break;
}
/* If only one argument, not enough buffer space. */
if (bxp == xp)
errx(1, "insufficient space for argument");
/* Didn't hit argument limit, so if xflag object. */
if (xflag)
errx(1, "insufficient space for arguments");
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
xp = bxp;
cnt = ebp - argp;
(void)memcpy(bbp, argp, (size_t)cnt);
p = (argp = bbp) + cnt;
*p++ = ch;
break;
}
}
/*
* Do things necessary before run()'ing, such as -I substitution,
* and then call run().
*/
static void
prerun(int argc, char *argv[])
{
char **tmp, **tmp2, **avj;
int repls;
repls = Rflag;
if (argc == 0 || repls == 0) {
*xp = NULL;
run(argv);
return;
}
avj = argv;
/*
* Allocate memory to hold the argument list, and
* a NULL at the tail.
*/
tmp = malloc((argc + 1) * sizeof(char**));
if (tmp == NULL)
errx(1, "malloc failed");
tmp2 = tmp;
/*
* Save the first argument and iterate over it, we
* cannot do strnsubst() to it.
*/
if ((*tmp++ = strdup(*avj++)) == NULL)
errx(1, "strdup failed");
/*
* For each argument to utility, if we have not used up
* the number of replacements we are allowed to do, and
* if the argument contains at least one occurrence of
* replstr, call strnsubst(), else just save the string.
* Iterations over elements of avj and tmp are done
* where appropriate.
*/
while (--argc) {
*tmp = *avj++;
if (repls && strstr(*tmp, replstr) != NULL) {
strnsubst(tmp++, replstr, inpline, (size_t)Sflag);
if (repls > 0)
repls--;
} else {
if ((*tmp = strdup(*tmp)) == NULL)
errx(1, "strdup failed");
tmp++;
}
}
/*
* Run it.
*/
*tmp = NULL;
run(tmp2);
/*
* Walk from the tail to the head, free along the way.
*/
for (; tmp2 != tmp; tmp--)
free(*tmp);
/*
* Now free the list itself.
*/
free(tmp2);
/*
* Free the input line buffer, if we have one.
*/
if (inpline != NULL) {
free(inpline);
inpline = NULL;
}
}
static void
run(char **argv)
{
int fd;
char **avec;
/*
* If the user wants to be notified of each command before it is
* executed, notify them. If they want the notification to be
* followed by a prompt, then prompt them.
*/
if (tflag || pflag) {
(void)fprintf(stderr, "%s", *argv);
for (avec = argv + 1; *avec != NULL; ++avec)
(void)fprintf(stderr, " %s", *avec);
/*
* If the user has asked to be prompted, do so.
*/
if (pflag)
/*
* If they asked not to exec, return without execution
* but if they asked to, go to the execution. If we
* could not open their tty, break the switch and drop
* back to -t behaviour.
*/
switch (prompt()) {
case 0:
return;
case 1:
goto exec;
case 2:
break;
}
(void)fprintf(stderr, "\n");
(void)fflush(stderr);
}
exec:
childerr = 0;
switch (vfork()) {
case -1:
err(1, "vfork");
/*NOTREACHED*/
case 0:
if (oflag) {
if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
err(1, "can't open /dev/tty");
} else {
fd = open(_PATH_DEVNULL, O_RDONLY);
}
if (fd > STDIN_FILENO) {
if (dup2(fd, STDIN_FILENO) != 0)
err(1, "can't dup2 to stdin");
(void)close(fd);
}
(void)execvp(argv[0], argv);
childerr = errno;
_exit(1);
}
curprocs++;
waitchildren(*argv, 0);
}
static void
waitchildren(const char *name, int waitall)
{
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
WNOHANG : 0)) > 0) {
curprocs--;
/* If we couldn't invoke the utility, exit. */
if (childerr != 0) {
errno = childerr;
err(errno == ENOENT ? 127 : 126, "%s", name);
}
/*
* According to POSIX, we have to exit if the utility exits
* with a 255 status, or is interrupted by a signal. xargs
* is allowed to return any exit status between 1 and 125
* in these cases, but we'll use 124 and 125, the same
* values used by GNU xargs.
*/
if (WIFEXITED(status)) {
if (WEXITSTATUS (status) == 255) {
warnx ("%s exited with status 255", name);
exit(124);
} else if (WEXITSTATUS (status) != 0) {
rval = 123;
}
} else if (WIFSIGNALED (status)) {
if (WTERMSIG(status) < NSIG) {
warnx("%s terminated by SIG%s", name,
sys_signame[WTERMSIG(status)]);
} else {
warnx("%s terminated by signal %d", name,
WTERMSIG(status));
}
exit(125);
}
}
if (pid == -1 && errno != ECHILD)
err(1, "waitpid");
}
/*
* Prompt the user about running a command.
*/
static int
prompt(void)
{
regex_t cre;
size_t rsize;
int match;
char *response;
FILE *ttyfp;
if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
return (2); /* Indicate that the TTY failed to open. */
(void)fprintf(stderr, "?...");
(void)fflush(stderr);
if ((response = fgetln(ttyfp, &rsize)) == NULL ||
regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) {
(void)fclose(ttyfp);
return (0);
}
response[rsize - 1] = '\0';
match = regexec(&cre, response, 0, NULL, 0);
(void)fclose(ttyfp);
regfree(&cre);
return (match == 0);
}
static void
usage(void)
{
(void)fprintf(stderr,
"Usage: %s [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n"
" [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n"
" [-s size] [utility [argument ...]]\n", getprogname());
exit(1);
}