]> git.saurik.com Git - apple/file_cmds.git/commitdiff
file_cmds-116.9.tar.gz mac-os-x-104 mac-os-x-1041 mac-os-x-1042 mac-os-x-1043 mac-os-x-1044ppc mac-os-x-1044x86 mac-os-x-1045ppc mac-os-x-1045x86 mac-os-x-1046ppc mac-os-x-1046x86 v116.9
authorApple <opensource@apple.com>
Mon, 21 Mar 2005 21:58:58 +0000 (21:58 +0000)
committerApple <opensource@apple.com>
Mon, 21 Mar 2005 21:58:58 +0000 (21:58 +0000)
65 files changed:
Makefile
PB.project
chmod/Makefile
chmod/chmod.1
chmod/chmod.c
chmod/chmod_acl.c [new file with mode: 0644]
chmod/chmod_acl.h [new file with mode: 0644]
chown/chown.8
chown/chown.c
compress/Makefile
compress/compress.1
compress/compress.c
compress/zopen.3
compress/zopen.c
compress/zopen.h [new file with mode: 0644]
cp/Makefile
cp/cp.1
cp/cp.c
cp/utils.c
dd/args.c
dd/dd.c
dd/misc.c
df/Makefile
df/df.1
df/df.c
du/du.c
install/xinstall.c
ipcrm/Makefile [new file with mode: 0644]
ipcrm/Makefile.postamble [new file with mode: 0644]
ipcrm/Makefile.preamble [new file with mode: 0644]
ipcrm/PB.project [new file with mode: 0644]
ipcrm/ipcrm.1 [new file with mode: 0644]
ipcrm/ipcrm.c [new file with mode: 0644]
ipcs/Makefile [new file with mode: 0644]
ipcs/Makefile.postamble [new file with mode: 0644]
ipcs/Makefile.preamble [new file with mode: 0644]
ipcs/PB.project [new file with mode: 0644]
ipcs/ipcs.1 [new file with mode: 0644]
ipcs/ipcs.c [new file with mode: 0644]
ls/Makefile
ls/ls.1
ls/ls.c
ls/ls.h
ls/print.c
ls/util.c
mv/mv.c
pathchk/Makefile [new file with mode: 0644]
pathchk/Makefile.postamble [new file with mode: 0644]
pathchk/Makefile.preamble [new file with mode: 0644]
pathchk/PB.project [new file with mode: 0644]
pathchk/pathchk.1 [new file with mode: 0644]
pathchk/pathchk.c [new file with mode: 0644]
pax/ar_io.c
pax/ar_subs.c
pax/file_subs.c
pax/options.c
pax/pax.1
pax/tar.c
rm/rm.c
stat/Makefile [new file with mode: 0644]
stat/Makefile.postamble [new file with mode: 0644]
stat/Makefile.preamble [new file with mode: 0644]
stat/PB.project [new file with mode: 0644]
stat/stat.1 [new file with mode: 0644]
stat/stat.c [new file with mode: 0644]

index cd9ede3e4299a9840f23e006012add552075ccd2..20e69e6c996eb3741781c2ca59f23cd42c436a0e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -12,8 +12,8 @@ NAME = file_cmds
 PROJECTVERSION = 2.8
 PROJECT_TYPE = Aggregate
 
-TOOLS = chflags chmod chown compress cp dd df du install ln ls\
-        mkdir mkfifo mknod mtree mv pax rm rmdir rmt shar tcopy\
+TOOLS = chflags chmod chown compress cp dd df du install ipcrm ipcs ln ls\
+        mkdir mkfifo mknod mtree mv pathchk pax rm rmdir rmt shar stat tcopy\
         touch
 
 OTHERSRCS = PROJECT Makefile.preamble Makefile Makefile.postamble
@@ -26,7 +26,7 @@ DEBUG_LIBS = $(LIBS)
 PROF_LIBS = $(LIBS)
 
 
-NEXTSTEP_PB_CFLAGS = -no-cpp-precomp
+NEXTSTEP_PB_CFLAGS = -mdynamic-no-pic -no-cpp-precomp -I/System/Library/Frameworks/System.framework/PrivateHeaders
 
 
 NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
index 09da8c8a8363a772d58ae443615b88b3bbbe068a..5ab950943efb84b0bc687ac4a2d41ea8b8808d9e 100644 (file)
@@ -12,6 +12,8 @@
             df, 
             du, 
             install, 
+            ipcrm, 
+            ipcs, 
             ln, 
             ls, 
             mkdir, 
index 34a6a6319cd9f70943032a4ce41d9ed5458204d2..e46896c3a98de18146521fe002e4cf8d385add3c 100644 (file)
@@ -12,7 +12,9 @@ NAME = chmod
 PROJECTVERSION = 2.8
 PROJECT_TYPE = Tool
 
-CFILES = chmod.c
+HFILES = chmod_acl.h
+
+CFILES = chmod.c chmod_acl.c
 
 OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chmod.1
 
index d0b29171f720a486b9ac0444964cf134174aa952..90c773486d28cf94ae2028ccc4385e2c057eba8d 100644 (file)
 .\"    @(#)chmod.1     8.4 (Berkeley) 3/31/94
 .\" $FreeBSD: src/bin/chmod/chmod.1,v 1.33 2002/10/01 20:32:59 trhodes Exp $
 .\"
-.Dd March 31, 1994
+.Dd July 08, 2004
 .Dt CHMOD 1
 .Os
 .Sh NAME
 .Nm chmod
-.Nd change file modes
+.Nd change file modes or Access Control Lists
 .Sh SYNOPSIS
 .Nm
 .Op Fl fv
 .Op Fl R Op Fl H | L | P
 .Ar mode
 .Ar
+.Nm
+.Op Fl fv
+.Op Fl R Op Fl H | L | P 
+.Op -a | +a | =a
+.Ar ACE
+.Ar
+.Nm
+.Op Fl fv
+.Op Fl R Op Fl H | L | P 
+.Op Fl E
+.Ar
+.Nm
+.Op Fl fv
+.Op Fl R Op Fl H | L | P 
+.Op Fl C
+.Ar
 .Sh DESCRIPTION
 The
 .Nm
 utility modifies the file mode bits of the listed files
 as specified by the
 .Ar mode
-operand.
+operand. It may also be used to modify the Access Control
+Lists (ACLs) associated with the listed files.
 .Pp
-The options are as follows:
+The generic options are as follows:
 .Bl -tag -width Ds
 .It Fl H
 If the
@@ -274,7 +291,7 @@ Operations upon the other permissions only (specified by the symbol
 ``o'' by itself), in combination with the
 .Ar perm
 symbols ``s'' or ``t'', are ignored.
-.Sh EXAMPLES
+.Sh EXAMPLES OF VALID MODES
 .Bl -tag -width "u=rwx,go=u-w" -compact
 .It Li 644
 make a file readable by anyone and writable by the owner only.
@@ -301,10 +318,209 @@ clear all mode bits for group and others.
 .It Li g=u-w
 set the group bits equal to the user bits, but clear the group write bit.
 .El
-.Sh BUGS
-There's no
-.Ar perm
-option for the naughty bits.
+.Sh ACL MANIPULATION OPTIONS
+ACLs are manipulated using extensions to the symbolic mode
+grammar.  Each file has one ACL, containing an ordered list of entries.
+Each entry refers to a user or group, and grants or denies a set of
+permissions.
+.Pp
+The following permissions are applicable to all filesystem objects:
+.Bl -tag -width 6n -compact -offset indent
+.It delete
+Delete the item.  Deletion may be granted by either this permission
+on an object or the delete_child right on the containing directory.
+.It readattr
+Read an objects basic attributes.  This is implicitly granted if 
+the object can be looked up and not explicitly denied.
+.It writeattr
+Write an object's basic attributes.
+.It readextattr
+Read extended attributes.
+.It writeextattr
+Write extended attributes.
+.It readsecurity
+Read an object's extended security information (ACL).
+.It writesecurity
+Write an object's security information (ownership, mode, ACL).
+.It chown
+Change an object's ownership.
+.El
+.Pp
+The following permissions are applicable to directories:
+.Bl -tag -width 6n -compact -offset indent
+.It list
+List entries.
+.It search
+Look up files by name.
+.It add_file
+Add a file.
+.It add_subdirectory
+Add a subdirectory.
+.It delete_child
+Delete a contained object.  See the file delete permission above.
+.El
+.Pp
+The following permissions are applicable to non-directory filesystem objects:
+.Bl -tag -width 6n -compact -offset indent
+.It read
+Open for reading.
+.It write
+Open for writing.
+.It append
+Open for writing, but in a fashion that only allows writes into areas of 
+the file not previously written.
+.It execute
+Execute the file as a script or program.
+.El
+.Pp
+ACL inheritance is controlled with the following permissions words, which
+may only be applied to directories:
+.Bl -tag -width 6n -compact -offset indent
+.It file_inherit
+Inherit to files.
+.It directory_inherit
+Inherit to directories.
+.It limit_inherit
+This flag is only relevant to entries inherited by subdirectories; it
+causes the directory_inherit flag to be cleared in the entry that is
+inherited, preventing further nested subdirectories from also
+inheriting the entry.
+.It only_inherit
+The entry is inherited by created items but not considered when processing
+the ACL.
+.El
+.Pp
+The ACL manipulation options are as follows:
+.Bl -tag -width Ds
+.It \fB+a\fR
+The +a mode parses a new ACL entry from the next argument on
+the commandline and inserts it into the canonical location in the
+ACL. If the supplied entry refers to an identity already listed, the
+two entries are combined.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+ # chmod +a "admin allow write" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: admin allow write
+ # chmod +a "guest deny read" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write
+ # chmod +a "admin allow delete" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write,delete
+.Pp
+The +a mode strives to maintain correct canonical form for the ACL.
+                 local deny
+                 local allow
+                 inherited deny
+                 inherited allow
+.Pp
+By default, chmod adds entries to the top of the local deny and local
+allow lists. Inherited entries are added by using the +ai mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write,delete
+   3: juser inherited deny delete
+   4: admin inherited allow delete
+   5: backup inherited deny read
+   6: admin inherited allow write-security
+ # chmod +ai "others allow write" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write,delete
+   3: juser inherited deny delete
+   4: others inherited allow read
+   5: admin inherited allow delete
+   6: backup inherited deny read
+   7: admin inherited allow write-security
+.It \fB+a#\fR
+When a specific ordering is required, the exact location at which an
+entry will be inserted is specified with the +a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write
+ # chmod +a# 2 "others deny read" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: others deny read
+   3: admin allow write
+.Pp
+The +ai# mode may be used to insert inherited entries at a specific
+location. Note that these modes allow non-canonical ACL ordering to
+be constructed.
+.It Fl a
+The -a mode is used to delete ACL entries. All entries exactly
+matching the supplied entry will be deleted. If the entry lists a
+subset of rights granted by an entry, only the rights listed are
+removed. Entries may also be deleted by index using the -a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: guest deny read
+   2: admin allow write,delete
+ # chmod -a# 1 file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: admin allow write,delete
+ # chmod -a "admin allow write" file1
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: admin allow delete
+.Pp
+Inheritance is not considered when processing the -a mode; rights and
+entries will be removed regardless of their inherited state.
+.It \fB=a#\fR
+Individual entries are rewritten using the =a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: admin allow delete
+ # chmod =a# 1 "admin allow write,chown"
+ # ls -le
+ -rw-r--r--+ 1 juser  wheel  0 Apr 28 14:06 file1
+   owner: juser
+   1: admin allow write,chown
+.Pp
+This mode may not be used to add new entries.
+.It Fl E    
+Reads the ACL information from stdin, as a sequential list
+of ACEs, separated by newlines.  If the information parses correctly,
+the existing information is replaced.
+.It Fl C
+Returns false if any of the named files have ACLs in non-canonical order.
+.It Fl i
+Removes the 'inherited' bit from all entries in the named file(s) ACLs.
+.It Fl I
+Removes all inherited entries from the named file(s) ACL(s).
+.El
 .Sh COMPATIBILITY
 The
 .Fl v
index c01c5dd32b3dfb4501880ee72d5f552189ccdd57..8651176b6a1a93cadeb4bca043785da9a17b512d 100644 (file)
@@ -57,21 +57,34 @@ __RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $
 #include <string.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include "chmod_acl.h"
+
+#endif /*__APPLE__*/
+
+int fflag = 0;
+
 int main(int, char *[]);
 void usage(void);
 
 int
 main(int argc, char *argv[])
 {
-       FTS *ftsp;
-       FTSENT *p;
-       mode_t *set;
-       long val;
-       int oct;
-       int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval;
+       FTS *ftsp = NULL;
+       FTSENT *p = NULL;
+       mode_t *set = NULL;
+       long val = 0;
+       int oct = 0;
+       int Hflag, Lflag, Rflag, ch, fts_options, hflag, rval;
        int vflag;
        char *ep, *mode;
        mode_t newmode, omode;
+#ifdef __APPLE__
+       unsigned int acloptflags = 0;
+       int aclpos = -1, inheritance_level = 0;
+       int index = 0, acloptlen = 0, ace_arg_not_required = 0;
+       acl_t acl_input = NULL;
+#endif /* __APPLE__*/
        int (*change_mode)(const char *, mode_t);
 
        set = NULL;
@@ -80,7 +93,7 @@ main(int argc, char *argv[])
 #ifndef __APPLE__
        while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
 #else
-       while ((ch = getopt(argc, argv, "HLPRXfgorstuvwx")) != -1)
+       while ((ch = getopt(argc, argv, "ACEHILPRVXafginorstuvwx")) != -1)
 #endif
                switch (ch) {
                case 'H':
@@ -112,6 +125,34 @@ main(int argc, char *argv[])
                         */
                        hflag = 1;
                        break;
+#else
+               case 'a':
+                       if (argv[optind - 1][0] == '-' &&
+                           argv[optind - 1][1] == ch)
+                               --optind;
+                       goto done;
+               case 'E':
+                       acloptflags |= ACL_FLAG | ACL_FROM_STDIN;
+                       goto done;
+               case 'C':
+                       acloptflags |= ACL_FLAG | ACL_CHECK_CANONICITY;
+                       ace_arg_not_required = 1;
+                       goto done;
+               case 'i':
+                       acloptflags |= ACL_FLAG | ACL_REMOVE_INHERIT_FLAG;
+                       ace_arg_not_required = 1;
+                       goto done;
+               case 'I':
+                       acloptflags |= ACL_FLAG | ACL_REMOVE_INHERITED_ENTRIES;
+                       ace_arg_not_required = 1;
+                       goto done;
+               case 'n':
+                       acloptflags |= ACL_FLAG | ACL_NO_TRANSLATE;
+                       break;
+               case 'V':
+                       acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR;
+                       ace_arg_not_required = 1;
+                       goto done;
 #endif /* __APPLE__ */
                /*
                 * XXX
@@ -136,8 +177,82 @@ main(int argc, char *argv[])
 done:  argv += optind;
        argc -= optind;
 
+#ifdef __APPLE__
+       if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2))
+               usage();
+#else
        if (argc < 2)
                usage();
+#endif
+
+#ifdef __APPLE__
+       if (!(acloptflags & ACL_FLAG) && ((acloptlen = strlen(argv[0])) > 1) && (argv[0][1] == 'a')) {
+               acloptflags |= ACL_FLAG;
+               switch (argv[0][0]) {
+               case '+':
+                       acloptflags |= ACL_SET_FLAG;
+                       break;
+               case '-':
+                       acloptflags |= ACL_DELETE_FLAG;
+                       break;
+               case '=':
+                       acloptflags |= ACL_REWRITE_FLAG;
+                       break;
+               default:
+                       acloptflags &= ~ACL_FLAG;
+                       goto apnoacl;
+               }
+               
+               if (argc < 3)
+                       usage();
+
+               if (acloptlen > 2) {
+                       for (index = 2; index < acloptlen; index++) {
+                               switch (argv[0][index]) {
+                               case '#':
+                                       acloptflags |= ACL_ORDER_FLAG;
+
+                                       if (argc < ((acloptflags & ACL_DELETE_FLAG)
+                                                   ? 3 : 4))
+                                               usage();
+                                       argv++;
+                                       argc--;
+                                       errno = 0;
+                                       aclpos = strtol(argv[0], &ep, 0);
+
+                                       if (aclpos > ACL_MAX_ENTRIES
+                                           || aclpos < 0)
+                                               errno = ERANGE;
+                                       if (errno || *ep)
+                                               err(1, "Invalid ACL entry number: %s", aclpos);
+                                       if (acloptflags & ACL_DELETE_FLAG)
+                                               ace_arg_not_required = 1;
+
+                                       goto apdone;
+                               case 'i':
+                                       acloptflags |= ACL_INHERIT_FLAG;
+                                       /* The +aii.. syntax to specify
+                                        * inheritance level is rather unwieldy,
+                                        * find an alternative.
+                                        */
+                                       inheritance_level++;
+                                       if (inheritance_level > 1)
+                                               warn("Inheritance across more than one generation is not currently supported");
+                                       if (inheritance_level >= MAX_INHERITANCE_LEVEL)
+                                               goto apdone;
+                                       break;
+                               default:
+                                       errno = EINVAL;
+                                       usage();
+                               }
+                       }
+               }
+apdone:
+               argv++;
+               argc--;
+       }
+apnoacl:
+#endif /*__APPLE__*/
 
        if (Rflag) {
                fts_options = FTS_PHYSICAL;
@@ -161,25 +276,72 @@ done:     argv += optind;
 #else
        change_mode = chmod;
 #endif /* __APPLE__ */
-
-       mode = *argv;
-       if (*mode >= '0' && *mode <= '7') {
-               errno = 0;
-               val = strtol(mode, &ep, 8);
-               if (val > USHRT_MAX || val < 0)
-                       errno = ERANGE;
-               if (errno)
-                       err(1, "invalid file mode: %s", mode);
-               if (*ep)
-                       errx(1, "invalid file mode: %s", mode);
-               omode = (mode_t)val;
-               oct = 1;
-       } else {
-               if ((set = setmode(mode)) == NULL)
-                       errx(1, "invalid file mode: %s", mode);
-               oct = 0;
+#ifdef __APPLE__
+       if (acloptflags & ACL_FROM_STDIN) {
+               int readval = 0, readtotal = 0;
+               
+               mode = (char *) malloc(MAX_ACL_TEXT_SIZE);
+               
+               if (mode == NULL)
+                       err(1, "Unable to allocate mode string");
+               /* Read the ACEs from STDIN */
+               do {
+                       readtotal += readval;
+                       readval = read(STDIN_FILENO, mode + readtotal, 
+                                      MAX_ACL_TEXT_SIZE);
+               } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE));
+                       
+               if (0 == readtotal)
+                       err(1, "-E specified, but read from STDIN failed");
+               else
+                       mode[readtotal - 1] = '\0';
+               --argv;
        }
+       else
+#endif /* __APPLE */
+               mode = *argv;
+
+#ifdef __APPLE__
+       if ((acloptflags & ACL_FLAG)) {
 
+               /* Are we deleting by entry number, verifying
+                * canonicity or performing some other operation that
+                * does not require an input entry? If so, there's no
+                * entry to convert.
+                */
+               if (ace_arg_not_required) {
+                       --argv;
+               }
+               else {
+                        /* Parse the text into an ACL*/
+                       acl_input = parse_acl_entries(mode);
+                       if (acl_input == NULL) {
+                               errno = EINVAL;
+                               err(1, "Invalid ACL specification: %s", mode);
+                       }
+               }
+       }
+       else {
+#endif /* __APPLE__*/
+               if (*mode >= '0' && *mode <= '7') {
+                       errno = 0;
+                       val = strtol(mode, &ep, 8);
+                       if (val > USHRT_MAX || val < 0)
+                               errno = ERANGE;
+                       if (errno)
+                               err(1, "Invalid file mode: %s", mode);
+                       if (*ep)
+                               errx(1, "Invalid file mode: %s", mode);
+                       omode = (mode_t)val;
+                       oct = 1;
+               } else {
+                       if ((set = setmode(mode)) == NULL)
+                               errx(1, "Invalid file mode: %s", mode);
+                       oct = 0;
+               }
+#ifdef __APPLE__
+       }
+#endif /* __APPLE__*/
        if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
                err(1, "fts_open");
        for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
@@ -211,36 +373,55 @@ done:     argv += optind;
                default:
                        break;
                }
-               newmode = oct ? omode : getmode(set, p->fts_statp->st_mode);
-               if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
-                       continue;
-               if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
-                       warn("%s", p->fts_path);
-                       rval = 1;
-               } else {
-                       if (vflag) {
-                               (void)printf("%s", p->fts_accpath);
-
-                               if (vflag > 1) {
-                                       char m1[12], m2[12];
-
-                                       strmode(p->fts_statp->st_mode, m1);
-                                       strmode((p->fts_statp->st_mode &
-                                           S_IFMT) | newmode, m2);
+#ifdef __APPLE__
+/* If an ACL manipulation option was specified, manipulate */
+               if (acloptflags & ACL_FLAG)     {
+                       if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, aclpos, inheritance_level))
+                               rval = 1;
+               }
+               else {
+#endif /* __APPLE__ */
+                       newmode = oct ? omode : getmode(set, p->fts_statp->st_mode);
+                       if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
+                               continue;
+                       if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
+                               warn("%s", p->fts_path);
+                               rval = 1;
+                       } else {
+                               if (vflag) {
+                                       (void)printf("%s", p->fts_accpath);
 
-                                       (void)printf(": 0%o [%s] -> 0%o [%s]",
-                                           p->fts_statp->st_mode, m1,
+                                       if (vflag > 1) {
+                                               char m1[12], m2[12];
+                                               
+                                               strmode(p->fts_statp->st_mode, m1);
+                                               strmode((p->fts_statp->st_mode &
+                                                        S_IFMT) | newmode, m2);
+                                               
+                                               (void)printf(": 0%o [%s] -> 0%o [%s]",
+                                                            p->fts_statp->st_mode, m1,
                                            (p->fts_statp->st_mode & S_IFMT) |
-                                           newmode, m2);
+                                                            newmode, m2);
+                                       }
+                                       (void)printf("\n");
                                }
-                               (void)printf("\n");
+                               
                        }
-
+#ifdef __APPLE__
                }
+#endif /* __APPLE__*/
        }
        if (errno)
                err(1, "fts_read");
-       free(set);
+#ifdef __APPLE__
+       if (acl_input)
+               acl_free(acl_input);
+       if (mode && (acloptflags & ACL_FROM_STDIN))
+               free(mode);
+       
+#endif /* __APPLE__ */
+       if (set)
+               free(set);
        exit(rval);
 }
 
@@ -249,7 +430,7 @@ usage(void)
 {
 #ifdef __APPLE__
        (void)fprintf(stderr,
-           "usage: chmod [-fv] [-R [-H | -L | -P]] mode file ...\n");
+           "usage: chmod [-fv] [-R [-H | -L | -P]] [-a | +a | =a  [i][# [ n]]] mode|entry file ...\n");
 #else
        (void)fprintf(stderr,
            "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
diff --git a/chmod/chmod_acl.c b/chmod/chmod_acl.c
new file mode 100644 (file)
index 0000000..eacc60d
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *     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.
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <membership.h>
+#include "chmod_acl.h"
+
+extern void usage(void);
+
+#ifdef __APPLE__
+static struct {
+       acl_perm_t      perm;
+       char            *name;
+       int             flags;
+#define ACL_PERM_DIR   (1<<0)
+#define ACL_PERM_FILE  (1<<1)
+} acl_perms[] = {
+       {ACL_READ_DATA,         "read",         ACL_PERM_FILE},
+       {ACL_LIST_DIRECTORY,    "list",         ACL_PERM_DIR},
+       {ACL_WRITE_DATA,        "write",        ACL_PERM_FILE},
+       {ACL_ADD_FILE,          "add_file",     ACL_PERM_DIR},
+       {ACL_EXECUTE,           "execute",      ACL_PERM_FILE},
+       {ACL_SEARCH,            "search",       ACL_PERM_DIR},
+       {ACL_DELETE,            "delete",       ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_APPEND_DATA,       "append",       ACL_PERM_FILE},
+       {ACL_ADD_SUBDIRECTORY,  "add_subdirectory", ACL_PERM_DIR},
+       {ACL_DELETE_CHILD,      "delete_child", ACL_PERM_DIR},
+       {ACL_READ_ATTRIBUTES,   "readattr",     ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_ATTRIBUTES,  "writeattr",    ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_READ_SECURITY,     "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_SECURITY,    "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_CHANGE_OWNER,      "chown",        ACL_PERM_FILE | ACL_PERM_DIR},
+       {0, NULL, 0}
+};
+
+static struct {
+       acl_flag_t      flag;
+       char            *name;
+       int             flags;
+} acl_flags[] = {
+       {ACL_ENTRY_INHERITED,           "inherited",            ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_ENTRY_FILE_INHERIT,        "file_inherit",         ACL_PERM_DIR},
+       {ACL_ENTRY_DIRECTORY_INHERIT,   "directory_inherit",    ACL_PERM_DIR},
+       {ACL_ENTRY_LIMIT_INHERIT,       "limit_inherit",        ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_ENTRY_ONLY_INHERIT,        "only_inherit",         ACL_PERM_DIR},
+       {0, NULL, 0}
+};
+
+/* TBD - Many of these routines could potentially be considered for
+ * inclusion in a library. If that is done, either avoid use of "err"
+ * and implement a better fall-through strategy in case of errors, 
+ * or use err_set_exit() and make various structures globals.
+ */
+
+/* Perform a name to uuid mapping - calls through to memberd */
+
+uuid_t *
+name_to_uuid(char *tok) {
+       struct passwd *tpass = NULL;
+       struct group *tgrp = NULL;
+       uuid_t *entryg = NULL;
+       char is_uid = 1;
+
+       if ((entryg = (uuid_t *) calloc(1,sizeof(uuid_t))) == NULL)
+               err(1, "Unable to allocate a uuid");
+       
+       tpass = getpwnam(tok);
+
+       if (tpass == NULL) {
+               tgrp = getgrnam(tok);
+               if (tgrp == NULL) {
+                       errno = EINVAL;
+                       err(1, "Unable to translate %s to a UID/GID", tok);
+               }
+               is_uid = 0;
+       }
+       
+       if (tpass) {
+               if (0 != mbr_uid_to_uuid(tpass->pw_uid, entryg)) {
+                       errno = EINVAL;
+                       err(1, "mbr_uid_to_uuid(): Unable to translate");
+               }
+       }
+       else {
+               if (0 != mbr_gid_to_uuid(tgrp->gr_gid, entryg)) {
+                       errno = EINVAL;
+                       err(1, "mbr_gid_to_uuid(): Unable to translate");
+               }
+       }
+       return entryg;
+}
+
+/* Convert an acl entry in string form to an acl_entry_t */
+int
+parse_entry(char *entrybuf, acl_entry_t newent) {
+       char *tok;
+       char *pebuf;
+       uuid_t *entryg;
+
+       acl_tag_t       tag;
+       acl_permset_t   perms;
+       acl_flagset_t   flags;
+       unsigned permcount = 0;
+       unsigned pindex = 0;
+
+       acl_get_permset(newent, &perms);
+       acl_get_flagset_np(newent, &flags);
+
+       pebuf = entrybuf;
+
+       tok = strsep(&pebuf, " ");
+       
+       if ((tok == NULL) || *tok == '\0') {
+               errno = EINVAL;
+               err(1, "Invalid entry format");
+       }
+
+       /* parse the name into a qualifier */
+       entryg = name_to_uuid(tok);
+
+       tok = strsep(&pebuf, " ");
+       if ((tok == NULL) || *tok == '\0') {
+               errno = EINVAL;
+               err(1, "Invalid entry format");
+       }
+
+       /* is the verb 'allow' or 'deny'? */
+       if (!strcmp(tok, "allow")) {
+               tag = ACL_EXTENDED_ALLOW;
+       } else if (!strcmp(tok, "deny")) {
+               tag = ACL_EXTENDED_DENY;
+       } else {
+               errno = EINVAL;
+               err(1, "Unknown tag type '%s'", tok);
+       }
+
+       /* parse permissions */
+       for (; (tok = strsep(&pebuf, ",")) != NULL;) {
+               if (*tok != '\0') {
+                       /* is it a permission? */
+                       for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) {
+                               if (!strcmp(acl_perms[pindex].name, tok)) {
+                                       /* got one */
+                                       acl_add_perm(perms, acl_perms[pindex].perm);
+                                       permcount++;
+                                       goto found;
+                               }
+                       }
+                       /* is it a flag? */
+                       for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) {
+                               if (!strcmp(acl_flags[pindex].name, tok)) {
+                                       /* got one */
+                                       acl_add_flag_np(flags, acl_flags[pindex].flag);
+                                       permcount++;
+                                       goto found;
+                               }
+                       }
+                       errno = EINVAL;
+                       err(1,"Invalid permission type %s", tok);
+               found:
+                       continue;
+               }
+       }
+       if (0 == permcount) {
+               errno = EINVAL;
+               err(1, "No permissions specified");
+       }
+       acl_set_tag_type(newent, tag);
+       acl_set_qualifier(newent, entryg);
+       acl_set_permset(newent, perms);
+       acl_set_flagset_np(newent, flags);
+       free(entryg);
+
+       return(0);
+}
+
+/* Convert one or more acl entries in string form to an acl_t */
+acl_t
+parse_acl_entries(const char *input) {
+       acl_t acl_input;
+       acl_entry_t newent;
+       char *inbuf;
+       char *oinbuf;
+
+       char **bufp, *entryv[ACL_MAX_ENTRIES];
+#if 0
+/* XXX acl_from_text(), when implemented, will presumably use the canonical 
+ * text representation format, which is what chmod should be using 
+ * We may need to add an entry number to the input
+ */
+       /* Translate the user supplied ACL entry */
+       /* acl_input = acl_from_text(input); */
+#else
+       inbuf = malloc(MAX_ACL_TEXT_SIZE);
+       
+       if (inbuf == NULL)
+               err(1, "malloc() failed");
+       strncpy(inbuf, input, MAX_ACL_TEXT_SIZE);
+       inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0';
+
+       if ((acl_input = acl_init(1)) == NULL)
+               err(1, "acl_init() failed");
+
+       oinbuf = inbuf;
+
+       for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;)
+               if (**bufp != '\0') {
+                       if (0 != acl_create_entry(&acl_input, &newent))
+                               err(1, "acl_create_entry() failed");
+                       if (0 != parse_entry(*bufp, newent)) {
+                               errno = EINVAL;
+                               err(1, "Failed parsing entry %s", *bufp);
+                       }
+                       if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) {
+                               errno = ERANGE;
+                               err(1, "Too many entries");
+                       }
+               }
+       
+       free(inbuf);
+       return acl_input;
+#endif /* #if 0 */
+}
+
+/* XXX No Libc support for inherited entries and generation determination yet */
+unsigned
+get_inheritance_level(acl_entry_t entry) {
+/* XXX to be implemented */
+       return 1;
+}
+
+/* Determine a "score" for an acl entry. The entry scores higher if it's
+ * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
+ * than inherited entries.
+ */
+
+int
+score_acl_entry(acl_entry_t entry) {
+
+       acl_tag_t       tag;
+       acl_flagset_t   flags;
+       acl_permset_t   perms;
+       
+       int score = 0;
+
+       if (entry == NULL)
+               return (MINIMUM_TIER);
+
+       if (acl_get_tag_type(entry, &tag) != 0) {
+               err(1, "Malformed ACL entry, no tag present");
+       }
+       if (acl_get_flagset_np(entry, &flags) != 0){
+               err(1, "Unable to obtain flagset");
+       }
+       if (acl_get_permset(entry, &perms) != 0)
+               err(1, "Malformed ACL entry, no permset present");
+       
+       switch(tag) {
+       case ACL_EXTENDED_ALLOW:
+               break;
+       case ACL_EXTENDED_DENY:
+               score++;
+               break;
+       default:
+               errno = EINVAL;
+               err(1, "Unknown tag type present in ACL entry");
+               /* NOTREACHED */
+       }
+
+       if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
+               score += get_inheritance_level(entry) * INHERITANCE_TIER;
+
+       return score;
+}
+
+int
+compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) {
+       return bcmp(qa, qb, sizeof(uuid_t));
+}
+
+/* Compare two ACL permsets. 
+ *  Returns :
+ *  MATCH_SUBSET if bperms is a subset of aperms
+ *  MATCH_SUPERSET if bperms is a superset of aperms
+ *  MATCH_PARTIAL if the two permsets have a common subset
+ *  MATCH_EXACT if the two permsets are identical
+ *  MATCH_NONE if they are disjoint
+ */
+
+int
+compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms)
+{
+       int i;
+/* TBD Implement other match levels as needed */
+       for (i = 0; acl_perms[i].name != NULL; i++) {
+               if (acl_get_perm_np(aperms, acl_perms[i].perm) != 
+                   acl_get_perm_np(bperms, acl_perms[i].perm))
+                       return MATCH_NONE;
+       }
+       return MATCH_EXACT;
+}
+
+int
+compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags)
+{
+       int i;
+/* TBD Implement other match levels as needed */
+       for (i = 0; acl_flags[i].name != NULL; i++) {
+               if (acl_get_flag_np(aflags, acl_flags[i].flag) != 
+                   acl_get_flag_np(bflags, acl_flags[i].flag))
+                       return MATCH_NONE;
+       }
+       return MATCH_EXACT;
+}
+
+/* Compares two ACL entries for equality */
+int
+compare_acl_entries(acl_entry_t a, acl_entry_t b)
+{
+       acl_tag_t atag, btag;
+       acl_permset_t aperms, bperms;
+       acl_flagset_t aflags, bflags;
+       int pcmp = 0, fcmp = 0;
+
+       if (0 != compare_acl_qualifiers(acl_get_qualifier(a), 
+                                       acl_get_qualifier(b)))
+               return MATCH_NONE;
+
+       if (0 != acl_get_tag_type(a, &atag))
+               err(1, "No tag type present in entry");
+       if (0!= acl_get_tag_type(b, &btag))
+               err(1, "No tag type present in entry");
+
+       if (atag != btag)
+               return MATCH_NONE;
+
+       if ((acl_get_permset(a, &aperms) != 0) ||
+           (acl_get_flagset_np(a, &aflags) != 0) ||
+           (acl_get_permset(b, &bperms) != 0) ||
+           (acl_get_flagset_np(b, &bflags) != 0))
+               err(1, "error fetching permissions");
+
+       pcmp = compare_acl_permsets(aperms, bperms);
+       fcmp = compare_acl_flagsets(aflags, bflags);
+
+       if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE))
+               return(MATCH_PARTIAL);
+       else
+               return(MATCH_EXACT);
+}
+
+/* Verify that an ACL is in canonical order. Currently, the canonical
+ * form is:
+ * local deny
+ * local allow
+ * inherited deny (parent)
+ * inherited allow (parent)
+ * inherited deny (grandparent)
+ * inherited allow (grandparent)
+ * ...
+ */
+unsigned int
+is_canonical(acl_t acl) {
+       
+       unsigned aindex;
+       acl_entry_t entry;
+       int score = 0, next_score = 0;
+
+/* XXX - is a zero entry ACL in canonical form? */     
+       if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
+               return 1;
+
+       score = score_acl_entry(entry);
+       
+       for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0;
+            aindex++)  {
+               if (score < (next_score = score_acl_entry(entry)))
+                       return 0;
+               score = next_score;
+       }
+       return 1;
+}
+
+
+/* Iterate through an ACL, and find the canonical position for the
+ * specified entry 
+ */
+unsigned int
+find_canonical_position(acl_t acl, acl_entry_t modifier) {
+
+       acl_entry_t entry;
+       int mscore = 0;
+       unsigned mpos = 0;
+
+       /* Check if there's an entry with the same qualifier
+        * and tag type; if not, find the appropriate slot
+        * for the score.
+        */
+
+       if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
+               return 0;
+
+       mscore = score_acl_entry(modifier);
+
+       while (mscore < score_acl_entry(entry)) {
+
+               mpos++;
+
+              if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry))
+                      break;
+
+              }
+       return mpos;
+}
+
+int canonicalize_acl_entries(acl_t acl);
+
+/* For a given acl_entry_t "modifier", find the first exact or 
+ * partially matching entry from the specified acl_t acl
+ */
+
+int
+find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp,
+                    unsigned match_inherited) {
+
+       acl_entry_t entry = NULL;
+
+       unsigned aindex;
+       int cmp, fcmp = MATCH_NONE;
+       
+       for (aindex = 0; 
+            acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : 
+                          ACL_NEXT_ENTRY, &entry) == 0;
+            aindex++)  {
+               cmp = compare_acl_entries(entry, modifier);
+               if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) {
+                       if (match_inherited) {
+                               acl_flagset_t eflags, mflags;
+                               
+                               if (0 != acl_get_flagset_np(modifier, &mflags))
+                                       err(1, "Unable to get flagset");
+                               
+                               if (0 != acl_get_flagset_np(entry, &eflags))
+                                       err(1, "Unable to get flagset");
+                                       
+                               if (acl_get_flag_np(mflags, ACL_ENTRY_INHERITED) ==
+                                   acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) {
+                                       *rentryp = entry;
+                                       fcmp = cmp;
+                               }
+                       }
+                       else {
+                               *rentryp = entry;
+                               fcmp = cmp;
+                       }
+               }
+               if (fcmp == MATCH_EXACT)
+                       break;
+       }
+       return fcmp;
+}
+
+/* Remove all perms specified in modifier from rentry*/
+int
+subtract_from_entry(acl_entry_t rentry, acl_entry_t  modifier)
+{
+       acl_permset_t rperms, mperms;
+       acl_flagset_t rflags, mflags;
+       int i;
+
+       if ((acl_get_permset(rentry, &rperms) != 0) ||
+           (acl_get_flagset_np(rentry, &rflags) != 0) ||
+           (acl_get_permset(modifier, &mperms) != 0) ||
+           (acl_get_flagset_np(modifier, &mflags) != 0))
+               err(1, "error computing ACL modification");
+
+       for (i = 0; acl_perms[i].name != NULL; i++) {
+               if (acl_get_perm_np(mperms, acl_perms[i].perm))
+                       acl_delete_perm(rperms, acl_perms[i].perm);
+       }
+       for (i = 0; acl_flags[i].name != NULL; i++) {
+               if (acl_get_flag_np(mflags, acl_flags[i].flag))
+                       acl_delete_flag_np(rflags, acl_flags[i].flag);
+       }
+       acl_set_permset(rentry, rperms);
+       acl_set_flagset_np(rentry, rflags);
+       return 0;
+}
+/* Add the perms specified in modifier to rentry */
+int
+merge_entry_perms(acl_entry_t rentry, acl_entry_t  modifier)
+{
+       acl_permset_t rperms, mperms;
+       acl_flagset_t rflags, mflags;
+       int i;
+
+       if ((acl_get_permset(rentry, &rperms) != 0) ||
+           (acl_get_flagset_np(rentry, &rflags) != 0) ||
+           (acl_get_permset(modifier, &mperms) != 0) ||
+           (acl_get_flagset_np(modifier, &mflags) != 0))
+               err(1, "error computing ACL modification");
+
+       for (i = 0; acl_perms[i].name != NULL; i++) {
+               if (acl_get_perm_np(mperms, acl_perms[i].perm))
+                       acl_add_perm(rperms, acl_perms[i].perm);
+       }
+       for (i = 0; acl_flags[i].name != NULL; i++) {
+               if (acl_get_flag_np(mflags, acl_flags[i].flag))
+                       acl_add_flag_np(rflags, acl_flags[i].flag);
+       }
+       acl_set_permset(rentry, rperms);
+       acl_set_flagset_np(rentry, rflags);
+       return 0;
+}
+
+int
+modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags,
+          int position, int inheritance_level, 
+          unsigned flag_new_acl) {
+
+       unsigned cpos = 0;
+       acl_entry_t newent = NULL;
+       int dmatch = 0;
+       acl_entry_t rentry = NULL;
+       unsigned retval = 0;
+       acl_t oacl = *oaclp;
+       
+/* Add the inherited flag if requested by the user*/
+       if (modifier && (optflags & ACL_INHERIT_FLAG)) {
+               acl_flagset_t mflags;
+
+               acl_get_flagset_np(modifier, &mflags);
+               acl_add_flag_np(mflags, ACL_ENTRY_INHERITED);
+               acl_set_flagset_np(modifier, mflags);
+       }
+
+       if (optflags & ACL_SET_FLAG) {
+               if (position != -1) {
+                       if (0 != acl_create_entry_np(&oacl, &newent, position))
+                               err(1, "acl_create_entry() failed");
+                       acl_copy_entry(newent, modifier);
+               }
+               else {
+/* If an entry exists, add the new permissions to it, else add an
+ * entry in the canonical position.
+ */
+
+/* First, check for a matching entry - if one exists, merge flags */
+                       dmatch = find_matching_entry(oacl, modifier, &rentry, 1);
+
+                       if (dmatch != MATCH_NONE) {
+                               if (dmatch == MATCH_EXACT)
+/* Nothing to be done */
+                                       goto ma_exit; 
+                               
+                               if (dmatch == MATCH_PARTIAL) {
+                                       merge_entry_perms(rentry, modifier);
+                                       goto ma_exit;
+                               }
+                       }
+/* Insert the entry in canonical order */
+                       cpos = find_canonical_position(oacl, modifier);
+                       if (0!= acl_create_entry_np(&oacl, &newent, cpos))
+                               err(1, "acl_create_entry() failed");
+                       acl_copy_entry(newent, modifier);
+               }
+       }
+       else
+               if (optflags & ACL_DELETE_FLAG) {
+
+                       if (flag_new_acl) {
+                               errno = EINVAL;
+                               err(1, "No ACL present");
+                       }
+                       if (position != -1 ) {
+                               if (0 != acl_get_entry(oacl, position, &rentry))
+                                       err(1, "Invalid entry number");
+                               acl_delete_entry(oacl, rentry);
+                       }
+                       else {
+                               unsigned match_found = 0, aindex;
+                               for (aindex = 0; 
+                                    acl_get_entry(oacl, rentry == NULL ? 
+                                                  ACL_FIRST_ENTRY : 
+                                                  ACL_NEXT_ENTRY, &rentry) 
+                                            == 0; aindex++)    {
+                                       unsigned cmp;
+                                       cmp = compare_acl_entries(rentry, 
+                                                                 modifier);
+                                       if ((cmp == MATCH_EXACT) || 
+                                           (cmp == MATCH_PARTIAL)) {
+                                               match_found++;
+                                               if (cmp == MATCH_EXACT)
+                                                       acl_delete_entry(oacl, rentry);
+                                               else
+/* In the event of a partial match, remove the specified perms from the 
+ * entry */
+                                                       subtract_from_entry(rentry, modifier);
+                                       }
+                               }
+                               if (0 == match_found) {
+                                       errno = EINVAL;
+                                       warn("Entry not found when attempting delete");
+                                       retval = 1;
+                               }
+                       }
+               }
+               else
+                       if (optflags & ACL_REWRITE_FLAG) {
+                               acl_entry_t rentry;
+                               
+                               if (-1 == position) {
+                                       usage();
+                               }
+                               if (0 == flag_new_acl) {
+                                       if (0 != acl_get_entry(oacl, position,
+                                                              &rentry))
+                                               err(1, "Invalid entry number");
+                                       
+                                       if (0 != acl_delete_entry(oacl, rentry))
+                                               err(1, "Unable to delete entry");
+                               }
+                               if (0!= acl_create_entry_np(&oacl, &newent, position))
+                                       err(1, "acl_create_entry() failed");
+                               acl_copy_entry(newent, modifier);
+                       }
+ma_exit:
+       *oaclp = oacl;
+       return retval;
+}
+
+int
+modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level) {
+       
+       acl_t oacl = NULL;
+       unsigned aindex  = 0, flag_new_acl = 0;
+       acl_entry_t newent = NULL;
+       acl_entry_t entry = NULL;
+       unsigned retval = 0 ;
+
+       extern int fflag;
+
+/* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
+ * associated with the file, and has had its entries removed.
+ * However, POSIX 1003.1e states that a zero entry ACL should be 
+ * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is 
+ * associated with the path; it
+ * does not specifically state that a request for ACL_TYPE_EXTENDED
+ * should not return a zero entry ACL, however.
+ */
+
+/* Determine if we've been given a zero entry ACL, or create an ACL if 
+ * none exists. There are some issues to consider here: Should we create
+ * a zero-entry ACL for a delete or check canonicity operation?
+ */
+
+       if (path == NULL)
+               usage();
+
+       if (optflags & ACL_FROM_STDIN)
+               oacl = acl_dup(modifier);
+       else {
+               oacl = acl_get_file(path, ACL_TYPE_EXTENDED);
+               
+               if ((oacl == NULL) ||
+                   (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) {
+                       if ((oacl = acl_init(1)) == NULL)
+                               err(1, "acl_init() failed");
+                       flag_new_acl = 1;
+                       position = 0;
+               }
+       
+               if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG | 
+                                                       ACL_REMOVE_INHERITED_ENTRIES))) {
+                       acl_t facl = NULL;
+                       if ((facl = acl_init(1)) == NULL)
+                               err(1, "acl_init() failed");
+                       for (aindex = 0; 
+                            acl_get_entry(oacl, 
+                                          (entry == NULL ? ACL_FIRST_ENTRY : 
+                                           ACL_NEXT_ENTRY), &entry) == 0; 
+                            aindex++) {
+                               acl_flagset_t eflags;
+                               acl_entry_t fent = NULL;
+                               if (acl_get_flagset_np(entry, &eflags) != 0) {
+                                       err(1, "Unable to obtain flagset");
+                               }
+                               
+                               if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) {
+                                       if (optflags & ACL_REMOVE_INHERIT_FLAG) {
+                                               acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED);
+                                               acl_set_flagset_np(entry, eflags);
+                                               acl_create_entry(&facl, &fent);
+                                               acl_copy_entry(fent, entry);
+                                       }
+                               }
+                               else {
+                                       acl_create_entry(&facl, &fent);
+                                       acl_copy_entry(fent, entry);
+                               }
+                       }
+                       if (oacl)
+                               acl_free(oacl);
+                       oacl = facl;
+               }
+               else
+               if (optflags & ACL_CHECK_CANONICITY) {
+                       if (flag_new_acl) {
+                               errno = EINVAL;
+                               warn("No ACL currently associated with file %s", path);
+                       }
+                       return(is_canonical(oacl));
+               }
+               else
+               if ((optflags & ACL_SET_FLAG) && (position == -1) && 
+                   (!is_canonical(oacl))) {
+                       errno = EINVAL;
+                       warn("The specified file %s does not have an ACL in canonical order, please specify a position with +a# ", path);
+                       retval = 1;
+               }
+               else
+               if (((optflags & ACL_DELETE_FLAG) && (position != -1))
+                   || (optflags & ACL_CHECK_CANONICITY)) {
+                       retval = modify_acl(&oacl, NULL, optflags, position, 
+                                  inheritance_level, flag_new_acl);
+               }
+               else
+                       for (aindex = 0; 
+                            acl_get_entry(modifier, 
+                                          (entry == NULL ? ACL_FIRST_ENTRY : 
+                                           ACL_NEXT_ENTRY), &entry) == 0; 
+                            aindex++) {
+
+                                           retval += modify_acl(&oacl, entry, optflags, 
+                                                                position, inheritance_level, 
+                                                                flag_new_acl);
+                                   }
+                       }
+
+/* XXX Potential race here, since someone else could've modified or
+ * read the ACL on this file (with the intention of modifying it) in
+ * the interval from acl_get_file() to acl_set_file(); we can
+ * minimize one aspect of this  window by comparing the original acl
+ * to a fresh one from acl_get_file() but we could consider a
+ * "changeset" mechanism, common locking  strategy, or kernel
+ * supplied reservation mechanism to prevent this race.
+ */
+       if (!(optflags & ACL_CHECK_CANONICITY) && 
+           (0 != acl_set_file(path, ACL_TYPE_EXTENDED, oacl))){
+               if (!fflag)
+                       warn("Failed to set ACL on file %s", path);
+               retval = 1;
+       }
+       
+       if (oacl)
+               acl_free(oacl);
+       
+       return retval;
+}
+
+#endif /*__APPLE__*/
diff --git a/chmod/chmod_acl.h b/chmod/chmod_acl.h
new file mode 100644 (file)
index 0000000..1bab724
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *     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.
+ */
+
+#ifdef __APPLE__
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+#include <sys/acl.h>
+#include <sys/kauth.h>
+#include <uuid/uuid.h>
+
+#define ACL_FLAG (1<<0)
+#define ACL_SET_FLAG (1<<1)
+#define ACL_DELETE_FLAG (1<<2)
+#define ACL_REWRITE_FLAG (1<<3)
+#define ACL_ORDER_FLAG (1<<4)
+#define ACL_INHERIT_FLAG (1<<5)
+#define ACL_FOLLOW_LINK (1<<6)
+#define ACL_FROM_STDIN (1<<7)
+#define ACL_CHECK_CANONICITY (1<<8)
+#define ACL_REMOVE_INHERIT_FLAG (1<<9)
+#define ACL_REMOVE_INHERITED_ENTRIES (1<<10)
+#define ACL_NO_TRANSLATE (1<<11)
+#define ACL_INVOKE_EDITOR (1<<12)
+
+#define INHERITANCE_TIER (-5)
+#define MINIMUM_TIER (-1000)
+
+#define MATCH_EXACT (2)
+#define MATCH_PARTIAL (1)
+#define MATCH_NONE (-1)
+#define MATCH_SUBSET (-2)
+#define MATCH_SUPERSET (-3)
+
+#define MAX_ACL_TEXT_SIZE 4096
+#define MAX_INHERITANCE_LEVEL 1024
+
+extern int search_acl_block(char *tok);
+extern int parse_entry(char *entrybuf, acl_entry_t newent);
+extern acl_t parse_acl_entries(const char *input);
+extern int score_acl_entry(acl_entry_t entry);
+extern unsigned get_inheritance_level(acl_entry_t entry);
+extern int compare_acl_qualifiers(uuid_t *qa, uuid_t *qb);
+extern int compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms);
+extern int compare_acl_entries(acl_entry_t a, acl_entry_t b);
+extern unsigned is_canonical(acl_t acl);
+extern int find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentry, unsigned match_inherited);
+extern unsigned find_canonical_position(acl_t acl, acl_entry_t modifier);
+extern int subtract_from_entry(acl_entry_t rentry, acl_entry_t  modifier);
+extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl);
+extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level);
+extern uuid_t *name_to_uuid(char *tok);
+#endif /* __APPLE__*/
index 9f141bfb66c072d5a59003b0cd2a7bffc0f5cb28..18d467ddd347eb6ac4cf1680799021c3d8c4a31a 100644 (file)
@@ -40,7 +40,7 @@
 .Nd change file owner and group
 .Sh SYNOPSIS
 .Nm
-.Op Fl fv
+.Op Fl fhv
 .Oo
 .Fl R
 .Op Fl H | Fl L | Fl P
@@ -48,7 +48,7 @@
 .Ar owner Ns Op : Ns Ar group
 .Ar
 .Nm
-.Op Fl fv
+.Op Fl fhv
 .Oo
 .Fl R
 .Op Fl H | Fl L | Fl P
@@ -59,6 +59,9 @@
 The
 .Nm
 utility changes the user ID and/or the group ID of the specified files.
+Symbolic links named by arguments are silently left unchanged unless
+.Fl h
+is used.
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
@@ -82,6 +85,9 @@ in the files instead of just the files themselves.
 .It Fl f
 Don't report any failure to change file owner or group, nor modify
 the exit status to reflect such failures.
+.It Fl h
+If the file is a symbolic link, change the user ID and/or the
+group ID of the link itself.
 .It Fl v
 Cause
 .Nm
index 03305f55b80087c160aded286ccf0ffb2ced31bc..44a0f4317b7bef64f5488e67b139c8567db0f7f9 100644 (file)
@@ -84,11 +84,7 @@ main(int argc, char **argv)
        ischown = (strcmp(cp, "chown") == 0);
 
        Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
-#ifndef __APPLE__
        while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
-#else
-       while ((ch = getopt(argc, argv, "HLPRfv")) != -1)
-#endif
                switch (ch) {
                case 'H':
                        Hflag = 1;
@@ -107,11 +103,9 @@ main(int argc, char **argv)
                case 'f':
                        fflag = 1;
                        break;
-#ifndef __APPLE__
                case 'h':
                        hflag = 1;
                        break;
-#endif
                case 'v':
                        vflag = 1;
                        break;
@@ -192,11 +186,7 @@ main(int argc, char **argv)
                if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
                    (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
                        continue;
-#ifndef __APPLE__
                if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
-#else
-               if (chown(p->fts_accpath, uid, gid) == -1) {
-#endif /* __APPLE__ */
                        if (!fflag) {
                                chownerr(p->fts_path);
                                rval = 1;
@@ -289,10 +279,6 @@ usage(void)
                    "       chown [-fhv] [-R [-H | -L | -P]] :group file ...");
        else
                (void)fprintf(stderr, "%s\n",
-#ifndef __APPLE__
                    "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
-#else
-                   "usage: chgrp [-fv] [-R [-H | -L | -P]] group file ...");
-#endif
        exit(1);
 }
index 0c13072ecf58bdbb6ca2be6f26e0861a5f3058b5..9478ad1d8e20228ced707eff6ff5d4e6b209afb0 100644 (file)
@@ -15,8 +15,9 @@ PROJECT_TYPE = Tool
 CFILES = compress.c zopen.c
 
 OTHERSRCS = Makefile Makefile.preamble Makefile.postamble compress.1\
-            zopen.3 uncompress.1
+            zopen.3 uncompress.1 zopen.h
 
+NEXTSTEP_PB_CFLAGS += -D__FBSDID=__RCSID -D"__printflike(a,b)="
 
 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
 CODE_GEN_STYLE = DYNAMIC
@@ -26,10 +27,7 @@ LIBS =
 DEBUG_LIBS = $(LIBS)
 PROF_LIBS = $(LIBS)
 
-
-
-
-NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/file_cmds/Build
 
 NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
 WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
index 25348552fd0d7f9cd9ce90154ac022b74d703641..b616c13f2a54281937391b1f7a8266b1d273a2d8 100644 (file)
@@ -1,5 +1,3 @@
-.\"    $NetBSD: compress.1,v 1.6 1997/09/15 10:58:37 lukem Exp $
-.\"
 .\" Copyright (c) 1986, 1990, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\" $FreeBSD: src/usr.bin/compress/compress.1,v 1.19 2004/07/05 17:12:53 ru Exp $
 .\"
-.Dd April 18, 1994
+.Dd May 17, 2002
 .Dt COMPRESS 1
-.Os BSD 4.3
+.Os
 .Sh NAME
 .Nm compress ,
-.\".Nm uncompress ,
 .Nm uncompress
-.\".Nm zcat
 .Nd compress and expand data
 .Sh SYNOPSIS
 .Nm
 .Op Ar
 .Nm uncompress
 .Op Fl cfv
-.Op Ar 
-.\".Nm zcat
-.\".Op Ar
+.Op Ar
 .Sh DESCRIPTION
+The
 .Nm
-reduces the size of the named files using adaptive Lempel-Ziv coding.
+utility reduces the size of the named files using adaptive Lempel-Ziv coding.
 Each
 .Ar file
 is renamed to the same name plus the extension
@@ -70,15 +66,12 @@ If compression would not reduce the size of a
 .Ar file ,
 the file is ignored.
 .Pp
+The
 .Nm uncompress
-restores the compressed files to their original form, renaming the
+utility restores the compressed files to their original form, renaming the
 files by deleting the
 .Dq .Z
 extension.
-.\".Pp
-.\".Nm Zcat
-.\"is an alias for
-.\".Dq "uncompress -c" .
 .Pp
 If renaming the files would cause files to be overwritten and the standard
 input device is a terminal, the user is prompted (on the standard error
@@ -86,14 +79,17 @@ output) for confirmation.
 If prompting is not possible or confirmation is not received, the files
 are not overwritten.
 .Pp
-If no files are specified, the standard input is compressed or uncompressed
-to the standard output.
+If no files are specified or a
+.Ar file
+argument is a single dash
+.Pq Sq Fl ,
+the standard input is compressed or uncompressed to the standard output.
 If either the input and output files are not regular files, the checks for
 reduction in size and file overwriting are not performed, the input file is
 not removed, and the attributes of the input file are not retained.
 .Pp
 The options are as follows:
-.Bl -tag -width Ds
+.Bl -tag -width indent
 .It Fl b
 Specify the
 .Ar bits
@@ -110,8 +106,9 @@ Additionally, files are overwritten without prompting for confirmation.
 Print the percentage reduction of each file.
 .El
 .Pp
+The
 .Nm
-uses a modified Lempel-Ziv algorithm.
+utility uses a modified Lempel-Ziv algorithm.
 Common substrings in the file are first replaced by 9-bit codes 257 and up.
 When code 512 is reached, the algorithm switches to 10-bit codes and
 continues to use more bits until the
@@ -131,13 +128,14 @@ If it is increasing,
 continues to use the existing code dictionary.
 However, if the compression ratio decreases,
 .Nm
-discards the table of substrings and rebuilds it from scratch.  This allows
+discards the table of substrings and rebuilds it from scratch.
+This allows
 the algorithm to adapt to the next "block" of the file.
 .Pp
 The
 .Fl b
 flag is omitted for
-.Ar uncompress
+.Nm uncompress
 since the
 .Ar bits
 parameter specified during compression
@@ -145,7 +143,6 @@ is encoded within the output, along with
 a magic number to ensure that neither decompression of random data nor
 recompression of compressed data is attempted.
 .Pp
-.ne 8
 The amount of compression obtained depends on the size of the
 input, the number of
 .Ar bits
@@ -155,12 +152,22 @@ Compression is generally much better than that achieved by Huffman
 coding (as used in the historical command pack), or adaptive Huffman
 coding (as used in the historical command compact), and takes less
 time to compute.
+.Sh DIAGNOSTICS
+.Ex -std compress uncompress
 .Pp
 The
-.Nm
-utility exits 0 on success, and >0 if an error occurs.
+.Nm compress
+utility exits 2 if attempting to compress the file would not reduce its size
+and the
+.Fl f
+option was not specified.
 .Sh SEE ALSO
-.Xr zcat 1
+.Xr gunzip 1 ,
+.Xr gzexe 1 ,
+.Xr gzip 1 ,
+.Xr zcat 1 ,
+.Xr zmore 1 ,
+.Xr znew 1
 .Rs
 .%A Welch, Terry A.
 .%D June, 1984
@@ -169,6 +176,13 @@ utility exits 0 on success, and >0 if an error occurs.
 .%V 17:6
 .%P pp. 8-19
 .Re
+.Sh STANDARDS
+The
+.Nm compress
+and
+.Nm uncompress
+utilities conform to
+.St -p1003.1-2001 .
 .Sh HISTORY
 The
 .Nm
index 2aa84ee5877c068caef8285d4ba1acb7b880b829..14b509fe84cdd397a391daa9803db76327d1e448 100644 (file)
@@ -1,5 +1,3 @@
-/*     $NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $     */
-
 /*-
  * Copyright (c) 1992, 1993
  *     The Regents of the University of California.  All rights reserved.
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
 #ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\
-       The Regents of the University of California.  All rights reserved.\n");
-#endif /* not lint */
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif
 
-#ifndef lint
 #if 0
+#ifndef lint
 static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
-#else
-__RCSID("$NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $");
 #endif
-#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.21 2003/06/14 13:41:31 trhodes Exp $");
 
 #include <sys/param.h>
-#include <sys/time.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 
 #include <err.h>
 #include <errno.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-void   compress __P((char *, char *, int));
-void   cwarn __P((const char *, ...));
-void   cwarnx __P((const char *, ...));
-void   decompress __P((char *, char *, int));
-int    permission __P((char *));
-void   setfile __P((char *, struct stat *));
-void   usage __P((int));
+#include "zopen.h"
 
-int    main __P((int, char *[]));
-extern FILE *zopen __P((const char *fname, const char *mode, int bits));
+void   compress(const char *, const char *, int);
+void   cwarn(const char *, ...) __printflike(1, 2);
+void   cwarnx(const char *, ...) __printflike(1, 2);
+void   decompress(const char *, const char *, int);
+int    permission(const char *);
+void   setfile(const char *, struct stat *);
+void   usage(int);
 
-int eval, force, verbose;
-int isstdout, isstdin;
+int eval, force, verbose, cat;
 
 int
-main(argc, argv)
-       int argc;
-       char *argv[];
+main(int argc, char *argv[])
 {
-        enum {COMPRESS, DECOMPRESS} style = COMPRESS;
+       enum {COMPRESS, DECOMPRESS} style;
        size_t len;
-       int bits, cat, ch;
+       int bits, ch;
        char *p, newname[MAXPATHLEN];
 
-       if ((p = strrchr(argv[0], '/')) == NULL)
+       cat = 0;
+       if ((p = rindex(argv[0], '/')) == NULL)
                p = argv[0];
        else
                ++p;
        if (!strcmp(p, "uncompress"))
                style = DECOMPRESS;
-        else if (!strcmp(p, "compress"))
-                style = COMPRESS;
-        else if (!strcmp(p, "zcat")) {
-                style = DECOMPRESS;
-                cat = 1;
-        }
-       else
+       else if (!strcmp(p, "compress"))
+               style = COMPRESS;
+       else if (!strcmp(p, "zcat")) {
+               cat = 1;
+               style = DECOMPRESS;
+       } else
                errx(1, "unknown program name");
 
-       bits = cat = 0;
+       bits = 0;
        while ((ch = getopt(argc, argv, "b:cdfv")) != -1)
                switch(ch) {
                case 'b':
@@ -131,15 +121,12 @@ main(argc, argv)
        argv += optind;
 
        if (argc == 0) {
+               cat = 1;
                switch(style) {
                case COMPRESS:
-                       isstdout = 1;
-                       isstdin = 1;
                        (void)compress("/dev/stdin", "/dev/stdout", bits);
                        break;
                case DECOMPRESS:
-                       isstdout = 1;
-                       isstdin = 1;
                        (void)decompress("/dev/stdin", "/dev/stdout", bits);
                        break;
                }
@@ -149,16 +136,18 @@ main(argc, argv)
        if (cat == 1 && argc > 1)
                errx(1, "the -c option permits only a single file argument");
 
-       for (; *argv; ++argv) {
-               isstdout = 0;
+       for (; *argv; ++argv)
                switch(style) {
                case COMPRESS:
-                       if (cat) {
-                               isstdout = 1;
+                       if (strcmp(*argv, "-") == 0) {
+                               cat = 1;
+                               compress("/dev/stdin", "/dev/stdout", bits);
+                               break;
+                       } else if (cat) {
                                compress(*argv, "/dev/stdout", bits);
                                break;
                        }
-                       if ((p = strrchr(*argv, '.')) != NULL &&
+                       if ((p = rindex(*argv, '.')) != NULL &&
                            !strcmp(p, ".Z")) {
                                cwarnx("%s: name already has trailing .Z",
                                    *argv);
@@ -176,8 +165,13 @@ main(argc, argv)
                        compress(*argv, newname, bits);
                        break;
                case DECOMPRESS:
+                       if (strcmp(*argv, "-") == 0) {
+                               cat = 1;
+                               decompress("/dev/stdin", "/dev/stdout", bits);
+                               break;
+                       }
                        len = strlen(*argv);
-                       if ((p = strrchr(*argv, '.')) == NULL ||
+                       if ((p = rindex(*argv, '.')) == NULL ||
                            strcmp(p, ".Z")) {
                                if (len > sizeof(newname) - 3) {
                                        cwarnx("%s: name too long", *argv);
@@ -189,8 +183,6 @@ main(argc, argv)
                                newname[len + 2] = '\0';
                                decompress(newname,
                                    cat ? "/dev/stdout" : *argv, bits);
-                               if (cat)
-                                       isstdout = 1;
                        } else {
                                if (len - 2 > sizeof(newname) - 1) {
                                        cwarnx("%s: name too long", *argv);
@@ -200,50 +192,38 @@ main(argc, argv)
                                newname[len - 2] = '\0';
                                decompress(*argv,
                                    cat ? "/dev/stdout" : newname, bits);
-                               if (cat)
-                                       isstdout = 1;
                        }
                        break;
                }
-       }
        exit (eval);
 }
 
 void
-compress(in, out, bits)
-       char *in, *out;
-       int bits;
+compress(const char *in, const char *out, int bits)
 {
-       int nr;
+       size_t nr;
        struct stat isb, sb;
        FILE *ifp, *ofp;
        int exists, isreg, oreg;
-       u_char buf[BUFSIZ];
+       u_char buf[1024];
 
-       if (!isstdout) {
-               exists = !stat(out, &sb);
-               if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
-                       return;
-               oreg = !exists || S_ISREG(sb.st_mode);
-       } else
-               oreg = 0;
+       exists = !stat(out, &sb);
+       if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) {
+               cwarnx("%s already exists", out);
+               return;
+       }
+       isreg = oreg = !exists || S_ISREG(sb.st_mode);
 
        ifp = ofp = NULL;
        if ((ifp = fopen(in, "r")) == NULL) {
                cwarn("%s", in);
                return;
        }
-
-       if (!isstdin) {
-               if (stat(in, &isb)) {           /* DON'T FSTAT! */
-                       cwarn("%s", in);
-                       goto err;
-               }
-               if (!S_ISREG(isb.st_mode))
-                       isreg = 0;
-               else
-                       isreg = 1;
-       } else
+       if (stat(in, &isb)) {           /* DON'T FSTAT! */
+               cwarn("%s", in);
+               goto err;
+       }
+       if (!S_ISREG(isb.st_mode))
                isreg = 0;
 
        if ((ofp = zopen(out, "w", bits)) == NULL) {
@@ -268,7 +248,7 @@ compress(in, out, bits)
        }
        ofp = NULL;
 
-       if (isreg && oreg) {
+       if (!cat && isreg) {
                if (stat(out, &sb)) {
                        cwarn("%s", out);
                        goto err;
@@ -276,7 +256,9 @@ compress(in, out, bits)
 
                if (!force && sb.st_size >= isb.st_size) {
                        if (verbose)
-               (void)printf("%s: file would grow; left unmodified\n", in);
+               (void)fprintf(stderr, "%s: file would grow; left unmodified\n",
+                   in);
+                       eval = 2;
                        if (unlink(out))
                                cwarn("%s", out);
                        goto err;
@@ -288,19 +270,19 @@ compress(in, out, bits)
                        cwarn("%s", in);
 
                if (verbose) {
-                       (void)printf("%s: ", out);
+                       (void)fprintf(stderr, "%s: ", out);
                        if (isb.st_size > sb.st_size)
-                               (void)printf("%.0f%% compression\n",
-                                   ((double)sb.st_size / isb.st_size) * 100.0);
+                               (void)fprintf(stderr, "%.0f%% compression\n",
+                                   ((float)sb.st_size / isb.st_size) * 100.0);
                        else
-                               (void)printf("%.0f%% expansion\n",
-                                   ((double)isb.st_size / sb.st_size) * 100.0);
+                               (void)fprintf(stderr, "%.0f%% expansion\n",
+                                   ((float)isb.st_size / sb.st_size) * 100.0);
                }
        }
        return;
 
 err:   if (ofp) {
-               if (oreg)
+               if (!cat && oreg)
                        (void)unlink(out);
                (void)fclose(ofp);
        }
@@ -309,46 +291,49 @@ err:      if (ofp) {
 }
 
 void
-decompress(in, out, bits)
-       char *in, *out;
-       int bits;
+decompress(const char *in, const char *out, int bits)
 {
-       int nr;
+       size_t nr;
        struct stat sb;
        FILE *ifp, *ofp;
        int exists, isreg, oreg;
-       u_char buf[BUFSIZ];
-
-       if (!isstdout) {
-               exists = !stat(out, &sb);
-               if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
-                       return;
-               oreg = !exists || S_ISREG(sb.st_mode);
-       } else
-               oreg = 0;
+       u_char buf[1024];
 
-       ifp = ofp = NULL;
-       if ((ofp = fopen(out, "w")) == NULL) {
-               cwarn("%s", out);
+       exists = !stat(out, &sb);
+       if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) {
+               cwarnx("%s already exists", out);
                return;
        }
+       isreg = oreg = !exists || S_ISREG(sb.st_mode);
 
+       ifp = ofp = NULL;
        if ((ifp = zopen(in, "r", bits)) == NULL) {
+               cwarn("%s", in);
+               return;
+       }
+       if (stat(in, &sb)) {
                cwarn("%s", in);
                goto err;
        }
-       if (!isstdin) {
-               if (stat(in, &sb)) {
-                       cwarn("%s", in);
-                       goto err;
-               }
-               if (!S_ISREG(sb.st_mode))
-                       isreg = 0;
-               else
-                       isreg = 1;
-       } else
+       if (!S_ISREG(sb.st_mode))
                isreg = 0;
 
+       /*
+        * Try to read the first few uncompressed bytes from the input file
+        * before blindly truncating the output file.
+        */
+       if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) {
+               cwarn("%s", in);
+               (void)fclose(ifp);
+               return;
+       }
+       if ((ofp = fopen(out, "w")) == NULL ||
+           (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) {
+               cwarn("%s", out);
+               (void)fclose(ifp);
+               return;
+       }
+
        while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
                if (fwrite(buf, 1, nr, ofp) != nr) {
                        cwarn("%s", out);
@@ -366,16 +351,27 @@ decompress(in, out, bits)
                goto err;
        }
 
-       if (isreg && oreg) {
+       if (!cat && isreg) {
                setfile(out, &sb);
 
                if (unlink(in))
                        cwarn("%s", in);
+               if (verbose) {
+                       struct stat isb = sb;
+                       stat(out, &sb);
+                       (void)fprintf(stderr, "%s: ", out);
+                       if (isb.st_size > sb.st_size)
+                               (void)fprintf(stderr, "%.0f%% compression\n",
+                                   ((float)sb.st_size / isb.st_size) * 100.0);
+                       else
+                               (void)fprintf(stderr, "%.0f%% expansion\n",
+                                   ((float)isb.st_size / sb.st_size) * 100.0);
+               }
        }
        return;
 
 err:   if (ofp) {
-               if (oreg)
+               if (!cat && oreg)
                        (void)unlink(out);
                (void)fclose(ofp);
        }
@@ -384,9 +380,7 @@ err:        if (ofp) {
 }
 
 void
-setfile(name, fs)
-       char *name;
-       struct stat *fs;
+setfile(const char *name, struct stat *fs)
 {
        static struct timeval tv[2];
 
@@ -408,21 +402,15 @@ setfile(name, fs)
                        cwarn("chown: %s", name);
                fs->st_mode &= ~(S_ISUID|S_ISGID);
        }
-       if (chmod(name, fs->st_mode))
-               cwarn("chown: %s", name);
+       if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP)
+               cwarn("chmod: %s", name);
 
-       /*
-        * Restore the file's flags.  However, do this only if the original
-        * file had any flags set; this avoids a warning on file-systems that
-        * do not support flags.
-        */
-       if (fs->st_flags != 0 && chflags(name, fs->st_flags))
+       if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP)
                cwarn("chflags: %s", name);
 }
 
 int
-permission(fname)
-       char *fname;
+permission(const char *fname)
 {
        int ch, first;
 
@@ -436,55 +424,34 @@ permission(fname)
 }
 
 void
-usage(iscompress)
-       int iscompress;
+usage(int iscompress)
 {
        if (iscompress)
                (void)fprintf(stderr,
                    "usage: compress [-cfv] [-b bits] [file ...]\n");
        else
                (void)fprintf(stderr,
-                   "usage: uncompress [-c] [-b bits] [file ...]\n");
+                   "usage: uncompress [-cfv] [-b bits] [file ...]\n");
        exit(1);
 }
 
 void
-#if __STDC__
 cwarnx(const char *fmt, ...)
-#else
-cwarnx(fmt, va_alist)
-       int eval;
-       const char *fmt;
-       va_dcl
-#endif
 {
        va_list ap;
-#if __STDC__
+
        va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
        vwarnx(fmt, ap);
        va_end(ap);
        eval = 1;
 }
 
 void
-#if __STDC__
 cwarn(const char *fmt, ...)
-#else
-cwarn(fmt, va_alist)
-       int eval;
-       const char *fmt;
-       va_dcl
-#endif
 {
        va_list ap;
-#if __STDC__
+
        va_start(ap, fmt);
-#else
-       va_start(ap);
-#endif
        vwarn(fmt, ap);
        va_end(ap);
        eval = 1;
index 1e75687ea97fb74ca5afd723694066902a49a9e3..730a1200df7a6cae1b1f472e27c3d8948dda4996 100644 (file)
@@ -1,5 +1,3 @@
-.\"    $NetBSD: zopen.3,v 1.4 1998/02/06 06:17:43 perry Exp $
-.\"
 .\" Copyright (c) 1992, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
@@ -32,6 +30,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)zopen.3     8.1 (Berkeley) 6/9/93
+.\" $FreeBSD: src/usr.bin/compress/zopen.3,v 1.8 2001/10/01 15:01:57 ru Exp $
 .\"
 .Dd June 9, 1993
 .Dt ZOPEN 3
@@ -40,7 +39,7 @@
 .Nm zopen
 .Nd compressed stream open function
 .Sh SYNOPSIS
-.Fd #include <stdio.h>
+.Fd #include \&"zopen.h\&"
 .Ft FILE *
 .Fn zopen "const char *path" "const char *mode" "int bits"
 .Sh DESCRIPTION
@@ -88,7 +87,7 @@ The
 argument, if non-zero, is set to the bits code limit.
 If zero, the default is 16.
 See
-.Fn compress 1
+.Xr compress 1
 for more information.
 .Sh RETURN VALUES
 Upon successful completion
@@ -130,7 +129,7 @@ or
 .Xr funopen 3
 .Sh HISTORY
 The
-.Nm zopen
+.Nm
 function
 first appeared in
 .Bx 4.4 .
index c165117802bddcc6fe22452c08e53fcec8879b3c..3ec71631e51ecb1e39018ae774c69792e1fc03f9 100644 (file)
@@ -1,5 +1,3 @@
-/*     $NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $  */
-
 /*-
  * Copyright (c) 1985, 1986, 1992, 1993
  *     The Regents of the University of California.  All rights reserved.
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-#if 0
 static char sccsid[] = "@(#)zopen.c    8.1 (Berkeley) 6/27/93";
-#else
-static char rcsid[] = "$NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $";
-#endif
 #endif /* LIBC_SCCS and not lint */
 
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/compress/zopen.c,v 1.10 2002/07/28 15:32:17 dwmalone Exp $");
+
 /*-
  * fcompress.c - File compression ala IEEE Computer, June 1984.
  *
@@ -77,6 +74,7 @@ static char rcsid[] = "$NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $";
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include "zopen.h"
 
 #define        BITS            16              /* Default bits. */
 #define        HSIZE           69001           /* 95% occupancy */
@@ -106,8 +104,8 @@ struct s_zstate {
        enum {
                S_START, S_MIDDLE, S_EOF
        } zs_state;                     /* State of computation */
-       int zs_n_bits;                  /* Number of bits/code. */
-       int zs_maxbits;                 /* User settable max # bits/code. */
+       u_int zs_n_bits;                /* Number of bits/code. */
+       u_int zs_maxbits;               /* User settable max # bits/code. */
        code_int zs_maxcode;            /* Maximum code, given n_bits. */
        code_int zs_maxmaxcode;         /* Should NEVER generate this code. */
        count_int zs_htab [HSIZE];
@@ -122,7 +120,7 @@ struct s_zstate {
        int zs_clear_flg;
        long zs_ratio;
        count_int zs_checkpoint;
-       int zs_offset;
+       u_int zs_offset;
        long zs_in_count;               /* Length of input. */
        long zs_bytes_out;              /* Length of compressed output. */
        long zs_out_count;              /* # of codes output (for debugging). */
@@ -203,14 +201,13 @@ struct s_zstate {
 #define        FIRST   257             /* First free entry. */
 #define        CLEAR   256             /* Table clear output code. */
 
-static int     cl_block __P((struct s_zstate *));
-static void    cl_hash __P((struct s_zstate *, count_int));
-static code_int        getcode __P((struct s_zstate *));
-static int     output __P((struct s_zstate *, code_int));
-static int     zclose __P((void *));
-FILE          *zopen __P((const char *, const char *, int));
-static int     zread __P((void *, char *, int));
-static int     zwrite __P((void *, const char *, int));
+static int     cl_block(struct s_zstate *);
+static void    cl_hash(struct s_zstate *, count_int);
+static code_int        getcode(struct s_zstate *);
+static int     output(struct s_zstate *, code_int);
+static int     zclose(void *);
+static int     zread(void *, char *, int);
+static int     zwrite(void *, const char *, int);
 
 /*-
  * Algorithm from "A Technique for High Performance Data Compression",
@@ -239,10 +236,7 @@ static int zwrite __P((void *, const char *, int));
  * questions about this implementation to ames!jaw.
  */
 static int
-zwrite(cookie, wbp, num)
-       void *cookie;
-       const char *wbp;
-       int num;
+zwrite(void *cookie, const char *wbp, int num)
 {
        code_int i;
        int c, disp;
@@ -256,7 +250,7 @@ zwrite(cookie, wbp, num)
 
        zs = cookie;
        count = num;
-       bp = (u_char *)wbp;
+       bp = wbp;
        if (state == S_MIDDLE)
                goto middle;
        state = S_MIDDLE;
@@ -265,7 +259,7 @@ zwrite(cookie, wbp, num)
        if (fwrite(magic_header,
            sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header))
                return (-1);
-       tmp = (u_char)(maxbits | block_compress);
+       tmp = (u_char)((maxbits) | block_compress);
        if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp))
                return (-1);
 
@@ -330,8 +324,7 @@ nomatch:    if (output(zs, (code_int) ent) == -1)
 }
 
 static int
-zclose(cookie)
-       void *cookie;
+zclose(void *cookie)
 {
        struct s_zstate *zs;
        int rval;
@@ -376,11 +369,10 @@ static char_type rmask[9] =
        {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
 
 static int
-output(zs, ocode)
-       struct s_zstate *zs;
-       code_int ocode;
+output(struct s_zstate *zs, code_int ocode)
 {
-       int bits, r_off;
+       int r_off;
+       u_int bits;
        char_type *bp;
 
        r_off = offset;
@@ -465,10 +457,7 @@ output(zs, ocode)
  * compress() routine.  See the definitions above.
  */
 static int
-zread(cookie, rbp, num)
-       void *cookie;
-       char *rbp;
-       int num;
+zread(void *cookie, char *rbp, int num)
 {
        u_int count;
        struct s_zstate *zs;
@@ -576,8 +565,7 @@ eof:        return (num - count);
  *     code or -1 is returned.
  */
 static code_int
-getcode(zs)
-       struct s_zstate *zs;
+getcode(struct s_zstate *zs)
 {
        code_int gcode;
        int r_off, bits;
@@ -635,8 +623,7 @@ getcode(zs)
 }
 
 static int
-cl_block(zs)                   /* Table clear for block compress. */
-       struct s_zstate *zs;
+cl_block(struct s_zstate *zs)          /* Table clear for block compress. */
 {
        long rat;
 
@@ -664,9 +651,7 @@ cl_block(zs)                        /* Table clear for block compress. */
 }
 
 static void
-cl_hash(zs, cl_hsize)                  /* Reset code table. */
-       struct s_zstate *zs;
-       count_int cl_hsize;
+cl_hash(struct s_zstate *zs, count_int cl_hsize)       /* Reset code table. */
 {
        count_int *htab_p;
        long i, m1;
@@ -698,9 +683,7 @@ cl_hash(zs, cl_hsize)                       /* Reset code table. */
 }
 
 FILE *
-zopen(fname, mode, bits)
-       const char *fname, *mode;
-       int bits;
+zopen(const char *fname, const char *mode, int bits)
 {
        struct s_zstate *zs;
 
@@ -714,7 +697,7 @@ zopen(fname, mode, bits)
                return (NULL);
 
        maxbits = bits ? bits : BITS;   /* User settable max # bits/code. */
-       maxmaxcode = 1 << maxbits;      /* Should NEVER generate this code. */
+       maxmaxcode = 1L << maxbits;     /* Should NEVER generate this code. */
        hsize = HSIZE;                  /* For dynamic table sizing. */
        free_ent = 0;                   /* First unused entry. */
        block_compress = BLOCK_MASK;
diff --git a/compress/zopen.h b/compress/zopen.h
new file mode 100644 (file)
index 0000000..8ad5691
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 1996
+ *     FreeBSD Inc. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY FreeBSD Inc. 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 [your name] 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.
+ *
+ * $FreeBSD: src/usr.bin/compress/zopen.h,v 1.5 2002/03/22 01:19:31 imp Exp $
+ */
+
+#ifndef _ZOPEN_H_
+#define _ZOPEN_H_
+
+FILE  *zopen(const char *, const char *, int);
+
+#endif /* _ZOPEN_H_ */
index ce6bfd7dccd17e2dd8218af658b4095d0f0151ce..e12876649751df8a5e78283f1161bb47e460c5d4 100644 (file)
@@ -49,3 +49,6 @@ include $(MAKEFILEDIR)/$(MAKEFILE)
 -include Makefile.postamble
 
 -include Makefile.dependencies
+
+ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders
+
diff --git a/cp/cp.1 b/cp/cp.1
index 26e247ff4539c57b701ef6c93207581c102d0576..81825dde4057c7a8008d38d33fdc80629481120a 100644 (file)
--- a/cp/cp.1
+++ b/cp/cp.1
@@ -120,10 +120,10 @@ regardless of its permissions.
 (The
 .Fl f
 option overrides any previous
-.Fl i
-or
 .Fl n
-options.)
+option, but not
+.Fl i
+option. See also the legacy section.)
 .It Fl i
 Cause
 .Nm
@@ -242,18 +242,28 @@ The
 and
 .Fl n
 options are non-standard and their use in scripts is not recommended.
+.Sh LEGACY DESCRIPTION
+When invoked in legacy mode, both 
+.Fl n ,
+.Fl i
+options are overridden by the
+.Fl f
+option.
 .Sh SEE ALSO
 .Xr mv 1 ,
 .Xr rcp 1 ,
 .Xr umask 2 ,
 .Xr fts 3 ,
+.Xr compat 5 ,
 .Xr symlink 7
 .Sh STANDARDS
 The
 .Nm
 command is expected to be
 .St -p1003.2
-compatible.
+compatible. It is also
+.St -susv3
+conformant.
 .Sh HISTORY
 A
 .Nm
diff --git a/cp/cp.c b/cp/cp.c
index 43e094b8dbc5df13c655c6099ec4385ddc8dfed5..7e9dc8f5c31a400606192cd2e5648f9bf25e53c4 100644 (file)
--- a/cp/cp.c
+++ b/cp/cp.c
@@ -75,6 +75,11 @@ __RCSID("$FreeBSD: src/bin/cp/cp.c,v 1.42 2002/09/22 11:15:56 mckay Exp $");
 #include <string.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include <copyfile.h>
+#endif
+
+#include "get_compat.h"
 #include "extern.h"
 
 #define        STRIP_TRAILING_SLASH(p) {                                       \
@@ -122,7 +127,11 @@ main(int argc, char *argv[])
                        break;
                case 'f':
                        fflag = 1;
-                       iflag = nflag = 0;
+                       /* Determine if the STD is SUSv3 or Legacy */
+                       if (compat_mode("bin/cp", "unix2003"))
+                               nflag = 0;      /* reset nflag, but not iflag */
+                       else
+                               iflag = nflag = 0;      /* reset both */
                        break;
                case 'i':
                        iflag = 1;
@@ -362,6 +371,9 @@ copy(char *argv[], enum op type, int fts_options)
                         * normally want to preserve them on directories.
                         */
                        if (pflag) {
+#ifdef __APPLE__
+                               copyfile(curr->fts_path, to.p_path, 0, COPYFILE_ACL);
+#endif
                                if (setfile(curr->fts_statp, 0))
                                    rval = 1;
                        } else {
@@ -443,6 +455,9 @@ copy(char *argv[], enum op type, int fts_options)
                         * directory, or if the -p flag is in effect.
                         */
                        curr->fts_number = pflag || dne;
+#ifdef __APPLE__
+                       copyfile(curr->fts_path, to.p_path, 0, COPYFILE_XATTR);
+#endif
                        break;
                case S_IFBLK:
                case S_IFCHR:
index 56256f92cd85fc2fd77cf52c3ba4d4835e3ad742..707266e56e501d8ae579694d99e1f37786f31862 100644 (file)
@@ -40,6 +40,7 @@ static char sccsid[] = "@(#)utils.c   8.3 (Berkeley) 4/1/94";
 __RCSID("$FreeBSD: src/bin/cp/utils.c,v 1.38 2002/07/31 16:52:16 markm Exp $");
 
 #include <sys/param.h>
+#include <sys/time.h>
 #include <sys/stat.h>
 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
 #include <sys/mman.h>
@@ -55,6 +56,10 @@ __RCSID("$FreeBSD: src/bin/cp/utils.c,v 1.38 2002/07/31 16:52:16 markm Exp $");
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include <copyfile.h>
+#endif
+
 #include "extern.h"
 
 int
@@ -176,6 +181,12 @@ copy_file(FTSENT *entp, int dne)
                }
        }
 
+#ifdef __APPLE__
+       if (pflag)
+               copyfile(entp->fts_path, to.p_path, 0, COPYFILE_XATTR | COPYFILE_ACL);
+       else
+               copyfile(entp->fts_path, to.p_path, 0, COPYFILE_XATTR);
+#endif
        /*
         * Don't remove the target even after an error.  The target might
         * not be a regular file, or its attributes might be important,
@@ -212,6 +223,9 @@ copy_link(FTSENT *p, int exists)
                warn("symlink: %s", llink);
                return (1);
        }
+#ifdef __APPLE__
+           copyfile(p->fts_path, to.p_path, 0, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC);
+#endif
        return (0);
 }
 
index 2858db107b69d1b8361424bcbf4b3cad3c29df7b..e7e06274e09557f1fe25efb6f0a0b43089caf8ac 100644 (file)
--- a/dd/args.c
+++ b/dd/args.c
@@ -75,15 +75,15 @@ static const struct arg {
        void (*f)(char *);
        u_int set, noset;
 } args[] = {
-       { "bs",         f_bs,           C_BS,    C_BS|C_IBS|C_OBS|C_OSYNC },
+       { "bs",         f_bs,           C_BS,    C_BS|C_OSYNC },
        { "cbs",        f_cbs,          C_CBS,   C_CBS },
        { "conv",       f_conv,         0,       0 },
        { "count",      f_count,        C_COUNT, C_COUNT },
        { "files",      f_files,        C_FILES, C_FILES },
-       { "ibs",        f_ibs,          C_IBS,   C_BS|C_IBS },
+       { "ibs",        f_ibs,          C_IBS,   C_IBS },
        { "if",         f_if,           C_IF,    C_IF },
        { "iseek",      f_skip,         C_SKIP,  C_SKIP },
-       { "obs",        f_obs,          C_OBS,   C_BS|C_OBS },
+       { "obs",        f_obs,          C_OBS,   C_OBS },
        { "of",         f_of,           C_OF,    C_OF },
        { "oseek",      f_seek,         C_SEEK,  C_SEEK },
        { "seek",       f_seek,         C_SEEK,  C_SEEK },
@@ -103,6 +103,8 @@ jcl(char **argv)
 
        in.dbsz = out.dbsz = 512;
 
+       if (argv[1] && !strcmp(argv[1], "--")) /* skip delimiter before operands */
+               argv++;
        while ((oper = *++argv) != NULL) {
                if ((oper = strdup(oper)) == NULL)
                        errx(1, "unable to allocate space for the argument \"%s\"", *argv);
diff --git a/dd/dd.c b/dd/dd.c
index cbcb87a684300c6064bc92cf937b0b0fff50ac84..125acd7bcbd00bd3d416f2b77c64e3fa105dad7d 100644 (file)
--- a/dd/dd.c
+++ b/dd/dd.c
@@ -224,21 +224,27 @@ getfdtype(IO *io)
                if (ioctl(io->fd, FIODTYPE, &type) == -1) {
                        err(1, "%s", io->name);
                } else {
+#ifdef __APPLE__               /* MacOSX uses enumeration for type not a bitmask */
+                       if (type == D_TAPE)
+                               io->flags |= ISTAPE;
+                       else if (type == D_DISK || type == D_TTY) {
+#else  /* !__APPLE__ */
                        if (type & D_TAPE)
                                io->flags |= ISTAPE;
-#ifdef __APPLE__
-                       else if (type & D_DISK) {
-#else
                        else if (type & (D_DISK | D_MEM)) {
                                if (type & D_DISK) {
                                        const int one = 1;
 
                                        (void)ioctl(io->fd, DIOCWLABEL, &one);
                                }
-#endif
+#endif /* __APPLE__ */
                                io->flags |= ISSEEK;
                        }
+#ifdef __APPLE__
+                       if (S_ISCHR(sb.st_mode) && (type != D_TAPE))
+#else  /* !__APPLE__ */
                        if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0)
+#endif /* __APPLE__ */
                                io->flags |= ISCHR;
                }
                return;
index e589a73a85b110d405ae1ba9607261616e4b79de..d70d320c430bd34d70b00c362e1eeb3bd12f9c34 100644 (file)
--- a/dd/misc.c
+++ b/dd/misc.c
@@ -78,7 +78,7 @@ summary(void)
        }
        if (st.trunc) {
                (void)snprintf(buf, sizeof(buf), "%qu truncated %s\n",
-                    st.trunc, (st.trunc == 1) ? "block" : "blocks");
+                    st.trunc, (st.trunc == 1) ? "record" : "records");
                (void)write(STDERR_FILENO, buf, strlen(buf));
        }
        (void)snprintf(buf, sizeof(buf),
index cbcac45500f5b758e2b224293c55f2fe22442ab4..101cda15fd121e102e67ccb79f4a4c12d873fa7d 100644 (file)
@@ -46,3 +46,5 @@ include $(MAKEFILEDIR)/$(MAKEFILE)
 -include Makefile.postamble
 
 -include Makefile.dependencies
+
+ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders
diff --git a/df/df.1 b/df/df.1
index 99e4c1cdf2ba04ba22502c9fa80f96783ad19bd1..93a445dd26d138bf8f9ea79fd1f5fc2773ce6656 100644 (file)
--- a/df/df.1
+++ b/df/df.1
 .Fl m | P
 .Oc
 .Op Fl ailn
+.Op Fl t
+.Op Fl T Ar type
+.Op Ar file | filesystem ...
+.Sh LEGACY SYNOPSIS
+.Nm
+.Oo
+.Fl b | h | H | k |
+.Fl m | P
+.Oc
+.Op Fl ailn
 .Op Fl t Ar type
+.Op Fl T Ar type
 .Op Ar file | filesystem ...
 .Sh DESCRIPTION
 The
@@ -115,6 +126,12 @@ Note that this overrides the
 .Ev BLOCKSIZE
 specification from the environment.
 .It Fl t
+In legacy mode (see
+.Xr compat 5
+for details) acts like
+.Fl T
+otherwise it is a no-op.
+.It Fl T
 Only print out statistics for filesystems of the specified types.
 More than one type may be specified in a comma separated list.
 The list of filesystem types can be prefixed with
@@ -158,6 +175,7 @@ flags are ignored if a file or filesystem is specified.
 .Xr getfsstat 2 ,
 .Xr statfs 2 ,
 .Xr getmntinfo 3 ,
+.Xr compat 5
 .Xr fstab 5 ,
 .Xr mount 8 ,
 .Xr quot 8
diff --git a/df/df.c b/df/df.c
index fa104d2b64381b542afac53951e0596af414defc..dda262ccf9c791d635062bfe0400c117919c87cf 100644 (file)
--- a/df/df.c
+++ b/df/df.c
@@ -64,7 +64,6 @@ typedef int32_t ufs_daddr_t;
 #include <sys/sysctl.h>
 #include <ufs/ufs/ufsmount.h>
 #include <ufs/ffs/fs.h>
-
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -76,6 +75,12 @@ typedef int32_t ufs_daddr_t;
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
 #define UNITS_SI 1
 #define UNITS_2 2
 
@@ -147,12 +152,20 @@ main(int argc, char *argv[])
        const char *fstype;
        char *mntpath, *mntpt, **vfslist;
        long mntsize;
-       int ch, i, rv, tflag = 0;
+       int ch, i, rv, tflag = 0, kludge_tflag = 0;
+       const char *options = "abgHhiklmnPt:T:";
+       if (COMPAT_MODE("bin/df", "unix2003")) {
+               /* Unix2003 requires -t be "include total capicity". which df
+                 already does, but it conflits with the old -t so we need to
+                 *not* expect a string after -t (we provide -T in both cases
+                 to cover the old use of -t) */
+               options = "abgHhiklmnPtT:";
+       }
 
        fstype = "ufs";
 
        vfslist = NULL;
-       while ((ch = getopt(argc, argv, "abgHhiklmnPt:")) != -1)
+       while ((ch = getopt(argc, argv, options)) != -1)
                switch (ch) {
                case 'a':
                        aflag = 1;
@@ -184,7 +197,7 @@ main(int argc, char *argv[])
                        break;
                case 'l':
                        if (tflag)
-                               errx(1, "-l and -t are mutually exclusive.");
+                               errx(1, "-l and -T are mutually exclusive.");
                        if (vfslist != NULL)
                                break;
                        vfslist = makevfslist(makenetvfslist());
@@ -197,11 +210,17 @@ main(int argc, char *argv[])
                        nflag = 1;
                        break;
                case 't':
+                       /* Unix2003 uses -t for something we do by default */
+                       if (COMPAT_MODE("bin/df", "unix2003")) {
+                           kludge_tflag = 1;
+                           break;
+                       }
+               case 'T':
                        if (vfslist != NULL) {
                                if (tflag)
-                                       errx(1, "only one -t option may be specified");
+                                       errx(1, "only one -%c option may be specified", ch);
                                else
-                                       errx(1, "-l and -t are mutually exclusive.");
+                                       errx(1, "-l and -%c are mutually exclusive.", ch);
                        }
                        tflag++;
                        fstype = optarg;
@@ -214,6 +233,16 @@ main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
+       /* If we are in unix2003 mode, have seen a -t but no -T and the first
+         non switch arg isn't a file, let's pretend they used -T on it.
+         This makes the Lexmark printer installer happy (PR-3918471) */
+       if (tflag == 0 && kludge_tflag && *argv && stat(*argv, &stbuf) < 0
+         && errno == ENOENT) {
+           tflag = 1;
+           fstype = *argv++;
+           vfslist = makevfslist(fstype);
+       }
+
        mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
        bzero(&maxwidths, sizeof(maxwidths));
        for (i = 0; i < mntsize; i++)
@@ -239,7 +268,7 @@ main(int argc, char *argv[])
                                rv = 1;
                                continue;
                        }
-               } else if (S_ISCHR(stbuf.st_mode)) {
+               } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) {
                        if ((mntpt = getmntpt(*argv)) == 0) {
                                mdev.fspec = *argv;
                                mntpath = strdup("/tmp/df.XXXXXX");
@@ -582,8 +611,9 @@ void
 usage(void)
 {
 
+       char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]";
        (void)fprintf(stderr,
-           "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-t type] [file | filesystem ...]\n");
+           "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-T type] %s [file | filesystem ...]\n", t_flag);
        exit(EX_USAGE);
 }
 
@@ -655,7 +685,7 @@ makenetvfslist(void)
                *strptr = ',';
                free(listptr[i]);
        }
-       *(--strptr) = NULL;
+       *(--strptr) = '\0';
 
        free(listptr);
        return (str);
diff --git a/du/du.c b/du/du.c
index 813ac737e84cb6c15a3f15ee75d1d33c59c1f255..8b1c46b045a5f18895f62725662ce07240583d25 100644 (file)
--- a/du/du.c
+++ b/du/du.c
@@ -62,6 +62,14 @@ __RCSID("$FreeBSD: src/usr.bin/du/du.c,v 1.28 2002/12/30 18:13:07 mike Exp $");
 #include <string.h>
 #include <sysexits.h>
 #include <unistd.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#ifdef __APPLE__
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
 
 #define        KILO_SZ(n) (n)
 #define        MEGA_SZ(n) ((n) * (n))
@@ -130,18 +138,25 @@ main(int argc, char *argv[])
                switch (ch) {
                        case 'H':
                                Hflag = 1;
+                               Pflag = Lflag = 0;
                                break;
                        case 'I':
                                ignoreadd(optarg);
                                break;
                        case 'L':
-                               if (Pflag)
+                               if (Pflag && COMPAT_MODE("bin/du", "legacy")) {
                                        usage();
+                               } else {
+                                       Hflag = Pflag = 0;
+                               }
                                Lflag = 1;
                                break;
                        case 'P':
-                               if (Lflag)
+                               if (Lflag && COMPAT_MODE("bin/du", "legacy")) {
                                        usage();
+                               } else {
+                                       Hflag = Lflag = 0;
+                               }
                                Pflag = 1;
                                break;
                        case 'a':
@@ -268,6 +283,9 @@ main(int argc, char *argv[])
                                }
                                break;
                        case FTS_DC:                    /* Ignore. */
+                               if (COMPAT_MODE("bin/du", "unix2003")) {
+                                       errx(1, "Can't follow symlink cycle from %s to %s", p->fts_path, p->fts_cycle->fts_path);
+                               }
                                break;
                        case FTS_DNR:                   /* Warn, continue. */
                        case FTS_ERR:
@@ -275,6 +293,14 @@ main(int argc, char *argv[])
                                warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
                                rval = 1;
                                break;
+                       case FTS_SLNONE:
+                               if (COMPAT_MODE("bin/du", "unix2003")) {
+                                       struct stat sb;
+                                       int rc = stat(p->fts_path, &sb);
+                                       if (rc < 0 && errno == ELOOP) {
+                                               errx(1, "Too many symlinks at %s", p->fts_path);
+                                       }
+                               }
                        default:
                                if (ignorep(p))
                                        break;
@@ -351,8 +377,9 @@ linkchk(FTSENT *p)
        dev = p->fts_statp->st_dev;
        if ((start = files) != NULL)
                for (fp = start + nfiles - 1; fp >= start; --fp)
-                       if (ino == fp->inode && dev == fp->dev)
+                       if (ino == fp->inode && dev == fp->dev) {
                                return (1);
+                       }
 
        if (nfiles == maxfiles && (files = realloc((char *)files,
            (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
@@ -446,6 +473,16 @@ ignorep(FTSENT *ent)
 {
        struct ignentry *ign;
 
+       if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) {
+               struct statfs sfsb;
+               int rc = statfs(ent->fts_accpath, &sfsb);
+               if (rc >= 0 && !strcmp("fdesc", sfsb.f_fstypename)) {
+                       /* Don't cd into /dev/fd/N since one of those is likely to be
+                         the cwd as of the start of du which causes all manner of
+                         unpleasant surprises */
+                       return 1;
+               }
+       }
        SLIST_FOREACH(ign, &ignores, next)
                if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
                        return 1;
index a98ca93c0a9b0226b120bb07312063fdf08f39cf..31e542dcbc88077949aa32d9d03c8f34ab07a62c 100644 (file)
@@ -65,6 +65,10 @@ static const char rcsid[] =
 #include <sysexits.h>
 #include <utime.h>
 
+#ifdef __APPLE__
+#include <copyfile.h>
+#endif
+
 #include "pathnames.h"
 
 /* Bootstrap aid - this doesn't exist in most older releases */
@@ -504,6 +508,15 @@ install(from_name, to_name, fset, flags)
                }
        }
 
+#if __APPLE__
+       {
+           if (copyfile(from_name, to_name, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0)
+           {
+               warn("%s: copyfile", to_name);
+           }
+       }
+#endif
+
        (void)close(to_fd);
        if (!devnull)
                (void)close(from_fd);
diff --git a/ipcrm/Makefile b/ipcrm/Makefile
new file mode 100644 (file)
index 0000000..c14dc3e
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = ipcrm
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = ipcrm.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ipcrm.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+NEXTSTEP_PB_CFLAGS =
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/ipcrm/Makefile.postamble b/ipcrm/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/ipcrm/Makefile.preamble b/ipcrm/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/ipcrm/PB.project b/ipcrm/PB.project
new file mode 100644 (file)
index 0000000..6304e07
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (ipcrm.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ipcrm.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = ipcrm; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/ipcrm/ipcrm.1 b/ipcrm/ipcrm.1
new file mode 100644 (file)
index 0000000..4cf67c2
--- /dev/null
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1994 Adam Glass
+.\" 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. The name of the Author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\" 
+.\" THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 Adam Glass 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.
+.\" 
+.\" $Id: ipcrm.1,v 1.2.32.1 2005/02/26 03:36:38 nicolai Exp $
+.\""
+.Dd August 8, 1994
+.Dt ipcrm 1
+.Os
+.Sh NAME
+.Nm ipcrm
+.Nd remove the specified message queues, semaphore sets, and shared memory
+segments
+.Sh SYNOPSIS
+.Nm
+.Op Fl q Ar msqid
+.Op Fl m Ar shmid
+.Op Fl s Ar semid
+.Op Fl Q Ar msgkey
+.Op Fl M Ar shmkey
+.Op Fl S Ar semkey
+.Ar ...
+.Sh DESCRIPTION
+.Nm Ipcrm
+removes the specified message queues, semaphores and shared memory
+segments.  These System V IPC objects can be specified by their
+creation id or any associated key.
+.Pp
+The following options are used to specify which IPC objects will be removed.  Any number and combination of these options can be used:
+.Bl -tag -width indent
+.It Fl q Ar msqid
+Remove the message queue associated with the id
+.Nm msqid
+from the system.
+.It Fl m Ar shmid
+Mark the shared memory segment associated with id 
+.Nm shmid
+for removal.
+This marked segment will be destroyed after the last detach.
+.It Fl s Ar semid
+Removes the semaphore set associated with id
+.Nm semid
+from the system.
+.It Fl Q Ar msgkey
+Remove the message queue associated with key 
+.Nm msgkey
+from the system.
+.It Fl M Ar shmkey
+Mark the shared memory segment associated with key 
+.Nm shmkey
+for removal.
+This marked segment will be destroyed after the last detach.
+.It Fl S Ar semkey
+Remove the semaphore set associated with key
+.Nm semkey
+from the system.
+.El
+.Pp
+The identifiers and keys associated with these System V IPC objects can be
+determined by using
+.Xr ipcs 1
+.
+.Sh SEE ALSO
+.Xr ipcs 1
diff --git a/ipcrm/ipcrm.c b/ipcrm/ipcrm.c
new file mode 100644 (file)
index 0000000..567cfcb
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1994 Adam Glass
+ * 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 Adam Glass.
+ * 4. The name of the Author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 Adam Glass 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
+static const char rcsid[] =
+       "$Id: ipcrm.c,v 1.2.30.1 2005/02/26 03:36:38 nicolai Exp $";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#if 0
+#include <sys/msg.h>
+#include <sys/sem.h>
+#endif
+#include <sys/shm.h>
+
+#define IPC_TO_STR(x) (x == 'Q' ? "msq" : (x == 'M' ? "shm" : "sem"))
+#define IPC_TO_STRING(x) (x == 'Q' ? "message queue" : \
+       (x == 'M' ? "shared memory segment" : "semaphore"))
+
+int signaled;
+
+void usage()
+{
+       fprintf(stderr, "%s\n%s\n",
+               "usage: ipcrm [-q msqid] [-m shmid] [-s semid]",
+               "             [-Q msgkey] [-M shmkey] [-S semkey] ...");
+       exit(1);
+}
+
+int msgrm(key, id)
+    key_t key;
+    int id;
+{
+#if 0
+    if (key) {
+       id = msgget(key, 0);
+       if (id == -1)
+           return -1;
+    }
+#endif
+    return msgctl(id, IPC_RMID, NULL);
+}
+
+int shmrm(key, id)
+    key_t key;
+    int id;
+{
+    if (key) {
+       id = shmget(key, 0, 0);
+       if (id == -1)
+           return -1;
+    }
+    return shmctl(id, IPC_RMID, NULL);
+}
+
+int semrm(key, id)
+    key_t key;
+    int id;
+{
+#if 0
+    union semun arg;
+
+    if (key) {
+       id = semget(key, 0, 0);
+       if (id == -1)
+           return -1;
+    }
+    return semctl(id, 0, IPC_RMID, arg);
+#endif
+}
+
+void not_configured()
+{
+    signaled++;
+}
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+
+{
+    int c, result, errflg, target_id;
+    key_t target_key;
+
+    errflg = 0;
+    signal(SIGSYS, not_configured);
+    while ((c = getopt(argc, argv, ":q:m:s:Q:M:S:")) != -1) {
+
+       signaled = 0;
+       switch (c) {
+       case 'q':
+       case 'm':
+       case 's':
+           target_id = atoi(optarg);
+           if (c == 'q')
+               result = msgrm(0, target_id);
+           else if (c == 'm')
+               result = shmrm(0, target_id);
+           else
+               result = semrm(0, target_id);
+           if (result < 0) {
+               errflg++;
+               if (!signaled)
+                   warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id);
+               else
+                   warnx("%ss are not configured in the running kernel",
+                         IPC_TO_STRING(toupper(c)));
+           }
+           break;
+       case 'Q':
+       case 'M':
+       case 'S':
+           target_key = atol(optarg);
+           if (target_key == IPC_PRIVATE) {
+               warnx("can't remove private %ss", IPC_TO_STRING(c));
+               continue;
+           }
+           if (c == 'Q')
+               result = msgrm(target_key, 0);
+           else if (c == 'M')
+               result = shmrm(target_key, 0);
+           else
+               result = semrm(target_key, 0);
+           if (result < 0) {
+               errflg++;
+               if (!signaled)
+                   warn("%key(%ld): ", IPC_TO_STR(c), target_key);
+               else
+                   warnx("%ss are not configured in the running kernel",
+                         IPC_TO_STRING(c));
+           }
+           break;
+       case ':':
+           fprintf(stderr, "option -%c requires an argument\n", optopt);
+           usage();
+       case '?':
+           fprintf(stderr, "unrecognized option: -%c\n", optopt);
+           usage();
+       }
+    }
+
+    if (optind != argc) {
+           fprintf(stderr, "unknown argument: %s\n", argv[optind]);
+           usage();
+    }
+    exit(errflg);
+}
+
diff --git a/ipcs/Makefile b/ipcs/Makefile
new file mode 100644 (file)
index 0000000..2dd4656
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = ipcs
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = ipcs.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ipcs.1
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+NEXTSTEP_PB_CFLAGS =
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
+
+ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders
+
diff --git a/ipcs/Makefile.postamble b/ipcs/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/ipcs/Makefile.preamble b/ipcs/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/ipcs/PB.project b/ipcs/PB.project
new file mode 100644 (file)
index 0000000..3caab57
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (ipcs.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ipcs.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = ipcs; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/ipcs/ipcs.1 b/ipcs/ipcs.1
new file mode 100644 (file)
index 0000000..6b057e2
--- /dev/null
@@ -0,0 +1,132 @@
+.\"
+.\" Copyright (c) 1994 SigmaSoft, Th. Lockert
+.\" 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 SigmaSoft, Th. Lockert.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" $FreeBSD: src/usr.bin/ipcs/ipcs.1,v 1.16 2003/05/21 21:07:28 ru Exp $
+.\"
+.Dd June 18, 1994
+.Dt "IPCS" 1
+.Os
+.Sh NAME
+.Nm ipcs
+.Nd report System V interprocess communication facilities status
+.Sh SYNOPSIS
+.Nm
+.Op Fl abcmopqstMQST
+.Sh DESCRIPTION
+The
+.Nm
+utility provides information on System V interprocess communication
+(IPC) facilities on the system.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Show the maximum amount of information possible when
+displaying active semaphores, message queues,
+and shared memory segments.
+(This is shorthand for specifying the
+.Fl b ,
+.Fl c ,
+.Fl o ,
+.Fl p ,
+and
+.Fl t
+options.)
+.It Fl b
+Show the maximum allowed sizes for active semaphores, message queues,
+and shared memory segments.  The
+.Dq maximum allowed size
+is the maximum number of bytes in a message on a message queue,
+the size of a shared memory segment,
+or the number of semaphores in a set of semaphores.
+.It Fl c
+Show the creator's name and group for active semaphores, message queues,
+and shared memory segments.
+.It Fl m
+Display information about active shared memory segments.
+.It Fl o
+Show outstanding usage for active message queues,
+and shared memory segments.  The
+.Dq outstanding usage
+is the number of messages in a message queue, or the number
+of processes attached to a shared memory segment.
+.It Fl p
+Show the process ID information for active semaphores, message queues,
+and shared memory segments.  The
+.Dq process ID information
+is the last process to send a message to or receive a message from
+a message queue,
+the process that created a semaphore, or the last process to attach
+or detach a shared memory segment.
+.It Fl q
+Display information about active message queues.
+.It Fl s
+Display information about active semaphores.
+.It Fl t
+Show access times for active semaphores, message queues,
+and shared memory segments.  The access times is the time
+of the last control operation on an IPC object,
+the last send or receive of a message,
+the last attach or detach of a shared memory segment,
+or the last operation on a semaphore.
+.It Fl M
+Display system information about shared memory.
+.It Fl Q
+Display system information about messages queues.
+.It Fl S
+Display system information about semaphores.
+.It Fl T
+Display system information about shared memory, message queues
+and semaphores.
+.El
+.Pp
+If none of the
+.Fl M ,
+.Fl m ,
+.Fl Q ,
+.Fl q ,
+.Fl S ,
+or
+.Fl s
+options are specified, information about all active IPC facilities is
+listed.
+.Sh RESTRICTIONS
+System data structures may change while
+.Nm
+is running; the output of
+.Nm
+is not guaranteed to be consistent.
+.Sh BUGS
+This manual page is woefully incomplete, because it does not
+at all attempt to explain the information printed by
+.Nm .
+.Sh SEE ALSO
+.Xr ipcrm 1
+.Sh AUTHORS
+.An Thorsten Lockert Aq tholo@sigmasoft.com
diff --git a/ipcs/ipcs.c b/ipcs/ipcs.c
new file mode 100644 (file)
index 0000000..5094d69
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 1994 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <limits.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#define KERNEL
+
+#include <sys/ipc.h>
+#include <sys/sem_internal.h>
+#include <sys/shm_internal.h>
+#include <sys/msg.h>
+
+#include "sys/ipcs.h"
+
+/* The following is a kludge, until the problem of multiple inclusions
+   of ipc.h is taken care of. */
+#ifndef IXSEQ_TO_IPCID
+#define IXSEQ_TO_IPCID(ix,perm) (((perm.seq) << 16) | (ix & 0xffff))
+#endif
+
+char   *
+fmt_perm(mode)
+       u_short mode;
+{
+       static char buffer[100];
+
+       buffer[0] = '-';
+       buffer[1] = '-';
+       buffer[2] = ((mode & 0400) ? 'r' : '-');
+       buffer[3] = ((mode & 0200) ? 'w' : '-');
+       buffer[4] = ((mode & 0100) ? 'a' : '-');
+       buffer[5] = ((mode & 0040) ? 'r' : '-');
+       buffer[6] = ((mode & 0020) ? 'w' : '-');
+       buffer[7] = ((mode & 0010) ? 'a' : '-');
+       buffer[8] = ((mode & 0004) ? 'r' : '-');
+       buffer[9] = ((mode & 0002) ? 'w' : '-');
+       buffer[10] = ((mode & 0001) ? 'a' : '-');
+       buffer[11] = '\0';
+       return (&buffer[0]);
+}
+
+void
+cvt_time(t, buf)
+       time_t  t;
+       char   *buf;
+{
+       struct tm *tm;
+
+       if (t == 0) {
+               strcpy(buf, "no-entry");
+       } else {
+               tm = localtime(&t);
+               sprintf(buf, "%2d:%02d:%02d",
+                       tm->tm_hour, tm->tm_min, tm->tm_sec);
+       }
+}
+#define        SHMINFO         1
+#define        SHMTOTAL        2
+#define        MSGINFO         4
+#define        MSGTOTAL        8
+#define        SEMINFO         16
+#define        SEMTOTAL        32
+
+#define BIGGEST                1
+#define CREATOR                2
+#define OUTSTANDING    4
+#define PID            8
+#define TIME           16
+
+int
+main(argc, argv)
+       int     argc;
+       char   *argv[];
+{
+       int     display = SHMINFO | MSGINFO | SEMINFO;
+       int     option = 0;
+       char    kvmoferr[_POSIX2_LINE_MAX];  /* Error buf for kvm_openfiles. */
+       int     i;
+
+       while ((i = getopt(argc, argv, "MmQqSsabcoptT")) != -1)
+               switch (i) {
+               case 'M':
+                       display = SHMTOTAL;
+                       break;
+               case 'm':
+                       display = SHMINFO;
+                       break;
+               case 'Q':
+                       display = MSGTOTAL;
+                       break;
+               case 'q':
+                       display = MSGINFO;
+                       break;
+               case 'S':
+                       display = SEMTOTAL;
+                       break;
+               case 's':
+                       display = SEMINFO;
+                       break;
+               case 'T':
+                       display = SHMTOTAL | MSGTOTAL | SEMTOTAL;
+                       break;
+               case 'a':
+                       option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME;
+                       break;
+               case 'b':
+                       option |= BIGGEST;
+                       break;
+               case 'c':
+                       option |= CREATOR;
+                       break;
+               case 'o':
+                       option |= OUTSTANDING;
+                       break;
+               case 'p':
+                       option |= PID;
+                       break;
+               case 't':
+                       option |= TIME;
+                       break;
+               default:
+                       usage();
+               }
+
+       if ((display & (MSGINFO | MSGTOTAL))) {
+               if (display & MSGTOTAL) {
+                       struct IPCS_command ic;
+                       struct msginfo msginfo;
+                       size_t ic_size = sizeof(ic);
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_MSG_CONF;
+                       ic.ipcs_cursor = 0;     /* 0 for fw. compat. */
+                       ic.ipcs_data = &msginfo;
+                       ic.ipcs_datalen = sizeof(msginfo);
+
+                       sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size);
+                       printf("msginfo:\n");
+                       printf("\tmsgmax: %6d\t(max characters in a message)\n",
+                           msginfo.msgmax);
+                       printf("\tmsgmni: %6d\t(# of message queues)\n",
+                           msginfo.msgmni);
+                       printf("\tmsgmnb: %6d\t(max characters in a message queue)\n",
+                           msginfo.msgmnb);
+                       printf("\tmsgtql: %6d\t(max # of messages in system)\n",
+                           msginfo.msgtql);
+                       printf("\tmsgssz: %6d\t(size of a message segment)\n",
+                           msginfo.msgssz);
+                       printf("\tmsgseg: %6d\t(# of message segments in system)\n\n",
+                           msginfo.msgseg);
+               }
+               if (display & MSGINFO) {
+                       struct IPCS_command ic;
+                       struct msqid_ds ds;
+                       struct msqid_ds *msqptr = &ds;
+                       size_t ic_size = sizeof(ic);
+
+                       printf("Message Queues:\n");
+                       printf("T     ID     KEY        MODE       OWNER    GROUP");
+                       if (option & CREATOR)
+                               printf("  CREATOR   CGROUP");
+                       if (option & OUTSTANDING)
+                               printf(" CBYTES  QNUM");
+                       if (option & BIGGEST)
+                               printf(" QBYTES");
+                       if (option & PID)
+                               printf(" LSPID LRPID");
+                       if (option & TIME)
+                               printf("   STIME    RTIME    CTIME");
+                       printf("\n");
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_MSG_ITER;
+                       ic.ipcs_cursor = 0;     /* start */
+                       ic.ipcs_data = msqptr;
+                       ic.ipcs_datalen = sizeof(*msqptr);
+
+                       while(!(sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+                               if (msqptr->msg_qbytes != 0) {
+                                       char    stime_buf[100], rtime_buf[100],
+                                               ctime_buf[100];
+
+                                       cvt_time(msqptr->msg_stime, stime_buf);
+                                       cvt_time(msqptr->msg_rtime, rtime_buf);
+                                       cvt_time(msqptr->msg_ctime, ctime_buf);
+
+                                       printf("q %6d %10d %s %8s %8s",
+                                           IXSEQ_TO_IPCID(i, msqptr->msg_perm),
+                                           (int)msqptr->msg_perm.key,
+                                           fmt_perm(msqptr->msg_perm.mode),
+                                           user_from_uid(msqptr->msg_perm.uid, 0),
+                                           group_from_gid(msqptr->msg_perm.gid, 0));
+
+                                       if (option & CREATOR)
+                                               printf(" %8s %8s",
+                                                   user_from_uid(msqptr->msg_perm.cuid, 0),
+                                                   group_from_gid(msqptr->msg_perm.cgid, 0));
+
+                                       if (option & OUTSTANDING)
+                                               printf(" %6lu %6lu",
+                                                   msqptr->msg_cbytes,
+                                                   msqptr->msg_qnum);
+
+                                       if (option & BIGGEST)
+                                               printf(" %6lu",
+                                                   msqptr->msg_qbytes);
+
+                                       if (option & PID)
+                                               printf(" %6d %6d",
+                                                   msqptr->msg_lspid,
+                                                   msqptr->msg_lrpid);
+
+                                       if (option & TIME)
+                                               printf(" %s %s %s",
+                                                   stime_buf,
+                                                   rtime_buf,
+                                                   ctime_buf);
+
+                                       printf("\n");
+                               }
+                       }
+                       printf("\n");
+               }
+       } else
+               if (display & (MSGINFO | MSGTOTAL)) {
+                       fprintf(stderr,
+                           "SVID messages facility not configured in the system\n");
+               }
+
+       if ((display & (SHMINFO | SHMTOTAL))) {
+               if (display & SHMTOTAL) {
+                       struct IPCS_command ic;
+                       struct shminfo shminfo;
+                       size_t ic_size = sizeof(ic);
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_SHM_CONF;
+                       ic.ipcs_cursor = 0;     /* 0 for fw. compat. */
+                       ic.ipcs_data = &shminfo;
+                       ic.ipcs_datalen = sizeof(shminfo);
+
+                       sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size);
+                       printf("shminfo:\n");
+                       printf("\tshmmax: %7d\t(max shared memory segment size)\n",
+                           shminfo.shmmax);
+                       printf("\tshmmin: %7d\t(min shared memory segment size)\n",
+                           shminfo.shmmin);
+                       printf("\tshmmni: %7d\t(max number of shared memory identifiers)\n",
+                           shminfo.shmmni);
+                       printf("\tshmseg: %7d\t(max shared memory segments per process)\n",
+                           shminfo.shmseg);
+                       printf("\tshmall: %7d\t(max amount of shared memory in pages)\n\n",
+                           shminfo.shmall);
+               }
+               if (display & SHMINFO) {
+                       struct IPCS_command ic;
+                       struct shmid_ds ds;
+                       struct shmid_ds *shmptr = &ds;
+                       size_t ic_size = sizeof(ic);
+
+                       printf("Shared Memory:\n");
+                       printf("T     ID     KEY        MODE       OWNER    GROUP");
+                       if (option & CREATOR)
+                               printf("  CREATOR   CGROUP");
+                       if (option & OUTSTANDING)
+                               printf(" NATTCH");
+                       if (option & BIGGEST)
+                               printf("  SEGSZ");
+                       if (option & PID)
+                               printf("  CPID  LPID");
+                       if (option & TIME)
+                               printf("   ATIME    DTIME    CTIME");
+                       printf("\n");
+               {       /* XXX */
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_SHM_ITER;
+                       ic.ipcs_cursor = 0;     /* start */
+                       ic.ipcs_data = shmptr;
+                       ic.ipcs_datalen = sizeof(*shmptr);
+
+                       while(!(sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+                               if (shmptr->shm_perm.mode & 0x0800) {
+                                       char    atime_buf[100], dtime_buf[100],
+                                               ctime_buf[100];
+
+                                       cvt_time(shmptr->shm_atime, atime_buf);
+                                       cvt_time(shmptr->shm_dtime, dtime_buf);
+                                       cvt_time(shmptr->shm_ctime, ctime_buf);
+
+                                       printf("m %6d %10d %s %8s %8s",
+                                           IXSEQ_TO_IPCID(i, shmptr->shm_perm),
+                                           (int)shmptr->shm_perm.key,
+                                           fmt_perm(shmptr->shm_perm.mode),
+                                           user_from_uid(shmptr->shm_perm.uid, 0),
+                                           group_from_gid(shmptr->shm_perm.gid, 0));
+
+                                       if (option & CREATOR)
+                                               printf(" %8s %8s",
+                                                   user_from_uid(shmptr->shm_perm.cuid, 0),
+                                                   group_from_gid(shmptr->shm_perm.cgid, 0));
+
+                                       if (option & OUTSTANDING)
+                                               printf(" %6d",
+                                                   shmptr->shm_nattch);
+
+                                       if (option & BIGGEST)
+                                               printf(" %6d",
+                                                   shmptr->shm_segsz);
+
+                                       if (option & PID)
+                                               printf(" %6d %6d",
+                                                   shmptr->shm_cpid,
+                                                   shmptr->shm_lpid);
+
+                                       if (option & TIME)
+                                               printf(" %s %s %s",
+                                                   atime_buf,
+                                                   dtime_buf,
+                                                   ctime_buf);
+
+                                       printf("\n");
+                               }
+                       }
+               }       /* XXX */
+                       printf("\n");
+               }
+       }
+else
+               if (display & (SHMINFO | SHMTOTAL)) {
+                       fprintf(stderr,
+                           "SVID shared memory facility not configured in the system\n");
+               }
+
+       if ((display & (SEMINFO | SEMTOTAL))) {
+               if (display & SEMTOTAL) {
+                       struct IPCS_command ic;
+                       struct seminfo seminfo;
+                       size_t ic_size = sizeof(ic);
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_SEM_CONF;
+                       ic.ipcs_cursor = 0;     /* 0 for fw. compat. */
+                       ic.ipcs_data = &seminfo;
+                       ic.ipcs_datalen = sizeof(seminfo);
+
+                       sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size);
+                       printf("seminfo:\n");
+                       printf("\tsemmap: %6d\t(# of entries in semaphore map)\n",
+                           seminfo.semmap);
+                       printf("\tsemmni: %6d\t(# of semaphore identifiers)\n",
+                           seminfo.semmni);
+                       printf("\tsemmns: %6d\t(# of semaphores in system)\n",
+                           seminfo.semmns);
+                       printf("\tsemmnu: %6d\t(# of undo structures in system)\n",
+                           seminfo.semmnu);
+                       printf("\tsemmsl: %6d\t(max # of semaphores per id)\n",
+                           seminfo.semmsl);
+                       printf("\tsemopm: %6d\t(max # of operations per semop call)\n",
+                           seminfo.semopm);
+                       printf("\tsemume: %6d\t(max # of undo entries per process)\n",
+                           seminfo.semume);
+                       printf("\tsemusz: %6d\t(size in bytes of undo structure)\n",
+                           seminfo.semusz);
+                       printf("\tsemvmx: %6d\t(semaphore maximum value)\n",
+                           seminfo.semvmx);
+                       printf("\tsemaem: %6d\t(adjust on exit max value)\n\n",
+                           seminfo.semaem);
+               }
+               if (display & SEMINFO) {
+                       struct IPCS_command ic;
+                       struct semid_ds ds;
+                       struct semid_ds *semaptr = &ds;
+                       size_t ic_size = sizeof(ic);
+
+                       printf("Semaphores:\n");
+                       printf("T     ID     KEY        MODE       OWNER    GROUP");
+                       if (option & CREATOR)
+                               printf("  CREATOR   CGROUP");
+                       if (option & BIGGEST)
+                               printf(" NSEMS");
+                       if (option & TIME)
+                               printf("   OTIME    CTIME");
+                       printf("\n");
+
+                       ic.ipcs_magic = IPCS_MAGIC;
+                       ic.ipcs_op = IPCS_SEM_ITER;
+                       ic.ipcs_cursor = 0;     /* start */
+                       ic.ipcs_data = semaptr;
+                       ic.ipcs_datalen = sizeof(*semaptr);
+
+                       while(!(sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+                               if ((semaptr->sem_perm.mode & SEM_ALLOC) != 0) {
+                                       char    ctime_buf[100], otime_buf[100];
+
+                                       cvt_time(semaptr->sem_otime, otime_buf);
+                                       cvt_time(semaptr->sem_ctime, ctime_buf);
+
+                                       printf("s %6d %10d %s %8s %8s",
+                                           IXSEQ_TO_IPCID(i, semaptr->sem_perm),
+                                           (int)semaptr->sem_perm.key,
+                                           fmt_perm(semaptr->sem_perm.mode),
+                                           user_from_uid(semaptr->sem_perm.uid, 0),
+                                           group_from_gid(semaptr->sem_perm.gid, 0));
+
+                                       if (option & CREATOR)
+                                               printf(" %8s %8s",
+                                                   user_from_uid(semaptr->sem_perm.cuid, 0),
+                                                   group_from_gid(semaptr->sem_perm.cgid, 0));
+
+                                       if (option & BIGGEST)
+                                               printf(" %6d",
+                                                   semaptr->sem_nsems);
+
+                                       if (option & TIME)
+                                               printf(" %s %s",
+                                                   otime_buf,
+                                                   ctime_buf);
+
+                                       printf("\n");
+                               }
+                       }
+
+                       printf("\n");
+               }
+       } else
+               if (display & (SEMINFO | SEMTOTAL)) {
+                       fprintf(stderr, "SVID semaphores facility not configured in the system\n");
+               }
+
+       exit(0);
+}
+
+usage()
+{
+
+       fprintf(stderr,
+           "usage: ipcs [-abcmopqstMQST]\n");
+       exit(1);
+}
index 33d8b5a98e692c9a562f625eca97e8c0b8a31b82..35bb9ccd44ae51548b2c3e7de14a56a6f7de2b62 100644 (file)
@@ -18,7 +18,7 @@ CFILES = cmp.c ls.c print.c util.c
 
 OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ls.1
 
-OTHER_CFLAGS = -DCOLORLS
+OTHER_CFLAGS = -DCOLORLS -I/System/Library/Frameworks/System.framework/PrivateHeaders
 OTHER_LDFLAGS = -lncurses
 
 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
diff --git a/ls/ls.1 b/ls/ls.1
index 1672c135e5fc388d421cdea4216db71a3e5e11f8..dad0f657e60c7dad892dd67e290779de842f1455 100644 (file)
--- a/ls/ls.1
+++ b/ls/ls.1
@@ -43,7 +43,7 @@
 .Nd list directory contents
 .Sh SYNOPSIS
 .Nm
-.Op Fl ABCFGHLPRTWZabcdfghiklmnopqrstuwx1
+.Op Fl ABCFGHLPRTWZabcdefghiklmnopqrstuwx1
 .Op Ar
 .Sh DESCRIPTION
 For each operand that names a
@@ -136,6 +136,8 @@ and
 options.
 .It Fl R
 Recursively list subdirectories encountered.
+.It Fl S
+Sort files by size
 .It Fl T
 When used with the
 .Fl l
@@ -159,13 +161,14 @@ escape codes whenever possible.
 Use time when file status was last changed for sorting or printing.
 .It Fl d
 Directories are listed as plain files (not searched recursively).
+.It Fl e
+Print the Access Control List (ACL) associated with the file, if present.
 .It Fl f
 Output is not sorted.
 .It Fl g
-This option is deprecated and is only available for compatibility
-with
-.Bx 4.3 ;
-it was used to display the group name in the long
+This option is only available for compatibility
+with POSIX;
+it is used to display the group name in the long
 .Pq Fl l
 format output.
 .It Fl h
@@ -212,7 +215,10 @@ the character
 this is the default when output is to a terminal.
 .It Fl r
 Reverse the order of the sort to get reverse
-lexicographical order or the oldest entries first.
+lexicographical order or the oldest entries first (or largest files
+last, if combined with sort by size
+.Pq Fl S
+flag).
 .It Fl s
 Display the number of file system blocks actually used by each file, in units
 of 512 bytes, where partial units are rounded up to the next integer value.
@@ -312,6 +318,10 @@ In addition, for each directory whose contents are displayed, the total
 number of 512-byte blocks used by the files in the directory is displayed
 on a line by itself immediately before the information for the files in the
 directory.
+If the file or directory has extended security information, the permissions
+field printed by the 
+.Fl l
+option is followed by a '+' character.
 .Pp
 If the modification time of the file is more than 6 months
 in the past or future, then the year of the last modification
@@ -623,6 +633,7 @@ specification.
 .Xr chmod 1 ,
 .Xr sort 1 ,
 .Xr xterm 1 ,
+.Xr compat 5 ,
 .Xr termcap 5 ,
 .Xr symlink 7 ,
 .Xr sticky 8
diff --git a/ls/ls.c b/ls/ls.c
index 92e38b3fdcc87900910f8d71858d9d3fe9731426..f00991711d25fe10fd2736e8b9709c5d859aa568 100644 (file)
--- a/ls/ls.c
+++ b/ls/ls.c
@@ -68,12 +68,13 @@ __RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $");
 #include <termcap.h>
 #include <signal.h>
 #endif
-
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
 #include "ls.h"
 #include "extern.h"
-#ifndef __APPLE__
-#include "lomac.h"
-#endif /* __APPLE__ */
 
 /*
  * Upward approximation of the maximum number of characters needed to
@@ -121,7 +122,8 @@ static int f_timesort;              /* sort by time vice name */
 static int f_sizesort;         /* sort by size */
        int f_type;             /* add type character for non-regular files */
 static int f_whiteout;         /* show whiteout entries */
-       int f_lomac;            /* show LOMAC attributes */
+       int f_acl;              /* show ACLs in long listing */
+       int f_group;            /* show group */
 #ifdef COLORLS
        int f_color;            /* add type in color for non-regular files */
 
@@ -171,7 +173,7 @@ main(int argc, char *argv[])
                f_listdot = 1;
 
        fts_options = FTS_PHYSICAL;
-       while ((ch = getopt(argc, argv, "1ABCFGHLPRSTWZabcdfghiklmnopqrstuvwx")) 
+       while ((ch = getopt(argc, argv, "1ABCFGHLPRSTWabcdefghiklmnopqrstuvwx")) 
            != -1) {
                switch (ch) {
                /*
@@ -246,7 +248,13 @@ main(int argc, char *argv[])
                case 'f':
                        f_nosort = 1;
                        break;
-               case 'g':       /* Compatibility with 4.3BSD. */
+               case 'g':       /* Compatibility with Unix03 */
+                       if (COMPAT_MODE("bin/ls", "Unix2003")) {
+                               f_group = 1;
+                               f_longform = 1;
+                               f_singlecol = 0;
+                               f_stream = 0;
+                       }
                        break;
                case 'h':
                        f_humanval = 1;
@@ -310,8 +318,8 @@ main(int argc, char *argv[])
                        f_octal = 0;
                        f_octal_escape = 0;
                        break;
-               case 'Z':
-                       f_lomac = 1;
+               case 'e':
+                       f_acl = 1;
                        break;
                default:
                case '?':
@@ -619,10 +627,6 @@ display(FTSENT *p, FTSENT *list)
                maxnlink = makenines(maxnlink);
                maxsize = makenines(maxsize);
        }
-#ifndef __APPLE__
-       if (f_lomac)
-               lomac_start();
-#endif /* __APPLE__ */
        bcfile = 0;
        flags = NULL;
        for (cur = list, entries = 0; cur; cur = cur->fts_link) {
@@ -700,16 +704,8 @@ display(FTSENT *p, FTSENT *list)
                                } else
                                        flen = 0;
                                lattr = NULL;
-#ifndef __APPLE__
-                               if (f_lomac) {
-                                       lattr = get_lattr(cur);
-                                       lattrlen = strlen(lattr);
-                                       if (lattrlen > maxlattr)
-                                               maxlattr = lattrlen;
-                               } else
-#endif /* __APPLE__ */
-                                       lattrlen = 0;
-
+                               lattrlen = 0;
+                               
                                if ((np = malloc(sizeof(NAMES) + lattrlen +
                                    ulen + glen + flen + 4)) == NULL)
                                        err(1, "malloc");
@@ -728,14 +724,6 @@ display(FTSENT *p, FTSENT *list)
                                        (void)strcpy(np->flags, flags);
                                        free(flags);
                                }
-#ifndef __APPLE__
-                               if (f_lomac) {
-                                       np->lattr = &np->data[ulen + glen + 2
-                                           + (f_flags ? flen + 1 : 0)];
-                                       (void)strcpy(np->lattr, lattr);
-                                       free(lattr);
-                               }
-#endif /* __APPLE__ */
                                cur->fts_pointer = np;
                        }
                }
@@ -770,10 +758,6 @@ display(FTSENT *p, FTSENT *list)
        if (f_longform)
                for (cur = list; cur; cur = cur->fts_link)
                        free(cur->fts_pointer);
-#ifndef __APPLE__
-       if (f_lomac)
-               lomac_stop();
-#endif /* __APPLE__ */
 }
 
 /*
diff --git a/ls/ls.h b/ls/ls.h
index 4f15f9899e39b1aeaba3ca403c99915c691d2d3f..73e264d1a488e1135848250b4cc6f924abd8c032 100644 (file)
--- a/ls/ls.h
+++ b/ls/ls.h
@@ -45,9 +45,6 @@ extern long blocksize;                /* block size units */
 extern int f_accesstime;       /* use time of last access */
 extern int f_flags;            /* show flags associated with a file */
 extern int f_humanval;         /* show human-readable file sizes */
-#ifndef __APPLE__
-extern int f_lomac;            /* show LOMAC attributes */
-#endif /* __APPLE__ */
 extern int f_inode;            /* print inode */
 extern int f_longform;         /* long listing format */
 extern int f_octal;            /* print unprintables in octal */
@@ -60,6 +57,8 @@ extern int f_sortacross;      /* sort across rows, not down columns */
 extern int f_statustime;       /* use time of last mode change */
 extern int f_notabs;           /* don't use tab-separated multi-col output */
 extern int f_type;             /* add type character for non-regular files */
+extern int f_acl;              /* print ACLs in long format */
+extern int f_group;            /* list group without owner */
 #ifdef COLORLS
 extern int f_color;            /* add type in color for non-regular files */
 #endif
index 4962a95dea06f89f3b50f9729983d12f022a881b..a9d651e96f8655e6b26d65f8620669dbd0b67b5a 100644 (file)
@@ -44,6 +44,15 @@ __RCSID("$FreeBSD: src/bin/ls/print.c,v 1.57 2002/08/29 14:29:09 keramida Exp $"
 
 #include <sys/param.h>
 #include <sys/stat.h>
+#ifdef __APPLE__
+#include <sys/acl.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <membership.h>
+#include <membershipPriv.h>
+#include <uuid/uuid.h>
+#endif
 
 #include <err.h>
 #include <errno.h>
@@ -152,6 +161,162 @@ printname(const char *name)
                return printf("%s", name);
 }
 
+/*
+ * print access control list
+ */
+static struct {
+       acl_perm_t      perm;
+       char            *name;
+       int             flags;
+#define ACL_PERM_DIR   (1<<0)
+#define ACL_PERM_FILE  (1<<1)
+} acl_perms[] = {
+       {ACL_READ_DATA,         "read",         ACL_PERM_FILE},
+       {ACL_LIST_DIRECTORY,    "list",         ACL_PERM_DIR},
+       {ACL_WRITE_DATA,        "write",        ACL_PERM_FILE},
+       {ACL_ADD_FILE,          "add_file",     ACL_PERM_DIR},
+       {ACL_EXECUTE,           "execute",      ACL_PERM_FILE},
+       {ACL_SEARCH,            "search",       ACL_PERM_DIR},
+       {ACL_DELETE,            "delete",       ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_APPEND_DATA,       "append",       ACL_PERM_FILE},
+       {ACL_ADD_SUBDIRECTORY,  "add_subdirectory", ACL_PERM_DIR},
+       {ACL_DELETE_CHILD,      "delete_child", ACL_PERM_DIR},
+       {ACL_READ_ATTRIBUTES,   "readattr",     ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_ATTRIBUTES,  "writeattr",    ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_READ_SECURITY,     "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_WRITE_SECURITY,    "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_CHANGE_OWNER,      "chown",        ACL_PERM_FILE | ACL_PERM_DIR},
+       {0, NULL, 0}
+};
+
+static struct {
+       acl_flag_t      flag;
+       char            *name;
+       int             flags;
+} acl_flags[] = {
+       {ACL_ENTRY_FILE_INHERIT,        "file_inherit",         ACL_PERM_DIR},
+       {ACL_ENTRY_DIRECTORY_INHERIT,   "directory_inherit",    ACL_PERM_DIR},
+       {ACL_ENTRY_LIMIT_INHERIT,       "limit_inherit",        ACL_PERM_FILE | ACL_PERM_DIR},
+       {ACL_ENTRY_ONLY_INHERIT,        "only_inherit",         ACL_PERM_DIR},
+       {0, NULL, 0}
+};
+
+static char *
+uuid_to_name(uuid_t *uu) 
+{
+  int is_gid = -1;
+  struct group *tgrp = NULL;
+  struct passwd *tpass = NULL;
+  char *name = NULL;
+  uid_t id;
+
+
+#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
+  name = (char *) malloc(MAXNAMETAG);
+  
+  if (NULL == name)
+         err(1, "malloc");
+
+  if (0 != mbr_uuid_to_id(uu, &id, &is_gid))
+         goto errout;
+  
+  switch (is_gid) {
+  case ID_TYPE_UID:
+         tpass = getpwuid(id);
+         if (!tpass) {
+                 goto errout;
+         }
+         snprintf(name, MAXNAMETAG, "%s:%s", "user", tpass->pw_name);
+         break;
+  case ID_TYPE_GID:
+         tgrp = getgrgid((gid_t) id);
+         if (!tgrp) {
+                 goto errout;
+         }
+         snprintf(name, MAXNAMETAG, "%s:%s", "group", tgrp->gr_name);
+         break;
+  default:
+         if (0 != mbr_uuid_to_string(uu, name))
+                 goto errout;
+  }
+  return name;
+ errout:
+  fprintf(stderr, "Unable to translate qualifier on ACL\n");
+  strcpy(name, "<UNKNOWN>");
+  return name;
+}
+
+static void
+printacl(acl_t acl, int isdir)
+{
+       acl_entry_t     entry;
+       int             index;
+       uuid_t          *applicable;
+       char            *name = NULL;
+       acl_tag_t       tag;
+       acl_flagset_t   flags;
+       acl_permset_t   perms;
+       char            *type;
+       int             i, first;
+       
+
+       for (index = 0;
+            acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;
+            index++) {
+               if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
+                       continue;
+               if (acl_get_tag_type(entry, &tag) != 0)
+                       continue;
+               if (acl_get_flagset_np(entry, &flags) != 0)
+                       continue;
+               if (acl_get_permset(entry, &perms) != 0)
+                       continue;
+
+               name = uuid_to_name(applicable);
+               acl_free(applicable);
+
+               switch(tag) {
+               case ACL_EXTENDED_ALLOW:
+                       type = "allow";
+                       break;
+               case ACL_EXTENDED_DENY:
+                       type = "deny";
+                       break;
+               default:
+                       type = "unknown";
+               }
+
+               (void)printf(" %d: %s%s %s ",
+                   index,
+                   name,
+                   acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "",
+                   type);
+
+               if (name)
+                       free(name);
+
+               for (i = 0, first = 0; acl_perms[i].name != NULL; i++) {
+                       if (acl_get_perm_np(perms, acl_perms[i].perm) == 0)
+                               continue;
+                       if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+                               continue;
+                       (void)printf("%s%s", first++ ? "," : "", acl_perms[i].name);
+               }
+               for (i = 0; acl_flags[i].name != NULL; i++) {
+                       if (acl_get_flag_np(flags, acl_flags[i].flag) == 0)
+                               continue;
+                       if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+                               continue;
+                       (void)printf("%s%s", first++ ? "," : "", acl_flags[i].name);
+               }
+                       
+               (void)putchar('\n');
+       }
+
+}
+
 void
 printlong(DISPLAY *dp)
 {
@@ -159,6 +324,10 @@ printlong(DISPLAY *dp)
        FTSENT *p;
        NAMES *np;
        char buf[20];
+#ifdef __APPLE__
+       acl_t acl = NULL;
+       char full_path[MAXPATHLEN];
+#endif
 #ifdef COLORLS
        int color_printed = 0;
 #endif
@@ -177,15 +346,37 @@ printlong(DISPLAY *dp)
                            dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize));
                strmode(sp->st_mode, buf);
                np = p->fts_pointer;
-               (void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
-                   sp->st_nlink, dp->s_user, np->user, dp->s_group,
-                   np->group);
+#ifdef __APPLE__
+               if (p->fts_parent->fts_name && *p->fts_parent->fts_name)
+               {
+                   snprintf(full_path, sizeof full_path, "%s/%s",
+                           p->fts_parent->fts_accpath, p->fts_accpath);
+                   acl = acl_get_file(full_path, ACL_TYPE_EXTENDED);
+               } else
+                   acl = acl_get_file(p->fts_accpath, ACL_TYPE_EXTENDED);
+#endif /* __APPLE__ */
+               if (f_group) {
+#ifdef __APPLE__
+                       (void)printf("%s%s %*u %-*s  ", buf, acl == NULL ? " " : "+", dp->s_nlink,
+                                    sp->st_nlink, dp->s_group, np->group);
+#else  /* ! __APPLE__ */
+                       (void)printf("%s %*u %-*s  ", buf, dp->s_nlink,
+                                    sp->st_nlink, dp->s_group, np->group);
+#endif /* __APPLE__ */
+               }
+               else {
+#ifdef __APPLE__
+                       (void)printf("%s%s %*u %-*s  %-*s  ", buf, acl == NULL ? " " : "+", dp->s_nlink,
+                                    sp->st_nlink, dp->s_user, np->user, dp->s_group,
+                                    np->group);
+#else  /* ! __APPLE__ */
+                       (void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
+                                    sp->st_nlink, dp->s_user, np->user, dp->s_group,
+                                    np->group);
+#endif /* ! __APPLE__ */
+               }
                if (f_flags)
                        (void)printf("%-*s ", dp->s_flags, np->flags);
-#ifndef __APPLE__
-               if (f_lomac)
-                       (void)printf("%-*s ", dp->s_lattr, np->lattr);
-#endif /* __APPLE__ */
                if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
                        if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
                                (void)printf("%3d, 0x%08x ",
@@ -219,6 +410,11 @@ printlong(DISPLAY *dp)
                if (S_ISLNK(sp->st_mode))
                        printlink(p);
                (void)putchar('\n');
+#ifdef __APPLE__
+               if (f_acl && (acl != NULL))
+                       printacl(acl, S_ISDIR(sp->st_mode));
+               acl_free(acl);
+#endif /* __APPLE__ */
        }
 }
 
@@ -610,12 +806,12 @@ printsize(size_t width, off_t bytes)
                unit = unit_adjust(&bytes);
 
                if (bytes == 0)
-                       (void)printf("%*s ", width, "0B");
+                       (void)printf("%*s ", (int)width, "0B");
                else
-                       (void)printf("%*lld%c ", width - 1, bytes,
+                       (void)printf("%*lld%c ", (int)width - 1, bytes,
                            "BKMGTPE"[unit]);
        } else
-               (void)printf("%*lld ", width, bytes);
+               (void)printf("%*lld ", (int)width, bytes);
 }
 
 /*
index 49b438546665b5667dc81cf9a35f23a8f27d1dc6..e74fa61be5fec638306c7ff42e08149f08c7d914 100644 (file)
--- a/ls/util.c
+++ b/ls/util.c
@@ -156,9 +156,9 @@ usage(void)
 {
        (void)fprintf(stderr,
 #ifdef COLORLS
-       "usage: ls [-ABCFGHLPRSTWZabcdfghiklnoqrstuvx1]"
+       "usage: ls [-ABCFGHLPRSTWZabcdefghiklmnopqrstuwx1]"
 #else
-       "usage: ls [-ABCFHLPRSTWZabcdfghiklnoqrstuvx1]"
+       "usage: ls [-ABCFHLPRSTWZabcdefghiklmnopqrstuwx1]"
 #endif
                      " [file ...]\n");
        exit(1);
diff --git a/mv/mv.c b/mv/mv.c
index 994aec2afaf940dc17240cf6ff6d7659e4d0dee5..f79cc0ef6efa88556e9d809ab29b73b6200a05c0 100644 (file)
--- a/mv/mv.c
+++ b/mv/mv.c
@@ -67,6 +67,10 @@ __RCSID("$FreeBSD: src/bin/mv/mv.c,v 1.39 2002/07/09 17:45:13 johan Exp $");
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include <copyfile.h>
+#endif
+
 #include "pathnames.h"
 
 int fflg, iflg, nflg, vflg;
@@ -331,6 +335,9 @@ err:                if (unlink(to))
                (void)close(to_fd);
                return (1);
        }
+#ifdef __APPLE__
+       copyfile(from, to, 0, COPYFILE_ACL | COPYFILE_XATTR);
+#endif
        (void)close(from_fd);
 
        oldmode = sbp->st_mode & ALLPERMS;
diff --git a/pathchk/Makefile b/pathchk/Makefile
new file mode 100644 (file)
index 0000000..afd7d69
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = pathchk
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = pathchk.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble pathchk.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/pathchk/Makefile.postamble b/pathchk/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/pathchk/Makefile.preamble b/pathchk/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/pathchk/PB.project b/pathchk/PB.project
new file mode 100644 (file)
index 0000000..2ba4aa7
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (pathchk.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, pathchk.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = pathchk; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/pathchk/pathchk.1 b/pathchk/pathchk.1
new file mode 100644 (file)
index 0000000..efe2a05
--- /dev/null
@@ -0,0 +1,121 @@
+.\" Copyright (c) 2001, 2002 Chuck Rouillard
+.\" 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. The name of the author may not be used to endorse or promote
+.\"    products derived from this software without specific prior written
+.\"    permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" $FreeBSD: src/usr.bin/pathchk/pathchk.1,v 1.3 2002/12/12 17:26:01 ru Exp $
+.\"
+.Dd May 21, 2002
+.Dt PATHCHK 1
+.Os
+.Sh NAME
+.Nm pathchk
+.Nd check pathnames
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Ar pathname ...
+.Sh DESCRIPTION
+The
+.Nm
+utility checks whether each of the specified
+.Ar pathname
+arguments is valid or portable.
+.Pp
+A diagnostic message is written for each argument that:
+.Bl -bullet
+.It
+Is longer than
+.Dv PATH_MAX
+bytes.
+.It
+Contains any component longer than
+.Dv NAME_MAX
+bytes.
+(The value of
+.Dv NAME_MAX
+depends on the underlying file system.)
+.It
+Contains a directory component that is not searchable.
+.El
+.Pp
+It is not considered an error if a
+.Ar pathname
+argument contains a nonexistent component as long as a component by that
+name could be created.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl p
+Perform portability checks on the specified
+.Ar pathname
+arguments.
+Diagnostic messages will be written for each argument that:
+.Bl -bullet
+.It
+Is longer than
+.Dv _POSIX_PATH_MAX
+.Pq 255
+bytes.
+.It
+Contains a component longer than
+.Dv _POSIX_NAME_MAX
+.Pq 14
+bytes.
+.It
+Contains any character not in the portable filename character set (that is,
+alphanumeric characters,
+.Ql \&. ,
+.Ql \&-
+and
+.Ql _ ) .
+No component may start with the hyphen
+.Pq Ql \&-
+character.
+.El
+.El
+.Sh EXAMPLES
+Check whether the names of files in the current directory are portable to
+other
+.Tn POSIX
+systems:
+.Pp
+.Dl "find . -print | xargs pathchk -p"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr getconf 1 ,
+.Xr pathconf 2 ,
+.Xr stat 2
+.Sh STANDARDS
+The
+.Nm
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Fx 5.0 .
diff --git a/pathchk/pathchk.c b/pathchk/pathchk.c
new file mode 100644 (file)
index 0000000..233a6d0
--- /dev/null
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * pathchk -- check pathnames
+ *
+ * Check whether files could be created with the names specified on the
+ * command line. If -p is specified, check whether the pathname is portable
+ * to all POSIX systems.
+ */
+
+#include <sys/cdefs.h>
+
+/* Commenting __FBSDID, as it is not needed n OSX ......
+__FBSDID("$FreeBSD: src/usr.bin/pathchk/pathchk.c,v 1.4 2002/12/15 00:40:47 tjr Exp $");
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int      check(const char *);
+static int      portable(const char *);
+static void     usage(void);
+
+static int      pflag;                 /* Perform portability checks */
+
+int
+main(int argc, char *argv[])
+{
+       int ch, rval;
+       const char *arg;
+
+       while ((ch = getopt(argc, argv, "p")) > 0) {
+               switch (ch) {
+               case 'p':
+                       pflag = 1;
+                       break;
+               default:
+                       usage();
+                       /*NOTREACHED*/
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc == 0)
+               usage();
+
+       rval = 0;
+       while ((arg = *argv++) != NULL)
+               rval |= check(arg);
+
+       exit(rval);
+}
+
+static void
+usage(void)
+{
+
+       fprintf(stderr, "usage: pathchk [-p] pathname...\n");
+       exit(1);
+}
+
+static int
+check(const char *path)
+{
+       struct stat sb;
+       long complen, namemax, pathmax, svnamemax;
+       int badch, last;
+       char *end, *p, *pathd;
+
+       if ((pathd = strdup(path)) == NULL)
+               err(1, "strdup");
+
+       p = pathd;
+
+       if (!pflag) {
+               errno = 0;
+               namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX);
+               if (namemax == -1 && errno != 0)
+                       namemax = NAME_MAX;
+       } else
+               namemax = _POSIX_NAME_MAX;
+
+       for (;;) {
+               p += strspn(p, "/");
+               complen = (long)strcspn(p, "/");
+               end = p + complen;
+               last = *end == '\0';
+               *end = '\0';
+
+               if (namemax != -1 && complen > namemax) {
+                       warnx("%s: %s: component too long (limit %ld)", path,
+                           p, namemax);
+                       goto bad;
+               }
+
+               if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) {
+                       warn("%s: %.*s", path, (int)(strlen(pathd) -
+                           complen - 1), pathd);
+                       goto bad;
+               }
+
+               if (pflag && (badch = portable(p)) >= 0) {
+                       warnx("%s: %s: component contains non-portable "
+                           "character `%c'", path, p, badch);
+                       goto bad;
+               }
+
+               if (last)
+                       break;
+
+               if (!pflag) {
+                       errno = 0;
+                       svnamemax = namemax;
+                       namemax = pathconf(pathd, _PC_NAME_MAX);
+                       if (namemax == -1 && errno != 0)
+                               namemax = svnamemax;
+               }
+
+               *end = '/';
+               p = end + 1;
+       }
+
+       if (!pflag) {
+               errno = 0;
+               pathmax = pathconf(path, _PC_PATH_MAX);
+               if (pathmax == -1 && errno != 0)
+                       pathmax = PATH_MAX;
+       } else
+               pathmax = _POSIX_PATH_MAX;
+       if (pathmax != -1 && strlen(path) >= (size_t)pathmax) {
+               warnx("%s: path too long (limit %ld)", path, pathmax - 1);
+               goto bad;
+       }
+
+       free(pathd);
+       return (0);
+
+bad:   free(pathd);
+       return (1);
+}
+
+/*
+ * Check whether a path component contains only portable characters. Return
+ * the first non-portable character found.
+ */
+static int
+portable(const char *path)
+{
+       static const char charset[] =
+           "abcdefghijklmnopqrstuvwxyz"
+           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+           "0123456789._-";
+       long s;
+
+       if (*path == '-')
+               return (*path);
+
+       s = strspn(path, charset);
+       if (path[s] != '\0')
+               return (path[s]);
+
+       return (-1);
+}
index 9fd73460db620aaa005b650107b17c5df02293b0..74e13ca70a4dd10efedb70a056a647aeb4187b12 100644 (file)
@@ -128,8 +128,10 @@ ar_open(name)
                if (name == NULL) {
                        arfd = STDIN_FILENO;
                        arcname = STDN;
-               } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0)
+               } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) {
                        syswarn(0, errno, "Failed open to read on %s", name);
+                       exit(1);
+               }
                if (zflag)
                        ar_start_gzip(arfd);
                break;
@@ -649,7 +651,15 @@ ar_write(buf, bsz)
                wr_trail = 1;
                io_ok = 1;
                return(bsz);
+       } else if (res < 0 && artyp == ISPIPE && errno == EPIPE) { /* ignore it */
+               wr_trail = 1;
+               io_ok = 1;
+               errno = 0;
+               arfd = open("/dev/null", AR_MODE, DMOD);
+               artyp = ISREG;
+               return bsz; 
        }
+
        /*
         * write broke, see what we can do with it. We try to send any partial
         * writes that may violate pax spec to the next archive volume.
@@ -1186,7 +1196,8 @@ ar_next()
        if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0)
                syswarn(0, errno, "Unable to restore signal mask");
 
-       if (done || !wr_trail || strcmp(NM_TAR, argv0) == 0)
+       /* Don't query for new volume if format is unknown */
+       if (frmt == NULL || done || !wr_trail || strcmp(NM_TAR, argv0) == 0)
                return(-1);
 
        tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0);
index 95bd7d54a275dff56ff2d6f90ca430254789c562..8a455f29ebca3fa6492a9313039e7f6533babd9f 100644 (file)
@@ -57,6 +57,11 @@ static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_subs.c,v 1.13 19
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
+#ifdef __APPLE__
+#include <copyfile.h>
+#include <libgen.h>
+#include <sys/queue.h>
+#endif
 #include "pax.h"
 #include "extern.h"
 
@@ -178,6 +183,18 @@ extract()
        struct stat sb;
        int fd;
        time_t now;
+#ifdef __APPLE__
+       int copyfile_disable = (getenv(COPYFILE_DISABLE_VAR) != NULL);
+       LIST_HEAD(copyfile_list_t, copyfile_list_entry_t) copyfile_list;
+       struct copyfile_list_entry_t {
+           char *src;
+           char *dst;
+           char *tmp;
+           LIST_ENTRY(copyfile_list_entry_t) link;
+       } *cle;
+
+       LIST_INIT(&copyfile_list);
+#endif
 
        arcn = &archd;
        /*
@@ -347,6 +364,24 @@ extract()
                if (!res)
                        (void)rd_skip(cnt + arcn->pad);
 
+#ifdef __APPLE__
+               if (!strncmp(basename(arcn->name), "._", 2))
+               {
+                       cle = alloca(sizeof(struct copyfile_list_entry_t));
+                       cle->src = strdup(arcn->name);
+
+                       if (asprintf(&cle->tmp, "%s.XXX", cle->src) > MAXPATHLEN)
+                           continue;
+                       if (mktemp(cle->tmp) == NULL)
+                           continue;
+                       if (rename(cle->src, cle->tmp))
+                           continue;
+
+                       if (asprintf(&cle->dst, "%s/%s",
+                               dirname(arcn->name), basename(arcn->name) + 2) != -1)
+                               LIST_INSERT_HEAD(&copyfile_list, cle, link);
+               }
+#endif
                /*
                 * if required, chdir around.
                 */
@@ -356,6 +391,20 @@ extract()
                                    "Can't fchdir to starting directory");
        }
 
+#ifdef __APPLE__
+       LIST_FOREACH(cle, &copyfile_list, link)
+       {
+           if(copyfile_disable || copyfile(cle->tmp, cle->dst, 0,
+               COPYFILE_UNPACK | COPYFILE_XATTR | COPYFILE_ACL))
+               rename(cle->tmp, cle->src);
+           else
+               unlink(cle->tmp);
+           free(cle->dst);
+           free(cle->src);
+           free(cle->tmp);
+       }
+#endif
+
        /*
         * all done, restore directory modes and times as required; make sure
         * all patterns supplied by the user were matched; block off signals
@@ -391,6 +440,12 @@ wr_archive(arcn, is_app)
        int (*wrf)();
        int fd = -1;
        time_t now;
+#ifdef __APPLE__
+       int metadata = 0;
+       char *md_fname = NULL;
+       ARCHD arcn_copy;
+       char arcn_copy_name[PAXPATHLEN+1];
+#endif
 
        /*
         * if this format supports hard link storage, start up the database
@@ -429,6 +484,64 @@ wr_archive(arcn, is_app)
                 */
                if (sel_chk(arcn) != 0)
                        continue;
+#ifdef __APPLE__
+               /*
+                * synthesize ._ files for each node we encounter 
+                */
+               if (getenv(COPYFILE_DISABLE_VAR) == NULL
+                   && copyfile(arcn->name, NULL, NULL,
+                       COPYFILE_CHECK | COPYFILE_XATTR | COPYFILE_ACL)
+                   && arcn->nlen + 2 < sizeof(arcn->name))
+               {
+                   int size;
+                   char *new;
+
+                   md_fname = strdup("/tmp/pax.md.XXXX");
+                   memcpy(&arcn_copy, arcn, sizeof(ARCHD));
+                   strncpy(arcn_copy_name, arcn->name, PAXPATHLEN+1);
+
+                   arcn->skip = 0;
+                   arcn->pad = 0;
+                   arcn->ln_nlen = 0;
+                   arcn->ln_name[0] = '\0';
+                   arcn->type = PAX_REG;
+
+                   if(md_fname && mktemp(md_fname))
+                           if(copyfile(arcn->name, md_fname, NULL,
+                               COPYFILE_PACK | COPYFILE_XATTR | COPYFILE_ACL) < 0)
+                           {
+                               syswarn(1,EPERM,
+                                       "Unable to preserve metadata on %s", arcn->name);
+                               goto next;
+                           }
+
+                   stat(md_fname, &arcn->sb);
+                   arcn->skip = arcn->sb.st_size;
+
+                   if (!strncmp(dirname(arcn->name), ".", 2))
+                       size = asprintf(&new, "._%s",
+                               basename(arcn->name));
+                   else
+                       size = asprintf(&new, "%s/._%s",
+                               dirname(arcn->name), basename(arcn->name));
+
+                   if (size != -1 && size < sizeof arcn->name)
+                   {
+                       strncpy(arcn->name, new, PAXPATHLEN+1);
+                   } else {
+                       goto next;
+                   }
+                   arcn->nlen = strlen(arcn->name);
+                   arcn->org_name = arcn->name;
+                   free(new);
+                   metadata = 1;
+               } else if (metadata) {
+next:
+                   metadata = 0;
+                   memcpy(arcn, &arcn_copy, sizeof(ARCHD));
+                   strncpy(arcn->name, arcn_copy_name, PAXPATHLEN+1);
+               }
+#endif
                fd = -1;
                if (uflag) {
                        /*
@@ -457,7 +570,18 @@ wr_archive(arcn, is_app)
                         * we were later unable to read (we also purge it from
                         * the link table).
                         */
+#ifdef __APPLE__
+                       if (metadata)
+                       {
+                           fd = open(md_fname, O_RDONLY, 0);
+                           unlink(md_fname);
+                           free(md_fname);
+                       } else
+                           fd = open(arcn->org_name, O_RDONLY, 0);
+                       if (fd < 0) {
+#else
                        if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+#endif
                                syswarn(1,errno, "Unable to open %s to read",
                                        arcn->org_name);
                                purg_lnk(arcn);
@@ -543,6 +667,10 @@ wr_archive(arcn, is_app)
                if (((cnt > 0) && (wr_skip(cnt) < 0)) ||
                    ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0)))
                        break;
+#if __APPLE__
+               if (metadata)
+                   goto next;
+#endif
        }
 
        /*
@@ -976,6 +1104,12 @@ copy()
                        (void)putc('\n', stderr);
                        vfpart = 0;
                }
+#ifdef __APPLE__
+           if (getenv(COPYFILE_DISABLE_VAR) == NULL) {
+                   if (copyfile(arcn->org_name, arcn->name, 0, COPYFILE_ACL | COPYFILE_XATTR) < 0)
+                       paxwarn(1, "File %s had metadata that could not be copied", arcn->org_name);
+           }
+#endif
        }
 
        /*
index 0a1e285dac7b6efcc995000dfd39111a71034702..234dcc38ae97a09d73b104bf3030ba43ea1e3834 100644 (file)
@@ -174,7 +174,7 @@ file_close(arcn, fd)
         * if not preserving mode or we cannot set uid/gid, then PROHIBIT
         * set uid/gid bits
         */
-       if (!pmode || res)
+       if (!pids || res)
                arcn->sb.st_mode &= ~(SETBITS);
        if (pmode)
                set_pmode(arcn->name, arcn->sb.st_mode);
@@ -580,6 +580,9 @@ unlnk_exist(name, type)
        if (kflag)
                return(-1);
 
+       if(strstr(name, "._") != NULL) /* remove when stat works properly */
+           return(0);
+
        if (S_ISDIR(sb.st_mode)) {
                /*
                 * try to remove a directory, if it fails and we were going to
index 9a64e1f31d22ee17fe8b892757d32ecdac513dcc..0bbcaa9c5ea925420acaa379bee75be68a7748fc 100644 (file)
@@ -376,6 +376,9 @@ pax_options(argc, argv)
                         * specify an archive format on write
                         */
                        tmp.name = optarg;
+                       if (0 == strcmp("pax", optarg)) { /* alias for ustar */
+                               tmp.name = "ustar";
+                       }
                        if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
                            sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) {
                                flg |= XF;
index 590c9dfdd7ae0642718cd681509b79bee3fcdd05..8df49bd8f8dd73411e452a3c178863554c198944 100644 (file)
--- a/pax/pax.1
+++ b/pax/pax.1
@@ -1,6 +1,3 @@
-.\"    $OpenBSD: pax.1,v 1.5 1997/04/06 06:11:13 millert Exp $
-.\"    $NetBSD: pax.1,v 1.3 1995/03/21 09:07:37 cgd Exp $
-.\"
 .\" Copyright (c) 1992 Keith Muller.
 .\" Copyright (c) 1992, 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
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)pax.1       8.4 (Berkeley) 4/18/94
+.\" $FreeBSD: src/bin/pax/pax.1,v 1.33 2004/07/03 02:03:44 tjr Exp $
 .\"
-.Dd April 18, 1994
+.Dd July 3, 2004
 .Dt PAX 1
-.Os BSD 4.4
+.Os
 .Sh NAME
 .Nm pax
 .Nd read and write file archives and copy directory hierarchies
 .Sh SYNOPSIS
-.Nm pax
-.Op Fl cdnv
+.Nm
+.Op Fl cdnvz
 .Bk -words
 .Op Fl f Ar archive
 .Ek
 .Bk -words
 .Op Fl s Ar replstr
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl U Ar user
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl G Ar group
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Oo
 .Op Ar from_date
 .Op Ar ,to_date
 .Oc
-.Ar ...
+.Ar ...\&
 .Ek
-.Op Ar pattern ...
-.Nm pax
+.Op Ar pattern ...\&
+.Nm
 .Fl r
-.Op Fl cdiknuvDYZ
+.Op Fl cdiknuvzDYZ
 .Bk -words
 .Op Fl f Ar archive
 .Ek
 .Bk -words
 .Op Fl o Ar options
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl p Ar string
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl s Ar replstr
-.Ar ...
+.Ar ...\&
 .Ek
 .Op Fl E Ar limit
 .Bk -words
 .Op Fl U Ar user
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl G Ar group
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Oo
 .Op Ar from_date
 .Op Ar ,to_date
 .Oc
-.Ar ...
+.Ar ...\&
 .Ek
-.Op Ar pattern ...
-.Nm pax
+.Op Ar pattern ...\&
+.Nm
 .Fl w
-.Op Fl dituvHLPX
+.Op Fl dituvzHLPX
 .Bk -words
 .Op Fl b Ar blocksize
 .Ek
 .Ek
 .Bk -words
 .Op Fl s Ar replstr
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl o Ar options
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl U Ar user
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl G Ar group
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl B Ar bytes
 .Op Ar ,to_date
 .Op Ar /[c][m]
 .Oc
-.Ar ...
+.Ar ...\&
 .Ek
-.Op Ar file ...
-.Nm pax
+.Op Ar
+.Nm
 .Fl r
 .Fl w
 .Op Fl diklntuvDHLPXYZ
 .Bk -words
 .Op Fl p Ar string
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl s Ar replstr
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl U Ar user
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Op Fl G Ar group
-.Ar ...
+.Ar ...\&
 .Ek
 .Bk -words
 .Oo
 .Op Ar ,to_date
 .Op Ar /[c][m]
 .Oc
-.Ar ...
+.Ar ...\&
 .Ek
-.Op Ar file ...
+.Op Ar
 .Ar directory
 .Sh DESCRIPTION
-.Nm Pax
-will read, write, and list the members of an archive file,
+The
+.Nm
+utility will read, write, and list the members of an archive file,
 and will copy directory hierarchies.
-.Nm Pax 
-operation is independent of the specific archive format,
-and supports a wide variety of different archive formats.
+These operations are independent of the specific archive format,
+and support a wide variety of different archive formats.
 A list of supported archive formats can be found under the description of the
 .Fl x
 option.
@@ -196,30 +190,28 @@ The presence of the
 and the
 .Fl w
 options specifies which of the following functional modes
-.Nm pax
+.Nm
 will operate under:
 .Em list , read , write ,
 and
-.Em copy.
+.Em copy .
 .Bl -tag -width 6n
 .It <none>
 .Em List .
-.Nm Pax
-will write to
+Write to
 .Dv standard output
 a table of contents of the members of the archive file read from
 .Dv standard input ,
 whose pathnames match the specified
-.Ar patterns.
+.Ar patterns .
 The table of contents contains one filename per line
 and is written using single line buffering.
 .It Fl r
 .Em Read .
-.Nm Pax
-extracts the members of the archive file read from the
+Extract the members of the archive file read from the
 .Dv standard input ,
-with pathnames matching the specified 
-.Ar patterns.
+with pathnames matching the specified
+.Ar patterns .
 The archive format and blocking is automatically determined on input.
 When an extracted file is a directory, the entire file hierarchy
 rooted at that directory is extracted.
@@ -230,28 +222,26 @@ the extracted files are discussed in more detail under the
 option.
 .It Fl w
 .Em Write .
-.Nm Pax
-writes an archive containing the 
+Write an archive containing the
 .Ar file
 operands to
 .Dv standard output
 using the specified archive format.
-When no 
+When no
 .Ar file
-operands are specified, a list of files to copy with one per line is read from 
+operands are specified, a list of files to copy with one per line is read from
 .Dv standard input .
-When a 
+When a
 .Ar file
 operand is also a directory, the entire file hierarchy rooted
 at that directory will be included.
 .It Fl r Fl w
 .Em Copy .
-.Nm Pax
-copies the
+Copy the
 .Ar file
 operands to the destination
 .Ar directory .
-When no 
+When no
 .Ar file
 operands are specified, a list of files to copy with one per line is read from
 the
@@ -286,13 +276,12 @@ While processing a damaged archive during a
 or
 .Em list
 operation,
-.Nm pax
+.Nm
 will attempt to recover from media defects and will search through the archive
 to locate and process the largest number of archive members possible (see the
 .Fl E
 option for more details on error handling).
 .Sh OPERANDS
-.Pp
 The
 .Ar directory
 operand specifies a destination directory pathname.
@@ -300,7 +289,7 @@ If the
 .Ar directory
 operand does not exist, or it is not writable by the user,
 or it is not of type directory,
-.Nm Pax
+.Nm
 will exit with a non-zero exit status.
 .Pp
 The
@@ -309,17 +298,17 @@ operand is used to select one or more pathnames of archive members.
 Archive members are selected using the pattern matching notation described
 by
 .Xr fnmatch 3 .
-When the 
+When the
 .Ar pattern
 operand is not supplied, all members of the archive will be selected.
-When a 
+When a
 .Ar pattern
 matches a directory, the entire file hierarchy rooted at that directory will
 be selected.
 When a
 .Ar pattern
 operand does not select at least one archive member,
-.Nm pax
+.Nm
 will write these
 .Ar pattern
 operands in a diagnostic message to
@@ -332,14 +321,13 @@ operand specifies the pathname of a file to be copied or archived.
 When a
 .Ar file
 operand does not select at least one archive member,
-.Nm pax
+.Nm
 will write these
 .Ar file
 operand pathnames in a diagnostic message to
 .Dv standard error
 and then exit with a non-zero exit status.
 .Sh OPTIONS
-.Pp
 The following options are supported:
 .Bl -tag -width 4n
 .It Fl r
@@ -351,15 +339,15 @@ If any intermediate directories are needed in order to extract an archive
 member, these directories will be created as if
 .Xr mkdir 2
 was called with the bitwise inclusive
-.Dv OR 
+.Dv OR
 of
 .Dv S_IRWXU , S_IRWXG ,
 and
-.Dv S_IRWXO 
+.Dv S_IRWXO
 as the mode argument.
 When the selected archive format supports the specification of linked
 files and these files cannot be linked while the archive is being extracted,
-.Nm pax
+.Nm
 will write a diagnostic message to
 .Dv standard error
 and exit with a non-zero exit status at the completion of operation.
@@ -382,8 +370,8 @@ If an archive format is not specified with a
 .Fl x
 option, the format currently being used in the archive will be selected.
 Any attempt to append to an archive in a format different from the
-format already used in the archive will cause 
-.Nm pax
+format already used in the archive will cause
+.Nm
 to exit immediately
 with a non-zero exit status.
 The blocking size used in the archive volume where writing starts
@@ -423,7 +411,7 @@ can be separated by
 to indicate a product.
 A specific archive device may impose additional restrictions on the size
 of blocking it will support.
-When blocking is not specified, the default 
+When blocking is not specified, the default
 .Ar blocksize
 is dependent on the specific archive format being used (see the
 .Fl x
@@ -455,7 +443,7 @@ or
 .Em write ) .
 A single archive may span multiple files and different archive devices.
 When required,
-.Nm pax
+.Nm
 will prompt for the pathname of the file or device of the next volume in the
 archive.
 .It Fl i
@@ -465,19 +453,21 @@ For each archive member matching a
 operand or each file matching a
 .Ar file
 operand,
-.Nm pax
+.Nm
 will prompt to
 .Pa /dev/tty
 giving the name of the file, its file mode and its modification time.
-.Nm Pax
-will then read a line from
+The
+.Nm
+utility will then read a line from
 .Pa /dev/tty .
 If this line is blank, the file or archive member is skipped.
 If this line consists of a single period, the
 file or archive member is processed with no modification to its name.
 Otherwise, its name is replaced with the contents of the line.
-.Nm Pax
-will immediately exit with a non-zero exit status if 
+The
+.Nm
+utility will immediately exit with a non-zero exit status if
 .Dv <EOF>
 is encountered when reading a response or if
 .Pa /dev/tty
@@ -485,12 +475,12 @@ cannot be opened for reading and writing.
 .It Fl k
 Do not overwrite existing files.
 .It Fl l
-Link files. (The letter ell).
+Link files.
+(The letter ell).
 In the
 .Em copy
-mode (
-.Fl r
-.Fl w ) ,
+mode
+.Pq Fl r w ,
 hard links are made between the source and destination file hierarchies
 whenever possible.
 .It Fl n
@@ -501,7 +491,7 @@ No more than one archive member is matched for each
 .Ar pattern .
 When members of type directory are matched, the file hierarchy rooted at that
 directory is also matched (unless
-.Fl d 
+.Fl d
 is also specified).
 .It Fl o Ar options
 Information to modify the algorithm for extracting or writing archive files
@@ -523,7 +513,7 @@ and
 .Cm p .
 Multiple characteristics can be concatenated within the same string
 and multiple
-.Fl p 
+.Fl p
 options can be specified.
 The meaning of the specification characters are as follows:
 .Bl -tag -width 2n
@@ -538,10 +528,10 @@ This is intended to be used by
 .Em root ,
 someone with all the appropriate privileges, in order to preserve all
 aspects of the files as they are recorded in the archive.
-The 
+The
 .Cm e
 flag is the sum of the
-.Cm o 
+.Cm o
 and
 .Cm p
 flags.
@@ -554,7 +544,7 @@ Preserve the user ID and group ID.
 .Sq Preserve
 the file mode bits.
 This intended to be used by a
-.Em user 
+.Em user
 with regular privileges who wants to preserve all aspects of the file other
 than the ownership.
 The file times are preserved by default, but two other flags are offered to
@@ -568,22 +558,22 @@ extracted file, subject to the permissions of the invoking
 process.
 Otherwise the attribute of the extracted file is determined as
 part of the normal file creation action.
-If neither the 
+If neither the
 .Cm e
 nor the
 .Cm o
 specification character is specified, or the user ID and group ID are not
 preserved for any reason,
-.Nm pax
+.Nm
 will not set the
-.Dv S_ISUID 
+.Dv S_ISUID
 .Em ( setuid )
 and
 .Dv S_ISGID
 .Em ( setgid )
 bits of the file mode.
 If the preservation of any of these items fails for any reason,
-.Nm pax
+.Nm
 will write a diagnostic message to
 .Dv standard error .
 Failure to preserve these items will affect the final exit status,
@@ -628,7 +618,8 @@ The optional trailing
 .Cm g
 continues to apply the substitution expression to the pathname substring
 which starts with the first character following the end of the last successful
-substitution. The first unsuccessful substitution stops the operation of the
+substitution.
+The first unsuccessful substitution stops the operation of the
 .Cm g
 option.
 The optional trailing
@@ -641,9 +632,9 @@ File or archive member names that substitute to the empty string
 are not selected and will be skipped.
 .It Fl t
 Reset the access times of any file or directory read or accessed by
-.Nm pax
+.Nm
 to be the same as they were before being read or accessed by
-.Nm pax .
+.Nm .
 .It Fl u
 Ignore files that are older (having a less recent file modification time)
 than a pre-existing file or archive member with the same name.
@@ -673,13 +664,13 @@ the output has the format:
 .Dl <ls -l listing> == <link name>
 For pathnames representing a symbolic link, the output has the format:
 .Dl <ls -l listing> => <link name>
-Where <ls -l listing> is the output format specified by the 
+Where <ls -l listing> is the output format specified by the
 .Xr ls 1
 utility when used with the
 .Fl l
 option.
-Otherwise for all the other operational modes (
-.Em read , write ,
+Otherwise for all the other operational modes
+.Em read , write ,
 and
 .Em copy ) ,
 pathnames are written and flushed to
@@ -694,8 +685,9 @@ is not buffered, and is written only after the file has been read or written.
 .It Fl x Ar format
 Specify the output archive format, with the default format being
 .Ar ustar .
-.Nm Pax
-currently supports the following formats:
+The
+.Nm
+utility currently supports the following formats:
 .Bl -tag -width "sv4cpio"
 .It Ar cpio
 The extended cpio interchange format specified in the
@@ -704,7 +696,7 @@ standard.
 The default blocksize for this format is 5120 bytes.
 Inode and device information about a file (used for detecting file hard links
 by this format) which may be truncated by this format is detected by
-.Nm pax
+.Nm
 and is repaired.
 .It Ar bcpio
 The old binary cpio format.
@@ -713,32 +705,35 @@ This format is not very portable and should not be used when other formats
 are available.
 Inode and device information about a file (used for detecting file hard links
 by this format) which may be truncated by this format is detected by
-.Nm pax
+.Nm
 and is repaired.
 .It Ar sv4cpio
 The System V release 4 cpio.
 The default blocksize for this format is 5120 bytes.
 Inode and device information about a file (used for detecting file hard links
 by this format) which may be truncated by this format is detected by
-.Nm pax
+.Nm
 and is repaired.
 .It Ar sv4crc
 The System V release 4 cpio with file crc checksums.
 The default blocksize for this format is 5120 bytes.
 Inode and device information about a file (used for detecting file hard links
 by this format) which may be truncated by this format is detected by
-.Nm pax
+.Nm
 and is repaired.
 .It Ar tar
-The old BSD tar format as found in BSD4.3.
+The old
+.Bx
+tar format as found in
+.Bx 4.3 .
 The default blocksize for this format is 10240 bytes.
 Pathnames stored by this format must be 100 characters or less in length.
 Only
 .Em regular
 files,
-.Em  hard links , soft links ,
+.Em hard links , soft links ,
 and
-.Em  directories
+.Em directories
 will be archived (other file system types are not supported).
 For backwards compatibility with even older tar formats, a
 .Fl o
@@ -753,12 +748,19 @@ The default blocksize for this format is 10240 bytes.
 Pathnames stored by this format must be 250 characters or less in length.
 .El
 .Pp
-.Nm Pax
-will detect and report any file that it is unable to store or extract
+The
+.Nm
+utility will detect and report any file that it is unable to store or extract
 as the result of any specific archive format restrictions.
 The individual archive formats may impose additional restrictions on use.
 Typical archive format restrictions include (but are not limited to):
 file pathname length, file size, link pathname length and the type of the file.
+.It Fl z
+Use
+.Xr gzip 1
+to compress (decompress) the archive while writing (reading).
+Incompatible with
+.Fl a .
 .It Fl B Ar bytes
 Limit the number of bytes written to a single archive volume to
 .Ar bytes .
@@ -779,7 +781,7 @@ to indicate a product.
 .Em Warning :
 Only use this option when writing an archive to a device which supports
 an end of file read condition based on last (or largest) write offset
-(such as a regular file or a tape drive). 
+(such as a regular file or a tape drive).
 The use of this option with a floppy or hard disk is not recommended.
 .It Fl D
 This option is the same as the
@@ -787,7 +789,7 @@ This option is the same as the
 option, except that the file inode change time is checked instead of the
 file modification time.
 The file inode change time can be used to select files whose inode information
-(e.g. uid, gid, etc.) is newer than a copy of the file in the destination
+(e.g.\& uid, gid, etc.) is newer than a copy of the file in the destination
 .Ar directory .
 .It Fl E Ar limit
 Limit the number of consecutive read faults while trying to read a flawed
@@ -795,30 +797,30 @@ archives to
 .Ar limit .
 With a positive
 .Ar limit ,
-.Nm pax
+.Nm
 will attempt to recover from an archive read error and will
 continue processing starting with the next file stored in the archive.
 A
 .Ar limit
 of 0 will cause
-.Nm pax
+.Nm
 to stop operation after the first read error is detected on an archive volume.
 A
 .Ar limit
 of
 .Li NONE
 will cause
-.Nm pax 
+.Nm
 to attempt to recover from read errors forever.
-The default 
+The default
 .Ar limit
 is a small positive number of retries.
 .Pp
-.Em Warning
+.Em Warning :
 Using this option with
 .Li NONE
 should be used with extreme caution as
-.Nm pax
+.Nm
 may get stuck in an infinite loop on a very badly flawed archive.
 .It Fl G Ar group
 Select a file based on its
@@ -828,7 +830,7 @@ name, or when starting with a
 a numeric gid.
 A '\\' can be used to escape the
 .Cm # .
-Multiple 
+Multiple
 .Fl G
 options may be supplied and checking stops with the first match.
 .It Fl H
@@ -841,12 +843,12 @@ Do not follow symbolic links, perform a physical file system traversal.
 This is the default mode.
 .It Fl T Ar [from_date][,to_date][/[c][m]]
 Allow files to be selected based on a file modification or inode change
-time falling within a specified time range of 
+time falling within a specified time range of
 .Ar from_date
 to
 .Ar to_date
 (the dates are inclusive).
-If only a 
+If only a
 .Ar from_date
 is supplied, all files with a modification or inode change time
 equal to or younger are selected.
@@ -854,7 +856,7 @@ If only a
 .Ar to_date
 is supplied, all files with a modification or inode change time
 equal to or older will be selected.
-When the 
+When the
 .Ar from_date
 is equal to the
 .Ar to_date ,
@@ -862,8 +864,8 @@ only files with a modification or inode change time of exactly that
 time will be selected.
 .Pp
 When
-.Nm pax
-is in the 
+.Nm
+is in the
 .Em write
 or
 .Em copy
@@ -879,8 +881,8 @@ the file was last written).
 The
 .Ar c
 specifies the comparison of inode change time (the time when the file
-inode was last changed; e.g. a change of owner, group, mode, etc).
-When 
+inode was last changed; e.g.\& a change of owner, group, mode, etc).
+When
 .Ar c
 and
 .Ar m
@@ -892,7 +894,7 @@ created and had their modification time reset to an older time (as what
 happens when a file is extracted from an archive and the modification time
 is preserved).
 Time comparisons using both file times is useful when
-.Nm pax
+.Nm
 is used to create a time based incremental archive (only files that were
 changed during a specified time range will be archived).
 .Pp
@@ -921,14 +923,14 @@ The minute field
 is required, while the other fields are optional and must be added in the
 following order:
 .Dl Cm hh , dd , mm , yy .
-The 
+The
 .Cm ss
 field may be added independently of the other fields.
 Time ranges are relative to the current time, so
 .Dl Fl T Ar 1234/cm
 would select all files with a modification or inode change time
 of 12:34 PM today or later.
-Multiple 
+Multiple
 .Fl T
 time range can be supplied and checking stops with the first match.
 .It Fl U Ar user
@@ -939,14 +941,14 @@ name, or when starting with a
 a numeric uid.
 A '\\' can be used to escape the
 .Cm # .
-Multiple 
+Multiple
 .Fl U
 options may be supplied and checking stops with the first match.
 .It Fl X
 When traversing the file hierarchy specified by a pathname,
 do not descend into directories that have a different device ID.
 See the
-.Li st_dev 
+.Li st_dev
 field as described in
 .Xr stat 2
 for more information about device ID's.
@@ -962,8 +964,8 @@ option, except that the modification time is checked using the
 pathname created after all the file name modifications have completed.
 .El
 .Pp
-The options that operate on the names of files or archive members (
-.Fl c ,
+The options that operate on the names of files or archive members
+.Fl c ,
 .Fl i ,
 .Fl n ,
 .Fl s ,
@@ -1001,7 +1003,7 @@ Then the
 and
 .Fl Z
 options will be applied based on the final pathname.
-Finally the 
+Finally the
 .Fl v
 option will write the names resulting from these modifications.
 .Pp
@@ -1017,13 +1019,13 @@ based only on the user specified pathnames as modified by the
 .Fl D ,
 .Fl G ,
 .Fl T ,
-and 
+and
 .Fl U
 options (the
 .Fl D
 option only applies during a copy operation).
 Then any
-.Fl s 
+.Fl s
 and
 .Fl i
 options will modify in that order, the names of these selected files.
@@ -1048,9 +1050,9 @@ option, a file is not considered selected unless it is newer
 than the file to which it is compared.
 .Sh EXAMPLES
 The command:
-.Dl pax -w -f /dev/rst0 .\ 
+.Dl "pax -w -f /dev/sa0 ."
 copies the contents of the current directory to the device
-.Pa /dev/rst0 .
+.Pa /dev/sa0 .
 .Pp
 The command:
 .Dl pax -v -f filename
@@ -1058,17 +1060,17 @@ gives the verbose table of contents for an archive stored in
 .Pa filename .
 .Pp
 The following commands:
-.Dl mkdir newdir
-.Dl cd olddir
-.Dl pax -rw .\ newdir
+.Dl mkdir /tmp/to
+.Dl cd /tmp/from
+.Dl pax -rw .\ /tmp/to
 will copy the entire
-.Pa olddir
+.Pa /tmp/from
 directory hierarchy to
-.Pa newdir .
+.Pa /tmp/to .
 .Pp
 The command:
 .Dl pax -r -s ',^//*usr//*,,' -f a.pax
-reads the archive 
+reads the archive
 .Pa a.pax ,
 with all files rooted in ``/usr'' into the archive extracted relative to the
 current directory.
@@ -1098,11 +1100,12 @@ files with the same name found in the source file tree
 .Pa home .
 .Sh STANDARDS
 The
-.Nm pax
+.Nm
 utility is a superset of the
 .St -p1003.2
 standard.
 The options
+.Fl z ,
 .Fl B ,
 .Fl D ,
 .Fl E ,
@@ -1127,22 +1130,29 @@ operations are extensions to the
 .Tn POSIX
 standard.
 .Sh SEE ALSO
-.Xr tar 1 ,
-.Xr cpio 1
-.Sh AUTHOR
-Keith Muller at the University of California, San Diego
-.Sh ERRORS
-.Nm pax
-will exit with one of the following values:
+.Xr cpio 1 ,
+.Xr tar 1
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.4 .
+.Sh AUTHORS
+.An Keith Muller
+at the University of California, San Diego
+.Sh DIAGNOSTICS
+The
+.Nm
+utility will exit with one of the following values:
 .Bl -tag -width 2n
 .It 0
 All files were processed successfully.
-.It 1 
+.It 1
 An error occurred.
 .El
 .Pp
 Whenever
-.Nm pax
+.Nm
 cannot create a file or a link when reading an archive or cannot
 find a file when writing an archive, or cannot preserve the user ID,
 group ID, or file mode when the
@@ -1151,28 +1161,32 @@ option is specified, a diagnostic message is written to
 .Dv standard error
 and a non-zero exit status will be returned, but processing will continue.
 In the case where pax cannot create a link to a file,
-.Nm pax
+.Nm
 will not create a second copy of the file.
 .Pp
 If the extraction of a file from an archive is prematurely terminated by
 a signal or error,
-.Nm pax
+.Nm
 may have only partially extracted a file the user wanted.
 Additionally, the file modes of extracted files and directories
 may have incorrect file bits, and the modification and access times may be
 wrong.
 .Pp
 If the creation of an archive is prematurely terminated by a signal or error,
-.Nm pax
+.Nm
 may have only partially created the archive which may violate the specific
 archive format specification.
 .Pp
 If while doing a
 .Em copy ,
-.Nm pax
+.Nm
 detects a file is about to overwrite itself, the file is not copied,
 a diagnostic message is written to
 .Dv standard error
 and when
-.Nm pax
+.Nm
 completes it will exit with a non-zero exit status.
+.Sh BUGS
+The
+.Nm
+utility does not recognize multibyte characters.
index 9044fe54c786b21b5da0017e67e639fe536ed4f9..11ec0978d0cc5a8227eed950fe6e27ca20adc7d8 100644 (file)
--- a/pax/tar.c
+++ b/pax/tar.c
@@ -1039,8 +1039,6 @@ ustar_wr(arcn)
        case PAX_DIR:
                hd->typeflag = DIRTYPE;
                memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               memset(hd->devminor, 0, sizeof(hd->devminor));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1051,18 +1049,13 @@ ustar_wr(arcn)
                else
                        hd->typeflag = BLKTYPE;
                memset(hd->linkname, 0, sizeof(hd->linkname));
-               if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
-                  sizeof(hd->devmajor), 3) ||
-                  ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
-                  sizeof(hd->devminor), 3) ||
-                  ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
+               if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
+
                break;
        case PAX_FIF:
                hd->typeflag = FIFOTYPE;
                memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               memset(hd->devminor, 0, sizeof(hd->devminor));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1075,8 +1068,6 @@ ustar_wr(arcn)
                        hd->typeflag = LNKTYPE;
                l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
                hd->linkname[sizeof(hd->linkname) - 1] = '\0';
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               memset(hd->devminor, 0, sizeof(hd->devminor));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1091,8 +1082,6 @@ ustar_wr(arcn)
                else
                        hd->typeflag = REGTYPE;
                memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               memset(hd->devminor, 0, sizeof(hd->devminor));
                arcn->pad = TAR_PAD(arcn->sb.st_size);
 #              ifdef NET2_STAT
                if (ul_oct((u_long)arcn->sb.st_size, hd->size,
@@ -1107,6 +1096,13 @@ ustar_wr(arcn)
                break;
        }
 
+               /* set devmajor and devminor for all types as per spec */
+       if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
+          sizeof(hd->devmajor), 3) ||
+          ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
+                 sizeof(hd->devminor), 3))
+               goto out;
+
        l_strncpy(hd->magic, TMAGIC, TMAGLEN);
        l_strncpy(hd->version, TVERSION, TVERSLEN);
 
diff --git a/rm/rm.c b/rm/rm.c
index 7997af50cd9d2ce7335c02f6f7d197319bb2dade..c5c4da57ba28eb3e2ec516867f195686c636ca76 100644 (file)
--- a/rm/rm.c
+++ b/rm/rm.c
@@ -60,10 +60,18 @@ static const char rcsid[] =
 #include <sysexits.h>
 #include <unistd.h>
 
+#ifdef __APPLE__
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
 int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
 uid_t uid;
 
 int    check __P((char *, char *, struct stat *));
+int checkdir __P((char *));
+int            yes_or_no __P((void));
 void   checkdot __P((char **));
 void   rm_file __P((char **));
 void   rm_overwrite __P((char *, struct stat *));
@@ -165,7 +173,7 @@ rm_tree(argv)
        int needstat;
        int flags;
        int rval;
-
+       int wantConformance = COMPAT_MODE("bin/rm", "unix2003");
        /*
         * Remove a file hierarchy.  If forcing removal (-f), or interactive
         * (-i) or can't ask anyway (stdin_ok), don't stat the file.
@@ -183,8 +191,11 @@ rm_tree(argv)
                flags |= FTS_NOSTAT;
        if (Wflag)
                flags |= FTS_WHITEOUT;
-       if (!(fts = fts_open(argv, flags, NULL)))
+       if (!(fts = fts_open(argv, flags, NULL))) {
+               if (fflag && errno == ENOENT)
+                       return;
                err(1, NULL);
+       }
        while ((p = fts_read(fts)) != NULL) {
                switch (p->fts_info) {
                case FTS_DNR:
@@ -211,8 +222,13 @@ rm_tree(argv)
                        continue;
                case FTS_D:
                        /* Pre-order: give user chance to skip. */
-                       if (!fflag && !check(p->fts_path, p->fts_accpath,
-                           p->fts_statp)) {
+                       /* In conformance mode the user is prompted to skip processing the contents.
+                        * Then the option to delete the dir is presented post-order */
+                       if (!fflag && 
+                                       ( (wantConformance && !checkdir(p->fts_path)) ||
+                                         (!wantConformance && !check(p->fts_path, p->fts_accpath, p->fts_statp))
+                                       )
+                          ){
                                (void)fts_set(fts, p, FTS_SKIP);
                                p->fts_number = SKIPPED;
                        }
@@ -225,8 +241,16 @@ rm_tree(argv)
                        continue;
                case FTS_DP:
                        /* Post-order: see if user skipped. */
-                       if (p->fts_number == SKIPPED)
+                       if(p->fts_number == SKIPPED)/*in legacy mode, the user was prompted pre-order */
                                continue;
+                       else if(wantConformance)
+                       {
+                               /* delete directory if force is on, or if user answers Y to prompt */
+                               if(fflag || check(p->fts_path, p->fts_accpath, p->fts_statp)) 
+                                       break;
+                               else
+                                       continue;
+                       }
                        break;
                default:
                        if (!fflag &&
@@ -414,13 +438,33 @@ err:      eval = 1;
        warn("%s", file);
 }
 
+int 
+yes_or_no()
+{
+       int ch, first;
+       (void)fflush(stderr);
+
+       first = ch = getchar();
+       while (ch != '\n' && ch != EOF)
+               ch = getchar();
+       return (first == 'y' || first == 'Y');
+}
+
+int
+checkdir(path)
+       char *path;
+{
+       if(!iflag)
+               return 1;       //if not interactive, process directory's contents
+       (void)fprintf(stderr, "examine files in directory %s? ", path);
+       return yes_or_no();
+}
 
 int
 check(path, name, sp)
        char *path, *name;
        struct stat *sp;
 {
-       int ch, first;
        char modep[15], *flagsp;
 
        /* Check -i first. */
@@ -449,14 +493,10 @@ check(path, name, sp)
                    path);
                free(flagsp);
        }
-       (void)fflush(stderr);
-
-       first = ch = getchar();
-       while (ch != '\n' && ch != EOF)
-               ch = getchar();
-       return (first == 'y' || first == 'Y');
+       return yes_or_no();
 }
 
+
 #define ISDOT(a)       ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
 void
 checkdot(argv)
diff --git a/stat/Makefile b/stat/Makefile
new file mode 100644 (file)
index 0000000..550d148
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = stat
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = stat.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble stat.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+NEXTSTEP_PB_CFLAGS =
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/stat/Makefile.postamble b/stat/Makefile.postamble
new file mode 100644 (file)
index 0000000..9d53ad2
--- /dev/null
@@ -0,0 +1,6 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
+
+after_install::
+       $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/readlink
+       mkdir -p "$(DSTROOT)/usr/share/man/man1"
+       ln -f "$(DSTROOT)/usr/share/man/man1/stat.1" "$(DSTROOT)/usr/share/man/man1/readlink.1"
diff --git a/stat/Makefile.preamble b/stat/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/stat/PB.project b/stat/PB.project
new file mode 100644 (file)
index 0000000..0d40a66
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (stat.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, stat.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = stat; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/stat/stat.1 b/stat/stat.1
new file mode 100644 (file)
index 0000000..4f4a784
--- /dev/null
@@ -0,0 +1,522 @@
+.\"    $NetBSD: stat.1,v 1.11 2003/05/08 13:07:10 wiz Exp $
+.\"
+.\" Copyright (c) 2002 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Andrew Brown and Jan Schaumann.
+.\"
+.\" 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 NetBSD
+.\"        Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.\" $FreeBSD: src/usr.bin/stat/stat.1,v 1.6 2003/06/02 11:19:23 ru Exp $
+.\"
+.Dd May 8, 2003
+.Dt STAT 1
+.Os
+.Sh NAME
+.Nm stat ,
+.Nm readlink
+.Nd display file status
+.Sh SYNOPSIS
+.Nm
+.Op Fl FLnq
+.Op Fl f Ar format | Fl l | r | s | x
+.Op Fl t Ar timefmt
+.Op Ar
+.Nm readlink
+.Op Fl n
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility displays information about the file pointed to by
+.Ar file .
+Read, write or execute permissions of the named file are not required, but
+all directories listed in the path name leading to the file must be
+searchable.
+If no argument is given,
+.Nm
+displays information about the file descriptor for standard input.
+.Pp
+When invoked as
+.Nm readlink ,
+only the target of the symbolic link is printed.
+If the given argument is not a symbolic link,
+.Nm readlink
+will print nothing and exit with an error.
+.Pp
+The information displayed is obtained by calling
+.Xr lstat 2
+with the given argument and evaluating the returned structure.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl F
+As in
+.Xr ls 1 ,
+display a slash
+.Pq Ql /
+immediately after each pathname that is a directory,
+an asterisk
+.Pq Ql *
+after each that is executable,
+an at sign
+.Pq Ql @
+after each symbolic link,
+a percent sign
+.Pq Ql %
+after each whiteout,
+an equal sign
+.Pq Ql =
+after each socket,
+and a vertical bar
+.Pq Ql |
+after each that is a FIFO.
+The use of
+.Fl F
+implies
+.Fl l .
+.It Fl L
+Use
+.Xr stat 2
+instead of
+.Xr lstat 2 .
+The information reported by
+.Nm
+will refer to the target of
+.Ar file ,
+if file is a symbolic link, and not to
+.Ar file
+itself.
+.It Fl n
+Do not force a newline to appear at the end of each piece of output.
+.It Fl q
+Suppress failure messages if calls to
+.Xr stat 2
+or
+.Xr lstat 2
+fail.
+When run as
+.Nm readlink ,
+error messages are automatically suppressed.
+.It Fl f Ar format
+Display information using the specified format.
+See the
+.Sx FORMATS
+section for a description of valid formats.
+.It Fl l
+Display output in
+.Nm ls Fl lT
+format.
+.It Fl r
+Display raw information.
+That is, for all the fields in the
+.Vt stat
+structure,
+display the raw, numerical value (for example, times in seconds since the
+epoch, etc.).
+.It Fl s
+Display information in
+.Dq "shell output" ,
+suitable for initializing variables.
+.It Fl x
+Display information in a more verbose way as known from some
+.Tn Linux
+distributions.
+.It Fl t Ar timefmt
+Display timestamps using the specified format.
+This format is
+passed directly to
+.Xr strftime 3 .
+.El
+.Ss Formats
+Format strings are similar to
+.Xr printf 3
+formats in that they start with
+.Cm % ,
+are then followed by a sequence of formatting characters, and end in
+a character that selects the field of the
+.Vt "struct stat"
+which is to be formatted.
+If the
+.Cm %
+is immediately followed by one of
+.Cm n , t , % ,
+or
+.Cm @ ,
+then a newline character, a tab character, a percent character,
+or the current file number is printed, otherwise the string is
+examined for the following:
+.Pp
+Any of the following optional flags:
+.Bl -tag -width indent
+.It Cm #
+Selects an alternate output form for octal and hexadecimal output.
+Non-zero octal output will have a leading zero, and non-zero
+hexadecimal output will have
+.Dq Li 0x
+prepended to it.
+.It Cm +
+Asserts that a sign indicating whether a number is positive or negative
+should always be printed.
+Non-negative numbers are not usually printed
+with a sign.
+.It Cm -
+Aligns string output to the left of the field, instead of to the right.
+.It Cm 0
+Sets the fill character for left padding to the
+.Ql 0
+character, instead of a space.
+.It space
+Reserves a space at the front of non-negative signed output fields.
+A
+.Sq Cm +
+overrides a space if both are used.
+.El
+.Pp
+Then the following fields:
+.Bl -tag -width indent
+.It Ar size
+An optional decimal digit string specifying the minimum field width.
+.It Ar prec
+An optional precision composed of a decimal point
+.Sq Cm \&.
+and a decimal digit string that indicates the maximum string length,
+the number of digits to appear after the decimal point in floating point
+output, or the minimum number of digits to appear in numeric output.
+.It Ar fmt
+An optional output format specifier which is one of
+.Cm D , O , U , X , F ,
+or
+.Cm S .
+These represent signed decimal output, octal output, unsigned decimal
+output, hexadecimal output, floating point output, and string output,
+respectively.
+Some output formats do not apply to all fields.
+Floating point output only applies to
+.Vt timespec
+fields (the
+.Cm a , m ,
+and
+.Cm c
+fields).
+.Pp
+The special output specifier
+.Cm S
+may be used to indicate that the output, if
+applicable, should be in string format.
+May be used in combination with:
+.Bl -tag -width indent
+.It Cm amc
+Display date in
+.Xr strftime 3
+format.
+.It Cm dr
+Display actual device name.
+.It Cm gu
+Display group or user name.
+.It Cm p
+Display the mode of
+.Ar file
+as in
+.Nm ls Fl lTd .
+.It Cm N
+Displays the name of
+.Ar file .
+.It Cm T
+Displays the type of
+.Ar file .
+.It Cm Y
+Insert a
+.Dq Li " -\*[Gt] "
+into the output.
+Note that the default output format
+for
+.Cm Y
+is a string, but if specified explicitly, these four characters are
+prepended.
+.El
+.It Ar sub
+An optional sub field specifier (high, middle, low).
+Only applies to
+the
+.Cm p , d , r ,
+and
+.Cm T
+output formats.
+It can be one of the following:
+.Bl -tag -width indent
+.It Cm H
+.Dq High
+\[em]
+specifies the major number for devices from
+.Cm r
+or
+.Cm d ,
+the
+.Dq user
+bits for permissions from the string form of
+.Cm p ,
+the file
+.Dq type
+bits from the numeric forms of
+.Cm p ,
+and the long output form of
+.Cm T .
+.It Cm L
+.Dq Low
+\[em]
+specifies the minor number for devices from
+.Cm r
+or
+.Cm d ,
+the
+.Dq other
+bits for permissions from the string form of
+.Cm p ,
+the
+.Dq user ,
+.Dq group ,
+and
+.Dq other
+bits from the numeric forms of
+.Cm p ,
+and the
+.Nm ls Fl F
+style output character for file type when used with
+.Cm T
+(the use of
+.Cm L
+for this is optional).
+.It Cm M
+.Dq Middle
+\[em]
+specifies the
+.Dq group
+bits for permissions from the
+string output form of
+.Cm p ,
+or the
+.Dq suid ,
+.Dq sgid ,
+and
+.Dq sticky
+bits for the numeric forms of
+.Cm p .
+.El
+.It Ar datum
+A required field specifier, being one of the following:
+.Bl -tag -width indent
+.It Cm d
+Device upon which
+.Ar file
+resides.
+.It Cm i
+.Ar file Ns 's
+inode number.
+.It Cm p
+File type and permissions.
+.It Cm l
+Number of hard links to
+.Ar file .
+.It Cm u , g
+User ID and group ID of
+.Ar file Ns 's
+owner.
+.It Cm r
+Device number for character and block device special files.
+.It Cm a , m , c , B
+The time
+.Ar file
+was last accessed or modified, of when the inode was last changed, or
+the birth time of the inode.
+.It Cm z
+The size of
+.Ar file
+in bytes.
+.It Cm b
+Number of blocks allocated for
+.Ar file .
+.It Cm k
+Optimal file system I/O operation block size.
+.It Cm f
+User defined flags for
+.Ar file .
+.It Cm v
+Inode generation number.
+.El
+.Pp
+The following four field specifiers are not drawn directly from the
+data in
+.Vt "struct stat" ,
+but are:
+.Bl -tag -width indent
+.It Cm N
+The name of the file.
+.It Cm T
+The file type, either as in
+.Nm ls Fl F
+or in a more descriptive form if the
+.Ar sub
+field specifier
+.Cm H
+is given.
+.It Cm Y
+The target of a symbolic link.
+.It Cm Z
+Expands to
+.Dq major,minor
+from the
+.Va rdev
+field for character or block
+special devices and gives size output for all others.
+.El
+.El
+.Pp
+Only the
+.Cm %
+and the field specifier are required.
+Most field specifiers default to
+.Cm U
+as an output form, with the
+exception of
+.Cm p
+which defaults to
+.Cm O ,
+.Cm a , m ,
+and
+.Cm c
+which default to
+.Cm D ,
+and
+.Cm Y , T ,
+and
+.Cm N
+which default to
+.Cm S .
+.Sh EXIT STATUS
+.Ex -std stat readlink
+.Sh EXAMPLES
+Given a symbolic link
+.Pa foo
+that points from
+.Pa /tmp/foo
+to
+.Pa / ,
+you would use
+.Nm
+as follows:
+.Bd -literal -offset indent
+\*[Gt] stat -F /tmp/foo
+lrwxrwxrwx 1 jschauma cs 1 Apr 24 16:37:28 2002 /tmp/foo@ -\*[Gt] /
+
+\*[Gt] stat -LF /tmp/foo
+drwxr-xr-x 16 root wheel 512 Apr 19 10:57:54 2002 /tmp/foo/
+.Ed
+.Pp
+To initialize some shell variables, you could use the
+.Fl s
+flag as follows:
+.Bd -literal -offset indent
+\*[Gt] csh
+% eval set `stat -s .cshrc`
+% echo $st_size $st_mtimespec
+1148 1015432481
+
+\*[Gt] sh
+$ eval $(stat -s .profile)
+$ echo $st_size $st_mtimespec
+1148 1015432481
+.Ed
+.Pp
+In order to get a list of the kind of files including files pointed to if the
+file is a symbolic link, you could use the following format:
+.Bd -literal -offset indent
+$ stat -f "%N: %HT%SY" /tmp/*
+/tmp/bar: Symbolic Link -\*[Gt] /tmp/foo
+/tmp/output25568: Regular File
+/tmp/blah: Directory
+/tmp/foo: Symbolic Link -\*[Gt] /
+.Ed
+.Pp
+In order to get a list of the devices, their types and the major and minor
+device numbers, formatted with tabs and linebreaks, you could use the
+following format:
+.Bd -literal -offset indent
+stat -f "Name: %N%n%tType: %HT%n%tMajor: %Hr%n%tMinor: %Lr%n%n" /dev/*
+[...]
+Name: /dev/wt8
+        Type: Block Device
+        Major: 3
+        Minor: 8
+
+Name: /dev/zero
+        Type: Character Device
+        Major: 2
+        Minor: 12
+.Ed
+.Pp
+In order to determine the permissions set on a file separately, you could use
+the following format:
+.Bd -literal -offset indent
+\*[Gt] stat -f "%Sp -\*[Gt] owner=%SHp group=%SMp other=%SLp" .
+drwxr-xr-x -\*[Gt] owner=rwx group=r-x other=r-x
+.Ed
+.Pp
+In order to determine the three files that have been modified most recently,
+you could use the following format:
+.Bd -literal -offset indent
+\*[Gt] stat -f "%m%t%Sm %N" /tmp/* | sort -rn | head -3 | cut -f2-
+Apr 25 11:47:00 2002 /tmp/blah
+Apr 25 10:36:34 2002 /tmp/bar
+Apr 24 16:47:35 2002 /tmp/foo
+.Ed
+.Sh SEE ALSO
+.Xr file 1 ,
+.Xr ls 1 ,
+.Xr lstat 2 ,
+.Xr readlink 2 ,
+.Xr stat 2 ,
+.Xr printf 3 ,
+.Xr strftime 3
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Andrew Brown
+.Aq atatat@NetBSD.org .
+This man page was written by
+.An Jan Schaumann
+.Aq jschauma@NetBSD.org .
diff --git a/stat/stat.c b/stat/stat.c
new file mode 100644 (file)
index 0000000..78f8107
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Brown.
+ *
+ * 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 NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#if 0
+#ifndef lint
+__RCSID("$NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $");
+#endif
+#endif
+
+/* Commenting FBSDID, as it is not needed in OSX.
+__FBSDID("$FreeBSD: src/usr.bin/stat/stat.c,v 1.6 2003/10/06 01:55:17 dougb Exp $");
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h" 
+#else  /* HAVE_CONFIG_H */
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_GEN 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 0 /* was 1; not needed if ! __BSD_VISIBLE ? */
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
+#define HAVE_DEVNAME 1
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#if HAVE_STRUCT_STAT_ST_FLAGS
+#define DEF_F "%#Xf "
+#define RAW_F "%f "
+#define SHELL_F " st_flags=%f"
+#else /* HAVE_STRUCT_STAT_ST_FLAGS */
+#define DEF_F
+#define RAW_F
+#define SHELL_F
+#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
+
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+#define DEF_B "\"%SB\" "
+#define RAW_B "%B "
+#define SHELL_B "st_birthtime=%B "
+#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
+#define DEF_B
+#define RAW_B
+#define SHELL_B
+#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
+
+#if HAVE_STRUCT_STAT_ST_ATIM
+#define st_atimespec st_atim
+#define st_ctimespec st_ctim
+#define st_mtimespec st_mtim
+#endif /* HAVE_STRUCT_STAT_ST_ATIM */
+
+#define DEF_FORMAT \
+       "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \
+       "%k %b " DEF_F "%N"
+#define RAW_FORMAT     "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \
+       "%k %b " RAW_F "%N"
+#define LS_FORMAT      "%Sp %l %Su %Sg %Z %Sm %N%SY"
+#define LSF_FORMAT     "%Sp %l %Su %Sg %Z %Sm %N%T%SY"
+#define SHELL_FORMAT \
+       "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
+       "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
+       "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \
+       "st_blksize=%k st_blocks=%b" SHELL_F
+#define LINUX_FORMAT \
+       "  File: \"%N\"%n" \
+       "  Size: %-11z  FileType: %HT%n" \
+       "  Mode: (%04OLp/%.10Sp)         Uid: (%5u/%8Su)  Gid: (%5g/%8Sg)%n" \
+       "Device: %Hd,%Ld   Inode: %i    Links: %l%n" \
+       "Access: %Sa%n" \
+       "Modify: %Sm%n" \
+       "Change: %Sc"
+
+#define TIME_FORMAT    "%b %e %T %Y"
+
+#define FLAG_POUND     0x01
+#define FLAG_SPACE     0x02
+#define FLAG_PLUS      0x04
+#define FLAG_ZERO      0x08
+#define FLAG_MINUS     0x10
+
+/*
+ * These format characters must all be unique, except the magic one.
+ */
+#define FMT_MAGIC      '%'
+#define FMT_DOT                '.'
+
+#define SIMPLE_NEWLINE 'n'
+#define SIMPLE_TAB     't'
+#define SIMPLE_PERCENT '%'
+#define SIMPLE_NUMBER  '@'
+
+#define FMT_POUND      '#'
+#define FMT_SPACE      ' '
+#define FMT_PLUS       '+'
+#define FMT_ZERO       '0'
+#define FMT_MINUS      '-'
+
+#define FMT_DECIMAL    'D'
+#define FMT_OCTAL      'O'
+#define FMT_UNSIGNED   'U'
+#define FMT_HEX        'X'
+#define FMT_FLOAT      'F'
+#define FMT_STRING     'S'
+
+#define FMTF_DECIMAL   0x01
+#define FMTF_OCTAL     0x02
+#define FMTF_UNSIGNED  0x04
+#define FMTF_HEX       0x08
+#define FMTF_FLOAT     0x10
+#define FMTF_STRING    0x20
+
+#define HIGH_PIECE     'H'
+#define MIDDLE_PIECE   'M'
+#define LOW_PIECE      'L'
+
+#define SHOW_st_dev    'd'
+#define SHOW_st_ino    'i'
+#define SHOW_st_mode   'p'
+#define SHOW_st_nlink  'l'
+#define SHOW_st_uid    'u'
+#define SHOW_st_gid    'g'
+#define SHOW_st_rdev   'r'
+#define SHOW_st_atime  'a'
+#define SHOW_st_mtime  'm'
+#define SHOW_st_ctime  'c'
+#define SHOW_st_btime  'B'
+#define SHOW_st_size   'z'
+#define SHOW_st_blocks 'b'
+#define SHOW_st_blksize        'k'
+#define SHOW_st_flags  'f'
+#define SHOW_st_gen    'v'
+#define SHOW_symlink   'Y'
+#define SHOW_filetype  'T'
+#define SHOW_filename  'N'
+#define SHOW_sizerdev  'Z'
+
+void   usage(const char *);
+void   output(const struct stat *, const char *,
+           const char *, int, int, int);
+int    format1(const struct stat *,    /* stat info */
+           const char *,               /* the file name */
+           const char *, int,          /* the format string itself */
+           char *, size_t,             /* a place to put the output */
+           int, int, int, int,         /* the parsed format */
+           int, int);
+
+char *timefmt;
+int linkfail;
+
+#define addchar(s, c, nl) \
+       do { \
+               (void)fputc((c), (s)); \
+               (*nl) = ((c) == '\n'); \
+       } while (0/*CONSTCOND*/)
+
+int
+main(int argc, char *argv[])
+{
+       struct stat st;
+       int ch, rc, errs, am_readlink;
+       int lsF, fmtchar, usestat, fn, nonl, quiet;
+       char *statfmt, *options, *synopsis;
+
+       am_readlink = 0;
+       lsF = 0;
+       fmtchar = '\0';
+       usestat = 0;
+       nonl = 0;
+       quiet = 0;
+       linkfail = 0;
+       statfmt = NULL;
+       timefmt = NULL;
+
+       if (strcmp(getprogname(), "readlink") == 0) {
+               am_readlink = 1;
+               options = "n";
+               synopsis = "[-n] [file ...]";
+               statfmt = "%Y";
+               fmtchar = 'f';
+               quiet = 1;
+       } else {
+               options = "f:FlLnqrst:x";
+               synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]";
+       }
+
+       while ((ch = getopt(argc, argv, options)) != -1)
+               switch (ch) {
+               case 'F':
+                       lsF = 1;
+                       break;
+               case 'L':
+                       usestat = 1;
+                       break;
+               case 'n':
+                       nonl = 1;
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'f':
+                       statfmt = optarg;
+                       /* FALLTHROUGH */
+               case 'l':
+               case 'r':
+               case 's':
+               case 'x':
+                       if (fmtchar != 0)
+                               errx(1, "can't use format '%c' with '%c'",
+                                   fmtchar, ch);
+                       fmtchar = ch;
+                       break;
+               case 't':
+                       timefmt = optarg;
+                       break;
+               default:
+                       usage(synopsis);
+               }
+
+       argc -= optind;
+       argv += optind;
+       fn = 1;
+
+       if (fmtchar == '\0') {
+               if (lsF)
+                       fmtchar = 'l';
+               else {
+                       fmtchar = 'f';
+                       statfmt = DEF_FORMAT;
+               }
+       }
+
+       if (lsF && fmtchar != 'l')
+               errx(1, "can't use format '%c' with -F", fmtchar);
+
+       switch (fmtchar) {
+       case 'f':
+               /* statfmt already set */
+               break;
+       case 'l':
+               statfmt = lsF ? LSF_FORMAT : LS_FORMAT;
+               break;
+       case 'r':
+               statfmt = RAW_FORMAT;
+               break;
+       case 's':
+               statfmt = SHELL_FORMAT;
+               break;
+       case 'x':
+               statfmt = LINUX_FORMAT;
+               if (timefmt == NULL)
+                       timefmt = "%c";
+               break;
+       default:
+               usage(synopsis);
+               /*NOTREACHED*/
+       }
+
+       if (timefmt == NULL)
+               timefmt = TIME_FORMAT;
+
+       errs = 0;
+       do {
+               if (argc == 0)
+                       rc = fstat(STDIN_FILENO, &st);
+               else if (usestat)
+                       rc = stat(argv[0], &st);
+               else
+                       rc = lstat(argv[0], &st);
+
+               if (rc == -1) {
+                       errs = 1;
+                       linkfail = 1;
+                       if (!quiet)
+                               warn("%s: stat",
+                                   argc == 0 ? "(stdin)" : argv[0]);
+               }
+               else
+                       output(&st, argv[0], statfmt, fn, nonl, quiet);
+
+               argv++;
+               argc--;
+               fn++;
+       } while (argc > 0);
+
+       return (am_readlink ? linkfail : errs);
+}
+
+void
+usage(const char *synopsis)
+{
+
+       (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
+       exit(1);
+}
+
+/* 
+ * Parses a format string.
+ */
+void
+output(const struct stat *st, const char *file,
+    const char *statfmt, int fn, int nonl, int quiet)
+{
+       int flags, size, prec, ofmt, hilo, what;
+       char buf[PATH_MAX];
+       const char *subfmt;
+       int nl, t, i;
+
+       nl = 1;
+       while (*statfmt != '\0') {
+
+               /*
+                * Non-format characters go straight out.
+                */
+               if (*statfmt != FMT_MAGIC) {
+                       addchar(stdout, *statfmt, &nl);
+                       statfmt++;
+                       continue;
+               }
+
+               /*
+                * The current format "substring" starts here,
+                * and then we skip the magic.
+                */
+               subfmt = statfmt;
+               statfmt++;
+
+               /*
+                * Some simple one-character "formats".
+                */
+               switch (*statfmt) {
+               case SIMPLE_NEWLINE:
+                       addchar(stdout, '\n', &nl);
+                       statfmt++;
+                       continue;
+               case SIMPLE_TAB:
+                       addchar(stdout, '\t', &nl);
+                       statfmt++;
+                       continue;
+               case SIMPLE_PERCENT:
+                       addchar(stdout, '%', &nl);
+                       statfmt++;
+                       continue;
+               case SIMPLE_NUMBER: {
+                       char num[12], *p;
+
+                       snprintf(num, sizeof(num), "%d", fn);
+                       for (p = &num[0]; *p; p++)
+                               addchar(stdout, *p, &nl);
+                       statfmt++;
+                       continue;
+               }
+               }
+
+               /*
+                * This must be an actual format string.  Format strings are
+                * similar to printf(3) formats up to a point, and are of
+                * the form:
+                *
+                *      %       required start of format
+                *      [-# +0] opt. format characters
+                *      size    opt. field width
+                *      .       opt. decimal separator, followed by
+                *      prec    opt. precision
+                *      fmt     opt. output specifier (string, numeric, etc.)
+                *      sub     opt. sub field specifier (high, middle, low)
+                *      datum   required field specifier (size, mode, etc)
+                *
+                * Only the % and the datum selector are required.  All data
+                * have reasonable default output forms.  The "sub" specifier
+                * only applies to certain data (mode, dev, rdev, filetype).
+                * The symlink output defaults to STRING, yet will only emit
+                * the leading " -> " if STRING is explicitly specified.  The
+                * sizerdev datum will generate rdev output for character or
+                * block devices, and size output for all others.
+                */
+               flags = 0;
+               do {
+                       if      (*statfmt == FMT_POUND)
+                               flags |= FLAG_POUND;
+                       else if (*statfmt == FMT_SPACE)
+                               flags |= FLAG_SPACE;
+                       else if (*statfmt == FMT_PLUS)
+                               flags |= FLAG_PLUS;
+                       else if (*statfmt == FMT_ZERO)
+                               flags |= FLAG_ZERO;
+                       else if (*statfmt == FMT_MINUS)
+                               flags |= FLAG_MINUS;
+                       else
+                               break;
+                       statfmt++;
+               } while (1/*CONSTCOND*/);
+
+               size = -1;
+               if (isdigit((unsigned)*statfmt)) {
+                       size = 0;
+                       while (isdigit((unsigned)*statfmt)) {
+                               size = (size * 10) + (*statfmt - '0');
+                               statfmt++;
+                               if (size < 0)
+                                       goto badfmt;
+                       }
+               }
+
+               prec = -1;
+               if (*statfmt == FMT_DOT) {
+                       statfmt++;
+
+                       prec = 0;
+                       while (isdigit((unsigned)*statfmt)) {
+                               prec = (prec * 10) + (*statfmt - '0');
+                               statfmt++;
+                               if (prec < 0)
+                                       goto badfmt;
+                       }
+               }
+
+#define fmtcase(x, y)          case (y): (x) = (y); statfmt++; break
+#define fmtcasef(x, y, z)      case (y): (x) = (z); statfmt++; break
+               switch (*statfmt) {
+                       fmtcasef(ofmt, FMT_DECIMAL,     FMTF_DECIMAL);
+                       fmtcasef(ofmt, FMT_OCTAL,       FMTF_OCTAL);
+                       fmtcasef(ofmt, FMT_UNSIGNED,    FMTF_UNSIGNED);
+                       fmtcasef(ofmt, FMT_HEX,         FMTF_HEX);
+                       fmtcasef(ofmt, FMT_FLOAT,       FMTF_FLOAT);
+                       fmtcasef(ofmt, FMT_STRING,      FMTF_STRING);
+               default:
+                       ofmt = 0;
+                       break;
+               }
+
+               switch (*statfmt) {
+                       fmtcase(hilo, HIGH_PIECE);
+                       fmtcase(hilo, MIDDLE_PIECE);
+                       fmtcase(hilo, LOW_PIECE);
+               default:
+                       hilo = 0;
+                       break;
+               }
+
+               switch (*statfmt) {
+                       fmtcase(what, SHOW_st_dev);
+                       fmtcase(what, SHOW_st_ino);
+                       fmtcase(what, SHOW_st_mode);
+                       fmtcase(what, SHOW_st_nlink);
+                       fmtcase(what, SHOW_st_uid);
+                       fmtcase(what, SHOW_st_gid);
+                       fmtcase(what, SHOW_st_rdev);
+                       fmtcase(what, SHOW_st_atime);
+                       fmtcase(what, SHOW_st_mtime);
+                       fmtcase(what, SHOW_st_ctime);
+                       fmtcase(what, SHOW_st_btime);
+                       fmtcase(what, SHOW_st_size);
+                       fmtcase(what, SHOW_st_blocks);
+                       fmtcase(what, SHOW_st_blksize);
+                       fmtcase(what, SHOW_st_flags);
+                       fmtcase(what, SHOW_st_gen);
+                       fmtcase(what, SHOW_symlink);
+                       fmtcase(what, SHOW_filetype);
+                       fmtcase(what, SHOW_filename);
+                       fmtcase(what, SHOW_sizerdev);
+               default:
+                       goto badfmt;
+               }
+#undef fmtcasef
+#undef fmtcase
+
+               t = format1(st,
+                    file,
+                    subfmt, statfmt - subfmt,
+                    buf, sizeof(buf),
+                    flags, size, prec, ofmt, hilo, what);
+
+               for (i = 0; i < t && i < sizeof(buf); i++)
+                       addchar(stdout, buf[i], &nl);
+
+               continue;
+
+       badfmt:
+               errx(1, "%.*s: bad format",
+                   (int)(statfmt - subfmt + 1), subfmt);
+       }
+
+       if (!nl && !nonl)
+               (void)fputc('\n', stdout);
+       (void)fflush(stdout);
+}
+
+/*
+ * Arranges output according to a single parsed format substring.
+ */
+int
+format1(const struct stat *st,
+    const char *file,
+    const char *fmt, int flen,
+    char *buf, size_t blen,
+    int flags, int size, int prec, int ofmt,
+    int hilo, int what)
+{
+       u_int64_t data;
+       char *sdata, lfmt[24], tmp[20];
+       char smode[12], sid[12], path[PATH_MAX + 4];
+       struct passwd *pw;
+       struct group *gr;
+       const struct timespec *tsp;
+       struct timespec ts;
+       struct tm *tm;
+       int l, small, formats;
+
+       tsp = NULL;
+       formats = 0;
+       small = 0;
+
+       /*
+        * First, pick out the data and tweak it based on hilo or
+        * specified output format (symlink output only).
+        */
+       switch (what) {
+       case SHOW_st_dev:
+       case SHOW_st_rdev:
+               small = (sizeof(st->st_dev) == 4);
+               data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev;
+#if HAVE_DEVNAME
+               sdata = (what == SHOW_st_dev) ?
+                   devname(st->st_dev, S_IFBLK) :
+                   devname(st->st_rdev, 
+                   S_ISCHR(st->st_mode) ? S_IFCHR :
+                   S_ISBLK(st->st_mode) ? S_IFBLK :
+                   0U);
+               if (sdata == NULL)
+                       sdata = "???";
+#endif /* HAVE_DEVNAME */
+               if (hilo == HIGH_PIECE) {
+                       data = major(data);
+                       hilo = 0;
+               }
+               else if (hilo == LOW_PIECE) {
+                       data = minor((unsigned)data);
+                       hilo = 0;
+               }
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
+#if HAVE_DEVNAME
+                   FMTF_STRING;
+#else /* HAVE_DEVNAME */
+                   0;
+#endif /* HAVE_DEVNAME */
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_ino:
+               small = (sizeof(st->st_ino) == 4);
+               data = st->st_ino;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_mode:
+               small = (sizeof(st->st_mode) == 4);
+               data = st->st_mode;
+               strmode(st->st_mode, smode);
+               sdata = smode;
+               l = strlen(sdata);
+               if (sdata[l - 1] == ' ')
+                       sdata[--l] = '\0';
+               if (hilo == HIGH_PIECE) {
+                       data >>= 12;
+                       sdata += 1;
+                       sdata[3] = '\0';
+                       hilo = 0;
+               }
+               else if (hilo == MIDDLE_PIECE) {
+                       data = (data >> 9) & 07;
+                       sdata += 4;
+                       sdata[3] = '\0';
+                       hilo = 0;
+               }
+               else if (hilo == LOW_PIECE) {
+                       data &= 0777;
+                       sdata += 7;
+                       sdata[3] = '\0';
+                       hilo = 0;
+               }
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
+                   FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_OCTAL;
+               break;
+       case SHOW_st_nlink:
+               small = (sizeof(st->st_dev) == 4);
+               data = st->st_nlink;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_uid:
+               small = (sizeof(st->st_uid) == 4);
+               data = st->st_uid;
+               if ((pw = getpwuid(st->st_uid)) != NULL)
+                       sdata = pw->pw_name;
+               else {
+                       snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
+                       sdata = sid;
+               }
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
+                   FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_gid:
+               small = (sizeof(st->st_gid) == 4);
+               data = st->st_gid;
+               if ((gr = getgrgid(st->st_gid)) != NULL)
+                       sdata = gr->gr_name;
+               else {
+                       snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
+                       sdata = sid;
+               }
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
+                   FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_atime:
+               tsp = &st->st_atimespec;
+               /* FALLTHROUGH */
+       case SHOW_st_mtime:
+               if (tsp == NULL)
+                       tsp = &st->st_mtimespec;
+               /* FALLTHROUGH */
+       case SHOW_st_ctime:
+               if (tsp == NULL)
+                       tsp = &st->st_ctimespec;
+               /* FALLTHROUGH */
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+       case SHOW_st_btime:
+               if (tsp == NULL)
+                       tsp = &st->st_birthtimespec;
+#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
+               ts = *tsp;              /* copy so we can muck with it */
+               small = (sizeof(ts.tv_sec) == 4);
+               data = ts.tv_sec;
+               small = 1;
+               tm = localtime(&ts.tv_sec);
+               (void)strftime(path, sizeof(path), timefmt, tm);
+               sdata = path;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
+                   FMTF_FLOAT | FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_DECIMAL;
+               break;
+       case SHOW_st_size:
+               small = (sizeof(st->st_size) == 4);
+               data = st->st_size;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_blocks:
+               small = (sizeof(st->st_blocks) == 4);
+               data = st->st_blocks;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+       case SHOW_st_blksize:
+               small = (sizeof(st->st_blksize) == 4);
+               data = st->st_blksize;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+       case SHOW_st_flags:
+               small = (sizeof(st->st_flags) == 4);
+               data = st->st_flags;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
+#if HAVE_STRUCT_STAT_ST_GEN
+       case SHOW_st_gen:
+               small = (sizeof(st->st_gen) == 4);
+               data = st->st_gen;
+               sdata = NULL;
+               formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
+               if (ofmt == 0)
+                       ofmt = FMTF_UNSIGNED;
+               break;
+#endif /* HAVE_STRUCT_STAT_ST_GEN */
+       case SHOW_symlink:
+               small = 0;
+               data = 0;
+               if (S_ISLNK(st->st_mode)) {
+                       snprintf(path, sizeof(path), " -> ");
+                       l = readlink(file, path + 4, sizeof(path) - 4 - 1);
+                       if (l == -1) {
+                               linkfail = 1;
+                               l = 0;
+                               path[0] = '\0';
+                       }
+                       path[l + 4] = '\0';
+                       sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
+               }
+               else {
+                       linkfail = 1;
+                       sdata = "";
+               }
+               formats = FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_STRING;
+               break;
+       case SHOW_filetype:
+               small = 0;
+               data = 0;
+               sdata = smode;
+               sdata[0] = '\0';
+               if (hilo == 0 || hilo == LOW_PIECE) {
+                       switch (st->st_mode & S_IFMT) {
+                       case S_IFIFO:   (void)strcat(sdata, "|");       break;
+                       case S_IFDIR:   (void)strcat(sdata, "/");       break;
+                       case S_IFREG:
+                               if (st->st_mode &
+                                   (S_IXUSR | S_IXGRP | S_IXOTH))
+                                       (void)strcat(sdata, "*");
+                               break;
+                       case S_IFLNK:   (void)strcat(sdata, "@");       break;
+                       case S_IFSOCK:  (void)strcat(sdata, "=");       break;
+#ifdef S_IFWHT
+                       case S_IFWHT:   (void)strcat(sdata, "%");       break;
+#endif /* S_IFWHT */
+#ifdef S_IFDOOR
+                       case S_IFDOOR:  (void)strcat(sdata, ">");       break;
+#endif /* S_IFDOOR */
+                       }
+                       hilo = 0;
+               }
+               else if (hilo == HIGH_PIECE) {
+                       switch (st->st_mode & S_IFMT) {
+                       case S_IFIFO:   sdata = "Fifo File";            break;
+                       case S_IFCHR:   sdata = "Character Device";     break;
+                       case S_IFDIR:   sdata = "Directory";            break;
+                       case S_IFBLK:   sdata = "Block Device";         break;
+                       case S_IFREG:   sdata = "Regular File";         break;
+                       case S_IFLNK:   sdata = "Symbolic Link";        break;
+                       case S_IFSOCK:  sdata = "Socket";               break;
+#ifdef S_IFWHT
+                       case S_IFWHT:   sdata = "Whiteout File";        break;
+#endif /* S_IFWHT */
+#ifdef S_IFDOOR
+                       case S_IFDOOR:  sdata = "Door";                 break;
+#endif /* S_IFDOOR */
+                       default:        sdata = "???";                  break;
+                       }
+                       hilo = 0;
+               }
+               formats = FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_STRING;
+               break;
+       case SHOW_filename:
+               small = 0;
+               data = 0;
+               if (file == NULL)
+                       (void)strncpy(path, "(stdin)", sizeof(path));
+               else
+                       (void)strncpy(path, file, sizeof(path));
+               sdata = path;
+               formats = FMTF_STRING;
+               if (ofmt == 0)
+                       ofmt = FMTF_STRING;
+               break;
+       case SHOW_sizerdev:
+               if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+                       char majdev[20], mindev[20];
+                       int l1, l2;
+
+                       l1 = format1(st,
+                           file,
+                           fmt, flen,
+                           majdev, sizeof(majdev),
+                           flags, size, prec,
+                           ofmt, HIGH_PIECE, SHOW_st_rdev);
+                       l2 = format1(st,
+                           file,
+                           fmt, flen,
+                           mindev, sizeof(mindev),
+                           flags, size, prec,
+                           ofmt, LOW_PIECE, SHOW_st_rdev);
+                       return (snprintf(buf, blen, "%.*s,%.*s",
+                           l1, majdev, l2, mindev));
+               }
+               else {
+                       return (format1(st,
+                           file,
+                           fmt, flen,
+                           buf, blen,
+                           flags, size, prec,
+                           ofmt, 0, SHOW_st_size));
+               }
+               /*NOTREACHED*/
+       default:
+               errx(1, "%.*s: bad format", (int)flen, fmt);
+       }
+
+       /*
+        * If a subdatum was specified but not supported, or an output
+        * format was selected that is not supported, that's an error.
+        */
+       if (hilo != 0 || (ofmt & formats) == 0)
+               errx(1, "%.*s: bad format", (int)flen, fmt);
+
+       /*
+        * Assemble the format string for passing to printf(3).
+        */
+       lfmt[0] = '\0';
+       (void)strcat(lfmt, "%");
+       if (flags & FLAG_POUND)
+               (void)strcat(lfmt, "#");
+       if (flags & FLAG_SPACE)
+               (void)strcat(lfmt, " ");
+       if (flags & FLAG_PLUS)
+               (void)strcat(lfmt, "+");
+       if (flags & FLAG_MINUS)
+               (void)strcat(lfmt, "-");
+       if (flags & FLAG_ZERO)
+               (void)strcat(lfmt, "0");
+
+       /*
+        * Only the timespecs support the FLOAT output format, and that
+        * requires work that differs from the other formats.
+        */ 
+       if (ofmt == FMTF_FLOAT) {
+               /*
+                * Nothing after the decimal point, so just print seconds.
+                */
+               if (prec == 0) {
+                       if (size != -1) {
+                               (void)snprintf(tmp, sizeof(tmp), "%d", size);
+                               (void)strcat(lfmt, tmp);
+                       }
+                       (void)strcat(lfmt, "d");
+                       return (snprintf(buf, blen, lfmt, ts.tv_sec));
+               }
+
+               /*
+                * Unspecified precision gets all the precision we have:
+                * 9 digits.
+                */
+               if (prec == -1)
+                       prec = 9;
+
+               /*
+                * Adjust the size for the decimal point and the digits
+                * that will follow.
+                */
+               size -= prec + 1;
+
+               /*
+                * Any leftover size that's legitimate will be used.
+                */
+               if (size > 0) {
+                       (void)snprintf(tmp, sizeof(tmp), "%d", size);
+                       (void)strcat(lfmt, tmp);
+               }
+               (void)strcat(lfmt, "d");
+
+               /*
+                * The stuff after the decimal point always needs zero
+                * filling.
+                */
+               (void)strcat(lfmt, ".%0");
+
+               /*
+                * We can "print" at most nine digits of precision.  The
+                * rest we will pad on at the end.
+                */
+               (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec);
+               (void)strcat(lfmt, tmp);
+
+               /*
+                * For precision of less that nine digits, trim off the
+                * less significant figures.
+                */
+               for (; prec < 9; prec++)
+                       ts.tv_nsec /= 10;
+
+               /*
+                * Use the format, and then tack on any zeroes that
+                * might be required to make up the requested precision.
+                */
+               l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec);
+               for (; prec > 9 && l < blen; prec--, l++)
+                       (void)strcat(buf, "0");
+               return (l);
+       }
+
+       /*
+        * Add on size and precision, if specified, to the format.
+        */
+       if (size != -1) {
+               (void)snprintf(tmp, sizeof(tmp), "%d", size);
+               (void)strcat(lfmt, tmp);
+       }
+       if (prec != -1) {
+               (void)snprintf(tmp, sizeof(tmp), ".%d", prec);
+               (void)strcat(lfmt, tmp);
+       }
+
+       /*
+        * String output uses the temporary sdata.
+        */
+       if (ofmt == FMTF_STRING) {
+               if (sdata == NULL)
+                       errx(1, "%.*s: bad format", (int)flen, fmt);
+               (void)strcat(lfmt, "s");
+               return (snprintf(buf, blen, lfmt, sdata));
+       }
+
+       /*
+        * Ensure that sign extension does not cause bad looking output
+        * for some forms.
+        */
+       if (small && ofmt != FMTF_DECIMAL)
+               data = (u_int32_t)data;
+
+       /*
+        * The four "numeric" output forms.
+        */
+       (void)strcat(lfmt, "ll");
+       switch (ofmt) {
+       case FMTF_DECIMAL:      (void)strcat(lfmt, "d");        break;
+       case FMTF_OCTAL:                (void)strcat(lfmt, "o");        break;
+       case FMTF_UNSIGNED:     (void)strcat(lfmt, "u");        break;
+       case FMTF_HEX:          (void)strcat(lfmt, "x");        break;
+       }
+
+       return (snprintf(buf, blen, lfmt, data));
+}