summaryrefslogtreecommitdiff
path: root/usr.bin/xargs
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2003-06-12 01:09:24 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2003-06-12 01:09:24 +0000
commit440a72dbbe9f75bb9d9a404868834b3964f7a2e1 (patch)
treedc25264bf27ded21ead2217e356148afb9cac98f /usr.bin/xargs
parent24617c732408bf6ac398ff94a455bb1d21d18a46 (diff)
Sync with FreeBSD's xargs plus some fixes by me. This makes us
POSIX-compliant and adds some useful extensions. Most of the work in FreeBSD was done by jmallett.
Diffstat (limited to 'usr.bin/xargs')
-rw-r--r--usr.bin/xargs/Makefile3
-rw-r--r--usr.bin/xargs/strnsubst.c100
-rw-r--r--usr.bin/xargs/xargs.1188
-rw-r--r--usr.bin/xargs/xargs.c598
4 files changed, 707 insertions, 182 deletions
diff --git a/usr.bin/xargs/Makefile b/usr.bin/xargs/Makefile
index 0118885f85b..a9bf6c6713c 100644
--- a/usr.bin/xargs/Makefile
+++ b/usr.bin/xargs/Makefile
@@ -1,5 +1,6 @@
-# $OpenBSD: Makefile,v 1.3 1997/09/21 11:52:09 deraadt Exp $
+# $OpenBSD: Makefile,v 1.4 2003/06/12 01:09:22 millert Exp $
PROG= xargs
+SRCS= xargs.c strnsubst.c
.include <bsd.prog.mk>
diff --git a/usr.bin/xargs/strnsubst.c b/usr.bin/xargs/strnsubst.c
new file mode 100644
index 00000000000..01405717ba4
--- /dev/null
+++ b/usr.bin/xargs/strnsubst.c
@@ -0,0 +1,100 @@
+/* $OpenBSD: strnsubst.c,v 1.1 2003/06/12 01:09:23 millert Exp $ */
+/* $FreeBSD: strnsubst.c,v 1.6 2002/06/22 12:58:42 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.
+ */
+
+#ifndef lint
+static const char rcsid[] = "$OpenBSD: strnsubst.c,v 1.1 2003/06/12 01:09:23 millert Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.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.
+ * 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, n;
+
+ if ((s1 = *str) == NULL)
+ return;
+ if ((s2 = malloc(maxsize)) == NULL)
+ err(1, NULL);
+
+ 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 + s2len + strlen(this + matchlen) >= maxsize)
+ break; /* out of room */
+ s2len += n;
+ s1 = this + matchlen;
+ }
+ strlcpy(s2 + s2len, s1, maxsize - s2len);
+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)
+ printf("strnsubst() seems to work!\n");
+ else
+ printf("strnsubst() is broken.\n");
+ printf("%s\n", z);
+ free(x);
+ free(y);
+ free(z);
+ free(za);
+ return 0;
+}
+#endif
diff --git a/usr.bin/xargs/xargs.1 b/usr.bin/xargs/xargs.1
index 45e52816e76..54cd46901c5 100644
--- a/usr.bin/xargs/xargs.1
+++ b/usr.bin/xargs/xargs.1
@@ -1,5 +1,5 @@
-.\" $OpenBSD: xargs.1,v 1.11 2003/06/03 02:56:24 millert Exp $
-.\" $NetBSD: xargs.1,v 1.6 1994/11/14 06:51:40 jtc Exp $
+.\" $OpenBSD: xargs.1,v 1.12 2003/06/12 01:09:23 millert Exp $
+.\" $FreeBSD: xargs.1,v 1.30 2003/05/21 21:07:28 ru Exp $$
.\"
.\" Copyright (c) 1990, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -34,32 +34,39 @@
.\"
.\" @(#)xargs.1 8.1 (Berkeley) 6/6/93
.\"
-.Dd June 6, 1993
+.Dd May 7, 2001
.Dt XARGS 1
.Os
.Sh NAME
.Nm xargs
.Nd "construct argument list(s) and execute utility"
.Sh SYNOPSIS
-.Nm xargs
-.Op Fl 0
-.Op Fl t
-.Oo Op Fl x
+.Nm
+.Op Fl 0opt
+.Op Fl E Ar eofstr
+.Oo
+.Fl I Ar replstr
+.Op Fl R Ar replacements
+.Oc
+.Op Fl J Ar replstr
+.Op Fl L Ar number
+.Oo
.Fl n Ar number
+.Op Fl x
.Oc
+.Op Fl P Ar maxjobs
.Op Fl s Ar size
-.Op Ar utility Op Ar arguments ...
+.Op Ar utility Op Ar argument ...
.Sh DESCRIPTION
The
.Nm
-utility reads space, tab, newline, and end-of-file delimited arguments
+utility reads space, tab, newline, and end-of-file delimited strings
from the standard input and executes the specified
.Ar utility
-with them as
+with the strings as
arguments.
.Pp
-The utility and any arguments specified on the command line are given
-to the
+Any arguments specified on the command line are given to the
.Ar utility
upon each invocation, followed by some number of the arguments read
from standard input.
@@ -82,7 +89,7 @@ Any single character, including newlines, may be escaped by a backslash.
The options are as follows:
.Bl -tag -width Ds
.It Fl 0
-Changes
+Change
.Nm
to expect NUL
.Pq Ql \e0
@@ -91,9 +98,84 @@ 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 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
+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 effect 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 xargs .
+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
+of lines read.
+If EOF is reached and fewer than
+.Ar number
+lines have been read 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.
+invocation of
+.Ar utility .
An invocation of
.Ar utility
will use less than
@@ -109,12 +191,44 @@ arguments remaining for the last invocation of
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 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 size
-Set the maximum number of bytes for the command-line length provided to
+Set the maximum number of bytes for the command line length provided to
.Ar utility .
-The sum of the length of the utility name and the arguments passed to
+The sum of the length of the utility name, the arguments passed to
.Ar utility
-(including null terminators) will be less than or equal to this number.
+(including
+.Dv NUL
+terminators) and the current environment will be less than or equal to
+this number.
The current default value for
.Ar size
is
@@ -128,7 +242,7 @@ Force
.Nm
to terminate immediately if a command line containing
.Ar number
-arguments will not fit in the specified (or default) command-line length.
+arguments will not fit in the specified (or default) command line length.
.El
.Pp
If no
@@ -146,9 +260,13 @@ The
utility exits immediately (without processing any further input) if a
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
+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 DIAGNOSTICS
.Nm
exits with one of the following values:
.Pp
@@ -172,7 +290,7 @@ was killed or stopped by a signal.
.It 126
The
.Ar utility
-was found but could not be invoked.
+was found but could not be executed.
.It 127
The
.Ar utility
@@ -182,13 +300,33 @@ Some other error occurred.
.El
.Sh SEE ALSO
.Xr echo 1 ,
-.Xr find 1
+.Xr find 1 ,
+.Xr execvp 3
.Sh STANDARDS
The
.Nm
utility is expected to be
.St -p1003.2
compliant.
-.Sh HISTORY
-The meaning of 123, 124, and 125 exit values were taken from GNU
+The
+.Fl J , o , P
+and
+.Fl R
+options are non-standard
+extensions which may not be available on other operating systems.
+.Pp
+The meanings of the 123, 124, and 125 exit values were taken from
+.Tn GNU
.Nm xargs .
+.Sh HISTORY
+The
+.Nm
+command appeared in PWB UNIX.
+.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 .
diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c
index 8db095a67b4..ff2272446dc 100644
--- a/usr.bin/xargs/xargs.c
+++ b/usr.bin/xargs/xargs.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: xargs.c,v 1.13 2003/06/10 22:20:54 deraadt Exp $ */
-/* $NetBSD: xargs.c,v 1.7 1994/11/14 06:51:41 jtc Exp $ */
+/* $OpenBSD: xargs.c,v 1.14 2003/06/12 01:09:23 millert Exp $ */
+/* $FreeBSD: xargs.c,v 1.51 2003/05/03 19:09:11 obrien Exp $ */
/*-
* Copyright (c) 1990, 1993
@@ -31,50 +31,76 @@
* 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
-static char copyright[] =
+static const char copyright[] =
"@(#) Copyright (c) 1990, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
#if 0
-static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
+static const char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
+#else
+static const char rcsid[] = "$OpenBSD: xargs.c,v 1.14 2003/06/12 01:09:23 millert Exp $";
#endif
-static char rcsid[] = "$OpenBSD: xargs.c,v 1.13 2003/06/10 22:20:54 deraadt Exp $";
#endif /* not lint */
-#include <sys/types.h>
+#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 <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <limits.h>
-#include <locale.h>
-#include <signal.h>
-#include <err.h>
+
#include "pathnames.h"
-int tflag, rval;
-int zflag;
+static void parse_input(int, char *[]);
+static void prerun(int, char *[]);
+static int prompt(void);
+static void run(char **);
+static void usage(void);
+void strnsubst(char **, const char *, const char *, size_t);
+static void waitchildren(const char *, int);
+
+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, wasquoted, xflag;
+static int curprocs, maxprocs;
+static size_t inpsize;
+
+static volatile int childerr;
-void run(char **);
-void usage(void);
+extern char **environ;
int
main(int argc, char *argv[])
{
- int ch;
- char *p, *bbp, *ebp, **bxp, **exp, **xp;
- int cnt, indouble, insingle, nargs, nflag, nline, xflag;
- char **av, *argp;
- int arg_max;
+ long arg_max;
+ int ch, Jflag, nargs, nflag, nline;
+ size_t linelen;
+ char *endptr;
- setlocale(LC_ALL, "");
+ inpline = replstr = NULL;
+ ep = environ;
+ eofstr = "";
+ Jflag = nflag = 0;
+
+ (void)setlocale(LC_MESSAGES, "");
/*
* POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
@@ -93,14 +119,50 @@ main(int argc, char *argv[])
if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
errx(1, "sysconf(_SC_ARG_MAX) failed");
nline = arg_max - 4 * 1024;
- nflag = xflag = 0;
- while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
+ 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:P:pR:s:tx")) != -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 's':
nline = atoi(optarg);
break;
@@ -120,13 +182,24 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (!Iflag && Rflag)
+ usage();
+ if (Iflag && !Rflag)
+ Rflag = 5;
+ 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 **))))
+ linelen = 1 + argc + nargs + 1;
+ if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
err(1, NULL);
/*
@@ -134,13 +207,20 @@ main(int argc, char *argv[])
* shell. Echo is the default. Set up pointers for the user's
* arguments.
*/
- if (!*argv)
+ if (*argv == NULL)
cnt = strlen(*bxp++ = _PATH_ECHO);
else {
- cnt = 0;
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);
+ } while (*++argv != NULL);
}
/*
@@ -148,7 +228,7 @@ main(int argc, char *argv[])
* count doesn't include the trailing NULL pointer, so the malloc
* added in an extra slot.
*/
- exp = (xp = bxp) + nargs;
+ endxp = (xp = bxp) + nargs;
/*
* Allocate buffer space for the arguments read from stdin and the
@@ -162,176 +242,382 @@ main(int argc, char *argv[])
if (nline <= 0)
errx(1, "insufficient space for command");
- if (!(bbp = malloc((u_int)nline + 1)))
- err(1, NULL);
+ if ((bbp = malloc((size_t)(nline + 1))) == NULL)
+ errx(1, NULL);
ebp = (argp = p = bbp) + nline - 1;
+ for (;;)
+ parse_input(argc, argv);
+}
- for (insingle = indouble = 0;;)
- switch(ch = getchar()) {
- case EOF:
- /* No arguments since last exec. */
- if (p == bbp)
- exit(rval);
+static void
+parse_input(int argc, char *argv[])
+{
+ int ch, foundeof;
+ char **avj;
- /* Nothing since end of last argument. */
- if (argp == p) {
- *xp = NULL;
- run(av);
- exit(rval);
- }
- goto arg1;
- case ' ':
- case '\t':
- /* Quotes escape tabs and spaces. */
- if (insingle || indouble || zflag)
- goto addch;
+ 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)
goto arg2;
- case '\0':
- if (zflag)
- goto arg2;
+ goto addch;
+ case '\n':
+ count++;
+ if (zflag)
goto addch;
- case '\n':
- if (zflag)
- goto addch;
-
- /* Empty lines are skipped. */
- if (argp == p)
- continue;
- /* Quotes do not escape newlines. */
-arg1: if (insingle || indouble)
- errx(1, "unterminated quote");
+ /* Quotes do not escape newlines. */
+arg1: if (insingle || indouble)
+ errx(1, "unterminated quote");
+arg2:
+ foundeof = *eofstr != '\0' &&
+ strcmp(argp, eofstr) == 0;
-arg2: *p = '\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 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)
- errx(1, "insufficient space for arguments");
- *xp = NULL;
- run(av);
- if (ch == EOF)
- exit(rval);
- p = bbp;
- xp = bxp;
- } else
- ++p;
- argp = p;
- break;
- case '\'':
- if (indouble || zflag)
- goto addch;
- insingle = !insingle;
- break;
- case '"':
- if (insingle || zflag)
- goto addch;
- indouble = !indouble;
- 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 (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)))
+ strlcat(inpline, " ", inpsize);
+ }
+ 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.
+ */
+ inpsize = curlen + 2 + strlen(argp);
+ inpline = realloc(inpline, inpsize);
+ if (inpline == NULL)
+ errx(1, "realloc failed");
+ if (curlen == 1)
+ strlcpy(inpline, argp, inpsize);
+ else
+ strlcat(inpline, argp, inpsize);
}
+ }
- /* 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)
+ /*
+ * 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");
-
- *xp = NULL;
- run(av);
+ 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;
- cnt = ebp - argp;
- bcopy(argp, bbp, cnt);
- p = (argp = bbp) + cnt;
+ 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;
}
- /* NOTREACHED */
+
+ /* 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;
+ 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, NULL);
+ tmp2 = tmp;
+
+ /*
+ * Save the first argument and iterate over it, we
+ * cannot do strnsubst() to it.
+ */
+ if ((*tmp++ = strdup(*avj++)) == NULL)
+ errx(1, NULL);
+
+ /*
+ * 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)255);
+ if (repls > 0)
+ repls--;
+ } else {
+ if ((*tmp = strdup(*tmp)) == NULL)
+ errx(1, NULL);
+ 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;
+ }
}
-void
+static void
run(char **argv)
{
- volatile int noinvoke;
- char **p;
pid_t pid;
- int status;
+ int fd;
+ char **avec;
- if (tflag) {
+ /*
+ * 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 (p = argv + 1; *p; ++p)
- (void)fprintf(stderr, " %s", *p);
+ 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);
}
- noinvoke = 0;
+exec:
+ childerr = 0;
switch(pid = vfork()) {
case -1:
err(1, "vfork");
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");
+ close(STDIN_FILENO);
+ }
execvp(argv[0], argv);
- noinvoke = (errno == ENOENT) ? 127 : 126;
- warn("%s", argv[0]);
+ childerr = errno;
_exit(1);
}
- pid = waitpid(pid, &status, 0);
- if (pid == -1)
- err(1, "waitpid");
+ curprocs++;
+ waitchildren(*argv, 0);
+}
- /*
- * If we couldn't invoke the utility or the utility didn't exit
- * properly, quit with 127 or 126 respectively.
- */
- if (noinvoke)
- exit(noinvoke);
+static void
+waitchildren(const char *name, int waitall)
+{
+ pid_t pid;
+ int status;
- /*
- * 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", argv[0]);
- exit(124);
- } else if (WEXITSTATUS(status) != 0) {
- rval = 123;
+ 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);
}
- } else if (WIFSIGNALED(status)) {
- if (WTERMSIG(status) != SIGPIPE) {
- if (WTERMSIG(status) < NSIG)
- warnx("%s terminated by SIG%s", argv[0],
- sys_signame[WTERMSIG(status)]);
- else
- warnx("%s terminated by signal %d", argv[0],
- WTERMSIG(status));
+ /*
+ * According to POSIX, we have to exit if the utility exits
+ * with a 255 status, or is interrupted by a signal.
+ * We are 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) != SIGPIPE) {
+ 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);
}
- 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);
+ }
+ match = regexec(&cre, response, 0, NULL, 0);
+ (void)fclose(ttyfp);
+ regfree(&cre);
+ return (match == 0);
}
-void
+static void
usage(void)
{
fprintf(stderr,
- "usage: xargs [-0] [-t] [-n number [-x]] [-s size] "
- "[utility [argument ...]]\n");
+"usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
+" [-L number] [-n number [-x]] [-P maxprocs] [-s size]\n"
+" [utility [argument ...]]\n");
exit(1);
}