]> git.saurik.com Git - apple/shell_cmds.git/commitdiff
shell_cmds-216.40.4.tar.gz macos-1101 v216.40.4
authorApple <opensource@apple.com>
Tue, 4 Aug 2020 19:31:37 +0000 (19:31 +0000)
committerApple <opensource@apple.com>
Tue, 4 Aug 2020 19:31:37 +0000 (19:31 +0000)
22 files changed:
.upstream_base_commits [new file with mode: 0644]
echo/echo.c
kill/kill.c
printf/printf.1
printf/printf.c
printf/printf.plist.part
printf/tests/Makefile
printf/tests/Makefile.depend
printf/tests/legacy_test.sh
printf/tests/regress.bwidth.out [new file with mode: 0644]
printf/tests/regress.sh
shell_cmds.xcodeproj/project.pbxproj
su/su_entitlements.plist
tests/shell_cmds.plist
time/tests/test_time.sh [new file with mode: 0755]
time/time.1
time/time.c
xargs/pathnames.h
xargs/strnsubst.c
xargs/xargs.1
xargs/xargs.c
xcodescripts/install-files.sh

diff --git a/.upstream_base_commits b/.upstream_base_commits
new file mode 100644 (file)
index 0000000..23f2adb
--- /dev/null
@@ -0,0 +1,4 @@
+xargs/pathname.h       freebsd usr.bin/xargs/pathname.h        872b698bd4a1bfc0bf008c09228e6fd238809c75
+xargs/strnsubst.c      freebsd usr.bin/xargs/strnsubst.c       56640efcbe39386a0152f14fb18a314b85e7e014
+xargs/xargs.1  freebsd usr.bin/xargs/xargs.1   7e6cabd06e6caa6a02eeb86308dc0cb3f27e10da
+xargs/xargs.c  freebsd usr.bin/xargs/xargs.c   872b698bd4a1bfc0bf008c09228e6fd238809c75
index 629e22f1e09d8fe15ae8d1bc7f45202f78f79c18..6189420f6b62e70c49588d5da5ec590a506ab882 100644 (file)
@@ -41,97 +41,122 @@ static char sccsid[] = "@(#)echo.c 8.1 (Berkeley) 5/31/93";
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/bin/echo/echo.c,v 1.18 2005/01/10 08:39:22 imp Exp $");
 
-#include <sys/types.h>
-#include <sys/uio.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <limits.h>
+#include <err.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
+#include <wchar.h>
 
-/*
- * Report an error and exit.
- * Use it instead of err(3) to avoid linking-in stdio.
- */
-static void
-errexit(const char *prog, const char *reason)
+static char *
+print_one_char(char *cur, int *bytes_len_out)
 {
-       char *errstr = strerror(errno);
-       write(STDERR_FILENO, prog, strlen(prog));
-       write(STDERR_FILENO, ": ", 2);
-       write(STDERR_FILENO, reason, strlen(reason));
-       write(STDERR_FILENO, ": ", 2);
-       write(STDERR_FILENO, errstr, strlen(errstr));
-       write(STDERR_FILENO, "\n", 1);
-       exit(1);
+       wchar_t wc;
+       int bytes_len = mbtowc(&wc, cur, MB_CUR_MAX);
+       if (bytes_len <= 0) {
+               putchar(*cur);
+               bytes_len = 1;
+               goto out;
+       }
+
+       if (wc != '\\') {
+               putwchar(wc);
+               goto out;
+       }
+
+       cur += bytes_len;
+       bytes_len = 1;
+
+       switch (*cur) {
+               case 'a':
+                       putchar('\a');
+                       goto out;
+
+               case 'b':
+                       putchar('\b');
+                       goto out;
+
+               case 'c':
+                       if (fflush(stdout) != 0)
+                               err(1, "fflush");
+                       exit(0);
+
+               case 'f':
+                       putchar('\f');
+                       goto out;
+
+               case 'n':
+                       putchar('\n');
+                       goto out;
+
+               case 'r':
+                       putchar('\r');
+                       goto out;
+
+               case 't':
+                       putchar('\t');
+                       goto out;
+
+               case 'v':
+                       putchar('\v');
+                       goto out;
+
+               case '\\':
+                       putchar('\\');
+                       goto out;
+
+               case '0': {
+                       int j = 0, num = 0;
+                       while ((*++cur >= '0' && *cur <= '7') &&
+                              j++ < 3) {
+                               num <<= 3;
+                               num |= (*cur - '0');
+                       }
+                       putchar(num);
+                       --cur;
+                       goto out;
+               }
+               default:
+                       --cur;
+                       putchar(*cur);
+                       goto out;
+       }
+
+ out:
+       if (bytes_len_out)
+               *bytes_len_out = bytes_len;
+       return cur;
 }
-       
+
 int
 main(int argc, char *argv[])
 {
-       int nflag;      /* if not set, output a trailing newline. */
-       int veclen;     /* number of writev arguments. */
-       struct iovec *iov, *vp; /* Elements to write, current element. */
-       char space[] = " ";
-       char newline[] = "\n";
-       char *progname = argv[0];
-
-       /* This utility may NOT do getopt(3) option parsing. */
-       if (*++argv && !strcmp(*argv, "-n")) {
-               ++argv;
-               --argc;
+       int nflag = 0;
+       int posix = (getenv("POSIXLY_CORRECT") != NULL || getenv("POSIX_PEDANTIC") != NULL);
+
+       if (!posix && argv[1] && strcmp(argv[1], "-n") == 0)
                nflag = 1;
-       } else
-               nflag = 0;
-
-       veclen = (argc >= 2) ? (argc - 2) * 2 + 1 : 0;
-
-       if ((vp = iov = malloc((veclen + 1) * sizeof(struct iovec))) == NULL)
-               errexit(progname, "malloc");
-
-       while (argv[0] != NULL) {
-               size_t len;
-               
-               len = strlen(argv[0]);
-
-               /*
-                * If the next argument is NULL then this is this
-                * the last argument, therefore we need to check
-                * for a trailing \c.
-                */
-               if (argv[1] == NULL) {
-                       /* is there room for a '\c' and is there one? */
-                       if (len >= 2 &&
-                           argv[0][len - 2] == '\\' &&
-                           argv[0][len - 1] == 'c') {
-                               /* chop it and set the no-newline flag. */
-                               len -= 2;
-                               nflag = 1;
+
+       for (int i = 0; i < argc; i++) {
+               /* argv[0] == progname */
+               int ignore_arg = (i == 0 || (i == 1 && nflag == 1));
+               int last_arg = (i == (argc - 1));
+               if (!ignore_arg) {
+                       char *cur = argv[i];
+                       size_t arg_len = strlen(cur);
+                       int bytes_len = 0;
+
+                       for (const char *end = cur + arg_len; cur < end; cur += bytes_len) {
+                               cur = print_one_char(cur, &bytes_len);
                        }
                }
-               vp->iov_base = *argv;
-               vp++->iov_len = len;
-               if (*++argv) {
-                       vp->iov_base = space;
-                       vp++->iov_len = 1;
-               }
-       }
-       if (!nflag) {
-               veclen++;
-               vp->iov_base = newline;
-               vp++->iov_len = 1;
-       }
-       /* assert(veclen == (vp - iov)); */
-       while (veclen) {
-               int nwrite;
-
-               nwrite = (veclen > IOV_MAX) ? IOV_MAX : veclen;
-               if (writev(STDOUT_FILENO, iov, nwrite) == -1)
-                       errexit(progname, "write");
-               iov += nwrite;
-               veclen -= nwrite;
+               if (last_arg && !nflag)
+                       putchar('\n');
+               else if (!last_arg && !ignore_arg)
+                       putchar(' ');
+
+               if (fflush(stdout) != 0)
+                       err(1, "fflush");
        }
+
        return 0;
 }
index 258f691dbc9525bb292797d2ceb29905056c9c95..bd4d908528150d7fbdafc64d06ed4b3e57a72bca 100644 (file)
@@ -62,6 +62,7 @@ static void nosig(const char *);
 static void printsignals(FILE *);
 static int signame_to_signum(const char *);
 static void usage(void);
+static void print_signum(int, FILE *, char);
 
 #ifdef __APPLE__
 #define sys_nsig NSIG
@@ -93,7 +94,7 @@ main(int argc, char *argv[])
                                numsig -= 128;
                        if (numsig <= 0 || numsig >= sys_nsig)
                                nosig(*argv);
-                       printf("%s\n", sys_signame[numsig]);
+                       print_signum(numsig, stdout, '\n');
                        return (0);
                }
                printsignals(stdout);
@@ -155,6 +156,23 @@ main(int argc, char *argv[])
        return (errors);
 }
 
+static void
+print_signum(int numsig, FILE *fp, char separator)
+{
+       char *signame;
+       int i;
+
+       signame = strdup(sys_signame[numsig]);
+
+       for(i = 0; signame[i] != '\0'; i++) {
+               char upper = toupper(signame[i]);
+               signame[i] = upper;
+       }
+       fprintf(fp, "%s%c", signame, separator);
+
+       free(signame);
+}
+
 static int
 signame_to_signum(const char *sig)
 {
@@ -188,11 +206,10 @@ printsignals(FILE *fp)
        int n;
 
        for (n = 1; n < sys_nsig; n++) {
-               (void)fprintf(fp, "%s", sys_signame[n]);
+               char sep = ' ';
                if (n == (sys_nsig / 2) || n == (sys_nsig - 1))
-                       (void)fprintf(fp, "\n");
-               else
-                       (void)fprintf(fp, " ");
+                       sep = '\n';
+               print_signum(n, fp, sep);
        }
 }
 
index 59643b5bccc1531aa8a2e66b500f7a7baa98aed0..226b87b61f5ad2ad3a1970d2b4dcfd1b29ee9be2 100644 (file)
@@ -12,7 +12,7 @@
 .\" 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.
-.\" 4. Neither the name of the University nor the names of its contributors
+.\" 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.
 .\"
@@ -29,9 +29,9 @@
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)printf.1    8.1 (Berkeley) 6/6/93
-.\" $FreeBSD: head/usr.bin/printf/printf.1 264743 2014-04-21 22:47:18Z pfg $
+.\" $FreeBSD$
 .\"
-.Dd April 21, 2014
+.Dd July 1, 2020
 .Dt PRINTF 1
 .Os
 .Sh NAME
@@ -87,8 +87,6 @@ are as follows:
 Write a <bell> character.
 .It Cm \eb
 Write a <backspace> character.
-.It Cm \ec
-Ignore remaining characters in this string.
 .It Cm \ef
 Write a <form-feed> character.
 .It Cm \en
@@ -289,7 +287,12 @@ The permitted escape sequences are slightly different in that
 octal escapes are
 .Cm \e0 Ns Ar num
 instead of
-.Cm \e Ns Ar num .
+.Cm \e Ns Ar num
+and that an additional escape sequence
+.Cm \ec
+stops further output from this
+.Nm
+invocation.
 .It Cm n$
 Allows reordering of the output according to
 .Ar argument .
@@ -313,12 +316,48 @@ Consult the
 manual page.
 .Sh EXIT STATUS
 .Ex -std
+.Sh EXAMPLES
+Print the string
+.Qq hello :
+.Bd -literal -offset indent
+$ printf "%s\en" hello
+hello
+.Ed
+.Pp
+Same as above, but notice that the format string is not quoted and hence we
+do not get the expected behavior:
+.Bd -literal -offset indent
+$ printf %s\en hello
+hellon$
+.Ed
+.Pp
+Print arguments forcing sign only for the first argument:
+.Bd -literal -offset indent
+$ printf "%+d\en%d\en%d\en" 1 -2 13
++1
+-2
+13
+.Ed
+.Pp
+Same as above, but the single format string will be applied to the three
+arguments:
+.Bd -literal -offset indent
+$ printf "%+d\en" 1 -2 13
++1
+-2
++13
+.Ed
+.Pp
+Print number using only two digits after the decimal point:
+.Bd -literal -offset indent
+$ printf "%.2f\en" 31.7456
+31.75
+.Ed
 .Sh COMPATIBILITY
 The traditional
 .Bx
 behavior of converting arguments of numeric formats not beginning
-with a digit to the
-.Tn ASCII
+with a digit to the ASCII
 code of the first character is not supported.
 .Sh SEE ALSO
 .Xr builtin 1 ,
@@ -340,8 +379,7 @@ It is modeled
 after the standard library function,
 .Xr printf 3 .
 .Sh CAVEATS
-.Tn ANSI
-hexadecimal character constants were deliberately not provided.
+ANSI hexadecimal character constants were deliberately not provided.
 .Pp
 Trying to print a dash ("-") as the first character causes
 .Nm
@@ -361,10 +399,8 @@ and
 formats with a precision
 may not operate as expected.
 .Sh BUGS
-Since the floating point numbers are translated from
-.Tn ASCII
-to floating-point and
-then back again, floating-point precision may be lost.
+Since the floating point numbers are translated from ASCII
+to floating-point and then back again, floating-point precision may be lost.
 (By default, the number is translated to an IEEE-754 double-precision
 value before being printed.
 The
index 438b85ece61f18d90bbe68f01446a783386be8e3..6cc09ae731aa2178a8bd9f39ff4f4ea7ed9a793e 100644 (file)
@@ -1,4 +1,7 @@
 /*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1989, 1993
@@ -12,7 +15,7 @@
  * 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.
- * 4. Neither the name of the University nor the names of its contributors
+ * 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.
  *
@@ -46,7 +49,7 @@ static char const copyright[] =
 static char const sccsid[] = "@(#)printf.c     8.1 (Berkeley) 7/20/93";
 #endif
 static const char rcsid[] =
-  "$FreeBSD: head/usr.bin/printf/printf.c 279503 2015-03-01 21:46:55Z jilles $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -70,20 +73,15 @@ static const char rcsid[] =
 #endif
 
 #define        PF(f, func) do {                                                \
-       char *b = NULL;                                                 \
        if (havewidth)                                                  \
                if (haveprec)                                           \
-                       (void)asprintf(&b, f, fieldwidth, precision, func); \
+                       (void)printf(f, fieldwidth, precision, func);   \
                else                                                    \
-                       (void)asprintf(&b, f, fieldwidth, func);        \
+                       (void)printf(f, fieldwidth, func);              \
        else if (haveprec)                                              \
-               (void)asprintf(&b, f, precision, func);                 \
+               (void)printf(f, precision, func);                       \
        else                                                            \
-               (void)asprintf(&b, f, func);                            \
-       if (b) {                                                        \
-               (void)fputs(b, stdout);                                 \
-               free(b);                                                \
-       }                                                               \
+               (void)printf(f, func);                                  \
 } while (0)
 
 static int      asciicode(void);
@@ -378,13 +376,25 @@ printf_doformat(char *fmt, int *rval)
                char *p;
                int getout;
 
-               p = strdup(getstr());
-               if (p == NULL) {
+               /* Convert "b" to "s" for output. */
+               start[strlen(start) - 1] = 's';
+               if ((p = strdup(getstr())) == NULL) {
                        warnx("%s", strerror(ENOMEM));
                        return (NULL);
                }
                getout = escape(p, 0, &len);
-               fputs(p, stdout);
+               /*
+                * Special-case conversion of zero; print it instead of
+                * feeding an empty string to printf("%s") which would not
+                * print anything.
+                */
+               if (len == 1 && p[0] == '\0') {
+                       putchar(p[0]);
+               } else {
+                       PF(start, p);
+               }
+               /* Restore format for next loop. */
+
                free(p);
                if (getout)
                        return (end_fmt);
@@ -394,7 +404,8 @@ printf_doformat(char *fmt, int *rval)
                char p;
 
                p = getchr();
-               PF(start, p);
+               if (p != '\0')
+                       PF(start, p);
                break;
        }
        case 's': {
index 58f004007055f15612634032b187e69b8d69c9e5..a040cf7461377dedbd683b7c468f42e98c8eee6b 100644 (file)
@@ -2,15 +2,16 @@
                <key>OpenSourceProject</key>
                <string>printf</string>
                <key>OpenSourceVersion</key>
-               <string>2015-03-01</string>
+               <string>2020-07-01</string>
                <key>OpenSourceWebsiteURL</key>
-               <string>http://svnweb.freebsd.org/base/head/usr.bin/printf/</string>
+               <string>https://github.com/freebsd/freebsd/tree/master/usr.bin/printf</string>
                <key>OpenSourceSCM</key>
-               <string>svn co http://svn.freebsd.org/base/head/usr.bin/printf/</string>
+               <string>https://github.com/freebsd/freebsd/tree/master/usr.bin/printf</string>
                <key>OpenSourceImportDate</key>
-               <string>2015-07-23</string>
+               <string>2020-07-23</string>
                <key>OpenSourceModifications</key>
                <array>
+                       <string>65959587: UNIX Conformance | /usr/bin/printf, multiple format assertion failures in VSC</string>
                </array>
                <key>OpenSourceLicense</key>
                <string>bsd</string>
index 7d014a537591af6b224aa4cc75ff3638ecfcc5e3..b4ea79be33329e338848fd2447babe96122c5cb8 100644 (file)
@@ -1,10 +1,11 @@
-# $FreeBSD: head/usr.bin/printf/tests/Makefile 299094 2016-05-04 23:20:53Z ngie $
+# $FreeBSD$
 
 PACKAGE=       tests
 
 TAP_TESTS_SH=  legacy_test
 
 ${PACKAGE}FILES+=              regress.b.out
+${PACKAGE}FILES+=              regress.bwidth.out
 ${PACKAGE}FILES+=              regress.d.out
 ${PACKAGE}FILES+=              regress.f.out
 ${PACKAGE}FILES+=              regress.l1.out
index 0fd161689505f677259d317e16387fa3659c2fdb..f80275d86ab17b5248ab14673acee44f0d1c9e38 100644 (file)
@@ -1,4 +1,4 @@
-# $FreeBSD: head/usr.bin/printf/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $
+# $FreeBSD$
 # Autogenerated - do NOT edit!
 
 DIRDEPS = \
index 7b1f6948c6ec32e0782f5b00a2b52afbcb5f077b..a919deba63f7f7ee542c2028c917c33d5d257936 100644 (file)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $FreeBSD: head/usr.bin/printf/tests/legacy_test.sh 263227 2014-03-16 08:04:06Z jmmv $
+# $FreeBSD$
 
 SRCDIR="$(dirname "${0}")"; export SRCDIR
 
diff --git a/printf/tests/regress.bwidth.out b/printf/tests/regress.bwidth.out
new file mode 100644 (file)
index 0000000..9e2ff61
--- /dev/null
@@ -0,0 +1 @@
+      a
index 2f68c7d908e6e0cfc14f8734a03004b66dcaaf3f..a2cf5fb7712cb1d5112f2ce01616283b9ecb5de9 100644 (file)
@@ -1,10 +1,8 @@
-# $FreeBSD: head/usr.bin/printf/tests/regress.sh 266854 2014-05-29 19:48:18Z pfg $
-
-enable -n printf
+# $FreeBSD$
 
 REGRESSION_START($1)
 
-echo '1..23'
+echo '1..24'
 
 REGRESSION_TEST(`b', `printf "abc%b%b" "def\n" "\cghi"')
 REGRESSION_TEST(`d', `printf "%d,%5d,%.5d,%0*d,%.*d\n" 123 123 123 5 123 5 123')
@@ -29,5 +27,6 @@ REGRESSION_TEST(`missingpos1', `printf "%*.*1\$s" 1 1 1 2>&1')
 REGRESSION_TEST(`missingpos1', `printf "%1\$*2\$.*s" 1 1 1 2>&1')
 REGRESSION_TEST(`missingpos1', `printf "%*1\$.*2\$s" 1 1 1 2>&1')
 REGRESSION_TEST(`missingpos1', `printf "%1\$*.*2\$s" 1 1 1 2>&1')
+REGRESSION_TEST(`bwidth', `printf "%8.2b" "a\nb\n"')
 
 REGRESSION_END()
index f46f071d2676829945361b8026254fae72f72a5c..9a52eeb10e64710be39a7ef9ebc470fed0eecc3a 100644 (file)
                C6868576154725700025D623 /* systime */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = systime; sourceTree = BUILT_PRODUCTS_DIR; };
                C6868579154725700025D623 /* systime.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = systime.c; sourceTree = "<group>"; };
                C686857B154725700025D623 /* systime.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = systime.1; sourceTree = "<group>"; };
+               CE799E6024AD3B1B00E73238 /* test_time.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_time.sh; sourceTree = "<group>"; usesTabs = 1; };
+               CE799E6224B3982200E73238 /* install-files.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "install-files.sh"; sourceTree = "<group>"; };
+               CE799E6324B3982200E73238 /* builtins.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = builtins.txt; sourceTree = "<group>"; };
+               CE799E6424B3982200E73238 /* builtins-manpages.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "builtins-manpages.txt"; sourceTree = "<group>"; };
                FC5D636814B9808E00123E48 /* conv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv.c; sourceTree = "<group>"; };
                FC5D636914B9808E00123E48 /* display.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = display.c; sourceTree = "<group>"; };
                FC5D636A14B9808E00123E48 /* hexdump.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = hexdump.1; sourceTree = "<group>"; };
                FCBA13FA14A141A300AA698B /* test.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = test.1; sourceTree = "<group>"; };
                FCBA13FB14A141A300AA698B /* test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = test.c; sourceTree = "<group>"; };
                FCBA13FF14A141A300AA698B /* time.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = time.1; sourceTree = "<group>"; };
-               FCBA140014A141A300AA698B /* time.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = "<group>"; };
+               FCBA140014A141A300AA698B /* time.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = "<group>"; usesTabs = 1; };
                FCBA140314A141A300AA698B /* true.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = true.1; sourceTree = "<group>"; };
                FCBA140414A141A300AA698B /* true.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = true.c; sourceTree = "<group>"; };
                FCBA140814A141A300AA698B /* uname.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uname.1; sourceTree = "<group>"; };
                        path = systime;
                        sourceTree = "<group>";
                };
+               CE799E5F24AD3B1B00E73238 /* tests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CE799E6024AD3B1B00E73238 /* test_time.sh */,
+                       );
+                       path = tests;
+                       sourceTree = "<group>";
+               };
+               CE799E6124B3982200E73238 /* xcodescripts */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CE799E6224B3982200E73238 /* install-files.sh */,
+                               CE799E6324B3982200E73238 /* builtins.txt */,
+                               CE799E6424B3982200E73238 /* builtins-manpages.txt */,
+                       );
+                       path = xcodescripts;
+                       sourceTree = "<group>";
+               };
                FC5D636714B9808E00123E48 /* hexdump */ = {
                        isa = PBXGroup;
                        children = (
                FC80BF5514A05A2F00C6F7F5 = {
                        isa = PBXGroup;
                        children = (
+                               CE799E6124B3982200E73238 /* xcodescripts */,
                                FD60612F1B7D2DDE004BCA6A /* xcconfigs */,
                                FCBA134014A141A300AA698B /* alias */,
                                FCBA134514A141A300AA698B /* apply */,
                FCBA13FD14A141A300AA698B /* time */ = {
                        isa = PBXGroup;
                        children = (
+                               CE799E5F24AD3B1B00E73238 /* tests */,
                                FCBA13FF14A141A300AA698B /* time.1 */,
                                FCBA140014A141A300AA698B /* time.c */,
                        );
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh";
+                       shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh\n";
                        showEnvVarsInLog = 0;
                };
                FCE30F4D14B619C900CC0294 /* Run Script */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh";
+                       shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh\n";
                        showEnvVarsInLog = 0;
                };
                FD6060EF1B7C0590004BCA6A /* mkbuiltins */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       shellScript = "env -i xcrun -sdk macosx cc ${SRCROOT}/sh/mknodes.c -o ${DERIVED_FILE_DIR}/mknodes\ncd ${BUILT_PRODUCTS_DIR} && ${DERIVED_FILE_DIR}/mknodes ${SRCROOT}/sh/nodetypes ${SRCROOT}/sh/nodes.c.pat";
+                       shellScript = "mkdir -p ${DERIVED_FILE_DIR}\nenv -i xcrun -sdk macosx cc ${SRCROOT}/sh/mknodes.c -o ${DERIVED_FILE_DIR}/mknodes\ncd ${BUILT_PRODUCTS_DIR} && ${DERIVED_FILE_DIR}/mknodes ${SRCROOT}/sh/nodetypes ${SRCROOT}/sh/nodes.c.pat\n";
                };
                FD6060F21B7C0744004BCA6A /* mksyntax */ = {
                        isa = PBXShellScriptBuildPhase;
index a9f77f228d005bee4ba91a6ceb8ac3f7d77d5713..a96cb445b315dfa1b73b3e334c1b2a03983cce95 100644 (file)
@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+       <key>com.apple.keystore.console</key>
+       <true/>
        <key>com.apple.private.security.clear-library-validation</key>
        <true/>
 </dict>
index 3794d3d5fed38a84a910a3cc109a39585f148a72..bdeb182e152c066bdcd246a4d42bb4cd90800eda 100644 (file)
                                <string>/bin/sh</string>
                                <string>/AppleInternal/Tests/shell_cmds/printf/legacy_test.sh</string>
                        </array>
-                       <key>IgnoreOutput</key>
-                       <true/>
-                       <key>TestName</key>
-                       <string>shell_cmds: printf</string>
+                       <key>IgnoreOutput</key><true/>
+                       <key>TestName</key><string>shell_cmds.printf</string>
                        <key>WhenToRun</key>
                        <array>
                                <string>PRESUBMISSION</string>
                                <string>/bin/sh</string>
                                <string>/AppleInternal/Tests/shell_cmds/test/legacy_test.sh</string>
                        </array>
-                       <key>TestName</key>
-                       <string>shell_cmds: test</string>
+                       <key>TestName</key><string>shell_cmds.test</string>
+                       <key>WhenToRun</key>
+                       <array>
+                               <string>PRESUBMISSION</string>
+                               <string>NIGHTLY</string>
+                       </array>
+               </dict>
+               <dict>
+                       <key>Command</key>
+                       <array>
+                               <string>/bin/sh</string>
+                               <string>/AppleInternal/Tests/shell_cmds/time/test_time.sh</string>
+                       </array>
+                       <key>TestName</key><string>shell_cmds.time</string>
                        <key>WhenToRun</key>
                        <array>
                                <string>PRESUBMISSION</string>
diff --git a/time/tests/test_time.sh b/time/tests/test_time.sh
new file mode 100755 (executable)
index 0000000..6cf002d
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+set -o nounset
+
+TIME="${TIME-/usr/bin/time}"
+echo "SUITE: time(1)"
+what $TIME
+echo
+
+EXIT=0
+
+echo TEST: check real time
+
+TIME_SLEEP=`$TIME 2>&1 sleep 1 | sed -n -E 's/[ ]+([0-9]+).*/\1/p'`
+TIME_STATUS=$?
+
+if [ "$TIME_STATUS" -ne "0" ]; then
+       echo FAIL: time failed with "$TIME_STATUS"
+       EXIT=1
+fi
+
+if [ "$TIME_SLEEP" -lt "0" ]; then
+       echo FAIL: time mis-timed sleep
+       EXIT=2
+fi
+
+MONOTONIC=`sysctl -n kern.monotonic.task_thread_counting`
+if [ "$MONOTONIC" -ne "0" ]; then
+       echo TEST: check instructions retired
+
+       TIME_INSTRS=`$TIME -l 2>&1 sleep 1 | sed -E -n '/instructions/p'`
+       if [ -z "$TIME_INSTRS" ]; then
+               echo FAIL: time is not showing instructions retired
+               EXIT=3
+       fi
+else
+       echo SKIP: check instructions retired
+fi
+
+# NB: SIGINT and SIGQUIT work locally, but the automated test harnesses tries to
+# handle those signals itself before the fork.
+
+echo TEST: check child SIGUSR1
+
+TIME_USR1=`$TIME 2>&1 sh -c 'kill -USR1 $$ && sleep 5 && true'`
+TIME_STATUS=$?
+if [ "$TIME_STATUS" -eq "0" ]; then
+       echo FAIL: time should allow child to receive SIGUSR1
+       EXIT=4
+fi
+
+echo TEST: check non-existent binary
+TIME_NONEXIST=`$TIME 2>&1 ./this-wont-exist`
+TIME_STATUS=$?
+if [ "$TIME_STATUS" -ne "127" ]; then
+       echo FAIL: time should error when measuring a non-existent command
+       EXIT=5
+fi
+
+exit $EXIT
index 8c4ca8ca1b172475fef4693f14aa4077a327d2ba..b4e7017382eb8f7f8a59aeb54e1d14fcc40591c7 100644 (file)
@@ -114,7 +114,3 @@ The
 .Nm
 utility conforms to 
 .St -p1003.2-92 .
-.Sh BUGS
-The granularity of seconds on microprocessors is crude and
-can result in times being reported for CPU usage which are too large by
-a second.
index 0a3cb4738d8203575e86f53e8bf7c405f525a4b2..afaaed0e86b9fda95de530b39a794228090c8cce 100644 (file)
  * SUCH DAMAGE.
  */
 
+#include <errno.h>
+#include <err.h>
+#include <inttypes.h>
+#include <langinfo.h>
+#include <libproc.h>
+#include <locale.h>
 #include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1987, 1988, 1993\n\
-       The Regents of the University of California.  All rights reserved.\n");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)time.c     8.1 (Berkeley) 6/6/93";
-#endif
-__RCSID("$NetBSD: time.c,v 1.9 1997/10/20 03:28:21 lukem Exp $");
-#endif /* not lint */
-
+#include <sysexits.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/wait.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <time.h>
 #include <unistd.h>
-#include <errno.h>
-#include <langinfo.h>
-#include <locale.h>
 
 int lflag;
 int portableflag;
+bool child_running = true;
 
-int    main __P((int, char **));
+void
+child_handler(int sig)
+{
+       child_running = false;
+}
 
 int
-main(argc, argv)
-       int argc;
-       char **argv;
+main(int argc, char **argv)
 {
        int pid;
-       int ch, status;
-       struct timeval before, after;
+       int ch, status, rusage_ret = -1;
+       uint64_t before_ns, after_ns, duration_ns, duration_secs, duration_frac_ns;
        struct rusage ru;
-       char * radix = NULL;
+       struct rusage_info_v4 ruinfo;
+       sigset_t sigmask, suspmask, origmask;
 
-#ifdef __GNUC__                /* XXX: borken gcc */
-       (void)&argv;
-#endif
        lflag = 0;
-       while ((ch = getopt(argc, argv, "lp")) != -1)
+       while ((ch = getopt(argc, argv, "lp")) != -1) {
                switch((char)ch) {
                case 'p':
                        portableflag = 1;
@@ -88,37 +84,88 @@ main(argc, argv)
                        break;
                case '?':
                default:
-                       fprintf(stderr, "usage: time [-lp] command.\n");
+                       fprintf(stderr, "usage: time [-lp] <command>\n");
                        exit(1);
                }
+       }
 
-       if (!(argc -= optind))
+       if (!(argc -= optind)) {
                exit(0);
+       }
        argv += optind;
 
-       gettimeofday(&before, (struct timezone *)NULL);
-       switch(pid = vfork()) {
-       case -1:                        /* error */
-               perror("time");
-               exit(1);
-               /* NOTREACHED */
-       case 0:                         /* child */
+       sigemptyset(&sigmask);
+       /*
+        * Block SIGCHLD so that the check for `child_running` doesn't miss the
+        * handler before calling `sigsuspend` and blocking forever.
+        */
+       sigaddset(&sigmask, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &sigmask, &origmask);
+
+       /*
+        * Ensure child signals are handled by the parent prior to fork; otherwise,
+        * they could be missed between the child forking and calling `sigsuspend`.
+        */
+       (void)signal(SIGCHLD, child_handler);
+
+       sigemptyset(&suspmask);
+
+       before_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
+       /*
+        * NB: Don't add anything between these two lines -- measurement is
+        * happening now.
+        */
+       switch (pid = vfork()) {
+       case -1: /* error */
+               err(EX_OSERR, "time");
+               __builtin_unreachable();
+       case 0: /* child */
+               /*
+                * Allow the child to respond to signals by resetting to the original
+                * signal handling behavior.
+                */
+               (void)sigprocmask(SIG_SETMASK, &origmask, NULL);
                execvp(*argv, argv);
                perror(*argv);
                _exit((errno == ENOENT) ? 127 : 126);
-               /* NOTREACHED */
+               __builtin_unreachable();
+       default: /* parent */
+               break;
        }
 
-       /* parent */
+       /*
+        * Let the child handle signals that normally exit.
+        */
        (void)signal(SIGINT, SIG_IGN);
        (void)signal(SIGQUIT, SIG_IGN);
-       while (wait3(&status, 0, &ru) != pid);
-       gettimeofday(&after, (struct timezone *)NULL);
-       if (!WIFEXITED(status))
+
+       while (child_running) {
+               /*
+                * This would be racy, but SIGCHLD is blocked above (as part of
+                * `sigmask`.
+                */
+               sigsuspend(&suspmask);
+       }
+       /*
+        * NB: Minimize what's added between these statements to preserve the
+        * accuracy of the time measurement.
+        */
+       after_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
+       if (lflag) {
+               rusage_ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (void **)&ruinfo);
+       }
+       while (wait3(&status, 0, &ru) != pid) {
+       }
+       if (!WIFEXITED(status)) {
                fprintf(stderr, "Command terminated abnormally.\n");
-       timersub(&after, &before, &after);
+       }
+       duration_ns = after_ns - before_ns;
+       duration_secs = duration_ns / (1000 * 1000 * 1000);
+       duration_frac_ns = duration_ns - (duration_secs * 1000 * 1000 * 1000);
 
        if (portableflag) {
+               char *radix = NULL;
+
                setlocale(LC_ALL, "");
 
                radix = nl_langinfo(RADIXCHAR);
@@ -126,15 +173,15 @@ main(argc, argv)
                        radix = ".";
                }
 
-               fprintf (stderr, "real %9ld%s%02ld\n",
-                       (long)after.tv_sec, radix, (long)after.tv_usec/10000);
-               fprintf (stderr, "user %9ld%s%02ld\n",
+               fprintf(stderr, "real %9" PRIu64 "%s%02" PRIu64 "\n",
+                       duration_secs, radix, duration_frac_ns / (10 * 1000 * 1000));
+               fprintf(stderr, "user %9ld%s%02ld\n",
                        (long)ru.ru_utime.tv_sec, radix, (long)ru.ru_utime.tv_usec/10000);
-               fprintf (stderr, "sys  %9ld%s%02ld\n",
+               fprintf(stderr, "sys  %9ld%s%02ld\n",
                        (long)ru.ru_stime.tv_sec, radix, (long)ru.ru_stime.tv_usec/10000);
        } else {
-               fprintf(stderr, "%9ld.%02ld real ", 
-                       (long)after.tv_sec, (long)after.tv_usec/10000);
+               fprintf(stderr, "%9" PRIu64 ".%02" PRIu64 " real ",
+                       duration_secs, duration_frac_ns / (10 * 1000 * 1000));
                fprintf(stderr, "%9ld.%02ld user ",
                        (long)ru.ru_utime.tv_sec, (long)ru.ru_utime.tv_usec/10000);
                fprintf(stderr, "%9ld.%02ld sys\n",
@@ -142,41 +189,57 @@ main(argc, argv)
        }
 
        if (lflag) {
-               int hz = 100;                   /* XXX */
+               int hz = 100; /* XXX */
                long ticks;
 
                ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) +
                     hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000;
 
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_maxrss, "maximum resident set size");
-               fprintf(stderr, "%10ld  %s\n", ticks ? ru.ru_ixrss / ticks : 0,
+               fprintf(stderr, "%20ld  %s\n", ticks ? ru.ru_ixrss / ticks : 0,
                        "average shared memory size");
-               fprintf(stderr, "%10ld  %s\n", ticks ? ru.ru_idrss / ticks : 0,
+               fprintf(stderr, "%20ld  %s\n", ticks ? ru.ru_idrss / ticks : 0,
                        "average unshared data size");
-               fprintf(stderr, "%10ld  %s\n", ticks ? ru.ru_isrss / ticks : 0,
+               fprintf(stderr, "%20ld  %s\n", ticks ? ru.ru_isrss / ticks : 0,
                        "average unshared stack size");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_minflt, "page reclaims");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_majflt, "page faults");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_nswap, "swaps");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_inblock, "block input operations");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_oublock, "block output operations");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_msgsnd, "messages sent");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_msgrcv, "messages received");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_nsignals, "signals received");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_nvcsw, "voluntary context switches");
-               fprintf(stderr, "%10ld  %s\n",
+               fprintf(stderr, "%20ld  %s\n",
                        ru.ru_nivcsw, "involuntary context switches");
+
+               if (rusage_ret >= 0) {
+                       if (ruinfo.ri_instructions > 0) {
+                               fprintf(stderr, "%20" PRIu64 "  %s\n", ruinfo.ri_instructions,
+                                       "instructions retired");
+                       }
+                       if (ruinfo.ri_cycles > 0) {
+                               fprintf(stderr, "%20" PRIu64 "  %s\n", ruinfo.ri_cycles,
+                                       "cycles elapsed");
+                       }
+                       if (ruinfo.ri_lifetime_max_phys_footprint > 0) {
+                               fprintf(stderr, "%20" PRIu64 "  %s\n",
+                                       ruinfo.ri_lifetime_max_phys_footprint,
+                                       "peak memory footprint");
+                       }
+               }
        }
 
-       exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
+       exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
 }
index 3f05b3dcc2d172772650814ab952dfab55d29716..45c0fff962318d23fbdd181169ee7272bdf52eed 100644 (file)
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * 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
+ * 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.
  *
@@ -30,6 +28,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
+ * $FreeBSD$
+ *
  *     @(#)pathnames.h 8.1 (Berkeley) 6/6/93
  */
 
index 4b3f9b64d74ebe947d5c5fa395a999cf35286a71..b00b31969f725eaa756f45c8ac057ecedc62c136 100644 (file)
@@ -36,7 +36,19 @@ strnsubst(char **str, const char *match, const char *replstr, size_t maxsize)
        s1 = *str;
        if (s1 == NULL)
                return;
-       s2 = calloc(maxsize, 1);
+       /*
+        * If maxsize is 0 then set it 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(1, maxsize);
        if (s2 == NULL)
                err(1, "calloc");
 
index 90551f98a3d6a76b6e793dd6edeb469c51551a09..b9d5add2e6327be1aa035b59ab92b4054fe58725 100644 (file)
 .\" 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
+.\" 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.
 .\"
@@ -37,7 +33,7 @@
 .\" $FreeBSD: src/usr.bin/xargs/xargs.1,v 1.34 2005/05/21 09:55:09 ru Exp $
 .\" $xMach: xargs.1,v 1.2 2002/02/23 05:23:37 tim Exp $
 .\"
-.Dd August 2, 2004
+.Dd August 4, 2015
 .Dt XARGS 1
 .Os
 .Sh NAME
 .Nd "construct argument list(s) and execute utility"
 .Sh SYNOPSIS
 .Nm
-.Op Fl 0opt
+.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
@@ -74,8 +71,7 @@ Any arguments specified on the command line are given to
 upon each invocation, followed by some number of the arguments read
 from the standard input of
 .Nm .
-The utility
-is repeatedly executed until standard input is exhausted.
+This is repeated until standard input is exhausted.
 .Pp
 Spaces, tabs and newlines may be embedded in arguments using single
 (``\ '\ '')
@@ -115,13 +111,20 @@ 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
+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 255 bytes.
-The 255 byte limit does not apply to arguments to
+up to
+.Ar replsize
+bytes.
+The size limit does not apply to arguments to
 .Ar utility
 which do not contain
 .Ar replstr ,
@@ -160,8 +163,7 @@ 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
-.Pp
+.Dl /bin/ls -1d [A-Z]* | xargs -J % cp -Rp % destdir
 .It Fl L Ar number
 Call
 .Ar utility
@@ -211,6 +213,11 @@ Parallel mode: run at most
 invocations of
 .Ar utility
 at once.
+If
+.Ar maxprocs
+is set to 0,
+.Nm
+will run as many processes as possible.
 .It Fl p
 Echo each command to be executed and ask the user whether it should be
 executed.
@@ -220,6 +227,34 @@ 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
+.Fx
+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
+.Fx
+version of
+.Nm .
 .It Fl R Ar replacements
 Specify the maximum number of arguments that
 .Fl I
@@ -227,6 +262,13 @@ 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 .
@@ -272,7 +314,11 @@ cannot be invoked, an invocation of
 is terminated by a signal,
 or an invocation of
 .Ar utility
-exits with a value of 255.
+exits with a value of 255, the
+.Nm
+utility stops processing input and exits after all invocations of
+.Ar utility
+finish processing.
 .Sh LEGACY DESCRIPTION
 In legacy mode, the
 .Fl L
@@ -314,16 +360,16 @@ utility is expected to be
 .St -p1003.2
 compliant.
 The
-.Fl J , o , P
+.Fl J , o , P, R
 and
-.Fl R
+.Fl S
 options are non-standard
 .Fx
 extensions which may not be available on other operating systems.
 .Sh HISTORY
 The
 .Nm
-command appeared in PWB UNIX.
+utility appeared in PWB UNIX.
 .Sh BUGS
 If
 .Ar utility
index 2316012170292ae38a816585e3dc9fbbb4adf63e..a1ee4805bd8e53a5e906eda7e948652797b6958c 100644 (file)
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * 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
+ * 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.
  *
@@ -48,11 +46,13 @@ static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
 #endif /* not lint */
 #endif
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/usr.bin/xargs/xargs.c,v 1.57 2005/02/27 02:01:31 gad Exp $");
+__FBSDID("$FreeBSD$");
 
-#include <sys/param.h>
+#include <sys/types.h>
 #include <sys/wait.h>
-
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/resource.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -79,7 +79,17 @@ static int   prompt(void);
 static void    run(char **);
 static void    usage(void);
 void           strnsubst(char **, const char *, const char *, size_t);
+static pid_t  xwait(int block, int *status);
+static void   xexit(const char *, const int);
 static void    waitchildren(const char *, int);
+static void   pids_init(void);
+static int    pids_empty(void);
+static int    pids_full(void);
+static void   pids_add(pid_t pid);
+static int    pids_remove(pid_t pid);
+static int    findslot(pid_t pid);
+static int    findfreeslot(void);
+static void   clearslot(int slot);
 
 static int last_was_newline = 1;
 static int last_was_blank = 0;
@@ -89,9 +99,10 @@ 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 cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag;
 static int curprocs, maxprocs;
 static size_t pad9314053;
+static pid_t *childpids;
 
 static volatile int childerr;
 
@@ -104,7 +115,9 @@ main(int argc, char *argv[])
        int ch, Jflag, nflag, nline;
        size_t nargs;
        size_t linelen;
+       struct rlimit rl;
        char *endptr;
+       const char *errstr;
 
        inpline = replstr = NULL;
        ep = environ;
@@ -137,8 +150,8 @@ main(int argc, char *argv[])
        }
        nline -= pad9314053;
        maxprocs = 1;
-       while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:s:tx")) != -1)
-               switch(ch) {
+       while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1)
+               switch (ch) {
                case 'E':
                        eofstr = optarg;
                        break;
@@ -154,7 +167,9 @@ main(int argc, char *argv[])
                        replstr = optarg;
                        break;
                case 'L':
-                       Lflag = atoi(optarg);
+                       Lflag = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr)
+                               errx(1, "-L %s: %s", optarg, errstr);
                        if (COMPAT_MODE("bin/xargs", "Unix2003")) {
                                nflag = 0; /* Override */
                                nargs = 5000;
@@ -172,8 +187,13 @@ main(int argc, char *argv[])
                        oflag = 1;
                        break;
                case 'P':
-                       if ((maxprocs = atoi(optarg)) <= 0)
-                               errx(1, "max. processes must be >0");
+                       maxprocs = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr)
+                               errx(1, "-P %s: %s", optarg, errstr);
+                       if (getrlimit(RLIMIT_NPROC, &rl) != 0)
+                               errx(1, "getrlimit failed");
+                       if (maxprocs == 0 || maxprocs > rl.rlim_cur)
+                               maxprocs = rl.rlim_cur;
                        break;
                case 'p':
                        pflag = 1;
@@ -183,8 +203,18 @@ main(int argc, char *argv[])
                        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);
+                       nline = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr)
+                               errx(1, "-s %s: %s", optarg, errstr);
                        pad9314053 = 0; /* assume the -s value is valid */
                        break;
                case 't':
@@ -205,8 +235,12 @@ main(int argc, char *argv[])
 
        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)
@@ -214,13 +248,15 @@ main(int argc, char *argv[])
        if (replstr != NULL && *replstr == '\0')
                errx(1, "replstr may not be empty");
 
+       pids_init();
+
        /*
         * 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)
+       if ((av = bxp = malloc(linelen * sizeof(char *))) == NULL)
                errx(1, "malloc failed");
 
        /*
@@ -279,13 +315,11 @@ parse_input(int argc, char *argv[])
 
        foundeof = 0;
 
-       switch(ch = getchar()) {
+       switch (ch = getchar()) {
        case EOF:
                /* No arguments since last exec. */
-               if (p == bbp) {
-                       waitchildren(*argv, 1);
-                       exit(rval);
-               }
+               if (p == bbp)
+                       xexit(*av, rval);
                goto arg1;
        case ' ':
                last_was_blank = 1;
@@ -323,11 +357,13 @@ parse_input(int argc, char *argv[])
                last_was_newline = 1;
 
                /* Quotes do not escape newlines. */
-arg1:          if (insingle || indouble)
-                       errx(1, "unterminated quote");
+arg1:          if (insingle || indouble) {
+                       warnx("unterminated quote");
+                       xexit(*av, 1);
+               }
 arg2:
                foundeof = *eofstr != '\0' &&
-                   strcmp(argp, eofstr) == 0;
+                   strncmp(argp, eofstr, p - argp) == 0;
 
 #ifdef __APPLE__
                /* 6591323: -I specifies that it processes the entire line,
@@ -370,8 +406,10 @@ arg2:
                                 */
                                inpline = realloc(inpline, curlen + 2 +
                                    strlen(argp));
-                               if (inpline == NULL)
-                                       errx(1, "realloc failed");
+                               if (inpline == NULL) {
+                                       warnx("realloc failed");
+                                       xexit(*av, 1);
+                               }
                                if (curlen == 1)
                                        strcpy(inpline, argp);
                                else
@@ -388,17 +426,17 @@ arg2:
                 */
                if (xp == endxp || p + (count * pad9314053) > ebp || ch == EOF ||
                    (Lflag <= count && xflag) || foundeof) {
-                       if (xflag && xp != endxp && p + (count * pad9314053) > ebp)
-                               errx(1, "insufficient space for arguments");
+                       if (xflag && xp != endxp && p + (count * pad9314053) > ebp) {
+                               warnx("insufficient space for arguments");
+                               xexit(*av, 1);
+                       }
                        if (jfound) {
                                for (avj = argv; *avj; avj++)
                                        *xp++ = *avj;
                        }
                        prerun(argc, av);
-                       if (ch == EOF || foundeof) {
-                               waitchildren(*argv, 1);
-                               exit(rval);
-                       }
+                       if (ch == EOF || foundeof)
+                               xexit(*av, rval);
                        p = bbp;
                        xp = bxp;
                        count = 0;
@@ -423,8 +461,10 @@ arg2:
                if (zflag)
                        goto addch;
                /* Backslash escapes anything, is escaped by quotes. */
-               if (!insingle && !indouble && (ch = getchar()) == EOF)
-                       errx(1, "backslash at EOF");
+               if (!insingle && !indouble && (ch = getchar()) == EOF) {
+                       warnx("backslash at EOF");
+                       xexit(*av, 1);
+               }
                /* FALLTHROUGH */
        default:
 addch:         if (p < ebp) {
@@ -433,11 +473,15 @@ addch:            if (p < ebp) {
                }
 
                /* If only one argument, not enough buffer space. */
-               if (bxp == xp)
-                       errx(1, "insufficient space for argument");
+               if (bxp == xp) {
+                       warnx("insufficient space for argument");
+                       xexit(*av, 1);
+               }
                /* Didn't hit argument limit, so if xflag object. */
-               if (xflag)
-                       errx(1, "insufficient space for arguments");
+               if (xflag) {
+                       warnx("insufficient space for arguments");
+                       xexit(*av, 1);
+               }
 
                if (jfound) {
                        for (avj = argv; *avj; avj++)
@@ -481,17 +525,21 @@ prerun(int argc, char *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");
+       tmp = malloc((argc + 1) * sizeof(char *));
+       if (tmp == NULL) {
+               warnx("malloc failed");
+               xexit(*argv, 1);
+       }
        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");
+       if ((*tmp++ = strdup(*avj++)) == NULL) {
+               warnx("strdup failed");
+               xexit(*argv, 1);
+       }
 
        /*
         * For each argument to utility, if we have not used up
@@ -504,12 +552,14 @@ prerun(int argc, char *argv[])
        while (--argc) {
                *tmp = *avj++;
                if (repls && strstr(*tmp, replstr) != NULL) {
-                       strnsubst(tmp++, replstr, inpline, (size_t)255);
+                       strnsubst(tmp++, replstr, inpline, (size_t)Sflag);
                        if (repls > 0)
                                repls--;
                } else {
-                       if ((*tmp = strdup(*tmp)) == NULL)
-                               errx(1, "strdup failed");
+                       if ((*tmp = strdup(*tmp)) == NULL) {
+                               warnx("strdup failed");
+                               xexit(*argv, 1);
+                       }
                        tmp++;
                }
        }
@@ -578,9 +628,10 @@ run(char **argv)
        }
 exec:
        childerr = 0;
-       switch(pid = vfork()) {
+       switch (pid = vfork()) {
        case -1:
-               err(1, "vfork");
+               warn("vfork");
+               xexit(*argv, 1);
        case 0:
                if (oflag) {
                        if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
@@ -597,35 +648,156 @@ exec:
                childerr = errno;
                _exit(1);
        }
-       curprocs++;
+       pids_add(pid);
        waitchildren(*argv, 0);
 }
 
+/*
+ * Wait for a tracked child to exit and return its pid and exit status.
+ *
+ * Ignores (discards) all untracked child processes.
+ * Returns -1 and sets errno to ECHILD if no tracked children exist.
+ * If block is set, waits indefinitely for a child process to exit.
+ * If block is not set and no children have exited, returns 0 immediately.
+ */
+static pid_t
+xwait(int block, int *status) {
+       pid_t pid;
+
+       if (pids_empty()) {
+               errno = ECHILD;
+               return (-1);
+       }
+
+       while ((pid = waitpid(-1, status, block ? 0 : WNOHANG)) > 0)
+               if (pids_remove(pid))
+                       break;
+
+       return (pid);
+}
+
+static void
+xexit(const char *name, const int exit_code) {
+       waitchildren(name, 1);
+       exit(exit_code);
+}
+
 static void
 waitchildren(const char *name, int waitall)
 {
        pid_t pid;
        int status;
+       int cause_exit = 0;
 
-       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);
-               }
+       while ((pid = xwait(waitall || pids_full(), &status)) > 0) {
                /*
-                * If utility signaled or exited with a value of 255,
-                * exit 1-125.
+                * If we couldn't invoke the utility or if utility exited
+                * because of a signal or with a value of 255, warn (per
+                * POSIX), and then wait until all other children have
+                * exited before exiting 1-125. POSIX requires us to stop
+                * reading if child exits because of a signal or with 255,
+                * but it does not require us to exit immediately; waiting
+                * is preferable to orphaning.
                 */
-               if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
-                       exit(1);
-               if (WEXITSTATUS(status))
-                       rval = 1;
+               if (childerr != 0 && cause_exit == 0) {
+                       errno = childerr;
+                       waitall = 1;
+                       cause_exit = errno == ENOENT ? 127 : 126;
+                       warn("%s", name);
+               } else if (WIFSIGNALED(status)) {
+                       waitall = cause_exit = 1;
+                       warnx("%s: terminated with signal %d; aborting",
+                           name, WTERMSIG(status));
+               } else if (WEXITSTATUS(status) == 255) {
+                       waitall = cause_exit = 1;
+                       warnx("%s: exited with status 255; aborting", name);
+               } else if (WEXITSTATUS(status))
+                       rval = 1;
        }
+
+       if (cause_exit)
+               exit(cause_exit);
        if (pid == -1 && errno != ECHILD)
-               err(1, "wait3");
+               err(1, "waitpid");
+}
+
+#define        NOPID   (0)
+
+static void
+pids_init(void)
+{
+       int i;
+
+       if ((childpids = malloc(maxprocs * sizeof(*childpids))) == NULL)
+               errx(1, "malloc failed");
+
+       for (i = 0; i < maxprocs; i++)
+               clearslot(i);
+}
+
+static int
+pids_empty(void)
+{
+
+       return (curprocs == 0);
+}
+
+static int
+pids_full(void)
+{
+
+       return (curprocs >= maxprocs);
+}
+
+static void
+pids_add(pid_t pid)
+{
+       int slot;
+
+       slot = findfreeslot();
+       childpids[slot] = pid;
+       curprocs++;
+}
+
+static int
+pids_remove(pid_t pid)
+{
+       int slot;
+
+       if ((slot = findslot(pid)) < 0)
+               return (0);
+
+       clearslot(slot);
+       curprocs--;
+       return (1);
+}
+
+static int
+findfreeslot(void)
+{
+       int slot;
+
+       if ((slot = findslot(NOPID)) < 0)
+               errx(1, "internal error: no free pid slot");
+       return (slot);
+}
+
+static int
+findslot(pid_t pid)
+{
+       int slot;
+
+       for (slot = 0; slot < maxprocs; slot++)
+               if (childpids[slot] == pid)
+                       return (slot);
+       return (-1);
+}
+
+static void
+clearslot(int slot)
+{
+
+       childpids[slot] = NOPID;
 }
 
 /*
@@ -649,6 +821,7 @@ prompt(void)
                (void)fclose(ttyfp);
                return (0);
        }
+       response[rsize - 1] = '\0';
        match = regexec(&cre, response, 0, NULL, 0);
        (void)fclose(ttyfp);
        regfree(&cre);
@@ -658,9 +831,10 @@ prompt(void)
 static void
 usage(void)
 {
+
        fprintf(stderr,
-"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");
+"usage: xargs [-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");
        exit(1);
 }
index 11dcad9d2361ad322bb6b9eb4c0de911f0318e81..3710fcbd9deb59f69254b3a4290560d55d0795ab 100644 (file)
@@ -31,6 +31,20 @@ for manpage in `cat "$SRCROOT/xcodescripts/builtins-manpages.txt"`; do
 done
 set -x
 
+install -d -o root -g wheel -m 0755 "$DSTROOT"/AppleInternal/Tests/shell_cmds
+install -o root -g wheel -m 0644 "$SRCROOT"/tests/regress.m4 \
+       "$DSTROOT"/AppleInternal/Tests/shell_cmds
+
+install -d -o root -g wheel -m 0755 \
+       "$DSTROOT"/AppleInternal/Tests/shell_cmds/time
+install -o root -g wheel -m 0644 "$SRCROOT"/time/tests/test_time.sh \
+       "$DSTROOT"/AppleInternal/Tests/shell_cmds/time
+
+install -d -o root -g wheel -m 0755 \
+       "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests
+install -o root -g wheel -m 0644 "$SRCROOT"/tests/shell_cmds.plist \
+       "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests
+
 # Skip locate and su targets for iOS
 if [ "$TARGET_NAME" = "All_iOS" ]; then
        exit 0
@@ -50,9 +64,3 @@ echo ".so man8/locate.updatedb.8" > "$MANDIR"/man8/locate.mklocatedb.8
 
 install -d -o root -g wheel -m 0755 "$PAMDIR"
 install -c -o root -g wheel -m 0644 "$SRCROOT"/su/su.pam "$PAMDIR"/su
-
-install -d -o root -g wheel -m 0755 "$DSTROOT"/AppleInternal/Tests/shell_cmds
-install -o root -g wheel -m 0644 "$SRCROOT"/tests/regress.m4 "$DSTROOT"/AppleInternal/Tests/shell_cmds
-
-install -d -o root -g wheel -m 0755 "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests
-install -o root -g wheel -m 0644 "$SRCROOT"/tests/shell_cmds.plist "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests