]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing usr.bin/xargs 87/787/3
authorLionel Sambuc <lionel@minix3.org>
Fri, 23 Aug 2013 22:59:39 +0000 (00:59 +0200)
committerLionel Sambuc <lionel@minix3.org>
Sun, 25 Aug 2013 16:51:36 +0000 (18:51 +0200)
Change-Id: I6a8ce6a8eee31942abb4db797672da7d00dfa43d

commands/Makefile
commands/xargs/Makefile [deleted file]
commands/xargs/xargs.c [deleted file]
man/man1/Makefile
man/man1/xargs.1 [deleted file]
usr.bin/Makefile
usr.bin/xargs/Makefile [new file with mode: 0644]
usr.bin/xargs/pathnames.h [new file with mode: 0644]
usr.bin/xargs/strnsubst.c [new file with mode: 0644]
usr.bin/xargs/xargs.1 [new file with mode: 0644]
usr.bin/xargs/xargs.c [new file with mode: 0644]

index d8362e4f7b37888a13660f15654150b2fabd500e..cfd3960b24a2c97583ad9b78dde3fb0481f5b64e 100644 (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"
diff --git a/commands/xargs/Makefile b/commands/xargs/Makefile
deleted file mode 100644 (file)
index 8578e0f..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  xargs
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/xargs/xargs.c b/commands/xargs/xargs.c
deleted file mode 100644 (file)
index 645ef6c..0000000
+++ /dev/null
@@ -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 */
-}
index 206158ae04fefe9ee35dcd96c151112502e90ce1..e109aa106341ccec7ee0b597ede8cd9cd00b846e 100644 (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
diff --git a/man/man1/xargs.1 b/man/man1/xargs.1
deleted file mode 100644 (file)
index 7e69847..0000000
+++ /dev/null
@@ -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.
index 9eb86975e6b48d2fded92b5969656985dd7812c1..5b7e0dad3ab95c0d8b2d4cda597001bfabe24789 100644 (file)
@@ -31,7 +31,7 @@ SUBDIR= \
        uniq \
        \
        wc whatis who \
-       xinstall yes
+       xargs xinstall yes
 
 .if !defined(__MINIX)
 SUBDIR+= ../external/zlib/pigz/bin/pigz
diff --git a/usr.bin/xargs/Makefile b/usr.bin/xargs/Makefile
new file mode 100644 (file)
index 0000000..46b6fd3
--- /dev/null
@@ -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>
diff --git a/usr.bin/xargs/pathnames.h b/usr.bin/xargs/pathnames.h
new file mode 100644 (file)
index 0000000..3f05b3d
--- /dev/null
@@ -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"
diff --git a/usr.bin/xargs/strnsubst.c b/usr.bin/xargs/strnsubst.c
new file mode 100644 (file)
index 0000000..0953f49
--- /dev/null
@@ -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
diff --git a/usr.bin/xargs/xargs.1 b/usr.bin/xargs/xargs.1
new file mode 100644 (file)
index 0000000..d693838
--- /dev/null
@@ -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.
diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c
new file mode 100644 (file)
index 0000000..5c89377
--- /dev/null
@@ -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);
+}