aboutsummaryrefslogtreecommitdiffstats
path: root/file_cmds
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /file_cmds
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'file_cmds')
-rw-r--r--file_cmds/.upstream_base_commits2
-rw-r--r--file_cmds/chflags/chflags.1185
-rw-r--r--file_cmds/chflags/chflags.c204
-rw-r--r--file_cmds/chmod/chmod.1604
-rw-r--r--file_cmds/chmod/chmod.c458
-rw-r--r--file_cmds/chmod/chmod_acl.c859
-rw-r--r--file_cmds/chmod/chmod_acl.h90
-rw-r--r--file_cmds/chown/chgrp.1144
-rw-r--r--file_cmds/chown/chown.8177
-rw-r--r--file_cmds/chown/chown.c345
-rw-r--r--file_cmds/cksum/cksum.1182
-rw-r--r--file_cmds/cksum/cksum.c148
-rw-r--r--file_cmds/cksum/crc.c148
-rw-r--r--file_cmds/cksum/crc32.c122
-rw-r--r--file_cmds/cksum/extern.h52
-rw-r--r--file_cmds/cksum/print.c75
-rw-r--r--file_cmds/cksum/sum.11
-rw-r--r--file_cmds/cksum/sum1.c77
-rw-r--r--file_cmds/cksum/sum2.c79
-rw-r--r--file_cmds/compress/compress.1253
-rw-r--r--file_cmds/compress/compress.c467
-rw-r--r--file_cmds/compress/doc/NOTES142
-rw-r--r--file_cmds/compress/doc/README284
-rw-r--r--file_cmds/compress/doc/revision.log118
-rw-r--r--file_cmds/compress/uncompress.11
-rw-r--r--file_cmds/compress/zcat.sh38
-rw-r--r--file_cmds/compress/zopen.3137
-rw-r--r--file_cmds/compress/zopen.c738
-rw-r--r--file_cmds/compress/zopen.h34
-rw-r--r--file_cmds/cp/cp.1311
-rw-r--r--file_cmds/cp/cp.c571
-rw-r--r--file_cmds/cp/extern.h61
-rw-r--r--file_cmds/cp/utils.c541
-rw-r--r--file_cmds/csh/strpct.c99
-rw-r--r--file_cmds/dd/args.c421
-rw-r--r--file_cmds/dd/conv.c273
-rw-r--r--file_cmds/dd/conv_tab.c289
-rw-r--r--file_cmds/dd/dd.1382
-rw-r--r--file_cmds/dd/dd.c481
-rw-r--r--file_cmds/dd/dd.entitlements9
-rw-r--r--file_cmds/dd/dd.h102
-rw-r--r--file_cmds/dd/extern.h73
-rw-r--r--file_cmds/dd/install_symlink.sh16
-rw-r--r--file_cmds/dd/misc.c108
-rw-r--r--file_cmds/dd/position.c169
-rw-r--r--file_cmds/df/df.1217
-rw-r--r--file_cmds/df/df.c639
-rw-r--r--file_cmds/df/vfslist.c103
-rw-r--r--file_cmds/du/du.1172
-rw-r--r--file_cmds/du/du.c730
-rw-r--r--file_cmds/file_cmds.xcodeproj/project.pbxproj3984
-rw-r--r--file_cmds/gzip/futimens.c100
-rw-r--r--file_cmds/gzip/gzexe179
-rw-r--r--file_cmds/gzip/gzexe.173
-rw-r--r--file_cmds/gzip/gzip.1234
-rw-r--r--file_cmds/gzip/gzip.c2256
-rw-r--r--file_cmds/gzip/gzip.plist29
-rw-r--r--file_cmds/gzip/gzip.xcconfig49
-rw-r--r--file_cmds/gzip/install_scripts.sh22
-rw-r--r--file_cmds/gzip/unbzip2.c141
-rw-r--r--file_cmds/gzip/unpack.c329
-rw-r--r--file_cmds/gzip/unxz.c153
-rw-r--r--file_cmds/gzip/zdiff142
-rw-r--r--file_cmds/gzip/zdiff.1142
-rw-r--r--file_cmds/gzip/zforce55
-rw-r--r--file_cmds/gzip/zforce.153
-rw-r--r--file_cmds/gzip/zmore82
-rw-r--r--file_cmds/gzip/zmore.1110
-rw-r--r--file_cmds/gzip/znew137
-rw-r--r--file_cmds/gzip/znew.171
-rw-r--r--file_cmds/gzip/zuncompress.c396
-rw-r--r--file_cmds/install/install.1253
-rw-r--r--file_cmds/install/pathnames.h41
-rw-r--r--file_cmds/install/xinstall.c816
-rw-r--r--file_cmds/ipcrm/ipcrm.184
-rw-r--r--file_cmds/ipcrm/ipcrm.c179
-rw-r--r--file_cmds/ipcs/ipcs.1132
-rw-r--r--file_cmds/ipcs/ipcs.c553
-rw-r--r--file_cmds/ln/link.11
-rw-r--r--file_cmds/ln/ln.1233
-rw-r--r--file_cmds/ln/ln.c272
-rw-r--r--file_cmds/ln/symlink.7457
-rw-r--r--file_cmds/ls/cmp.c226
-rw-r--r--file_cmds/ls/extern.h73
-rw-r--r--file_cmds/ls/ls.1715
-rw-r--r--file_cmds/ls/ls.c977
-rw-r--r--file_cmds/ls/ls.h108
-rw-r--r--file_cmds/ls/print.c837
-rw-r--r--file_cmds/ls/util.c231
-rw-r--r--file_cmds/mkdir/mkdir.1108
-rw-r--r--file_cmds/mkdir/mkdir.c144
-rw-r--r--file_cmds/mkfifo/mkfifo.1103
-rw-r--r--file_cmds/mkfifo/mkfifo.c128
-rw-r--r--file_cmds/mknod/mknod.8137
-rw-r--r--file_cmds/mknod/mknod.c405
-rw-r--r--file_cmds/mtree/commoncrypto.c378
-rw-r--r--file_cmds/mtree/commoncrypto.h36
-rw-r--r--file_cmds/mtree/compare.c688
-rw-r--r--file_cmds/mtree/create.c611
-rw-r--r--file_cmds/mtree/excludes.c114
-rw-r--r--file_cmds/mtree/extern.h71
-rwxr-xr-xfile_cmds/mtree/fix_failure_locations.py77
-rw-r--r--file_cmds/mtree/metrics.c155
-rw-r--r--file_cmds/mtree/metrics.h48
-rw-r--r--file_cmds/mtree/misc.c189
-rw-r--r--file_cmds/mtree/mtree.8393
-rw-r--r--file_cmds/mtree/mtree.c358
-rw-r--r--file_cmds/mtree/mtree.h120
-rw-r--r--file_cmds/mtree/spec.c470
-rw-r--r--file_cmds/mtree/specspec.c327
-rw-r--r--file_cmds/mtree/test/test00.sh67
-rw-r--r--file_cmds/mtree/test/test01.sh40
-rw-r--r--file_cmds/mtree/test/test02.sh36
-rw-r--r--file_cmds/mtree/test/test03.sh60
-rw-r--r--file_cmds/mtree/test/test04.sh51
-rw-r--r--file_cmds/mtree/verify.c341
-rw-r--r--file_cmds/mv/mv.1184
-rw-r--r--file_cmds/mv/mv.c505
-rw-r--r--file_cmds/mv/pathnames.h45
-rw-r--r--file_cmds/pathchk/pathchk.1121
-rw-r--r--file_cmds/pathchk/pathchk.c196
-rw-r--r--file_cmds/pax/ar_io.c1318
-rw-r--r--file_cmds/pax/ar_subs.c1517
-rw-r--r--file_cmds/pax/buf_subs.c994
-rw-r--r--file_cmds/pax/cache.c426
-rw-r--r--file_cmds/pax/cache.h78
-rw-r--r--file_cmds/pax/cpio.1304
-rw-r--r--file_cmds/pax/cpio.c1149
-rw-r--r--file_cmds/pax/cpio.h155
-rw-r--r--file_cmds/pax/extern.h347
-rw-r--r--file_cmds/pax/file_subs.c1180
-rw-r--r--file_cmds/pax/ftree.c585
-rw-r--r--file_cmds/pax/ftree.h56
-rw-r--r--file_cmds/pax/gen_subs.c458
-rw-r--r--file_cmds/pax/getoldopt.c74
-rw-r--r--file_cmds/pax/options.c1747
-rw-r--r--file_cmds/pax/options.h118
-rw-r--r--file_cmds/pax/pat_rep.c1211
-rw-r--r--file_cmds/pax/pat_rep.h56
-rw-r--r--file_cmds/pax/pax.11185
-rw-r--r--file_cmds/pax/pax.c446
-rw-r--r--file_cmds/pax/pax.h255
-rw-r--r--file_cmds/pax/pax_format.c1624
-rw-r--r--file_cmds/pax/pax_format.h158
-rw-r--r--file_cmds/pax/sel_subs.c614
-rw-r--r--file_cmds/pax/sel_subs.h77
-rw-r--r--file_cmds/pax/tables.c1278
-rw-r--r--file_cmds/pax/tables.h175
-rw-r--r--file_cmds/pax/tar.c1206
-rw-r--r--file_cmds/pax/tar.h161
-rw-r--r--file_cmds/pax/tty_subs.c200
-rw-r--r--file_cmds/rm/rm.1216
-rw-r--r--file_cmds/rm/rm.c601
-rw-r--r--file_cmds/rm/unlink.11
-rw-r--r--file_cmds/rmdir/rmdir.1100
-rw-r--r--file_cmds/rmdir/rmdir.c122
-rw-r--r--file_cmds/rmt/rmt.8220
-rw-r--r--file_cmds/rmt/rmt.c259
-rw-r--r--file_cmds/shar/shar.1105
-rw-r--r--file_cmds/shar/shar.sh76
-rw-r--r--file_cmds/stat/readlink.11
-rw-r--r--file_cmds/stat/stat.1534
-rw-r--r--file_cmds/stat/stat.c1004
-rw-r--r--file_cmds/tests/chgrp.sh26
-rw-r--r--file_cmds/tests/file_cmds.plist33
-rw-r--r--file_cmds/touch/touch.1221
-rw-r--r--file_cmds/touch/touch.c430
-rw-r--r--file_cmds/xcodescripts/hardlink.sh16
168 files changed, 56679 insertions, 0 deletions
diff --git a/file_cmds/.upstream_base_commits b/file_cmds/.upstream_base_commits
new file mode 100644
index 0000000..9423598
--- /dev/null
+++ b/file_cmds/.upstream_base_commits
@@ -0,0 +1,2 @@
+#freebsd = https://github.com/freebsd/freebsd.git
+chmod/chmod.1 freebsd bin/chmod/chmod.1 14889ebf5bdeaead21c00f70044d9acbb923f6b0
diff --git a/file_cmds/chflags/chflags.1 b/file_cmds/chflags/chflags.1
new file mode 100644
index 0000000..cb0281f
--- /dev/null
+++ b/file_cmds/chflags/chflags.1
@@ -0,0 +1,185 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95
+.\" $FreeBSD: src/bin/chflags/chflags.1,v 1.30.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $
+.\"
+.Dd March 3, 2006
+.Dt CHFLAGS 1
+.Os
+.Sh NAME
+.Nm chflags
+.Nd change file flags
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar flags
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility modifies the file flags of the listed files
+as specified by the
+.Ar flags
+operand.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f
+Do not display a diagnostic message if
+.Nm
+could not modify the flags for
+.Va file ,
+nor modify the exit status to reflect such failures.
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl h
+If the
+.Ar file
+is a symbolic link,
+change the file flags of the link itself rather than the file to which it points.
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the file flags for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl v
+Cause
+.Nm
+to be verbose, showing filenames as the flags are modified.
+If the
+.Fl v
+option is specified more than once, the old and new flags of the file
+will also be printed, in octal notation.
+.El
+.Pp
+The flags are specified as an octal number or a comma separated list
+of keywords.
+The following keywords are currently defined:
+.Pp
+.Bl -tag -offset indent -width ".Cm opaque"
+.It Cm arch , archived
+set the archived flag (super-user only)
+.It Cm opaque
+set the opaque flag (owner or super-user only).
+[Directory is opaque when viewed through a union mount]
+.It Cm nodump
+set the nodump flag (owner or super-user only)
+.It Cm sappnd , sappend
+set the system append-only flag (super-user only)
+.It Cm schg , schange , simmutable
+set the system immutable flag (super-user only)
+.It Cm uappnd , uappend
+set the user append-only flag (owner or super-user only)
+.It Cm uchg , uchange , uimmutable
+set the user immutable flag (owner or super-user only)
+.It Cm hidden
+set the hidden flag
+[Hide item from GUI]
+.El
+.Pp
+Putting the letters
+.Dq Ar no
+before or removing the letters
+.Dq Ar no
+from a keyword causes the flag to be cleared.
+For example:
+.Pp
+.Bl -tag -offset indent -width "nouchg" -compact
+.It Ar nouchg
+clear the user immutable flag (owner or super-user only)
+.It Ar dump
+clear the nodump flag (owner or super-user only)
+.El
+.Pp
+Unless the
+.Fl H
+or
+.Fl L
+options are given,
+.Nm
+on a symbolic link always succeeds and has no effect.
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+You can use "ls -lO" to see the flags of existing files.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ls 1 ,
+.Xr chflags 2 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Bx 4.4 .
+.Sh BUGS
+Only a limited number of utilities are
+.Nm
+aware.
+Some of these tools include
+.Xr ls 1 ,
+.Xr cp 1 ,
+.Xr find 1 ,
+.Xr install 1 ,
+.Xr dump 8 ,
+and
+.Xr restore 8 .
+In particular a tool which is not currently
+.Nm
+aware is the
+.Xr pax 1
+utility.
diff --git a/file_cmds/chflags/chflags.c b/file_cmds/chflags/chflags.c
new file mode 100644
index 0000000..76c8027
--- /dev/null
+++ b/file_cmds/chflags/chflags.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 1992, 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/chflags/chflags.c,v 1.26.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *p;
+ u_long clear, newflags, set;
+ long val;
+ int Hflag, Lflag, Rflag, fflag, hflag, vflag;
+ int ch, fts_options, oct, rval;
+ char *flags, *ep;
+ int (*change_flags)(const char *, u_int);
+
+ Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
+ while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ if (Rflag) {
+ fts_options = FTS_PHYSICAL;
+ if (hflag)
+ errx(1, "the -R and -h options "
+ "may not be specified together");
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ if (hflag)
+ change_flags = lchflags;
+ else
+ change_flags = chflags;
+
+ flags = *argv;
+ if (*flags >= '0' && *flags <= '7') {
+ errno = 0;
+ val = strtol(flags, &ep, 8);
+ if (val < 0)
+ errno = ERANGE;
+ if (errno)
+ err(1, "invalid flags: %s", flags);
+ if (*ep)
+ errx(1, "invalid flags: %s", flags);
+ set = val;
+ oct = 1;
+ } else {
+ if (strtofflags(&flags, &set, &clear))
+ errx(1, "invalid flag: %s", flags);
+ clear = ~clear;
+ oct = 0;
+ }
+
+ if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D: /* Change it at FTS_DP if we're recursive. */
+ if (!Rflag)
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chflag, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL: /* Ignore. */
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (!hflag)
+ continue;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ if (oct)
+ newflags = set;
+ else
+ newflags = (p->fts_statp->st_flags | set) & clear;
+ if (newflags == p->fts_statp->st_flags)
+ continue;
+ if ((*change_flags)(p->fts_accpath, (u_int)newflags) && !fflag) {
+ warn("%s", p->fts_path);
+ rval = 1;
+ } else if (vflag) {
+ (void)printf("%s", p->fts_path);
+ if (vflag > 1)
+ (void)printf(": 0%lo -> 0%lo",
+ (u_long)p->fts_statp->st_flags,
+ newflags);
+ (void)printf("\n");
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: chflags [-fhv] [-R [-H | -L | -P]] flags file ...\n");
+ exit(1);
+}
diff --git a/file_cmds/chmod/chmod.1 b/file_cmds/chmod/chmod.1
new file mode 100644
index 0000000..0e89f26
--- /dev/null
+++ b/file_cmds/chmod/chmod.1
@@ -0,0 +1,604 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2017
+.Dt CHMOD 1
+.Os
+.Sh NAME
+.Nm chmod
+.Nd change file modes or Access Control Lists
+.Sh SYNOPSIS
+.Nm chmod
+.Op Fl fv
+.Op Fl R Op Fl H | L | P
+.Ar mode
+.Ar
+.Nm chmod
+.Op Fl fv
+.Op Fl R Op Fl H | L | P
+.Op -a | +a | =a
+.Ar ACE
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl E
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl C
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl N
+.Ar
+.Sh DESCRIPTION
+The
+.Nm chmod
+utility modifies the file mode bits of the listed files
+as specified by the
+.Ar mode
+operand. It may also be used to modify the Access Control
+Lists (ACLs) associated with the listed files.
+.Pp
+The generic options are as follows:
+.Bl -tag -width indent
+.It Fl f
+Do not display a diagnostic message if
+.Nm chmod
+could not modify the mode for
+.Va file ,
+nor modify the exit status to reflect such failures.
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed
+and hence unaffected by the command.
+(Symbolic links encountered during tree traversal are not followed.)
+.It Fl h
+If the file is a symbolic link, change the mode of the link itself
+rather than the file that the link points to.
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the modes of the file hierarchies rooted in the files,
+instead of just the files themselves.
+Beware of unintentionally matching the
+.Dq Pa ".."
+hard link to the parent directory when using wildcards like
+.Dq Li ".*" .
+.It Fl v
+Cause
+.Nm chmod
+to be verbose, showing filenames as the mode is modified.
+If the
+.Fl v
+flag is specified more than once, the old and new modes of the file
+will also be printed, in both octal and symbolic notation.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+If
+.Nm chmod
+receives a
+.Dv SIGINFO
+signal (see the
+.Cm status
+argument for
+.Xr stty 1 ) ,
+then the current filename as well as the old and new modes are displayed.
+.Pp
+Only the owner of a file or the super-user is permitted to change
+the mode of a file.
+.Sh EXIT STATUS
+.Ex -std
+.Sh MODES
+Modes may be absolute or symbolic.
+An absolute mode is an octal number constructed from the sum of
+one or more of the following values:
+.Pp
+.Bl -tag -width 6n -compact -offset indent
+.It Li 4000
+(the setuid bit).
+Executable files with this bit set
+will run with effective uid set to the uid of the file owner.
+Directories with this bit set will force all files and
+sub-directories created in them to be owned by the directory owner
+and not by the uid of the creating process, if the underlying file
+system supports this feature: see
+.Xr chmod 2
+and the
+.Cm suiddir
+option to
+.Xr mount 8 .
+.It Li 2000
+(the setgid bit).
+Executable files with this bit set
+will run with effective gid set to the gid of the file owner.
+.It Li 1000
+(the sticky bit).
+See
+.Xr chmod 2
+and
+.Xr sticky 7 .
+.It Li 0400
+Allow read by owner.
+.It Li 0200
+Allow write by owner.
+.It Li 0100
+For files, allow execution by owner.
+For directories, allow the owner to
+search in the directory.
+.It Li 0040
+Allow read by group members.
+.It Li 0020
+Allow write by group members.
+.It Li 0010
+For files, allow execution by group members.
+For directories, allow
+group members to search in the directory.
+.It Li 0004
+Allow read by others.
+.It Li 0002
+Allow write by others.
+.It Li 0001
+For files, allow execution by others.
+For directories allow others to
+search in the directory.
+.El
+.Pp
+For example, the absolute mode that permits read, write and execute by
+the owner, read and execute by group members, read and execute by
+others, and no set-uid or set-gid behaviour is 755
+(400+200+100+040+010+004+001).
+.Pp
+The symbolic mode is described by the following grammar:
+.Bd -literal -offset indent
+mode ::= clause [, clause ...]
+clause ::= [who ...] [action ...] action
+action ::= op [perm ...]
+who ::= a | u | g | o
+op ::= + | \- | =
+perm ::= r | s | t | w | x | X | u | g | o
+.Ed
+.Pp
+The
+.Ar who
+symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts
+of the mode bits, respectively.
+The
+.Ar who
+symbol ``a'' is equivalent to ``ugo''.
+.Pp
+The
+.Ar perm
+symbols represent the portions of the mode bits as follows:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It r
+The read bits.
+.It s
+The set-user-ID-on-execution and set-group-ID-on-execution bits.
+.It t
+The sticky bit.
+.It w
+The write bits.
+.It x
+The execute/search bits.
+.It X
+The execute/search bits if the file is a directory or any of the
+execute/search bits are set in the original (unmodified) mode.
+Operations with the
+.Ar perm
+symbol ``X'' are only meaningful in conjunction with the
+.Ar op
+symbol ``+'', and are ignored in all other cases.
+.It u
+The user permission bits in the original mode of the file.
+.It g
+The group permission bits in the original mode of the file.
+.It o
+The other permission bits in the original mode of the file.
+.El
+.Pp
+The
+.Ar op
+symbols represent the operation performed, as follows:
+.Bl -tag -width 4n
+.It +
+If no value is supplied for
+.Ar perm ,
+the ``+'' operation has no effect.
+If no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+(see
+.Xr umask 2 )
+is clear, is set.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are set.
+.It \&\-
+If no value is supplied for
+.Ar perm ,
+the ``\-'' operation has no effect.
+If no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+is set, is cleared.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are cleared.
+.It =
+The mode bits specified by the
+.Ar who
+value are cleared, or, if no
+.Ar who
+value is specified, the owner, group
+and other mode bits are cleared.
+Then, if no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+is clear, is set.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are set.
+.El
+.Pp
+Each
+.Ar clause
+specifies one or more operations to be performed on the mode
+bits, and each operation is applied to the mode bits in the
+order specified.
+.Pp
+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.
+.Pp
+The ``w'' permission on directories will permit file creation, relocation,
+and copy into that directory.
+Files created within the directory itself will inherit its group ID.
+.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.
+.Pp
+.It Li go-w
+deny write permission to group and others.
+.Pp
+.It Li =rw,+X
+set the read and write permissions to the usual defaults, but
+retain any execute permissions that are currently set.
+.Pp
+.It Li +X
+make a directory or file searchable/executable by everyone if it is
+already searchable/executable by anyone.
+.Pp
+.It Li 755
+.It Li u=rwx,go=rx
+.It Li u=rwx,go=u-w
+make a file readable/executable by everyone and writable by the owner only.
+.Pp
+.It Li go=
+clear all mode bits for group and others.
+.Pp
+.It Li g=u-w
+set the group bits equal to the user bits, but clear the group write bit.
+.El
+.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.
+In cases where a user and a group exist with the same name, the
+user/group name can be prefixed with "user:" or "group:" in order to
+specify the type of name.
+.Pp
+If the user or group name contains spaces you can use ':' as the delimiter
+between name and permission.
+.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 object's 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
+ # chmod +a "User 1:allow:read" file
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: User 1 allow read
+ 3: 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 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,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.
+.Pp
+If the user or group name contains spaces you can use ':' as the delimiter
+.Pp
+\fBExample\fR
+ # chmod +a "User 1:allow:read" file
+.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).
+.It Fl N
+Removes the ACL from the named file(s).
+.El
+.Sh COMPATIBILITY
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr install 1 ,
+.Xr chmod 2 ,
+.Xr stat 2 ,
+.Xr umask 2 ,
+.Xr fts 3 ,
+.Xr setmode 3 ,
+.Xr sticky 7 ,
+.Xr symlink 7 ,
+.Xr chown 8 ,
+.Xr mount 8
+.Sh STANDARDS
+The
+.Nm chmod
+utility is expected to be
+.St -p1003.2
+compatible with the exception of the
+.Ar perm
+symbol
+.Dq t
+which is not included in that standard.
+.Sh HISTORY
+A
+.Nm chmod
+command appeared in
+.At v1 .
diff --git a/file_cmds/chmod/chmod.c b/file_cmds/chmod/chmod.c
new file mode 100644
index 0000000..442815d
--- /dev/null
+++ b/file_cmds/chmod/chmod.c
@@ -0,0 +1,458 @@
+/*
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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 = NULL;
+ FTSENT *p = NULL;
+ mode_t *set = NULL;
+ long val = 0;
+ int oct = 0;
+ int Hflag, Lflag, Pflag, Rflag, ch, fts_options, hflag, rval;
+ int vflag;
+ char *ep, *mode;
+ mode_t newmode, omode;
+#ifdef __APPLE__
+ unsigned int acloptflags = 0;
+ long aclpos = -1;
+ int inheritance_level = 0;
+ int index = 0;
+ size_t acloptlen = 0;
+ int ace_arg_not_required = 0;
+ acl_t acl_input = NULL;
+#endif /* __APPLE__*/
+ int (*change_mode)(const char *, mode_t);
+
+ set = NULL;
+ omode = 0;
+ Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0;
+#ifndef __APPLE__
+ while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1)
+#endif
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ Pflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ Pflag = 1;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ /*
+ * In System V (and probably POSIX.2) the -h option
+ * causes chmod to change the mode of the symbolic
+ * link. 4.4BSD's symbolic links didn't have modes,
+ * so it was an undocumented noop. In FreeBSD 3.0,
+ * lchmod(2) is introduced and this option does real
+ * work.
+ */
+ hflag = 1;
+ break;
+#ifdef __APPLE__
+ case 'a':
+ if (argv[optind - 1][0] == '-' &&
+ argv[optind - 1][1] == ch)
+ --optind;
+ goto done;
+ case 'A':
+// acloptflags |= ACL_FLAG | ACL_TO_STDOUT;
+// ace_arg_not_required = 1;
+ errx(1, "-A not implemented");
+ 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 'N':
+ acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG;
+ ace_arg_not_required = 1;
+ goto done;
+ case 'V':
+// acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR;
+// ace_arg_not_required = 1;
+ errx(1, "-V not implemented");
+ goto done;
+#endif /* __APPLE__ */
+ /*
+ * XXX
+ * "-[rwx]" are valid mode commands. If they are the entire
+ * argument, getopt has moved past them, so decrement optind.
+ * Regardless, we're done argument processing.
+ */
+ case 'g': case 'o': case 'r': case 's':
+ case 't': case 'u': case 'w': case 'X': case 'x':
+ if (argv[optind - 1][0] == '-' &&
+ argv[optind - 1][1] == ch &&
+ argv[optind - 1][2] == '\0')
+ --optind;
+ goto done;
+ case 'v':
+ vflag++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+done: argv += optind;
+ argc -= optind;
+
+#ifdef __APPLE__
+ if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2))
+ usage();
+ if (!Rflag && (Hflag || Lflag || Pflag))
+ warnx("options -H, -L, -P only useful with -R");
+#else /* !__APPLE__ */
+ if (argc < 2)
+ usage();
+#endif /* __APPLE__ */
+
+#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)
+ errx(1, "Invalid ACL entry number: %ld", 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)
+ warnx("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;
+ if (hflag)
+ errx(1,
+ "the -R and -h options may not be specified together.");
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ if (hflag)
+ change_mode = lchmod;
+ else
+ change_mode = chmod;
+#ifdef __APPLE__
+ if (acloptflags & ACL_FROM_STDIN) {
+ ssize_t readval = 0;
+ size_t 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)
+ errx(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) {
+ errx(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;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (!Rflag)
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ case FTS_DNR: /* Warn, chmod, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_DP: /* Already changed at FTS_D. */
+ continue;
+ case FTS_NS:
+ if (acloptflags & ACL_FLAG) /* don't need stat for -N */
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL: /* Ignore. */
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (!hflag)
+ continue;
+ /* else */
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+#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, (int)aclpos, inheritance_level, !hflag))
+ 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("Unable to change file mode on %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);
+
+ (void)printf(": 0%o [%s] -> 0%o [%s]",
+ p->fts_statp->st_mode, m1,
+ (p->fts_statp->st_mode & S_IFMT) |
+ newmode, m2);
+ }
+ (void)printf("\n");
+ }
+
+ }
+#ifdef __APPLE__
+ }
+#endif /* __APPLE__*/
+ }
+ if (errno)
+ err(1, "fts_read");
+#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);
+}
+
+void
+usage(void)
+{
+#ifdef __APPLE__
+ (void)fprintf(stderr,
+ "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n"
+ "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...\n"); /* add -A and -V when implemented */
+#else
+ (void)fprintf(stderr,
+ "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
+#endif /* __APPLE__ */
+ exit(1);
+}
diff --git a/file_cmds/chmod/chmod_acl.c b/file_cmds/chmod/chmod_acl.c
new file mode 100644
index 0000000..5ac4c17
--- /dev/null
+++ b/file_cmds/chmod/chmod_acl.c
@@ -0,0 +1,859 @@
+/*
+ * 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 <fcntl.h>
+#include <sys/stat.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.
+ */
+
+#define NAME_USER (1)
+#define NAME_GROUP (2)
+#define NAME_EITHER (NAME_USER | NAME_GROUP)
+
+/* Perform a name to uuid mapping - calls through to memberd */
+
+uuid_t *
+name_to_uuid(char *tok, int nametype) {
+ uuid_t *entryg = NULL;
+ size_t len = strlen(tok);
+
+ if ((entryg = (uuid_t *) calloc(1, sizeof(uuid_t))) == NULL) {
+ errx(1, "Unable to allocate a uuid");
+ }
+
+ if ((nametype & NAME_USER) && mbr_identifier_to_uuid(ID_TYPE_USERNAME, tok, len, *entryg) == 0) {
+ return entryg;
+ }
+
+ if ((nametype & NAME_GROUP) && mbr_identifier_to_uuid(ID_TYPE_GROUPNAME, tok, len, *entryg) == 0) {
+ return entryg;
+ }
+
+ errx(1, "Unable to translate '%s' to a UUID", tok);
+}
+
+/* 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;
+ char *delimiter = " ";
+ int nametype = NAME_EITHER;
+
+ acl_get_permset(newent, &perms);
+ acl_get_flagset_np(newent, &flags);
+
+ pebuf = entrybuf;
+
+ if (0 == strncmp(entrybuf, "user:", 5)) {
+ nametype = NAME_USER;
+ pebuf += 5;
+ } else if (0 == strncmp(entrybuf, "group:", 6)) {
+ nametype = NAME_GROUP;
+ pebuf += 6;
+ }
+
+ if (strchr(pebuf, ':')) /* User/Group names can have spaces */
+ delimiter = ":";
+ tok = strsep(&pebuf, delimiter);
+
+ if ((tok == NULL) || *tok == '\0') {
+ errx(1, "Invalid entry format -- expected user or group name");
+ }
+
+ /* parse the name into a qualifier */
+ entryg = name_to_uuid(tok, nametype);
+
+ tok = strsep(&pebuf, ": "); /* Stick with delimiter? */
+ if ((tok == NULL) || *tok == '\0') {
+ errx(1, "Invalid entry format -- expected allow or deny");
+ }
+
+ /* is the verb 'allow' or 'deny'? */
+ if (!strcmp(tok, "allow")) {
+ tag = ACL_EXTENDED_ALLOW;
+ } else if (!strcmp(tok, "deny")) {
+ tag = ACL_EXTENDED_DENY;
+ } else {
+ errx(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;
+ }
+ }
+ errx(1,"Invalid permission type '%s'", tok);
+ found:
+ continue;
+ }
+ }
+ if (0 == permcount) {
+ errx(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)) {
+ errx(1, "Failed parsing entry '%s'", *bufp);
+ }
+ if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) {
+ errx(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:
+ errx(1, "Unknown tag type %d present in ACL entry", tag);
+ /* 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;
+}
+
+static 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;
+ void *aqual, *bqual;
+
+ aqual = acl_get_qualifier(a);
+ bqual = acl_get_qualifier(b);
+
+ int compare = compare_acl_qualifiers(aqual, bqual);
+ acl_free(aqual);
+ acl_free(bqual);
+
+ if (compare != 0)
+ 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 (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) {
+ *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, int* valid_perms)
+{
+ acl_permset_t rperms, mperms;
+ acl_flagset_t rflags, mflags;
+ if (valid_perms)
+ *valid_perms = 0;
+ 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);
+ else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm))
+ (*valid_perms)++;
+ }
+ 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 */
+static 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, const char* path) {
+
+ 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) {
+ warnx("No ACL present '%s'", path);
+ retval = 1;
+ } else if (position != -1 ) {
+ if (0 != acl_get_entry(oacl, position, &rentry)) {
+ warnx("Invalid entry number '%s'", path);
+ retval = 1;
+ } else {
+ 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 {
+ int valid_perms;
+/* In the event of a partial match, remove the specified perms from the
+ * entry */
+ subtract_from_entry(rentry, modifier, &valid_perms);
+ /* if no perms survived then delete the entry */
+ if (valid_perms == 0)
+ acl_delete_entry(oacl, rentry);
+ }
+ }
+ }
+ if (0 == match_found) {
+ warnx("Entry not found when attempting delete '%s'",path);
+ 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 '%s'", path);
+
+ if (0 != acl_delete_entry(oacl, rentry))
+ err(1, "Unable to delete entry '%s'", path);
+ }
+ 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, int follow) {
+
+ 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_CLEAR_FLAG) {
+ filesec_t fsec = filesec_init();
+ if (fsec == NULL) {
+ err(1, "filesec_init() failed");
+ }
+ if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) {
+ err(1, "filesec_set_property() failed");
+ }
+ if (follow) {
+ if (chmodx_np(path, fsec) != 0) {
+ if (!fflag) {
+ warn("Failed to clear ACL on file %s", path);
+ }
+ retval = 1;
+ }
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ if (fchmodx_np(fd, fsec) != 0) {
+ if (!fflag) {
+ warn("Failed to clear ACL on file %s", path);
+ }
+ retval = 1;
+ }
+ close(fd);
+ } else {
+ if (!fflag) {
+ warn("Failed to open file %s", path);
+ }
+ retval = 1;
+ }
+ }
+ filesec_free(fsec);
+ return (retval);
+ }
+
+ if (optflags & ACL_FROM_STDIN) {
+ oacl = acl_dup(modifier);
+ } else {
+ if (follow) {
+ oacl = acl_get_file(path, ACL_TYPE_EXTENDED);
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED);
+ close(fd);
+ }
+ }
+ 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_TO_STDOUT) {
+ ssize_t len; /* need to get printacl() from ls(1) */
+ char *text = acl_to_text(oacl, &len);
+ puts(text);
+ acl_free(text);
+ } else if (optflags & ACL_CHECK_CANONICITY) {
+ if (flag_new_acl) {
+ warnx("No ACL currently associated with file '%s'", path);
+ }
+ retval = is_canonical(oacl);
+ } else if ((optflags & ACL_SET_FLAG) && (position == -1) &&
+ (!is_canonical(oacl))) {
+ warnx("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, path);
+ } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) {
+ warnx("No ACL currently associated with file '%s'", path);
+ retval = 1;
+ } else {
+ if (!modifier) { /* avoid bus error in acl_get_entry */
+ errx(1, "Internal error: modifier should not be NULL");
+ }
+ 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, path);
+ }
+ }
+ }
+
+/* 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_TO_STDOUT|ACL_CHECK_CANONICITY))) {
+ int status = -1;
+ if (follow) {
+ status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl);
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ status = acl_set_fd_np(fd, oacl,
+ ACL_TYPE_EXTENDED);
+ close(fd);
+ }
+ }
+ if (status != 0) {
+ 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/file_cmds/chmod/chmod_acl.h b/file_cmds/chmod/chmod_acl.h
new file mode 100644
index 0000000..b4667e9
--- /dev/null
+++ b/file_cmds/chmod/chmod_acl.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef _CHMOD_ACL_H_
+#define _CHMOD_ACL_H_
+
+#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 ACL_TO_STDOUT (1<<13)
+#define ACL_CLEAR_FLAG (1<<14)
+
+#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, int *valid_perms);
+extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl, const char* path);
+extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow);
+extern uuid_t *name_to_uuid(char *tok, int nametype);
+#endif /* __APPLE__*/
+
+#endif /* _CHMOD_ACL_H_ */
diff --git a/file_cmds/chown/chgrp.1 b/file_cmds/chown/chgrp.1
new file mode 100644
index 0000000..593c609
--- /dev/null
+++ b/file_cmds/chown/chgrp.1
@@ -0,0 +1,144 @@
+.\" Copyright (c) 1983, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD: src/usr.sbin/chown/chgrp.1,v 1.13 2001/08/15 09:09:46 ru Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHGRP 1
+.Os
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm chgrp
+.Op Fl fhnv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar group
+.Ar
+.Sh DESCRIPTION
+The
+.Nm chgrp
+utility sets the group ID of the file named by each
+.Ar file
+operand to the
+.Ar group
+ID specified by the group operand.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl f
+The force option ignores errors, except for usage errors and doesn't
+query about strange modes (unless the user does not have proper permissions).
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed).
+.It Fl h
+If the file is a symbolic link, the group ID of the link itself is changed
+rather than the file that is pointed to.
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default. Use
+.Fl h
+to change the group ID of a symbolic link.
+.It Fl R
+Change the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl n
+Interpret the group ID as numeric, avoiding the name lookup.
+.It Fl v
+Cause
+.Nm chgrp
+to be verbose, showing files as the group is modified.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar group
+operand can be either a group name from the group database,
+or a numeric group ID.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The user invoking
+.Nm chgrp
+must belong to the specified group and be the owner of the file,
+or be the super-user.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh COMPATIBILITY
+In previous versions of this system, symbolic links did not have groups.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group ID file
+.El
+.Sh SEE ALSO
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm chgrp
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/file_cmds/chown/chown.8 b/file_cmds/chown/chown.8
new file mode 100644
index 0000000..3455497
--- /dev/null
+++ b/file_cmds/chown/chown.8
@@ -0,0 +1,177 @@
+.\" Copyright (c) 1990, 1991, 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.
+.\"
+.\" @(#)chown.8 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD: src/usr.sbin/chown/chown.8,v 1.20 2002/07/14 14:42:43 charnier Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHOWN 8
+.Os
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm chown
+.Op Fl fhnv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar owner Ns Op : Ns Ar group
+.Ar
+.Nm chown
+.Op Fl fhnv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.No : Ns Ar group
+.Ar
+.Sh DESCRIPTION
+The
+.Nm chown
+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
+.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
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.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 L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+Instead, the user and/or group ID of the link itself are modified.
+This is the default. Use
+.Fl h
+to change the user ID and/or the group of symbolic links.
+.It Fl R
+Change the user ID and/or the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl n
+Interpret user ID and group ID as numeric, avoiding name lookups.
+.It Fl v
+Cause
+.Nm chown
+to be verbose, showing files as the owner is modified.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar owner
+and
+.Ar group
+operands are both optional;
+however, at least one must be specified.
+If the
+.Ar group
+operand is specified, it must be preceded by a colon (``:'') character.
+.Pp
+The
+.Ar owner
+may be either a numeric user ID or a user name.
+If a user name is also a numeric user ID, the operand is used as a
+user name.
+The
+.Ar group
+may be either a numeric group ID or a group name.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+For obvious security reasons,
+the ownership of a file may only be altered by a super-user.
+Similarly, only a member of a group can change a file's group ID
+to that group.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm chown
+utility used the dot (``.'') character to distinguish the group name.
+This has been changed to be a colon (``:'') character,
+so that user and group names may contain the dot character.
+.Pp
+On previous versions of this system,
+symbolic links did not have owners.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh LEGACY DESCRIPTION
+In legacy mode, the
+.Fl R
+and
+.Fl RP
+options do not change the user ID
+or the group ID of symbolic links.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr compat 5 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm chown
+utility is expected to be
+.St -p1003.2
+compliant.
+.Sh HISTORY
+A
+.Nm chown
+utility appeared in
+.At v1 .
diff --git a/file_cmds/chown/chown.c b/file_cmds/chown/chown.c
new file mode 100644
index 0000000..708a6c5
--- /dev/null
+++ b/file_cmds/chown/chown.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 1988, 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/usr.sbin/chown/chown.c,v 1.24 2002/07/17 16:22:24 dwmalone Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+void a_gid(const char *);
+void a_uid(const char *);
+void chownerr(const char *);
+static uid_t id(const char *, const char *);
+void usage(void);
+
+uid_t uid;
+gid_t gid;
+int ischown;
+#ifdef __APPLE__
+int isnumeric = 0;
+#endif
+const char *gname;
+
+int
+main(int argc, char **argv)
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Pflag, Rflag, fflag, hflag, vflag;
+ int ch, fts_options, rval;
+ char *cp;
+ int unix2003_compat = 0;
+ int symlink_found = 0;
+
+ if (argc < 1)
+ usage();
+ cp = strrchr(argv[0], '/');
+ cp = (cp != NULL) ? cp + 1 : argv[0];
+ ischown = (strcmp(cp, "chown") == 0);
+
+ Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0;
+#ifdef __APPLE__
+ while ((ch = getopt(argc, argv, "HLPRfhnv")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
+#endif
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+#ifdef __APPLE__
+ case 'n':
+ isnumeric = 1;
+ break;
+#endif
+ case 'v':
+ vflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+ if (!Rflag && (Hflag || Lflag || Pflag))
+ warnx("options -H, -L, -P only useful with -R");
+
+ if (Rflag) {
+ fts_options = FTS_PHYSICAL;
+ if (hflag && (Hflag || Lflag))
+ errx(1, "the -R%c and -h options may not be "
+ "specified together", Hflag ? 'H' : 'L');
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ else if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ if (ischown) {
+ unix2003_compat = COMPAT_MODE("bin/chown", "Unix2003");
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strchr(*argv, '.')) != NULL) {
+ warnx("separation of user and group with a period is deprecated");
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#endif
+ a_uid(*argv);
+ } else {
+ unix2003_compat = COMPAT_MODE("bin/chgrp", "Unix2003");
+ a_gid(*argv);
+ }
+
+ if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ symlink_found = 0;
+ switch (p->fts_info) {
+ case FTS_D: /* Change it at FTS_DP. */
+ if (!Rflag)
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chown. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL:
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (hflag)
+ break;
+ else {
+ symlink_found = 1;
+ if (unix2003_compat) {
+ if (Hflag || Lflag) { /* -H or -L was specified */
+ if (p->fts_errno) {
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ }
+ }
+ break; /* Otherwise symlinks keep going */
+ }
+ continue;
+ }
+ default:
+ break;
+ }
+ if (unix2003_compat) {
+ /* Can only avoid updating times if both uid and gid are -1 */
+ if ((uid == (uid_t)-1) && (gid == (gid_t)-1))
+ continue;
+ } else {
+ if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
+ (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
+ continue;
+ }
+ if (((hflag || symlink_found) ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
+ if (!fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ } else {
+ if (vflag)
+ printf("%s\n", p->fts_path);
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+a_gid(const char *s)
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gname = s;
+#ifdef __APPLE__
+ gid = (!isnumeric && ((gr = getgrnam(s)) != NULL)) ? gr->gr_gid : id(s, "group");
+#else
+ gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
+#endif
+}
+
+void
+a_uid(const char *s)
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+#ifdef __APPLE__
+ uid = (!isnumeric && ((pw = getpwnam(s)) != NULL)) ? pw->pw_uid : id(s, "user");
+#else
+ uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
+#endif
+}
+
+static uid_t
+id(const char *name, const char *type)
+{
+ unsigned long val;
+ char *ep;
+
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno || *ep != '\0' || val > UID_MAX)
+ errx(1, "%s: illegal %s name", name, type);
+ return (uid_t)val;
+}
+
+void
+chownerr(const char *file)
+{
+ static uid_t euid = -1;
+ static int ngroups = -1;
+ gid_t groups[NGROUPS_MAX];
+
+ /* Check for chown without being root. */
+ if (errno != EPERM || (uid != (uid_t)-1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0)) {
+ warn("%s", file);
+ return;
+ }
+
+ /* Check group membership; kernel just returns EPERM. */
+ if (gid != (gid_t)-1 && ngroups == -1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0) {
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ while (--ngroups >= 0 && gid != groups[ngroups]);
+ if (ngroups < 0) {
+ warnx("you are not a member of group %s", gname);
+ return;
+ }
+ }
+ warn("%s", file);
+}
+
+void
+usage(void)
+{
+
+ if (ischown)
+ (void)fprintf(stderr, "%s\n%s\n",
+#ifdef __APPLE__
+ "usage: chown [-fhnv] [-R [-H | -L | -P]] owner[:group]"
+ " file ...",
+ " chown [-fhnv] [-R [-H | -L | -P]] :group file ...");
+#else
+ "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]"
+ " file ...",
+ " chown [-fhv] [-R [-H | -L | -P]] :group file ...");
+#endif
+ else
+ (void)fprintf(stderr, "%s\n",
+#ifdef __APPLE__
+ "usage: chgrp [-fhnv] [-R [-H | -L | -P]] group file ...");
+#else
+ "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
+#endif
+ exit(1);
+}
diff --git a/file_cmds/cksum/cksum.1 b/file_cmds/cksum/cksum.1
new file mode 100644
index 0000000..b4161fe
--- /dev/null
+++ b/file_cmds/cksum/cksum.1
@@ -0,0 +1,182 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cksum.1 8.2 (Berkeley) 4/28/95
+.\" $FreeBSD: src/usr.bin/cksum/cksum.1,v 1.19 2005/01/17 07:44:13 ru Exp $
+.\"
+.Dd April 28, 1995
+.Dt CKSUM 1
+.Os
+.Sh NAME
+.Nm cksum ,
+.Nm sum
+.Nd display file checksums and block counts
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar 1 | 2 | 3
+.Op Ar
+.Nm sum
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility writes to the standard output three whitespace separated
+fields for each input file.
+These fields are a checksum
+.Tn CRC ,
+the total number of octets in the file and the file name.
+If no file name is specified, the standard input is used and no file name
+is written.
+.Pp
+The
+.Nm sum
+utility is identical to the
+.Nm
+utility, except that it defaults to using historic algorithm 1, as
+described below.
+It is provided for compatibility only.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Use historic algorithms instead of the (superior) default one.
+.Pp
+Algorithm 1 is the algorithm used by historic
+.Bx
+systems as the
+.Xr sum 1
+algorithm and by historic
+.At V
+systems as the
+.Xr sum 1
+algorithm when using the
+.Fl r
+option.
+This is a 16-bit checksum, with a right rotation before each addition;
+overflow is discarded.
+.Pp
+Algorithm 2 is the algorithm used by historic
+.At V
+systems as the
+default
+.Xr sum 1
+algorithm.
+This is a 32-bit checksum, and is defined as follows:
+.Bd -unfilled -offset indent
+s = sum of all bytes;
+r = s % 2^16 + (s % 2^32) / 2^16;
+cksum = (r % 2^16) + r / 2^16;
+.Ed
+.Pp
+Algorithm 3 is what is commonly called the
+.Ql 32bit CRC
+algorithm.
+This is a 32-bit checksum.
+.Pp
+Both algorithm 1 and 2 write to the standard output the same fields as
+the default algorithm except that the size of the file in bytes is
+replaced with the size of the file in blocks.
+For historic reasons, the block size is 1024 for algorithm 1 and 512
+for algorithm 2.
+Partial blocks are rounded up.
+.El
+.Pp
+The default
+.Tn CRC
+used is based on the polynomial used for
+.Tn CRC
+error checking
+in the networking standard
+.St -iso8802-3 .
+The
+.Tn CRC
+checksum encoding is defined by the generating polynomial:
+.Pp
+.Bd -unfilled -offset indent
+G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 +
+ x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
+.Ed
+.Pp
+Mathematically, the
+.Tn CRC
+value corresponding to a given file is defined by
+the following procedure:
+.Bd -ragged -offset indent
+The
+.Ar n
+bits to be evaluated are considered to be the coefficients of a mod 2
+polynomial M(x) of degree
+.Ar n Ns \-1 .
+These
+.Ar n
+bits are the bits from the file, with the most significant bit being the most
+significant bit of the first octet of the file and the last bit being the least
+significant bit of the last octet, padded with zero bits (if necessary) to
+achieve an integral number of octets, followed by one or more octets
+representing the length of the file as a binary value, least significant octet
+first.
+The smallest number of octets capable of representing this integer are used.
+.Pp
+M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by
+G(x) using mod 2 division, producing a remainder R(x) of degree <= 31.
+.Pp
+The coefficients of R(x) are considered to be a 32-bit sequence.
+.Pp
+The bit sequence is complemented and the result is the CRC.
+.Ed
+.Sh EXIT STATUS
+.Ex -std cksum sum
+.Sh SEE ALSO
+.Xr md5 1
+.Pp
+The default calculation is identical to that given in pseudo-code
+in the following
+.Tn ACM
+article.
+.Rs
+.%T "Computation of Cyclic Redundancy Checks Via Table Lookup"
+.%A Dilip V. Sarwate
+.%J "Communications of the" Tn ACM
+.%D "August 1988"
+.Re
+.Sh STANDARDS
+The
+.Nm
+utility is expected to conform to
+.St -p1003.2-92 .
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.4 .
diff --git a/file_cmds/cksum/cksum.c b/file_cmds/cksum/cksum.c
new file mode 100644
index 0000000..ae6a7ed
--- /dev/null
+++ b/file_cmds/cksum/cksum.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/cksum.c,v 1.17 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ uint32_t val;
+ int ch, fd, rval;
+ off_t len;
+ char *fn, *p;
+ int (*cfncn)(int, uint32_t *, off_t *);
+ void (*pfncn)(char *, u_int32_t, off_t);
+
+ cfncn=NULL;
+
+ if(*argv) {
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ if (!strcmp(p, "sum")) {
+ cfncn = csum1;
+ pfncn = psum1;
+ ++argv;
+ }
+ }
+
+ if(!cfncn) {
+ cfncn = crc;
+ pfncn = pcrc;
+
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ switch (ch) {
+ case 'o':
+ if (!strcmp(optarg, "1")) {
+ cfncn = csum1;
+ pfncn = psum1;
+ } else if (!strcmp(optarg, "2")) {
+ cfncn = csum2;
+ pfncn = psum2;
+ } else if (!strcmp(optarg, "3")) {
+ cfncn = crc32;
+ pfncn = pcrc;
+ } else {
+ warnx("illegal argument to -o option");
+ usage();
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+// argc -= optind;
+ argv += optind;
+ }
+
+ fd = STDIN_FILENO;
+ fn = NULL;
+ rval = 0;
+ do {
+ if (*argv) {
+ fn = *argv++;
+ if ((fd = open(fn, O_RDONLY, 0)) < 0) {
+ warn("%s", fn);
+ rval = 1;
+ continue;
+ }
+ }
+ if (cfncn(fd, &val, &len)) {
+ warn("%s", fn ? fn : "stdin");
+ rval = 1;
+ } else
+ pfncn(fn, val, len);
+ (void)close(fd);
+ } while (*argv);
+ exit(rval);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: cksum [-o 1 | 2 | 3] [file ...]\n");
+ (void)fprintf(stderr, " sum [file ...]\n");
+ exit(1);
+}
diff --git a/file_cmds/cksum/crc.c b/file_cmds/cksum/crc.c
new file mode 100644
index 0000000..57179d5
--- /dev/null
+++ b/file_cmds/cksum/crc.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/crc.c,v 1.8 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static const uint32_t crctab[] = {
+ 0x0,
+ 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+ 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+ 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+ 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+ 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+ 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+ 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+ 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+ 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+ 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+ 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+ 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+ 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+ 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+ 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+ 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+ 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+ 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+ 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+ 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+ 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+ 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+ 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+ 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+ 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+ 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+ 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+ 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+ 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+ 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+ 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+ 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+ 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+ 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/*
+ * Compute a POSIX 1003.2 checksum. This routine has been broken out so that
+ * other programs can use it. It takes a file descriptor to read from and
+ * locations to store the crc and the number of bytes read. It returns 0 on
+ * success and 1 on failure. Errno is set on failure.
+ */
+uint32_t crc_total = ~0; /* The crc over a number of files. */
+
+int
+crc(int fd, uint32_t *cval, off_t *clen)
+{
+ uint32_t lcrc;
+ ssize_t nr;
+ off_t len;
+ u_char *p;
+ u_char buf[16 * 1024];
+
+#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+
+ lcrc = 0;
+ len = 0;
+ crc_total = ~crc_total;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (len += nr, p = buf; nr--; ++p) {
+ COMPUTE(lcrc, *p);
+ COMPUTE(crc_total, *p);
+ }
+ if (nr < 0)
+ return (1);
+
+ *clen = len;
+
+ /* Include the length of the file. */
+ for (; len != 0; len >>= 8) {
+ COMPUTE(lcrc, len & 0xff);
+ COMPUTE(crc_total, len & 0xff);
+ }
+
+ *cval = ~lcrc;
+ crc_total = ~crc_total;
+ return (0);
+}
diff --git a/file_cmds/cksum/crc32.c b/file_cmds/cksum/crc32.c
new file mode 100644
index 0000000..ff17565
--- /dev/null
+++ b/file_cmds/cksum/crc32.c
@@ -0,0 +1,122 @@
+/*
+ * This code implements the AUTODIN II polynomial used by Ethernet,
+ * and can be used to calculate multicast address hash indices.
+ * It assumes that the low order bits will be transmitted first,
+ * and consequently the low byte should be sent first when
+ * the crc computation is finished. The crc should be complemented
+ * before transmission.
+ * The variable corresponding to the macro argument "crc" should
+ * be an unsigned long and should be preset to all ones for Ethernet
+ * use. An error-free packet will leave 0xDEBB20E3 in the crc.
+ * Spencer Garrett <srg@quick.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/crc32.c,v 1.9 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+uint32_t crc32_total = 0;
+
+int
+crc32(int fd, uint32_t *cval, off_t *clen)
+{
+ uint32_t lcrc = ~0;
+ ssize_t nr;
+ off_t len ;
+ char buf[BUFSIZ], *p ;
+
+ len = 0 ;
+ crc32_total = ~crc32_total ;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (len += nr, p = buf; nr--; ++p) {
+ CRC(lcrc, *p) ;
+ CRC(crc32_total, *p) ;
+ }
+ if (nr < 0)
+ return 1 ;
+
+ *clen = len ;
+ *cval = ~lcrc ;
+ crc32_total = ~crc32_total ;
+ return 0 ;
+}
diff --git a/file_cmds/cksum/extern.h b/file_cmds/cksum/extern.h
new file mode 100644
index 0000000..3e9c8c6
--- /dev/null
+++ b/file_cmds/cksum/extern.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.bin/cksum/extern.h,v 1.6 2003/03/13 23:32:28 robert Exp $
+ */
+
+#ifndef _CKSUM_EXTERN_H_
+#define _CKSUM_EXTERN_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int crc(int, uint32_t *, off_t *);
+void pcrc(char *, uint32_t, off_t);
+void psum1(char *, uint32_t, off_t);
+void psum2(char *, uint32_t, off_t);
+int csum1(int, uint32_t *, off_t *);
+int csum2(int, uint32_t *, off_t *);
+int crc32(int, uint32_t *, off_t *);
+__END_DECLS
+
+#endif /* _CKSUM_EXTERN_H_ */
diff --git a/file_cmds/cksum/print.c b/file_cmds/cksum/print.c
new file mode 100644
index 0000000..4d732d9
--- /dev/null
+++ b/file_cmds/cksum/print.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/print.c,v 1.7 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "extern.h"
+
+void
+pcrc(char *fn, uint32_t val, off_t len)
+{
+ (void)printf("%lu %jd", (u_long)val, (intmax_t)len);
+ if (fn != NULL)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
+
+void
+psum1(char *fn, uint32_t val, off_t len)
+{
+ (void)printf("%lu %jd", (u_long)val, (intmax_t)(len + 1023) / 1024);
+ if (fn != NULL)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
+
+void
+psum2(char *fn, uint32_t val, off_t len)
+{
+ (void)printf("%lu %jd", (u_long)val, (intmax_t)(len + 511) / 512);
+ if (fn != NULL)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
diff --git a/file_cmds/cksum/sum.1 b/file_cmds/cksum/sum.1
new file mode 100644
index 0000000..db04800
--- /dev/null
+++ b/file_cmds/cksum/sum.1
@@ -0,0 +1 @@
+.so man1/cksum.1
diff --git a/file_cmds/cksum/sum1.c b/file_cmds/cksum/sum1.c
new file mode 100644
index 0000000..fa016ce
--- /dev/null
+++ b/file_cmds/cksum/sum1.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/sum1.c,v 1.8 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <stdint.h>
+
+#include "extern.h"
+
+int
+csum1(int fd, uint32_t *cval, off_t *clen)
+{
+ ssize_t nr;
+ u_int lcrc;
+ off_t total;
+ u_char *p;
+ u_char buf[8192];
+
+ /*
+ * 16-bit checksum, rotating right before each addition;
+ * overflow is discarded.
+ */
+ lcrc = 0;
+ total = 0;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (total += nr, p = buf; nr--; ++p) {
+ if (lcrc & 1)
+ lcrc |= 0x10000;
+ lcrc = ((lcrc >> 1) + *p) & 0xffff;
+ }
+ if (nr < 0)
+ return (1);
+
+ *cval = lcrc;
+ *clen = total;
+ return (0);
+}
diff --git a/file_cmds/cksum/sum2.c b/file_cmds/cksum/sum2.c
new file mode 100644
index 0000000..126b428
--- /dev/null
+++ b/file_cmds/cksum/sum2.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/cksum/sum2.c,v 1.8 2003/03/13 23:32:28 robert Exp $");
+
+#include <sys/types.h>
+
+#include <unistd.h>
+#include <stdint.h>
+
+#include "extern.h"
+
+int
+csum2(int fd, uint32_t *cval, off_t *clen)
+{
+ uint32_t lcrc;
+ ssize_t nr;
+ off_t total;
+ u_char *p;
+ u_char buf[8192];
+
+ /*
+ * Draft 8 POSIX 1003.2:
+ *
+ * s = sum of all bytes
+ * r = s % 2^16 + (s % 2^32) / 2^16
+ * lcrc = (r % 2^16) + r / 2^16
+ */
+ lcrc = 0;
+ total = 0;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (total += nr, p = buf; nr--; ++p)
+ lcrc += *p;
+ if (nr < 0)
+ return (1);
+
+ lcrc = (lcrc & 0xffff) + (lcrc >> 16);
+ lcrc = (lcrc & 0xffff) + (lcrc >> 16);
+
+ *cval = lcrc;
+ *clen = total;
+ return (0);
+}
diff --git a/file_cmds/compress/compress.1 b/file_cmds/compress/compress.1
new file mode 100644
index 0000000..963cd7a
--- /dev/null
+++ b/file_cmds/compress/compress.1
@@ -0,0 +1,253 @@
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" James A. Woods, derived from original work by Spencer Thomas
+.\" and Joseph Orost.
+.\"
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\" $FreeBSD: src/usr.bin/compress/compress.1,v 1.23 2010/12/11 08:32:16 joel Exp $
+.\"
+.Dd May 17, 2002
+.Dt COMPRESS 1
+.Os
+.Sh NAME
+.Nm compress ,
+.Nm uncompress
+.Nd compress and expand data
+.Sh SYNOPSIS
+.Nm
+.Op Fl fv
+.Op Fl b Ar bits
+.Op Ar
+.Nm
+.Fl c
+.Op Fl b Ar bits
+.Op Ar
+.Nm uncompress
+.Op Fl fv
+.Op Ar
+.Nm uncompress
+.Fl c
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reduces the size of files using adaptive Lempel-Ziv coding.
+Each
+.Ar file
+is renamed to the same name plus the extension
+.Pa .Z .
+A
+.Ar file
+argument with a
+.Pa .Z
+extension will be ignored except it will cause an
+error exit after other arguments are processed.
+If compression would not reduce the size of a
+.Ar file ,
+the file is ignored.
+.Pp
+The
+.Nm uncompress
+utility restores compressed files to their original form, renaming the
+files by deleting the
+.Pa .Z
+extensions.
+A file specification need not include the file's
+.Pa .Z
+extension.
+If a file's name in its file system does not have a
+.Pa .Z
+extension, it will not be uncompressed and it will cause
+an error exit after other arguments are processed.
+.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
+output) for confirmation.
+If prompting is not possible or confirmation is not received, the files
+are not overwritten.
+.Pp
+As many of the modification time, access time, file flags, file mode,
+user ID, and group ID as allowed by permissions are retained in the
+new file.
+.Pp
+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
+in the output file.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl b Ar bits"
+.It Fl b Ar bits
+The code size (see below) is limited to
+.Ar bits ,
+which must be in the range 9..16.
+The default is 16.
+.It Fl c
+Compressed or uncompressed output is written to the standard output.
+No files are modified.
+The
+.Fl v
+option is ignored.
+Compression is attempted even if the results will be larger than the
+original.
+.It Fl f
+Files are overwritten without prompting for confirmation.
+Also, for
+.Nm compress ,
+files are compressed even if they are not actually reduced in size.
+.It Fl v
+Print the percentage reduction of each file.
+Ignored by
+.Nm uncompress
+or if the
+.Fl c
+option is also used.
+.El
+.Pp
+The
+.Nm
+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
+limit specified by the
+.Fl b
+option or its default is reached.
+.Pp
+After the limit is reached,
+.Nm
+periodically checks the compression ratio.
+If it is increasing,
+.Nm
+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
+the algorithm to adapt to the next "block" of the file.
+.Pp
+The
+.Fl b
+option is unavailable for
+.Nm uncompress
+since the
+.Ar bits
+parameter specified during compression
+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
+The amount of compression obtained depends on the size of the
+input, the number of
+.Ar bits
+per code, and the distribution of common substrings.
+Typically, text such as source code or English is reduced by 50\-60%.
+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 EXIT STATUS
+.Ex -std compress uncompress
+.Pp
+The
+.Nm compress
+utility exits 2 if attempting to compress a file would not reduce its size
+and the
+.Fl f
+option was not specified and if no other error occurs.
+.Sh SEE ALSO
+.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
+.%T "A Technique for High Performance Data Compression"
+.%J "IEEE Computer"
+.%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
+command appeared in
+.Bx 4.3 .
+.Sh BUGS
+Some of these might be considered otherwise-undocumented features.
+.Pp
+.Nm compress :
+If the utility does not compress a file because doing so would not
+reduce its size, and a file of the same name except with an
+.Pa .Z
+extension exists, the named file is not really ignored as stated above;
+it causes a prompt to confirm the overwriting of the file with the extension.
+If the operation is confirmed, that file is deleted.
+.Pp
+.Nm uncompress :
+If an empty file is compressed (using
+.Fl f ) ,
+the resulting
+.Pa .Z
+file is also empty.
+That seems right, but if
+.Nm uncompress
+is then used on that file, an error will occur.
+.Pp
+Both utilities: If a
+.Sq Fl
+argument is used and the utility prompts the user, the standard input
+is taken as the user's reply to the prompt.
+.Pp
+Both utilities:
+If the specified file does not exist, but a similarly-named one with (for
+.Nm compress )
+or without (for
+.Nm uncompress )
+a
+.Pa .Z
+extension does exist, the utility will waste the user's time by not
+immediately emitting an error message about the missing file and
+continuing.
+Instead, it first asks for confirmation to overwrite
+the existing file and then does not overwrite it.
diff --git a/file_cmds/compress/compress.c b/file_cmds/compress/compress.c
new file mode 100644
index 0000000..26da7fa
--- /dev/null
+++ b/file_cmds/compress/compress.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
+#endif
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.23 2010/12/11 08:32:16 joel Exp $");
+
+#include <sys/param.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>
+#include <locale.h>
+
+#include "zopen.h"
+
+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, cat;
+
+int
+main(int argc, char *argv[])
+{
+ enum {COMPRESS, DECOMPRESS} style;
+ size_t len;
+ int bits, ch;
+ char *p, newname[MAXPATHLEN];
+
+ if (argc < 1)
+ usage(1);
+ 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")) {
+ cat = 1;
+ style = DECOMPRESS;
+ } else
+ errx(1, "unknown program name");
+
+ bits = 0;
+ while ((ch = getopt(argc, argv, "b:cdfv")) != -1)
+ switch(ch) {
+ case 'b':
+ bits = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal bit count -- %s", optarg);
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'd': /* Backward compatible. */
+ style = DECOMPRESS;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage(style == COMPRESS);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ cat = 1;
+ switch(style) {
+ case COMPRESS:
+ (void)compress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ case DECOMPRESS:
+ (void)decompress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ }
+ exit (eval);
+ }
+
+ /*
+ * The UNIX standard requires that `uncompress -c` be able to have multiple file parameters given.
+ */
+
+ for (; *argv; ++argv)
+ switch(style) {
+ case COMPRESS:
+ if (strcmp(*argv, "-") == 0) {
+ cat = 1;
+ compress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ } else if (cat) {
+ compress(*argv, "/dev/stdout", bits);
+ break;
+ }
+ if ((p = rindex(*argv, '.')) != NULL &&
+ !strcmp(p, ".Z")) {
+ cwarnx("%s: name already has trailing .Z",
+ *argv);
+ break;
+ }
+ len = strlen(*argv);
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ 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 = rindex(*argv, '.')) == NULL ||
+ strcmp(p, ".Z")) {
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ decompress(newname,
+ cat ? "/dev/stdout" : *argv, bits);
+ } else {
+ if (len - 2 > sizeof(newname) - 1) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len - 2);
+ newname[len - 2] = '\0';
+ decompress(*argv,
+ cat ? "/dev/stdout" : newname, bits);
+ }
+ break;
+ }
+ exit (eval);
+}
+
+void
+compress(const char *in, const char *out, int bits)
+{
+ size_t nr;
+ struct stat isb, sb;
+ FILE *ifp = NULL, *ofp = NULL;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ 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);
+
+ if ((ifp = fopen(in, "r")) == NULL) {
+ cwarn("%s", in);
+ return;
+ }
+ 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) {
+ cwarn("%s", out);
+ goto err;
+ }
+ while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
+ if (fwrite(buf, 1, nr, ofp) != nr) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+ ofp = NULL;
+
+ if (!cat && isreg) {
+ if (stat(out, &sb)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (!force && sb.st_size >= isb.st_size) {
+ if (verbose)
+ (void)fprintf(stderr, "%s: file would grow; left unmodified\n",
+ in);
+ eval = 2;
+ if (unlink(out))
+ cwarn("%s", out);
+ goto err;
+ }
+
+ setfile(out, &isb);
+
+ if (unlink(in))
+ cwarn("%s", in);
+
+ if (verbose) {
+ (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 (!cat && oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+decompress(const char *in, const char *out, int bits)
+{
+ size_t nr;
+ struct stat sb;
+ FILE *ifp, *ofp;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ 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);
+
+ ofp = NULL;
+ if ((ifp = zopen(in, "r", bits)) == NULL) {
+ cwarn("%s", in);
+ return;
+ }
+ if (stat(in, &sb)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ 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);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ 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 (!cat && oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+setfile(const char *name, struct stat *fs)
+{
+ static struct timeval tv[2];
+
+ fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+ if (utimes(name, tv))
+ cwarn("utimes: %s", name);
+
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
+ * the mode; current BSD behavior is to remove all setuid bits on
+ * chown. If chown fails, lose setuid/setgid bits.
+ */
+ if (chown(name, fs->st_uid, fs->st_gid)) {
+ if (errno != EPERM)
+ cwarn("chown: %s", name);
+ fs->st_mode &= ~(S_ISUID|S_ISGID);
+ }
+ if (chmod(name, fs->st_mode) && errno != ENOTSUP)
+ cwarn("chmod: %s", name);
+
+ if (chflags(name, fs->st_flags) && errno != ENOTSUP)
+ cwarn("chflags: %s", name);
+}
+
+int
+permission(const char *fname)
+{
+ int ch, first;
+ char resp[] = {'\0', '\0'};
+
+ if (!isatty(fileno(stderr)))
+ return (0);
+ (void)fprintf(stderr, "overwrite %s? ", fname);
+
+ /* Load user specified locale */
+ setlocale(LC_MESSAGES, "");
+
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+
+ /* only care about first character */
+ resp[0] = first;
+
+ return (rpmatch(resp) == 1);
+}
+
+void
+usage(int iscompress)
+{
+ if (iscompress)
+ (void)fprintf(stderr,
+ "usage: compress [-cfv] [-b bits] [file ...]\n");
+ else
+ (void)fprintf(stderr,
+ "usage: uncompress [-cfv] [file ...]\n");
+ exit(1);
+}
+
+void
+cwarnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}
+
+void
+cwarn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}
diff --git a/file_cmds/compress/doc/NOTES b/file_cmds/compress/doc/NOTES
new file mode 100644
index 0000000..4ced689
--- /dev/null
+++ b/file_cmds/compress/doc/NOTES
@@ -0,0 +1,142 @@
+
+ $FreeBSD: src/usr.bin/compress/doc/NOTES,v 1.3 2011/10/16 14:30:28 eadler Exp $
+
+From: James A. Woods <jaw@eos.arc.nasa.gov>
+
+>From vn Fri Dec 2 18:05:27 1988
+Subject: Re: Looking for C source for RSA
+Newsgroups: sci.crypt
+
+# Illegitimi noncarborundum
+
+Patents are a tar pit.
+
+A good case can be made that most are just a license to sue, and nothing
+is illegal until a patent is upheld in court.
+
+For example, if you receive netnews by means other than 'nntp',
+these very words are being modulated by 'compress',
+a variation on the patented Lempel-Ziv-Welch algorithm.
+
+Original Ziv-Lempel is patent number 4,464,650, and the more powerful
+LZW method is #4,558,302. Yet despite any similarities between 'compress'
+and LZW (the public-domain 'compress' code was designed and given to the
+world before the ink on the Welch patent was dry), no attorneys from Sperry
+(the assignee) have asked you to unplug your Usenet connection.
+
+Why? I can't speak for them, but it is possible the claims are too broad,
+or, just as bad, not broad enough. ('compress' does things not mentioned
+in the Welch patent.) Maybe they realize that they can commercialize
+LZW better by selling hardware implementations rather than by licensing
+software. Again, the LZW software delineated in the patent is *not*
+the same as that of 'compress'.
+
+At any rate, court-tested software patents are a different animal;
+corporate patents in a portfolio are usually traded like baseball cards
+to shut out small fry rather than actually be defended before
+non-technical juries. Perhaps RSA will undergo this test successfully,
+although the grant to "exclude others from making, using, or selling"
+the invention would then only apply to the U.S. (witness the
+Genentech patent of the TPA molecule in the U.S. but struck down
+in Great Britain as too broad.)
+
+The concept is still exotic for those who learned in school the rule of thumb
+that one may patent "apparatus" but not an "idea".
+Apparently this all changed in Diamond v. Diehr (1981) when the U. S. Supreme
+Court reversed itself.
+
+Scholars should consult the excellent article in the Washington and Lee
+Law Review (fall 1984, vol. 41, no. 4) by Anthony and Colwell for a
+comprehensive survey of an area which will remain murky for some time.
+
+Until the dust clears, how you approach ideas which are patented depends
+on how paranoid you are of a legal onslaught. Arbitrary? Yes. But
+the patent bar the CCPA (Court of Customs and Patent Appeals)
+thanks you for any uncertainty as they, at least, stand to gain
+from any trouble.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+From: James A. Woods <jaw@eos.arc.nasa.gov>
+Subject: Re: Looking for C source for RSA (actually 'compress' patents)
+
+ In article <2042@eos.UUCP> you write:
+ >The concept is still exotic for those who learned in school the rule of thumb
+ >that one may patent "apparatus" but not an "idea".
+
+A rule of thumb that has never been completely valid, as any chemical
+engineer can tell you. (Chemical processes were among the earliest patents,
+as I recall.)
+
+ ah yes -- i date myself when relaying out-of-date advice from elderly
+ attorneys who don't even specialize in patents. one other interesting
+ class of patents include the output of optical lens design programs,
+ which yield formulae which can then fairly directly can be molded
+ into glass. although there are restrictions on patenting equations,
+ the "embedded systems" seem to fly past the legal gauntlets.
+
+ anyway, I'm still learning about intellectual property law after
+ several conversations from a Unisys (nee sperry) lawyer re 'compress'.
+
+ it's more complicated than this, but they're letting (oral
+ communication only) software versions of 'compress' slide
+ as far as licensing fees go. this includes 'arc', 'stuffit',
+ and other commercial wrappers for 'compress'. yet they are
+ signing up licensees for hardware chips. Hewlett-Packard
+ supposedly has an active vlsi project, and Unisys has
+ board-level LZW-based tape controllers. (to build LZW into
+ a disk controller would be strange, as you'd have to build
+ in a filesystem too!)
+
+ it's byzantine
+ that Unisys is in a tiff with HP regarding the patents,
+ after discovering some sort of "compress" button on some
+ HP terminal product. why? well, professor Abraham Lempel jumped
+ from being department chairman of computer science at technion in
+ Israel to sperry (where he got the first patent), but then to work
+ at Hewlett-Packard on sabbatical. the second Welch patent
+ is only weakly derivative of the first, so they want chip
+ licenses and HP relented. however, everyone agrees something
+ like the current Unix implementation is the way to go with
+ software, so HP (and UCB) long ago asked spencer Thomas and i to sign
+ off on copyright permission (although they didn't need to, it being pd).
+ Lempel, HP, and Unisys grumbles they can't make money off the
+ software since a good free implementation (not the best --
+ i have more ideas!) escaped via Usenet. (Lempel's own pascal
+ code was apparently horribly slow.)
+ i don't follow the IBM 'arc' legal bickering; my impression
+ is that the pc folks are making money off the archiver/wrapper
+ look/feel of the thing [if ms-dos can be said to have a look and feel].
+
+ now where is telebit with the compress firmware? in a limbo
+ netherworld, probably, with sperry still welcoming outfits
+ to sign patent licenses, a common tactic to bring other small fry
+ into the fold. the guy who crammed 12-bit compress into the modem
+ there left. also what is transpiring with 'compress' and sys 5 rel 4?
+ beats me, but if sperry got a hold of them on these issues,
+ at&t would likely re-implement another algorithm if they
+ thought 'compress' infringes. needful to say, i don't think
+ it does after the above mentioned legal conversation.
+ my own beliefs on whether algorithms should be patentable at all
+ change with the weather. if the courts finally nail down
+ patent protection for algorithms, academic publication in
+ textbooks will be somewhat at odds with the engineering world,
+ where the textbook codes will simply be a big tease to get
+ money into the patent holder coffers...
+
+ oh, if you implement LZW from the patent, you won't get
+ good rates because it doesn't mention adaptive table reset,
+ lack thereof being *the* serious deficiency of Thomas' first version.
+
+ now i know that patent law generally protects against independent
+ re-invention (like the 'xor' hash function pleasantly mentioned
+ in the patent [but not the paper]).
+ but the upshot is that if anyone ever wanted to sue us,
+ we're partially covered with
+ independently-developed twists, plus the fact that some of us work
+ in a bureaucratic morass (as contractor to a public agency in my case).
+
+ quite a mess, huh? I've wanted to tell someone this stuff
+ for a long time, for posterity if nothing else.
+
+james
+
diff --git a/file_cmds/compress/doc/README b/file_cmds/compress/doc/README
new file mode 100644
index 0000000..0828cdf
--- /dev/null
+++ b/file_cmds/compress/doc/README
@@ -0,0 +1,284 @@
+
+ @(#)README 8.1 (Berkeley) 6/9/93
+ $FreeBSD: src/usr.bin/compress/doc/README,v 1.3 2002/12/30 21:18:11 schweikh Exp $
+
+Compress version 4.0 improvements over 3.0:
+ o compress() speedup (10-50%) by changing division hash to xor
+ o decompress() speedup (5-10%)
+ o Memory requirements reduced (3-30%)
+ o Stack requirements reduced to less than 4kb
+ o Removed 'Big+Fast' compress code (FBITS) because of compress speedup
+ o Portability mods for Z8000 and PC/XT (but not zeus 3.2)
+ o Default to 'quiet' mode
+ o Unification of 'force' flags
+ o Manual page overhaul
+ o Portability enhancement for M_XENIX
+ o Removed text on #else and #endif
+ o Added "-V" switch to print version and options
+ o Added #defines for SIGNED_COMPARE_SLOW
+ o Added Makefile and "usermem" program
+ o Removed all floating point computations
+ o New programs: [deleted]
+
+The "usermem" script attempts to determine the maximum process size. Some
+editing of the script may be necessary (see the comments). [It should work
+fine on 4.3 BSD.] If you can't get it to work at all, just create file
+"USERMEM" containing the maximum process size in decimal.
+
+The following preprocessor symbols control the compilation of "compress.c":
+
+ o USERMEM Maximum process memory on the system
+ o SACREDMEM Amount to reserve for other processes
+ o SIGNED_COMPARE_SLOW Unsigned compare instructions are faster
+ o NO_UCHAR Don't use "unsigned char" types
+ o BITS Overrules default set by USERMEM-SACREDMEM
+ o vax Generate inline assembler
+ o interdata Defines SIGNED_COMPARE_SLOW
+ o M_XENIX Makes arrays < 65536 bytes each
+ o pdp11 BITS=12, NO_UCHAR
+ o z8000 BITS=12
+ o pcxt BITS=12
+ o BSD4_2 Allow long filenames ( > 14 characters) &
+ Call setlinebuf(stderr)
+
+The difference "usermem-sacredmem" determines the maximum BITS that can be
+specified with the "-b" flag.
+
+memory: at least BITS
+------ -- ----- ----
+ 433,484 16
+ 229,600 15
+ 127,536 14
+ 73,464 13
+ 0 12
+
+The default is BITS=16.
+
+The maximum bits can be overruled by specifying "-DBITS=bits" at
+compilation time.
+
+WARNING: files compressed on a large machine with more bits than allowed by
+a version of compress on a smaller machine cannot be decompressed! Use the
+"-b12" flag to generate a file on a large machine that can be uncompressed
+on a 16-bit machine.
+
+The output of compress 4.0 is fully compatible with that of compress 3.0.
+In other words, the output of compress 4.0 may be fed into uncompress 3.0 or
+the output of compress 3.0 may be fed into uncompress 4.0.
+
+The output of compress 4.0 not compatible with that of
+compress 2.0. However, compress 4.0 still accepts the output of
+compress 2.0. To generate output that is compatible with compress
+2.0, use the undocumented "-C" flag.
+
+ -from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85
+--------------------------------
+
+Enclosed is compress version 3.0 with the following changes:
+
+1. "Block" compression is performed. After the BITS run out, the
+ compression ratio is checked every so often. If it is decreasing,
+ the table is cleared and a new set of substrings are generated.
+
+ This makes the output of compress 3.0 not compatible with that of
+ compress 2.0. However, compress 3.0 still accepts the output of
+ compress 2.0. To generate output that is compatible with compress
+ 2.0, use the undocumented "-C" flag.
+
+2. A quiet "-q" flag has been added for use by the news system.
+
+3. The character chaining has been deleted and the program now uses
+ hashing. This improves the speed of the program, especially
+ during decompression. Other speed improvements have been made,
+ such as using putc() instead of fwrite().
+
+4. A large table is used on large machines when a relatively small
+ number of bits is specified. This saves much time when compressing
+ for a 16-bit machine on a 32-bit virtual machine. Note that the
+ speed improvement only occurs when the input file is > 30000
+ characters, and the -b BITS is less than or equal to the cutoff
+ described below.
+
+Most of these changes were made by James A. Woods (ames!jaw). Thank you
+James!
+
+To compile compress:
+
+ cc -O -DUSERMEM=usermem -o compress compress.c
+
+Where "usermem" is the amount of physical user memory available (in bytes).
+If any physical memory is to be reserved for other processes, put in
+"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved.
+
+The difference "usermem-sacredmem" determines the maximum BITS that can be
+specified, and the cutoff bits where the large+fast table is used.
+
+memory: at least BITS cutoff
+------ -- ----- ---- ------
+ 4,718,592 16 13
+ 2,621,440 16 12
+ 1,572,864 16 11
+ 1,048,576 16 10
+ 631,808 16 --
+ 329,728 15 --
+ 178,176 14 --
+ 99,328 13 --
+ 0 12 --
+
+The default memory size is 750,000 which gives a maximum BITS=16 and no
+large+fast table.
+
+The maximum bits can be overruled by specifying "-DBITS=bits" at
+compilation time.
+
+If your machine doesn't support unsigned characters, define "NO_UCHAR"
+when compiling.
+
+If your machine has "int" as 16-bits, define "SHORT_INT" when compiling.
+
+After compilation, move "compress" to a standard executable location, such
+as /usr/local. Then:
+ cd /usr/local
+ ln compress uncompress
+ ln compress zcat
+
+On machines that have a fixed stack size (such as Perkin-Elmer), set the
+stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer).
+
+Next, install the manual (compress.l).
+ cp compress.l /usr/man/manl
+ cd /usr/man/manl
+ ln compress.l uncompress.l
+ ln compress.l zcat.l
+
+ - or -
+
+ cp compress.l /usr/man/man1/compress.1
+ cd /usr/man/man1
+ ln compress.1 uncompress.1
+ ln compress.1 zcat.1
+
+ regards,
+ petsd!joe
+
+Here is a note from the net:
+
+>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985
+Path: ames!hplabs!pesnta!amd!turtlevax!ken
+From: ken@turtlevax.UUCP (Ken Turkowski)
+Newsgroups: net.sources
+Subject: Re: Compress release 3.0 : sample Makefile
+Organization: CADLINC, Inc. @ Menlo Park, CA
+
+In the compress 3.0 source recently posted to mod.sources, there is a
+#define variable which can be set for optimum performance on a machine
+with a large amount of memory. A program (usermem) to calculate the
+usable amount of physical user memory is enclosed, as well as a sample
+4.2BSD Vax Makefile for compress.
+
+Here is the README file from the previous version of compress (2.0):
+
+>Enclosed is compress.c version 2.0 with the following bugs fixed:
+>
+>1. The packed files produced by compress are different on different
+> machines and dependent on the vax sysgen option.
+> The bug was in the different byte/bit ordering on the
+> various machines. This has been fixed.
+>
+> This version is NOT compatible with the original vax posting
+> unless the '-DCOMPATIBLE' option is specified to the C
+> compiler. The original posting has a bug which I fixed,
+> causing incompatible files. I recommend you NOT to use this
+> option unless you already have a lot of packed files from
+> the original posting by Thomas.
+>2. The exit status is not well defined (on some machines) causing the
+> scripts to fail.
+> The exit status is now 0,1 or 2 and is documented in
+> compress.l.
+>3. The function getopt() is not available in all C libraries.
+> The function getopt() is no longer referenced by the
+> program.
+>4. Error status is not being checked on the fwrite() and fflush() calls.
+> Fixed.
+>
+>The following enhancements have been made:
+>
+>1. Added facilities of "compact" into the compress program. "Pack",
+> "Unpack", and "Pcat" are no longer required (no longer supplied).
+>2. Installed work around for C compiler bug with "-O".
+>3. Added a magic number header (\037\235). Put the bits specified
+> in the file.
+>4. Added "-f" flag to force overwrite of output file.
+>5. Added "-c" flag and "zcat" program. 'ln compress zcat' after you
+> compile.
+>6. The 'uncompress' script has been deleted; simply
+> 'ln compress uncompress' after you compile and it will work.
+>7. Removed extra bit masking for machines that support unsigned
+> characters. If your machine doesn't support unsigned characters,
+> define "NO_UCHAR" when compiling.
+>
+>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a
+>standard executable location, such as /usr/local. Then:
+> cd /usr/local
+> ln compress uncompress
+> ln compress zcat
+>
+>On machines that have a fixed stack size (such as Perkin-Elmer), set the
+>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer).
+>
+>Next, install the manual (compress.l).
+> cp compress.l /usr/man/manl - or -
+> cp compress.l /usr/man/man1/compress.1
+>
+>Here is the README that I sent with my first posting:
+>
+>>Enclosed is a modified version of compress.c, along with scripts to make it
+>>run identically to pack(1), unpack(1), and pcat(1). Here is what I
+>>(petsd!joe) and a colleague (petsd!peora!srd) did:
+>>
+>>1. Removed VAX dependencies.
+>>2. Changed the struct to separate arrays; saves mucho memory.
+>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.)
+>>4. Sorted the character next chain and changed the search to stop
+>>prematurely. This saves a lot on the execution time when compressing.
+>>
+>>This version is totally compatible with the original version. Even though
+>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit
+>>machine, due to the size of the arrays.
+>>
+>>Here is the README file from the original author:
+>>
+>>>Well, with all this discussion about file compression (for news batching
+>>>in particular) going around, I decided to implement the text compression
+>>>algorithm described in the June Computer magazine. The author claimed
+>>>blinding speed and good compression ratios. It's certainly faster than
+>>>compact (but, then, what wouldn't be), but it's also the same speed as
+>>>pack, and gets better compression than both of them. On 350K bytes of
+>>>Unix-wizards, compact took about 8 minutes of CPU, pack took about 80
+>>>seconds, and compress (herein) also took 80 seconds. But, compact and
+>>>pack got about 30% compression, whereas compress got over 50%. So, I
+>>>decided I had something, and that others might be interested, too.
+>>>
+>>>As is probably true of compact and pack (although I haven't checked),
+>>>the byte order within a word is probably relevant here, but as long as
+>>>you stay on a single machine type, you should be ok. (Can anybody
+>>>elucidate on this?) There are a couple of asm's in the code (extv and
+>>>insv instructions), so anyone porting it to another machine will have to
+>>>deal with this anyway (and could probably make it compatible with Vax
+>>>byte order at the same time). Anyway, I've linted the code (both with
+>>>and without -p), so it should run elsewhere. Note the longs in the
+>>>code, you can take these out if you reduce BITS to <= 15.
+>>>
+>>>Have fun, and as always, if you make good enhancements, or bug fixes,
+>>>I'd like to see them.
+>>>
+>>>=Spencer (thomas@utah-20, {harpo,hplabs,arizona}!utah-cs!thomas)
+>>
+>> regards,
+>> joe
+>>
+>>--
+>>Full-Name: Joseph M. Orost
+>>UUCP: ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe
+>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724
+>>Phone: (201) 870-5844
diff --git a/file_cmds/compress/doc/revision.log b/file_cmds/compress/doc/revision.log
new file mode 100644
index 0000000..04c96e6
--- /dev/null
+++ b/file_cmds/compress/doc/revision.log
@@ -0,0 +1,118 @@
+/* $FreeBSD: src/usr.bin/compress/doc/revision.log,v 1.5 2011/03/31 14:35:33 emaste Exp $ */
+
+/*
+ * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $
+ *
+ * Revision 4.0 85/07/30 12:50:00 joe
+ * Removed ferror() calls in output routine on every output except first.
+ * Prepared for release to the world.
+ *
+ * Revision 3.6 85/07/04 01:22:21 joe
+ * Remove much wasted storage by overlaying hash table with the tables
+ * used by decompress: tab_suffix[1<<BITS], stack[8000]. Updated USERMEM
+ * computations. Fixed dump_tab() DEBUG routine.
+ *
+ * Revision 3.5 85/06/30 20:47:21 jaw
+ * Change hash function to use exclusive-or. Rip out hash cache. These
+ * speedups render the megamemory version defunct, for now. Make decoder
+ * stack global. Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
+ *
+ * Revision 3.4 85/06/27 12:00:00 ken
+ * Get rid of all floating-point calculations by doing all compression ratio
+ * calculations in fixed point.
+ *
+ * Revision 3.3 85/06/24 21:53:24 joe
+ * Incorporate portability suggestion for M_XENIX. Got rid of text on #else
+ * and #endif lines. Cleaned up #ifdefs for vax and interdata.
+ *
+ * Revision 3.2 85/06/06 21:53:24 jaw
+ * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
+ * Default to "quiet" output (no compression statistics).
+ *
+ * Revision 3.1 85/05/12 18:56:13 jaw
+ * Integrate decompress() stack speedups (from early pointer mods by McKie).
+ * Repair multi-file USERMEM gaffe. Unify 'force' flags to mimic semantics
+ * of SVR2 'pack'. Streamline block-compress table clear logic. Increase
+ * output byte count by magic number size.
+ *
+ * Revision 3.0 84/11/27 11:50:00 petsd!joe
+ * Set HSIZE depending on BITS. Set BITS depending on USERMEM. Unrolled
+ * loops in clear routines. Added "-C" flag for 2.0 compatibility. Used
+ * unsigned compares on Perkin-Elmer. Fixed foreground check.
+ *
+ * Revision 2.7 84/11/16 19:35:39 ames!jaw
+ * Cache common hash codes based on input statistics; this improves
+ * performance for low-density raster images. Pass on #ifdef bundle
+ * from Turkowski.
+ *
+ * Revision 2.6 84/11/05 19:18:21 ames!jaw
+ * Vary size of hash tables to reduce time for small files.
+ * Tune PDP-11 hash function.
+ *
+ * Revision 2.5 84/10/30 20:15:14 ames!jaw
+ * Junk chaining; replace with the simpler (and, on the VAX, faster)
+ * double hashing, discussed within. Make block compression standard.
+ *
+ * Revision 2.4 84/10/16 11:11:11 ames!jaw
+ * Introduce adaptive reset for block compression, to boost the rate
+ * another several percent. (See mailing list notes.)
+ *
+ * Revision 2.3 84/09/22 22:00:00 petsd!joe
+ * Implemented "-B" block compress. Implemented REVERSE sorting of tab_next.
+ * Bug fix for last bits. Changed fwrite to putchar loop everywhere.
+ *
+ * Revision 2.2 84/09/18 14:12:21 ames!jaw
+ * Fold in news changes, small machine typedef from thomas,
+ * #ifdef interdata from joe.
+ *
+ * Revision 2.1 84/09/10 12:34:56 ames!jaw
+ * Configured fast table lookup for 32-bit machines.
+ * This cuts user time in half for b <= FBITS, and is useful for news batching
+ * from VAX to PDP sites. Also sped up decompress() [fwrite->putc] and
+ * added signal catcher [plus beef in writeerr()] to delete effluvia.
+ *
+ * Revision 2.0 84/08/28 22:00:00 petsd!joe
+ * Add check for foreground before prompting user. Insert maxbits into
+ * compressed file. Force file being uncompressed to end with ".Z".
+ * Added "-c" flag and "zcat". Prepared for release.
+ *
+ * Revision 1.10 84/08/24 18:28:00 turtlevax!ken
+ * Will only compress regular files (no directories), added a magic number
+ * header (plus an undocumented -n flag to handle old files without headers),
+ * added -f flag to force overwriting of possibly existing destination file,
+ * otherwise the user is prompted for a response. Will tack on a .Z to a
+ * filename if it doesn't have one when decompressing. Will only replace
+ * file if it was compressed.
+ *
+ * Revision 1.9 84/08/16 17:28:00 turtlevax!ken
+ * Removed scanargs(), getopt(), added .Z extension and unlimited number of
+ * filenames to compress. Flags may be clustered (-Ddvb12) or separated
+ * (-D -d -v -b 12), or combination thereof. Modes and other status is
+ * copied with copystat(). -O bug for 4.2 seems to have disappeared with
+ * 1.8.
+ *
+ * Revision 1.8 84/08/09 23:15:00 joe
+ * Made it compatible with vax version, installed jim's fixes/enhancements
+ *
+ * Revision 1.6 84/08/01 22:08:00 joe
+ * Sped up algorithm significantly by sorting the compress chain.
+ *
+ * Revision 1.5 84/07/13 13:11:00 srd
+ * Added C version of vax asm routines. Changed structure to arrays to
+ * save much memory. Do unsigned compares where possible (faster on
+ * Perkin-Elmer)
+ *
+ * Revision 1.4 84/07/05 03:11:11 thomas
+ * Clean up the code a little and lint it. (Lint complains about all
+ * the regs used in the asm, but I'm not going to "fix" this.)
+ *
+ * Revision 1.3 84/07/05 02:06:54 thomas
+ * Minor fixes.
+ *
+ * Revision 1.2 84/07/05 00:27:27 thomas
+ * Add variable bit length output.
+ *
+ */
+
+static char rcs_ident[] =
+ "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
diff --git a/file_cmds/compress/uncompress.1 b/file_cmds/compress/uncompress.1
new file mode 100644
index 0000000..208c3ce
--- /dev/null
+++ b/file_cmds/compress/uncompress.1
@@ -0,0 +1 @@
+.so man1/compress.1 \ No newline at end of file
diff --git a/file_cmds/compress/zcat.sh b/file_cmds/compress/zcat.sh
new file mode 100644
index 0000000..6799f46
--- /dev/null
+++ b/file_cmds/compress/zcat.sh
@@ -0,0 +1,38 @@
+#!/bin/sh -
+# $NetBSD: zcat.sh,v 1.4 1995/03/26 19:54:37 glass Exp $
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)zcat.sh 8.1 (Berkeley) 6/6/93
+#
+
+uncompress -c $*
diff --git a/file_cmds/compress/zopen.3 b/file_cmds/compress/zopen.3
new file mode 100644
index 0000000..f6c7f34
--- /dev/null
+++ b/file_cmds/compress/zopen.3
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" @(#)zopen.3 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD: src/usr.bin/compress/zopen.3,v 1.9 2010/12/11 08:32:16 joel Exp $
+.\"
+.Dd June 9, 1993
+.Dt ZOPEN 3
+.Os
+.Sh NAME
+.Nm zopen
+.Nd compressed stream open function
+.Sh SYNOPSIS
+.Fd #include \&"zopen.h\&"
+.Ft FILE *
+.Fn zopen "const char *path" "const char *mode" "int bits"
+.Sh DESCRIPTION
+The
+.Fn zopen
+function
+opens the compressed file whose name is the string pointed to by
+.Fa path
+and associates a stream with it.
+.Pp
+The argument
+.Fa mode
+points to one of the following one-character strings:
+.Bl -tag -width indent
+.It Dq Li r
+Open compressed file for reading.
+The stream is positioned at the beginning of the file.
+.It Dq Li w
+Truncate file to zero length or create compressed file for writing.
+The stream is positioned at the beginning of the file.
+.El
+.Pp
+Any created files will have mode
+.Pf \\*q Dv S_IRUSR
+\&|
+.Dv S_IWUSR
+\&|
+.Dv S_IRGRP
+\&|
+.Dv S_IWGRP
+\&|
+.Dv S_IROTH
+\&|
+.Dv S_IWOTH Ns \\*q
+.Pq Li 0666 ,
+as modified by the process'
+umask value (see
+.Xr umask 2 ) .
+.Pp
+Files may only be read or written.
+Seek operations are not allowed.
+.Pp
+The
+.Fa bits
+argument, if non-zero, is set to the bits code limit.
+If zero, the default is 16.
+See
+.Xr compress 1
+for more information.
+.Sh RETURN VALUES
+Upon successful completion
+.Fn zopen
+returns a
+.Tn FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width [EINVAL]
+.It Bq Er EINVAL
+The
+.Fa mode
+or
+.Fa bits
+arguments specified to
+.Fn zopen
+were invalid.
+.It Bq Er EFTYPE
+The compressed file starts with an invalid header, or the compressed
+file is compressed with more bits than can be handled.
+.El
+.Pp
+The
+.Fn zopen
+function may also fail and set
+.Va errno
+for any of the errors specified for the routines
+.Xr fopen 3
+or
+.Xr funopen 3 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr fopen 3 ,
+.Xr funopen 3
+.Sh HISTORY
+The
+.Nm
+function
+first appeared in
+.Bx 4.4 .
+.Sh BUGS
+The
+.Fn zopen
+function
+may not be portable to systems other than
+.Bx .
diff --git a/file_cmds/compress/zopen.c b/file_cmds/compress/zopen.c
new file mode 100644
index 0000000..589ce52
--- /dev/null
+++ b/file_cmds/compress/zopen.c
@@ -0,0 +1,738 @@
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * 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.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/compress/zopen.c,v 1.17 2011/09/28 08:47:17 bz Exp $");
+
+/*-
+ * fcompress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * Compress authors:
+ * Spencer W. Thomas (decvax!utah-cs!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ *
+ * Cleaned up and converted to library returning I/O streams by
+ * Diomidis Spinellis <dds@doc.ic.ac.uk>.
+ *
+ * zopen(filename, mode, bits)
+ * Returns a FILE * that can be used for read or write. The modes
+ * supported are only "r" and "w". Seeking is not allowed. On
+ * reading the file is decompressed, on writing it is compressed.
+ * The output is compatible with compress(1) with 16 bit tables.
+ * Any file produced by compress(1) can be read.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "zopen.h"
+
+#define BITS 16 /* Default bits. */
+#define HSIZE 69001 /* 95% occupancy */
+
+/* A code_int must be able to hold 2**BITS values of type int, and also -1. */
+typedef long code_int;
+typedef long count_int;
+
+typedef u_char char_type;
+static char_type magic_header[] =
+ {'\037', '\235'}; /* 1F 9D */
+
+#define BIT_MASK 0x1f /* Defines for third byte of header. */
+#define BLOCK_MASK 0x80
+
+/*
+ * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
+ * a fourth header byte (for expansion).
+ */
+#define INIT_BITS 9 /* Initial number of bits/code. */
+
+#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
+
+struct s_zstate {
+ FILE *zs_fp; /* File stream for I/O */
+ char zs_mode; /* r or w */
+ enum {
+ S_START, S_MIDDLE, S_EOF
+ } zs_state; /* State of computation */
+ 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];
+ u_short zs_codetab [HSIZE];
+ code_int zs_hsize; /* For dynamic table sizing. */
+ code_int zs_free_ent; /* First unused entry. */
+ /*
+ * Block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+ int zs_block_compress;
+ int zs_clear_flg;
+ long zs_ratio;
+ count_int zs_checkpoint;
+ 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). */
+ char_type zs_buf[BITS];
+ union {
+ struct {
+ long zs_fcode;
+ code_int zs_ent;
+ code_int zs_hsize_reg;
+ int zs_hshift;
+ } w; /* Write parameters */
+ struct {
+ char_type *zs_stackp;
+ int zs_finchar;
+ code_int zs_code, zs_oldcode, zs_incode;
+ int zs_roffset, zs_size;
+ char_type zs_gbuf[BITS];
+ } r; /* Read parameters */
+ } u;
+};
+
+/* Definitions to retain old variable names */
+#define fp zs->zs_fp
+#define zmode zs->zs_mode
+#define state zs->zs_state
+#define n_bits zs->zs_n_bits
+#define maxbits zs->zs_maxbits
+#define maxcode zs->zs_maxcode
+#define maxmaxcode zs->zs_maxmaxcode
+#define htab zs->zs_htab
+#define codetab zs->zs_codetab
+#define hsize zs->zs_hsize
+#define free_ent zs->zs_free_ent
+#define block_compress zs->zs_block_compress
+#define clear_flg zs->zs_clear_flg
+#define ratio zs->zs_ratio
+#define checkpoint zs->zs_checkpoint
+#define offset zs->zs_offset
+#define in_count zs->zs_in_count
+#define bytes_out zs->zs_bytes_out
+#define out_count zs->zs_out_count
+#define buf zs->zs_buf
+#define fcode zs->u.w.zs_fcode
+#define hsize_reg zs->u.w.zs_hsize_reg
+#define ent zs->u.w.zs_ent
+#define hshift zs->u.w.zs_hshift
+#define stackp zs->u.r.zs_stackp
+#define finchar zs->u.r.zs_finchar
+#define code zs->u.r.zs_code
+#define oldcode zs->u.r.zs_oldcode
+#define incode zs->u.r.zs_incode
+#define roffset zs->u.r.zs_roffset
+#define size zs->u.r.zs_size
+#define gbuf zs->u.r.zs_gbuf
+
+/*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by decompress(). The tab_prefix table is the same size and type as
+ * the codetab. The tab_suffix table needs 2**BITS characters. We get this
+ * from the beginning of htab. The output stack uses the rest of htab, and
+ * contains characters. There is plenty of room for any possible stack
+ * (stack used to be 8000 characters).
+ */
+
+#define htabof(i) htab[i]
+#define codetabof(i) codetab[i]
+
+#define tab_prefixof(i) codetabof(i)
+#define tab_suffixof(i) ((char_type *)(htab))[i]
+#define de_stack ((char_type *)&tab_suffixof(1 << BITS))
+
+#define CHECK_GAP 10000 /* Ratio check interval. */
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+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",
+ * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
+ *
+ * Algorithm:
+ * Modified Lempel-Ziv method (LZW). Basically finds common
+ * substrings and replaces them with a variable size code. This is
+ * deterministic, and can be done on the fly. Thus, the decompression
+ * procedure needs no input table, but tracks the way the table was built.
+ */
+
+/*-
+ * compress write
+ *
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation. Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please direct
+ * questions about this implementation to ames!jaw.
+ */
+static int
+zwrite(void *cookie, const char *wbp, int num)
+{
+ code_int i;
+ int c, disp;
+ struct s_zstate *zs;
+ const u_char *bp;
+ u_char tmp;
+ int count;
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (const u_char *)wbp;
+ if (state == S_MIDDLE)
+ goto middle;
+ state = S_MIDDLE;
+
+ maxmaxcode = 1L << maxbits;
+ if (fwrite(magic_header,
+ sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header))
+ return (-1);
+ tmp = (u_char)((maxbits) | block_compress);
+ if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp))
+ return (-1);
+
+ offset = 0;
+ bytes_out = 3; /* Includes 3-byte header mojo. */
+ out_count = 0;
+ clear_flg = 0;
+ ratio = 0;
+ in_count = 1;
+ checkpoint = CHECK_GAP;
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ free_ent = ((block_compress) ? FIRST : 256);
+
+ ent = *bp++;
+ --count;
+
+ hshift = 0;
+ for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L)
+ hshift++;
+ hshift = 8 - hshift; /* Set hash code range bound. */
+
+ hsize_reg = hsize;
+ cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */
+
+middle: for (; count--;) {
+ c = *bp++;
+ in_count++;
+ fcode = (long)(((long)c << maxbits) + ent);
+ i = ((c << hshift) ^ ent); /* Xor hashing. */
+
+ if (htabof(i) == fcode) {
+ ent = codetabof(i);
+ continue;
+ } else if ((long)htabof(i) < 0) /* Empty slot. */
+ goto nomatch;
+ disp = hsize_reg - i; /* Secondary hash (after G. Knott). */
+ if (i == 0)
+ disp = 1;
+probe: if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (htabof(i) == fcode) {
+ ent = codetabof(i);
+ continue;
+ }
+ if ((long)htabof(i) >= 0)
+ goto probe;
+nomatch: if (output(zs, (code_int) ent) == -1)
+ return (-1);
+ out_count++;
+ ent = c;
+ if (free_ent < maxmaxcode) {
+ codetabof(i) = free_ent++; /* code -> hashtable */
+ htabof(i) = fcode;
+ } else if ((count_int)in_count >=
+ checkpoint && block_compress) {
+ if (cl_block(zs) == -1)
+ return (-1);
+ }
+ }
+ return (num);
+}
+
+static int
+zclose(void *cookie)
+{
+ struct s_zstate *zs;
+ int rval;
+
+ zs = cookie;
+ if (zmode == 'w') { /* Put out the final code. */
+ if (output(zs, (code_int) ent) == -1) {
+ (void)fclose(fp);
+ free(zs);
+ return (-1);
+ }
+ out_count++;
+ if (output(zs, (code_int) - 1) == -1) {
+ (void)fclose(fp);
+ free(zs);
+ return (-1);
+ }
+ }
+ rval = fclose(fp) == EOF ? -1 : 0;
+ free(zs);
+ return (rval);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static char_type lmask[9] =
+ {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
+static char_type rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output(struct s_zstate *zs, code_int ocode)
+{
+ int r_off;
+ u_int bits;
+ char_type *bp;
+
+ r_off = offset;
+ bits = n_bits;
+ bp = buf;
+ if (ocode >= 0) {
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+ /*
+ * Since ocode is always >= 8 bits, only need to mask the first
+ * hunk on the left.
+ */
+ *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]);
+ bp++;
+ bits -= (8 - r_off);
+ ocode >>= 8 - r_off;
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ *bp++ = ocode;
+ ocode >>= 8;
+ bits -= 8;
+ }
+ /* Last bits. */
+ if (bits)
+ *bp = ocode;
+ offset += n_bits;
+ if (offset == (n_bits << 3)) {
+ bp = buf;
+ bits = n_bits;
+ bytes_out += bits;
+ if (fwrite(bp, sizeof(char), bits, fp) != bits)
+ return (-1);
+// bp += bits;
+// bits = 0;
+ offset = 0;
+ }
+ /*
+ * If the next entry is going to be too big for the ocode size,
+ * then increase it, if possible.
+ */
+ if (free_ent > maxcode || (clear_flg > 0)) {
+ /*
+ * Write the whole buffer, because the input side won't
+ * discover the size increase until after it has read it.
+ */
+ if (offset > 0) {
+ if (fwrite(buf, 1, n_bits, fp) != n_bits)
+ return (-1);
+ bytes_out += n_bits;
+ }
+ offset = 0;
+
+ if (clear_flg) {
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ clear_flg = 0;
+ } else {
+ n_bits++;
+ if (n_bits == maxbits)
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ }
+ } else {
+ /* At EOF, write the rest of the buffer. */
+ if (offset > 0) {
+ offset = (offset + 7) / 8;
+ if (fwrite(buf, 1, offset, fp) != offset)
+ return (-1);
+ bytes_out += offset;
+ }
+ offset = 0;
+ }
+ return (0);
+}
+
+/*
+ * Decompress read. This routine adapts to the codes in the file building
+ * the "string" table on-the-fly; requiring no table to be stored in the
+ * compressed file. The tables used herein are shared with those of the
+ * compress() routine. See the definitions above.
+ */
+static int
+zread(void *cookie, char *rbp, int num)
+{
+ u_int count;
+ struct s_zstate *zs;
+ u_char *bp, header[3];
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)rbp;
+ switch (state) {
+ case S_START:
+ state = S_MIDDLE;
+ break;
+ case S_MIDDLE:
+ goto middle;
+ case S_EOF:
+ goto eof;
+ }
+
+ /* Check the magic number */
+ if (fread(header,
+ sizeof(char), sizeof(header), fp) != sizeof(header) ||
+ memcmp(header, magic_header, sizeof(magic_header)) != 0) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ maxbits = header[2]; /* Set -b from file. */
+ block_compress = maxbits & BLOCK_MASK;
+ maxbits &= BIT_MASK;
+ maxmaxcode = 1L << maxbits;
+ if (maxbits > BITS || maxbits < 12) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ /* As above, initialize the first 256 entries in the table. */
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ for (code = 255; code >= 0; code--) {
+ tab_prefixof(code) = 0;
+ tab_suffixof(code) = (char_type) code;
+ }
+ free_ent = block_compress ? FIRST : 256;
+
+ finchar = oldcode = getcode(zs);
+ if (oldcode == -1) /* EOF already? */
+ return (0); /* Get out of here */
+
+ /* First code must be 8 bits = char. */
+ *bp++ = (u_char)finchar;
+ count--;
+ stackp = de_stack;
+
+ while ((code = getcode(zs)) > -1) {
+
+ if ((code == CLEAR) && block_compress) {
+ for (code = 255; code >= 0; code--)
+ tab_prefixof(code) = 0;
+ clear_flg = 1;
+ free_ent = FIRST;
+ oldcode = -1;
+ continue;
+ }
+ incode = code;
+
+ /* Special case for kWkWk string. */
+ if (code >= free_ent) {
+ if (code > free_ent || oldcode == -1) {
+ /* Bad stream. */
+ errno = EINVAL;
+ return (-1);
+ }
+ *stackp++ = finchar;
+ code = oldcode;
+ }
+ /*
+ * The above condition ensures that code < free_ent.
+ * The construction of tab_prefixof in turn guarantees that
+ * each iteration decreases code and therefore stack usage is
+ * bound by 1 << BITS - 256.
+ */
+
+ /* Generate output characters in reverse order. */
+ while (code >= 256) {
+ *stackp++ = tab_suffixof(code);
+ code = tab_prefixof(code);
+ }
+ *stackp++ = finchar = tab_suffixof(code);
+
+ /* And put them out in forward order. */
+middle: do {
+ if (count-- == 0)
+ return (num);
+ *bp++ = *--stackp;
+ } while (stackp > de_stack);
+
+ /* Generate the new entry. */
+ if ((code = free_ent) < maxmaxcode && oldcode != -1) {
+ tab_prefixof(code) = (u_short) oldcode;
+ tab_suffixof(code) = finchar;
+ free_ent = code + 1;
+ }
+
+ /* Remember previous code. */
+ oldcode = incode;
+ }
+ state = S_EOF;
+eof: return (num - count);
+}
+
+/*-
+ * Read one code from the standard input. If EOF, return -1.
+ * Inputs:
+ * stdin
+ * Outputs:
+ * code or -1 is returned.
+ */
+static code_int
+getcode(struct s_zstate *zs)
+{
+ code_int gcode;
+ int r_off, bits;
+ char_type *bp;
+
+ bp = gbuf;
+ if (clear_flg > 0 || roffset >= size || free_ent > maxcode) {
+ /*
+ * If the next entry will be too big for the current gcode
+ * size, then we must increase the size. This implies reading
+ * a new buffer full, too.
+ */
+ if (free_ent > maxcode) {
+ n_bits++;
+ if (n_bits == maxbits) /* Won't get any bigger now. */
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ if (clear_flg > 0) {
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ clear_flg = 0;
+ }
+ size = fread(gbuf, 1, n_bits, fp);
+ if (size <= 0) /* End of file. */
+ return (-1);
+ roffset = 0;
+ /* Round size down to integral number of codes. */
+ size = (size << 3) - (n_bits - 1);
+ }
+ r_off = roffset;
+ bits = n_bits;
+
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+
+ /* Get first part (low order bits). */
+ gcode = (*bp++ >> r_off);
+ bits -= (8 - r_off);
+ r_off = 8 - r_off; /* Now, roffset into gcode word. */
+
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ gcode |= *bp++ << r_off;
+ r_off += 8;
+ bits -= 8;
+ }
+
+ /* High order bits. */
+ gcode |= (*bp & rmask[bits]) << r_off;
+ roffset += n_bits;
+
+ return (gcode);
+}
+
+static int
+cl_block(struct s_zstate *zs) /* Table clear for block compress. */
+{
+ long rat;
+
+ checkpoint = in_count + CHECK_GAP;
+
+ if (in_count > 0x007fffff) { /* Shift will overflow. */
+ rat = bytes_out >> 8;
+ if (rat == 0) /* Don't divide by zero. */
+ rat = 0x7fffffff;
+ else
+ rat = in_count / rat;
+ } else
+ rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */
+ if (rat > ratio)
+ ratio = rat;
+ else {
+ ratio = 0;
+ cl_hash(zs, (count_int) hsize);
+ free_ent = FIRST;
+ clear_flg = 1;
+ if (output(zs, (code_int) CLEAR) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+cl_hash(struct s_zstate *zs, count_int cl_hsize) /* Reset code table. */
+{
+ count_int *htab_p;
+ long i, m1;
+
+ m1 = -1;
+ htab_p = htab + cl_hsize;
+ i = cl_hsize - 16;
+ do { /* Might use Sys V memset(3) here. */
+ *(htab_p - 16) = m1;
+ *(htab_p - 15) = m1;
+ *(htab_p - 14) = m1;
+ *(htab_p - 13) = m1;
+ *(htab_p - 12) = m1;
+ *(htab_p - 11) = m1;
+ *(htab_p - 10) = m1;
+ *(htab_p - 9) = m1;
+ *(htab_p - 8) = m1;
+ *(htab_p - 7) = m1;
+ *(htab_p - 6) = m1;
+ *(htab_p - 5) = m1;
+ *(htab_p - 4) = m1;
+ *(htab_p - 3) = m1;
+ *(htab_p - 2) = m1;
+ *(htab_p - 1) = m1;
+ htab_p -= 16;
+ } while ((i -= 16) >= 0);
+ for (i += 16; i > 0; i--)
+ *--htab_p = m1;
+}
+
+FILE *
+zopen(const char *fname, const char *mode, int bits)
+{
+ struct s_zstate *zs;
+
+ if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0' ||
+ bits < 0 || bits > BITS) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL)
+ return (NULL);
+
+ maxbits = bits ? bits : BITS; /* User settable max # bits/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;
+ clear_flg = 0;
+ ratio = 0;
+ checkpoint = CHECK_GAP;
+ in_count = 1; /* Length of input. */
+ out_count = 0; /* # of codes output (for debugging). */
+ state = S_START;
+ roffset = 0;
+ size = 0;
+
+ /*
+ * Layering compress on top of stdio in order to provide buffering,
+ * and ensure that reads and write work with the data specified.
+ */
+ if ((fp = fopen(fname, mode)) == NULL) {
+ free(zs);
+ return (NULL);
+ }
+ switch (*mode) {
+ case 'r':
+ zmode = 'r';
+ return (funopen(zs, zread, NULL, NULL, zclose));
+ case 'w':
+ zmode = 'w';
+ return (funopen(zs, NULL, zwrite, NULL, zclose));
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
diff --git a/file_cmds/compress/zopen.h b/file_cmds/compress/zopen.h
new file mode 100644
index 0000000..8ad5691
--- /dev/null
+++ b/file_cmds/compress/zopen.h
@@ -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_ */
diff --git a/file_cmds/cp/cp.1 b/file_cmds/cp/cp.1
new file mode 100644
index 0000000..8c346c7
--- /dev/null
+++ b/file_cmds/cp/cp.1
@@ -0,0 +1,311 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" @(#)cp.1 8.3 (Berkeley) 4/18/94
+.\" $FreeBSD: src/bin/cp/cp.1,v 1.33 2005/02/25 00:40:46 trhodes Exp $
+.\"
+.Dd February 23, 2005
+.Dt CP 1
+.Os
+.Sh NAME
+.Nm cp
+.Nd copy files
+.Sh SYNOPSIS
+.Nm cp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl fi | n
+.Op Fl apvX
+.Ar source_file target_file
+.Nm cp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl fi | n
+.Op Fl apvX
+.Ar source_file ... target_directory
+.Sh DESCRIPTION
+In the first synopsis form, the
+.Nm cp
+utility copies the contents of the
+.Ar source_file
+to the
+.Ar target_file .
+In the second synopsis form,
+the contents of each named
+.Ar source_file
+is copied to the destination
+.Ar target_directory .
+The names of the files themselves are not changed.
+If
+.Nm cp
+detects an attempt to copy a file to itself, the copy will fail.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.It Fl a
+Same as
+.Fl pPR
+options. Preserves structure and attributes of files
+but not directory structure.
+.It Fl f
+.\"For each existing destination pathname, remove it and
+If the destination file cannot be opened, remove it and
+create a new file, without prompting for confirmation
+regardless of its permissions.
+(The
+.Fl f
+option overrides any previous
+.Fl n
+option.)
+.Pp
+The target file is not unlinked before the copy.
+Thus, any existing access rights will be retained.
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl i
+Cause
+.Nm cp
+to write a prompt to the standard error output before copying a file
+that would overwrite an existing file.
+If the response from the standard input begins with the character
+.Sq Li y
+or
+.Sq Li Y ,
+the file copy is attempted.
+(The
+.Fl i
+option overrides any previous
+.Fl n
+option.)
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl n
+Do not overwrite an existing file.
+(The
+.Fl n
+option overrides any previous
+.Fl f
+or
+.Fl i
+options.)
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl p
+Cause
+.Nm cp
+to preserve the following attributes of each source
+file in the copy: modification time, access time,
+file flags, file mode, user ID, and group ID, as allowed by permissions.
+Access Control Lists (ACLs) and Extended Attributes (EAs),
+including resource forks, will also be preserved.
+.Pp
+If the user ID and group ID cannot be preserved, no error message
+is displayed and the exit value is not altered.
+.Pp
+If the source file has its set-user-ID bit on and the user ID cannot
+be preserved, the set-user-ID bit is not preserved
+in the copy's permissions.
+If the source file has its set-group-ID bit on and the group ID cannot
+be preserved, the set-group-ID bit is not preserved
+in the copy's permissions.
+If the source file has both its set-user-ID and set-group-ID bits on,
+and either the user ID or group ID cannot be preserved, neither
+the set-user-ID nor set-group-ID bits are preserved in the copy's
+permissions.
+.It Fl R
+If
+.Ar source_file
+designates a directory,
+.Nm cp
+copies the directory and the entire subtree connected at that point.
+If the
+.Ar source_file
+ends in a
+.Pa / ,
+the contents of the directory are copied rather than the
+directory itself.
+This option also causes symbolic links to be copied, rather than
+indirected through, and for
+.Nm cp
+to create special files rather than copying them as normal files.
+Created directories have the same mode as the corresponding source
+directory, unmodified by the process' umask.
+.Pp
+In
+.Fl R
+mode,
+.Nm cp
+will continue copying even if errors are detected.
+.Pp
+Note that
+.Nm cp
+copies hard-linked files as separate files.
+If you need to preserve hard links, consider using
+.Xr tar 1 ,
+.Xr cpio 1 ,
+or
+.Xr pax 1
+instead.
+.It Fl v
+Cause
+.Nm cp
+to be verbose, showing files as they are copied.
+.It Fl X
+Do not copy Extended Attributes (EAs) or resource forks.
+.It Fl c
+copy files using clonefile(2)
+.El
+.Pp
+For each destination file that already exists, its contents are
+overwritten if permissions allow.
+Its mode, user ID, and group
+ID are unchanged unless the
+.Fl p
+option was specified.
+.Pp
+In the second synopsis form,
+.Ar target_directory
+must exist unless there is only one named
+.Ar source_file
+which is a directory and the
+.Fl R
+flag is specified.
+.Pp
+If the destination file does not exist, the mode of the source file is
+used as modified by the file mode creation mask
+.Pf ( Ic umask ,
+see
+.Xr csh 1 ) .
+If the source file has its set-user-ID bit on, that bit is removed
+unless both the source file and the destination file are owned by the
+same user.
+If the source file has its set-group-ID bit on, that bit is removed
+unless both the source file and the destination file are in the same
+group and the user is a member of that group.
+If both the set-user-ID and set-group-ID bits are set, all of the above
+conditions must be fulfilled or both bits are removed.
+.Pp
+Appropriate permissions are required for file creation or overwriting.
+.Pp
+Symbolic links are always followed unless the
+.Fl R
+flag is set, in which case symbolic links are not followed, by default.
+The
+.Fl H
+or
+.Fl L
+flags (in conjunction with the
+.Fl R
+flag) cause symbolic links to be followed as described above.
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+If
+.Nm cp
+receives a
+.Dv SIGINFO
+(see the
+.Cm status
+argument for
+.Xr stty 1 )
+signal, the current input and output file and the percentage complete
+will be written to the standard output.
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+Historic versions of the
+.Nm cp
+utility had a
+.Fl r
+option.
+This implementation supports that option;
+however, its use is strongly discouraged,
+as it does not correctly copy special files, symbolic links, or fifo's.
+.Pp
+The
+.Fl v
+and
+.Fl n
+options are non-standard and their use in scripts is not recommended.
+.Sh LEGACY DESCRIPTION
+In legacy mode,
+.Fl f
+will override
+.Fl i .
+Also, under the
+.Fl f
+option, the target file is always unlinked before the copy.
+Thus, new access rights will always be set.
+.Pp
+In
+.Fl R
+mode, copying will terminate if an error is encountered.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.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 cp
+command is expected to be
+.St -p1003.2
+compatible.
+.Sh HISTORY
+A
+.Nm cp
+command appeared in
+.At v1 .
diff --git a/file_cmds/cp/cp.c b/file_cmds/cp/cp.c
new file mode 100644
index 0000000..c856fd9
--- /dev/null
+++ b/file_cmds/cp/cp.c
@@ -0,0 +1,571 @@
+/*-
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.52 2005/09/05 04:36:08 csjp Exp $");
+
+/*
+ * Cp copies source files to target files.
+ *
+ * The global PATH_T structure "to" always contains the path to the
+ * current target file. Since fts(3) does not change directories,
+ * this path can be either absolute or dot-relative.
+ *
+ * The basic algorithm is to initialize "to" and use fts(3) to traverse
+ * the file hierarchy rooted in the argument list. A trivial case is the
+ * case of 'cp file1 file2'. The more interesting case is the case of
+ * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
+ * path (relative to the root of the traversal) is appended to dir (stored
+ * in "to") to form the final target path.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+#include <copyfile.h>
+#include <get_compat.h>
+#else /* !__APPLE__ */
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "extern.h"
+
+#define STRIP_TRAILING_SLASH(p) { \
+ while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
+ *--(p).p_end = 0; \
+}
+
+static char emptystring[] = "";
+
+PATH_T to = { to.p_path, emptystring, "" };
+
+int fflag, iflag, nflag, pflag, vflag;
+#ifdef __APPLE__
+int Xflag;
+#endif /* __APPLE__ */
+static int Rflag, rflag;
+ int cflag = 0;
+volatile sig_atomic_t info;
+
+enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
+
+static int copy(char *[], enum op, int);
+static void siginfo(int __unused);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat to_stat, tmp_stat;
+ enum op type;
+ int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
+ char *target;
+
+ Hflag = Lflag = Pflag = 0;
+ while ((ch = getopt(argc, argv, "cHLPRXafinprv")) != -1)
+ switch (ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'X':
+ Xflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ /* 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;
+ if (COMPAT_MODE("bin/cp", "unix2003"))
+ nflag = 0; /* reset nflag, but not fflag */
+ else
+ fflag = nflag = 0;
+ break;
+ case 'n':
+ nflag = 1;
+ fflag = iflag = 0;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'a':
+ pflag = 1;
+ Pflag = 1;
+ Rflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage();
+
+ if (cflag && Xflag) {
+ errx(1, "the -c and -X options may not be specified together");
+ }
+
+ fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
+ if (rflag) {
+ if (Rflag)
+ errx(1,
+ "the -R and -r options may not be specified together.");
+ if (Hflag || Lflag || Pflag)
+ errx(1,
+ "the -H, -L, and -P options may not be specified with the -r option.");
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ if (Rflag) {
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
+ }
+ (void)signal(SIGINFO, siginfo);
+
+ /* Save the target base in "to". */
+ target = argv[--argc];
+ if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
+ errx(1, "%s: name too long", target);
+ to.p_end = to.p_path + strlen(to.p_path);
+ if (to.p_path == to.p_end) {
+ *to.p_end++ = '.';
+ *to.p_end = 0;
+ }
+ have_trailing_slash = (to.p_end[-1] == '/');
+ if (have_trailing_slash)
+ STRIP_TRAILING_SLASH(to);
+ to.target_end = to.p_end;
+
+ /* Set end of argument list for fts(3). */
+ argv[argc] = NULL;
+
+ /*
+ * Cp has two distinct cases:
+ *
+ * cp [-R] source target
+ * cp [-R] source1 ... sourceN directory
+ *
+ * In both cases, source can be either a file or a directory.
+ *
+ * In (1), the target becomes a copy of the source. That is, if the
+ * source is a file, the target will be a file, and likewise for
+ * directories.
+ *
+ * In (2), the real target is not directory, but "directory/source".
+ */
+ r = stat(to.p_path, &to_stat);
+ if (r == -1 && errno != ENOENT)
+ err(1, "%s", to.p_path);
+ if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
+ /*
+ * Case (1). Target is not a directory.
+ */
+ if (argc > 1) {
+ usage();
+ exit(1);
+ }
+ /*
+ * Need to detect the case:
+ * cp -R dir foo
+ * Where dir is a directory and foo does not exist, where
+ * we want pathname concatenations turned on but not for
+ * the initial mkdir().
+ */
+ if (r == -1) {
+ if (rflag || (Rflag && (Lflag || Hflag)))
+ stat(*argv, &tmp_stat);
+ else
+ lstat(*argv, &tmp_stat);
+
+ if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
+ type = DIR_TO_DNE;
+ else
+ type = FILE_TO_FILE;
+ } else
+ type = FILE_TO_FILE;
+
+ if (have_trailing_slash && type == FILE_TO_FILE) {
+ if (r == -1)
+ errx(1, "directory %s does not exist",
+ to.p_path);
+ else
+ errx(1, "%s is not a directory", to.p_path);
+ }
+ } else
+ /*
+ * Case (2). Target is a directory.
+ */
+ type = FILE_TO_DIR;
+
+ exit (copy(argv, type, fts_options));
+}
+
+static int
+copy(char *argv[], enum op type, int fts_options)
+{
+ struct stat to_stat;
+ FTS *ftsp;
+ FTSENT *curr;
+ int base = 0, dne, badcp, rval;
+ size_t nlen;
+ char *p, *target_mid;
+ mode_t mask, mode;
+
+ /*
+ * Keep an inverted copy of the umask, for use in correcting
+ * permissions on created directories when not using -p.
+ */
+ mask = ~umask(0777);
+ umask(~mask);
+
+ if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
+ err(1, "fts_open");
+ for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) {
+ switch (curr->fts_info) {
+ case FTS_NS:
+ case FTS_DNR:
+ case FTS_ERR:
+ warnx("%s: %s",
+ curr->fts_path, strerror(curr->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_DC: /* Warn, continue. */
+ warnx("%s: directory causes a cycle", curr->fts_path);
+ rval = 1;
+ continue;
+ default:
+ ;
+ }
+#ifdef __APPLE__
+
+#ifdef __clang__
+#pragma clang diagnostic push
+/* clang doesn't like fts_name[1], but we know better... */
+#pragma clang diagnostic ignored "-Warray-bounds"
+#endif
+ /* Skip ._<file> when using copyfile and <file> exists */
+ if ((pflag || !Xflag) && (curr->fts_level != FTS_ROOTLEVEL) &&
+ (curr->fts_namelen > 2) && /* ._\0 is not AppleDouble */
+ (curr->fts_name[0] == '.') && (curr->fts_name[1] == '_')) {
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ struct stat statbuf;
+ char path[PATH_MAX];
+ char *p = strrchr(curr->fts_path, '/');
+ if (p) {
+ size_t s = p + 2 - curr->fts_path;
+ if (s > sizeof(path)) s = sizeof(path);
+ strlcpy(path, curr->fts_path, s);
+ strlcat(path, curr->fts_name+2, sizeof(path));
+ } else {
+ strlcpy(path, curr->fts_name+2, sizeof(path));
+ }
+ if (!lstat(path, &statbuf)) {
+ continue;
+ }
+ }
+#endif /* __APPLE__ */
+ /*
+ * If we are in case (2) or (3) above, we need to append the
+ * source name to the target name.
+ */
+ if (type != FILE_TO_FILE) {
+ /*
+ * Need to remember the roots of traversals to create
+ * correct pathnames. If there's a directory being
+ * copied to a non-existent directory, e.g.
+ * cp -R a/dir noexist
+ * the resulting path name should be noexist/foo, not
+ * noexist/dir/foo (where foo is a file in dir), which
+ * is the case where the target exists.
+ *
+ * Also, check for "..". This is for correct path
+ * concatenation for paths ending in "..", e.g.
+ * cp -R .. /tmp
+ * Paths ending in ".." are changed to ".". This is
+ * tricky, but seems the easiest way to fix the problem.
+ *
+ * XXX
+ * Since the first level MUST be FTS_ROOTLEVEL, base
+ * is always initialized.
+ */
+ if (curr->fts_level == FTS_ROOTLEVEL) {
+ if (type != DIR_TO_DNE) {
+ p = strrchr(curr->fts_path, '/');
+ base = (p == NULL) ? 0 :
+ (int)(p - curr->fts_path + 1);
+
+ if (!strcmp(&curr->fts_path[base],
+ ".."))
+ base += 1;
+ } else
+ base = curr->fts_pathlen;
+ }
+
+ p = &curr->fts_path[base];
+ nlen = curr->fts_pathlen - base;
+ target_mid = to.target_end;
+ if (*p != '/' && target_mid[-1] != '/')
+ *target_mid++ = '/';
+ *target_mid = 0;
+ if (target_mid - to.p_path + nlen >= PATH_MAX) {
+ warnx("%s%s: name too long (not copied)",
+ to.p_path, p);
+ rval = 1;
+ continue;
+ }
+ (void)strncat(target_mid, p, nlen);
+ to.p_end = target_mid + nlen;
+ *to.p_end = 0;
+ STRIP_TRAILING_SLASH(to);
+ }
+
+ if (curr->fts_info == FTS_DP) {
+ /*
+ * We are nearly finished with this directory. If we
+ * didn't actually copy it, or otherwise don't need to
+ * change its attributes, then we are done.
+ */
+ if (!curr->fts_number)
+ continue;
+ /*
+ * If -p is in effect, set all the attributes.
+ * Otherwise, set the correct permissions, limited
+ * by the umask. Optimise by avoiding a chmod()
+ * if possible (which is usually the case if we
+ * made the directory). Note that mkdir() does not
+ * honour setuid, setgid and sticky bits, but we
+ * normally want to preserve them on directories.
+ */
+ if (pflag) {
+ if (setfile(curr->fts_statp, -1))
+ rval = 1;
+#ifdef __APPLE__
+ /* setfile will fail if writeattr is denied */
+ if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_ACL)<0)
+ warn("%s: unable to copy ACL to %s", curr->fts_path, to.p_path);
+#else /* !__APPLE__ */
+ if (preserve_dir_acls(curr->fts_statp,
+ curr->fts_accpath, to.p_path) != 0)
+ rval = 1;
+#endif /* __APPLE__ */
+ } else {
+ mode = curr->fts_statp->st_mode;
+ if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) ||
+ ((mode | S_IRWXU) & mask) != (mode & mask))
+ if (chmod(to.p_path, mode & mask) != 0){
+ warn("chmod: %s", to.p_path);
+ rval = 1;
+ }
+ }
+ continue;
+ }
+
+ /* Not an error but need to remember it happened */
+ if (stat(to.p_path, &to_stat) == -1)
+ dne = 1;
+ else {
+ if (to_stat.st_dev == curr->fts_statp->st_dev &&
+ to_stat.st_ino == curr->fts_statp->st_ino) {
+ warnx("%s and %s are identical (not copied).",
+ to.p_path, curr->fts_path);
+ rval = 1;
+ if (S_ISDIR(curr->fts_statp->st_mode))
+ (void)fts_set(ftsp, curr, FTS_SKIP);
+ continue;
+ }
+ if (!S_ISDIR(curr->fts_statp->st_mode) &&
+ S_ISDIR(to_stat.st_mode)) {
+ warnx("cannot overwrite directory %s with "
+ "non-directory %s",
+ to.p_path, curr->fts_path);
+ rval = 1;
+ continue;
+ }
+ dne = 0;
+ }
+
+ switch (curr->fts_statp->st_mode & S_IFMT) {
+ case S_IFLNK:
+ /* Catch special case of a non-dangling symlink */
+ if ((fts_options & FTS_LOGICAL) ||
+ ((fts_options & FTS_COMFOLLOW) &&
+ curr->fts_level == 0)) {
+ if (copy_file(curr, dne))
+ badcp = rval = 1;
+ } else {
+ if (copy_link(curr, !dne))
+ badcp = rval = 1;
+ }
+ break;
+ case S_IFDIR:
+ if (!Rflag && !rflag) {
+ warnx("%s is a directory (not copied).",
+ curr->fts_path);
+ (void)fts_set(ftsp, curr, FTS_SKIP);
+ badcp = rval = 1;
+ break;
+ }
+ /*
+ * If the directory doesn't exist, create the new
+ * one with the from file mode plus owner RWX bits,
+ * modified by the umask. Trade-off between being
+ * able to write the directory (if from directory is
+ * 555) and not causing a permissions race. If the
+ * umask blocks owner writes, we fail..
+ */
+ if (dne) {
+ if (mkdir(to.p_path,
+ curr->fts_statp->st_mode | S_IRWXU) < 0) {
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ warn("%s", to.p_path);
+ } else {
+ err(1, "%s", to.p_path);
+ }
+ }
+ } else if (!S_ISDIR(to_stat.st_mode)) {
+ errno = ENOTDIR;
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ warn("%s", to.p_path);
+ } else {
+ err(1, "%s", to.p_path);
+ }
+ }
+ /*
+ * Arrange to correct directory attributes later
+ * (in the post-order phase) if this is a new
+ * directory, or if the -p flag is in effect.
+ */
+ curr->fts_number = pflag || dne;
+#ifdef __APPLE__
+ if (!Xflag) {
+ if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_XATTR) < 0)
+ warn("%s: unable to copy extended attributes to %s", curr->fts_path, to.p_path);
+ /* ACL and mtime set in postorder traversal */
+ }
+#endif /* __APPLE__ */
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ if (Rflag) {
+ if (copy_special(curr->fts_statp, !dne))
+ badcp = rval = 1;
+ } else {
+ if (copy_file(curr, dne))
+ badcp = rval = 1;
+ }
+ break;
+ case S_IFIFO:
+ if (Rflag) {
+ if (copy_fifo(curr->fts_statp, !dne))
+ badcp = rval = 1;
+ } else {
+ if (copy_file(curr, dne))
+ badcp = rval = 1;
+ }
+ break;
+ default:
+ if (copy_file(curr, dne))
+ badcp = rval = 1;
+ break;
+ }
+ if (vflag && !badcp)
+ (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
+ }
+ if (errno)
+ err(1, "fts_read");
+ fts_close(ftsp);
+ return (rval);
+}
+
+static void
+siginfo(int sig __unused)
+{
+
+ info = 1;
+}
diff --git a/file_cmds/cp/extern.h b/file_cmds/cp/extern.h
new file mode 100644
index 0000000..521091d
--- /dev/null
+++ b/file_cmds/cp/extern.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1991, 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.
+ * 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.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/1/94
+ * $FreeBSD: src/bin/cp/extern.h,v 1.20 2005/09/05 04:36:08 csjp Exp $
+ */
+
+#ifndef _CP_EXTERN_H_
+#define _CP_EXTERN_H_
+
+typedef struct {
+ char *p_end; /* pointer to NULL at end of path */
+ char *target_end; /* pointer to end of target base */
+ char p_path[PATH_MAX]; /* pointer to the start of a path */
+} PATH_T;
+
+extern PATH_T to;
+extern int fflag, iflag, nflag, pflag, vflag;
+#ifdef __APPLE__
+extern int Xflag;
+#endif /* __APPLE__ */
+extern int cflag;
+extern volatile sig_atomic_t info;
+
+__BEGIN_DECLS
+int copy_fifo(struct stat *, int);
+int copy_file(const FTSENT *, int);
+int copy_link(const FTSENT *, int);
+int copy_special(struct stat *, int);
+int setfile(struct stat *, int);
+int preserve_dir_acls(struct stat *, char *, char *);
+int preserve_fd_acls(int, int);
+void usage(void);
+__END_DECLS
+
+#endif /* _CP_EXTERN_H_ */
diff --git a/file_cmds/cp/utils.c b/file_cmds/cp/utils.c
new file mode 100644
index 0000000..af05cc3
--- /dev/null
+++ b/file_cmds/cp/utils.c
@@ -0,0 +1,541 @@
+/*-
+ * Copyright (c) 1991, 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
+
+#include <sys/types.h>
+#include <sys/acl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
+#include <sys/mman.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <locale.h>
+
+#ifdef __APPLE__
+#include <sys/time.h>
+#include <copyfile.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <get_compat.h>
+#include <sys/attr.h>
+#include <sys/clonefile.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "extern.h"
+#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y))
+
+int
+copy_file(const FTSENT *entp, int dne)
+{
+ static char buf[MAXBSIZE];
+ struct stat *fs;
+ int ch, checkch, from_fd, rval, to_fd;
+ ssize_t rcount;
+ ssize_t wcount;
+ size_t wresid;
+ off_t wtotal;
+ char *bufp;
+ char resp[] = {'\0', '\0'};
+#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
+ char *p;
+#endif
+ mode_t mode = 0;
+ struct stat to_stat;
+
+ if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
+ warn("%s", entp->fts_path);
+ return (1);
+ }
+
+ fs = entp->fts_statp;
+
+ /*
+ * If the file exists and we're interactive, verify with the user.
+ * If the file DNE, set the mode to be the from file, minus setuid
+ * bits, modified by the umask; arguably wrong, but it makes copying
+ * executables work right and it's been that way forever. (The
+ * other choice is 666 or'ed with the execute bits on the from file
+ * modified by the umask.)
+ */
+ if (!dne) {
+#define YESNO "(y/n [n]) "
+ if (nflag) {
+ if (vflag)
+ printf("%s not overwritten\n", to.p_path);
+ (void)close(from_fd);
+ return (1);
+ } else if (iflag) {
+ (void)fprintf(stderr, "overwrite %s? %s",
+ to.p_path, YESNO);
+
+ /* Load user specified locale */
+ setlocale(LC_MESSAGES, "");
+
+ checkch = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+
+ /* only care about the first character */
+ resp[0] = checkch;
+
+ if (rpmatch(resp) != 1) {
+ (void)close(from_fd);
+ (void)fprintf(stderr, "not overwritten\n");
+ return (1);
+ }
+ }
+
+ if (cflag) {
+ (void)unlink(to.p_path);
+ int error = clonefile(entp->fts_path, to.p_path, 0);
+ if (error)
+ warn("%s: clonefile failed", to.p_path);
+ (void)close(from_fd);
+ return error == 0 ? 0 : 1;
+ }
+
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ /* first try to overwrite existing destination file name */
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+ if (to_fd == -1) {
+ if (fflag) {
+ /* Only if it fails remove file and create a new one */
+ (void)unlink(to.p_path);
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+ fs->st_mode & ~(S_ISUID | S_ISGID));
+ }
+ }
+ } else {
+ if (fflag) {
+ /* remove existing destination file name,
+ * create a new file */
+ (void)unlink(to.p_path);
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+ fs->st_mode & ~(S_ISUID | S_ISGID));
+ } else
+ /* overwrite existing destination file name */
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+ }
+ } else {
+
+ if (cflag) {
+ int error = clonefile(entp->fts_path, to.p_path, 0);
+ if (error)
+ warn("%s: clonefile failed", to.p_path);
+ (void)close(from_fd);
+ return error == 0 ? 0 : 1;
+ }
+
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+ fs->st_mode & ~(S_ISUID | S_ISGID));
+ }
+
+ if (to_fd == -1) {
+ warn("%s", to.p_path);
+ (void)close(from_fd);
+ return (1);
+ }
+
+ rval = 0;
+
+#ifdef __APPLE__
+ if (S_ISREG(fs->st_mode)) {
+ struct statfs sfs;
+
+ /*
+ * Pre-allocate blocks for the destination file if it
+ * resides on Xsan.
+ */
+ if (fstatfs(to_fd, &sfs) == 0 &&
+ strcmp(sfs.f_fstypename, "acfs") == 0) {
+ fstore_t fst;
+
+ fst.fst_flags = 0;
+ fst.fst_posmode = F_PEOFPOSMODE;
+ fst.fst_offset = 0;
+ fst.fst_length = fs->st_size;
+
+ (void) fcntl(to_fd, F_PREALLOCATE, &fst);
+ }
+ }
+#endif /* __APPLE__ */
+
+ if (fstat(to_fd, &to_stat) != -1) {
+ mode = to_stat.st_mode;
+ if ((mode & (S_IRWXG|S_IRWXO))
+ && fchmod(to_fd, mode & ~(S_IRWXG|S_IRWXO))) {
+ if (errno != EPERM) /* we have write access but do not own the file */
+ warn("%s: fchmod failed", to.p_path);
+ mode = 0;
+ }
+ } else {
+ warn("%s", to.p_path);
+ }
+ /*
+ * Mmap and write if less than 8M (the limit is so we don't totally
+ * trash memory on big files. This is really a minor hack, but it
+ * wins some CPU back.
+ */
+#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
+ if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
+ fs->st_size <= 8 * 1048576) {
+ if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
+ MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
+ warn("%s", entp->fts_path);
+ rval = 1;
+ } else {
+ wtotal = 0;
+ for (bufp = p, wresid = fs->st_size; ;
+ bufp += wcount, wresid -= (size_t)wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ wtotal += wcount;
+ if (info) {
+ info = 0;
+ (void)fprintf(stderr,
+ "%s -> %s %3d%%\n",
+ entp->fts_path, to.p_path,
+ cp_pct(wtotal, fs->st_size));
+
+ }
+ if (wcount >= (ssize_t)wresid || wcount <= 0)
+ break;
+ }
+ if (wcount != (ssize_t)wresid) {
+ warn("%s", to.p_path);
+ rval = 1;
+ }
+ /* Some systems don't unmap on close(2). */
+ if (munmap(p, fs->st_size) < 0) {
+ warn("%s", entp->fts_path);
+ rval = 1;
+ }
+ }
+ } else
+#endif
+ {
+ wtotal = 0;
+ while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ for (bufp = buf, wresid = rcount; ;
+ bufp += wcount, wresid -= wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ wtotal += wcount;
+ if (info) {
+ info = 0;
+ (void)fprintf(stderr,
+ "%s -> %s %3d%%\n",
+ entp->fts_path, to.p_path,
+ cp_pct(wtotal, fs->st_size));
+
+ }
+ if (wcount >= (ssize_t)wresid || wcount <= 0)
+ break;
+ }
+ if (wcount != (ssize_t)wresid) {
+ warn("%s", to.p_path);
+ rval = 1;
+ break;
+ }
+ }
+ if (rcount < 0) {
+ warn("%s", entp->fts_path);
+ rval = 1;
+ }
+ }
+
+ /*
+ * Don't remove the target even after an error. The target might
+ * not be a regular file, or its attributes might be important,
+ * or its contents might be irreplaceable. It would only be safe
+ * to remove it if we created it and its length is 0.
+ */
+ if (mode != 0)
+ if (fchmod(to_fd, mode))
+ warn("%s: fchmod failed", to.p_path);
+#ifdef __APPLE__
+ /* do these before setfile in case copyfile changes mtime */
+ if (!Xflag && S_ISREG(fs->st_mode)) { /* skip devices, etc */
+ if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0)
+ warn("%s: could not copy extended attributes to %s", entp->fts_path, to.p_path);
+ }
+ if (pflag && setfile(fs, to_fd))
+ rval = 1;
+ if (pflag) {
+ /* If this ACL denies writeattr then setfile will fail... */
+ if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0)
+ warn("%s: could not copy ACL to %s", entp->fts_path, to.p_path);
+ }
+#else /* !__APPLE__ */
+ if (pflag && setfile(fs, to_fd))
+ rval = 1;
+ if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
+ rval = 1;
+#endif /* __APPLE__ */
+ (void)close(from_fd);
+ if (close(to_fd)) {
+ warn("%s", to.p_path);
+ rval = 1;
+ }
+ return (rval);
+}
+
+int
+copy_link(const FTSENT *p, int exists)
+{
+ ssize_t len;
+ char llink[PATH_MAX];
+
+ if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
+ warn("readlink: %s", p->fts_path);
+ return (1);
+ }
+ llink[len] = '\0';
+ if (exists && unlink(to.p_path)) {
+ warn("unlink: %s", to.p_path);
+ return (1);
+ }
+ if (symlink(llink, to.p_path)) {
+ warn("symlink: %s", llink);
+ return (1);
+ }
+#ifdef __APPLE__
+ if (!Xflag)
+ if (copyfile(p->fts_path, to.p_path, NULL, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC) <0)
+ warn("%s: could not copy extended attributes to %s",
+ p->fts_path, to.p_path);
+#endif
+ return (pflag ? setfile(p->fts_statp, -1) : 0);
+}
+
+int
+copy_fifo(struct stat *from_stat, int exists)
+{
+ if (exists && unlink(to.p_path)) {
+ warn("unlink: %s", to.p_path);
+ return (1);
+ }
+ if (mkfifo(to.p_path, from_stat->st_mode)) {
+ warn("mkfifo: %s", to.p_path);
+ return (1);
+ }
+ return (pflag ? setfile(from_stat, -1) : 0);
+}
+
+int
+copy_special(struct stat *from_stat, int exists)
+{
+ if (exists && unlink(to.p_path)) {
+ warn("unlink: %s", to.p_path);
+ return (1);
+ }
+ if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
+ warn("mknod: %s", to.p_path);
+ return (1);
+ }
+ return (pflag ? setfile(from_stat, -1) : 0);
+}
+
+int
+setfile(struct stat *fs, int fd)
+{
+ static struct timeval tv[2];
+ struct stat ts;
+ int rval, gotstat, islink, fdval;
+
+ rval = 0;
+ fdval = fd != -1;
+ islink = !fdval && S_ISLNK(fs->st_mode);
+ fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+ if (fdval ? futimes(fd, tv) : (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv))) {
+ warn("%sutimes: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ rval = 1;
+ }
+ if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) :
+ stat(to.p_path, &ts))) {
+ gotstat = 0;
+ } else {
+ gotstat = 1;
+ ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
+ * the mode; current BSD behavior is to remove all setuid bits on
+ * chown. If chown fails, lose setuid/setgid bits.
+ */
+ if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) {
+ if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ?
+ lchown(to.p_path, fs->st_uid, fs->st_gid) :
+ chown(to.p_path, fs->st_uid, fs->st_gid))) {
+ if (errno != EPERM) {
+ warn("%schown: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ rval = 1;
+ }
+ fs->st_mode &= ~(S_ISUID | S_ISGID);
+ }
+ }
+
+ if (!gotstat || fs->st_mode != ts.st_mode) {
+ if (fdval ? fchmod(fd, fs->st_mode) : (islink ?
+ lchmod(to.p_path, fs->st_mode) :
+ chmod(to.p_path, fs->st_mode))) {
+ warn("%schmod: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ rval = 1;
+ }
+ }
+
+ if (!gotstat || fs->st_flags != ts.st_flags) {
+ if (fdval ? fchflags(fd, fs->st_flags) : (islink ?
+ lchflags(to.p_path, fs->st_flags) :
+ chflags(to.p_path, fs->st_flags))) {
+ if (errno != EPERM) {
+ warn("%schflags: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ rval = 1;
+ }
+ }
+ }
+ return (rval);
+}
+
+#ifndef __APPLE__
+int
+preserve_fd_acls(int source_fd, int dest_fd)
+{
+ struct acl *aclp;
+ acl_t acl;
+
+ if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
+ fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
+ return (0);
+ acl = acl_get_fd(source_fd);
+ if (acl == NULL) {
+ warn("failed to get acl entries while setting %s", to.p_path);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclp->acl_cnt == 3)
+ return (0);
+ if (acl_set_fd(dest_fd, acl) < 0) {
+ warn("failed to set acl entries for %s", to.p_path);
+ return (1);
+ }
+ return (0);
+}
+
+int
+preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
+{
+ acl_t (*aclgetf)(const char *, acl_type_t);
+ int (*aclsetf)(const char *, acl_type_t, acl_t);
+ struct acl *aclp;
+ acl_t acl;
+
+ if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
+ pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
+ return (0);
+ /*
+ * If the file is a link we will not follow it
+ */
+ if (S_ISLNK(fs->st_mode)) {
+ aclgetf = acl_get_link_np;
+ aclsetf = acl_set_link_np;
+ } else {
+ aclgetf = acl_get_file;
+ aclsetf = acl_set_file;
+ }
+ /*
+ * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
+ * size ACL will be returned. So it is not safe to simply
+ * check the pointer to see if the default ACL is present.
+ */
+ acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
+ if (acl == NULL) {
+ warn("failed to get default acl entries on %s",
+ source_dir);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
+ ACL_TYPE_DEFAULT, acl) < 0) {
+ warn("failed to set default acl entries on %s",
+ dest_dir);
+ return (1);
+ }
+ acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
+ if (acl == NULL) {
+ warn("failed to get acl entries on %s", source_dir);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
+ warn("failed to set acl entries on %s", dest_dir);
+ return (1);
+ }
+ return (0);
+}
+#endif /* !__APPLE__ */
+
+void
+usage(void)
+{
+
+ if (COMPAT_MODE("bin/cp", "unix2003")) {
+ (void)fprintf(stderr, "%s\n%s\n",
+"usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file target_file",
+" cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file ... "
+"target_directory");
+ } else {
+ (void)fprintf(stderr, "%s\n%s\n",
+"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file target_file",
+" cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file ... "
+"target_directory");
+ }
+ exit(EX_USAGE);
+}
diff --git a/file_cmds/csh/strpct.c b/file_cmds/csh/strpct.c
new file mode 100644
index 0000000..ac3b32d
--- /dev/null
+++ b/file_cmds/csh/strpct.c
@@ -0,0 +1,99 @@
+/* $NetBSD: strpct.c,v 1.2 1998/05/08 18:43:54 fair Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Erik E. Fair
+ *
+ * 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.
+ */
+
+/*
+ * Calculate a percentage without resorting to floating point
+ * and return a pointer to a string
+ *
+ * "digits" is the number of digits past the decimal place you want
+ * (zero being the straight percentage with no decimals)
+ *
+ * Erik E. Fair <fair@clock.org>, May 8, 1997
+ */
+
+#include <sys/types.h>
+#include <machine/limits.h>
+
+#include <stdio.h>
+
+char * strpct __P((u_long, u_long, u_int));
+
+char *
+strpct(numerator, denominator, digits)
+ u_long numerator, denominator;
+ u_int digits;
+{
+ int i;
+ u_long result, factor;
+ static char percent[32];
+
+ /* I should check for digit overflow here, too XXX */
+ factor = 100L;
+ for(i = 0; i < digits; i++) {
+ factor *= 10;
+ }
+
+ /* watch out for overflow! */
+ if (numerator < (ULONG_MAX / factor)) {
+ numerator *= factor;
+ } else {
+ /* toss some of the bits of lesser significance */
+ denominator /= factor;
+ }
+
+ if (denominator == 0L)
+ denominator = 1L;
+
+ result = numerator / denominator;
+
+ if (digits == 0) {
+ (void) snprintf(percent, sizeof(percent), "%lu%%", result);
+ } else {
+ char fmt[32];
+
+ /* indirection to produce the right output format */
+ (void) snprintf(fmt, sizeof(fmt), "%%lu.%%0%ulu%%%%", digits);
+
+ factor /= 100L; /* undo initialization */
+
+ (void) snprintf(percent, sizeof(percent),
+ fmt, result / factor, result % factor);
+ }
+
+ return(percent);
+}
diff --git a/file_cmds/dd/args.c b/file_cmds/dd/args.c
new file mode 100644
index 0000000..463bd46
--- /dev/null
+++ b/file_cmds/dd/args.c
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/args.c,v 1.31 2002/02/22 20:51:00 markm Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static int c_arg(const void *, const void *);
+static int c_conv(const void *, const void *);
+static void f_bs(char *);
+static void f_cbs(char *);
+static void f_conv(char *);
+static void f_count(char *);
+static void f_files(char *);
+static void f_ibs(char *);
+static void f_if(char *);
+static void f_obs(char *);
+static void f_of(char *);
+static void f_seek(char *);
+static void f_skip(char *);
+static quad_t get_num(char *);
+static off_t get_offset(char *);
+
+static const struct arg {
+ const char *name;
+ void (*f)(char *);
+ u_int set, noset;
+} args[] = {
+ { "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_IBS },
+ { "if", f_if, C_IF, C_IF },
+ { "iseek", f_skip, C_SKIP, C_SKIP },
+ { "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 },
+ { "skip", f_skip, C_SKIP, C_SKIP },
+};
+
+static char *oper;
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void
+jcl(char **argv)
+{
+ struct arg *ap, tmp;
+ char *arg;
+
+ 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);
+ if ((arg = strchr(oper, '=')) == NULL)
+ errx(1, "unknown operand %s", oper);
+ *arg++ = '\0';
+ if (!*arg)
+ errx(1, "no value specified for %s", oper);
+ tmp.name = oper;
+ if (!(ap = (struct arg *)bsearch(&tmp, args,
+ sizeof(args)/sizeof(struct arg), sizeof(struct arg),
+ c_arg)))
+ errx(1, "unknown operand %s", tmp.name);
+ if (ddflags & ap->noset)
+ errx(1, "%s: illegal argument combination or already set",
+ tmp.name);
+ ddflags |= ap->set;
+ ap->f(arg);
+ }
+
+ /* Final sanity checks. */
+
+ if (ddflags & C_BS) {
+ /*
+ * Bs is turned off by any conversion -- we assume the user
+ * just wanted to set both the input and output block sizes
+ * and didn't want the bs semantics, so we don't warn.
+ */
+ if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+ C_UNBLOCK))
+ ddflags &= ~C_BS;
+
+ /* Bs supersedes ibs and obs. */
+ if (ddflags & C_BS && ddflags & (C_IBS | C_OBS))
+ warnx("bs supersedes ibs and obs");
+ }
+
+ /*
+ * Ascii/ebcdic and cbs implies block/unblock.
+ * Block/unblock requires cbs and vice-versa.
+ */
+ if (ddflags & (C_BLOCK | C_UNBLOCK)) {
+ if (!(ddflags & C_CBS))
+ errx(1, "record operations require cbs");
+ if (cbsz == 0)
+ errx(1, "cbs cannot be zero");
+ cfunc = ddflags & C_BLOCK ? block : unblock;
+ } else if (ddflags & C_CBS) {
+ if (ddflags & (C_ASCII | C_EBCDIC)) {
+ if (ddflags & C_ASCII) {
+ ddflags |= C_UNBLOCK;
+ cfunc = unblock;
+ } else {
+ ddflags |= C_BLOCK;
+ cfunc = block;
+ }
+ } else
+ errx(1, "cbs meaningless if not doing record operations");
+ } else
+ cfunc = def;
+
+ /*
+ * Bail out if the calculation of a file offset would overflow.
+ */
+ if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz)
+ errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX);
+}
+
+static int
+c_arg(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct arg *)a)->name,
+ ((const struct arg *)b)->name));
+}
+
+static void
+f_bs(char *arg)
+{
+ quad_t res;
+
+ res = get_num(arg);
+ if (res < 1 || res > SSIZE_MAX)
+ errx(1, "bs must be between 1 and %ld", SSIZE_MAX);
+ in.dbsz = out.dbsz = (size_t)res;
+}
+
+static void
+f_cbs(char *arg)
+{
+ quad_t res;
+
+ res = get_num(arg);
+ if (res < 1 || res > SSIZE_MAX)
+ errx(1, "cbs must be between 1 and %ld", SSIZE_MAX);
+ cbsz = (size_t)res;
+}
+
+static void
+f_count(char *arg)
+{
+
+ cpy_cnt = get_num(arg);
+ if (cpy_cnt < 0)
+ errx(1, "count cannot be negative");
+ if (cpy_cnt == 0)
+ cpy_cnt = -1;
+}
+
+static void
+f_files(char *arg)
+{
+
+ files_cnt = get_num(arg);
+ if (files_cnt < 1)
+ errx(1, "files must be between 1 and %qd", QUAD_MAX);
+}
+
+static void
+f_ibs(char *arg)
+{
+ quad_t res;
+
+ if (!(ddflags & C_BS)) {
+ res = get_num(arg);
+ if (res < 1 || res > SSIZE_MAX)
+ errx(1, "ibs must be between 1 and %ld", SSIZE_MAX);
+ in.dbsz = (size_t)res;
+ }
+}
+
+static void
+f_if(char *arg)
+{
+
+ in.name = arg;
+}
+
+static void
+f_obs(char *arg)
+{
+ quad_t res;
+
+ if (!(ddflags & C_BS)) {
+ res = get_num(arg);
+ if (res < 1 || res > SSIZE_MAX)
+ errx(1, "obs must be between 1 and %ld", SSIZE_MAX);
+ out.dbsz = (size_t)res;
+ }
+}
+
+static void
+f_of(char *arg)
+{
+
+ out.name = arg;
+}
+
+static void
+f_seek(char *arg)
+{
+
+ out.offset = get_offset(arg);
+}
+
+static void
+f_skip(char *arg)
+{
+
+ in.offset = get_offset(arg);
+}
+
+static const struct conv {
+ const char *name;
+ u_int set, noset;
+ const u_char *ctab;
+} clist[] = {
+ { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX },
+ { "block", C_BLOCK, C_UNBLOCK, NULL },
+ { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX },
+ { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX },
+ { "lcase", C_LCASE, C_UCASE, NULL },
+ { "noerror", C_NOERROR, 0, NULL },
+ { "notrunc", C_NOTRUNC, 0, NULL },
+ { "oldascii", C_ASCII, C_EBCDIC, e2a_32V },
+ { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V },
+ { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V },
+ { "osync", C_OSYNC, C_BS, NULL },
+ { "sparse", C_SPARSE, 0, NULL },
+ { "swab", C_SWAB, 0, NULL },
+ { "sync", C_SYNC, 0, NULL },
+ { "ucase", C_UCASE, C_LCASE, NULL },
+ { "unblock", C_UNBLOCK, C_BLOCK, NULL },
+};
+
+static void
+f_conv(char *arg)
+{
+ struct conv *cp, tmp;
+
+ while (arg != NULL) {
+ tmp.name = strsep(&arg, ",");
+ cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv),
+ sizeof(struct conv), c_conv);
+ if (cp == NULL)
+ errx(1, "unknown conversion %s", tmp.name);
+ if (ddflags & cp->noset)
+ errx(1, "%s: illegal conversion combination", tmp.name);
+ ddflags |= cp->set;
+ if (cp->ctab)
+ ctab = cp->ctab;
+ }
+}
+
+static int
+c_conv(const void *a, const void *b)
+{
+
+ return (strcmp(((const struct conv *)a)->name,
+ ((const struct conv *)b)->name));
+}
+
+/*
+ * Convert an expression of the following forms to a quad_t.
+ * 1) A positive decimal number.
+ * 2) A positive decimal number followed by a b (mult by 512).
+ * 3) A positive decimal number followed by a k (mult by 1 << 10).
+ * 4) A positive decimal number followed by a m (mult by 1 << 20).
+ * 5) A positive decimal number followed by a g (mult by 1 << 30).
+ * 5) A positive decimal number followed by a w (mult by sizeof int).
+ * 6) Two or more positive decimal numbers (with/without [bkmgw])
+ * separated by x (also * for backwards compatibility), specifying
+ * the product of the indicated values.
+ */
+static quad_t
+get_num(char *val)
+{
+ quad_t num, t;
+ char *expr;
+
+ errno = 0;
+ num = strtoq(val, &expr, 0);
+ if (errno != 0) /* Overflow or underflow. */
+ err(1, "%s", oper);
+
+ if (expr == val) /* No valid digits. */
+ errx(1, "%s: illegal numeric value", oper);
+
+ switch (*expr) {
+ case 'b':
+ t = num;
+ num *= 512;
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'k':
+ t = num;
+ num *= 1 << 10;
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'm':
+ t = num;
+ num *= 1 << 20;
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'g':
+ t = num;
+ num *= 1 << 30;
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'w':
+ t = num;
+ num *= sizeof(int);
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ }
+
+ switch (*expr) {
+ case '\0':
+ break;
+ case '*': /* Backward compatible. */
+ case 'x':
+ t = num;
+ num *= get_num(expr + 1);
+ if (t <= num)
+ break;
+erange:
+ errx(1, "%s: %s", oper, strerror(ERANGE));
+ default:
+ errx(1, "%s: illegal numeric value", oper);
+ }
+ return (num);
+}
+
+static off_t
+get_offset(char *val)
+{
+ quad_t num;
+
+ num = get_num(val);
+ if (num > QUAD_MAX) /* XXX can't happen && quad_t != off_t */
+ errx(1, "%s: illegal offset", oper); /* Too big. */
+ return ((off_t)num);
+}
diff --git a/file_cmds/dd/conv.c b/file_cmds/dd/conv.c
new file mode 100644
index 0000000..59c7b01
--- /dev/null
+++ b/file_cmds/dd/conv.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/conv.c,v 1.16 2002/02/02 06:24:12 imp Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <string.h>
+
+#include "dd.h"
+#include "extern.h"
+
+/*
+ * def --
+ * Copy input to output. Input is buffered until reaches obs, and then
+ * output until less than obs remains. Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void
+def(void)
+{
+ u_char *inp;
+ const u_char *t;
+ size_t cnt;
+
+ if ((t = ctab) != NULL)
+ for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+ *inp = t[*inp];
+
+ /* Make the output buffer look right. */
+ out.dbp = in.dbp;
+ out.dbcnt = in.dbcnt;
+
+ if (in.dbcnt >= out.dbsz) {
+ /* If the output buffer is full, write it. */
+ dd_out(0);
+
+ /*
+ * Ddout copies the leftover output to the beginning of
+ * the buffer and resets the output buffer. Reset the
+ * input buffer to match it.
+ */
+ in.dbp = out.dbp;
+ in.dbcnt = out.dbcnt;
+ }
+}
+
+void
+def_close(void)
+{
+ /* Just update the count, everything is already in the buffer. */
+ if (in.dbcnt)
+ out.dbcnt = in.dbcnt;
+}
+
+/*
+ * Copy variable length newline terminated records with a max size cbsz
+ * bytes to output. Records less than cbs are padded with spaces.
+ *
+ * max in buffer: MAX(ibs, cbsz)
+ * max out buffer: obs + cbsz
+ */
+void
+block(void)
+{
+ u_char *inp, *outp;
+ const u_char *t;
+ size_t cnt, maxlen;
+ static int intrunc;
+ int ch;
+
+ /*
+ * Record truncation can cross block boundaries. If currently in a
+ * truncation state, keep tossing characters until reach a newline.
+ * Start at the beginning of the buffer, as the input buffer is always
+ * left empty.
+ */
+ if (intrunc) {
+ for (inp = in.db, cnt = in.dbrcnt; cnt && *inp++ != '\n'; --cnt)
+ ;
+ if (!cnt) {
+ in.dbcnt = 0;
+ in.dbp = in.db;
+ return;
+ }
+ intrunc = 0;
+ /* Adjust the input buffer numbers. */
+ in.dbcnt = cnt - 1;
+ in.dbp = inp + cnt - 1;
+ }
+
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation is done as we copy into the output buffer.
+ */
+ ch = 0;
+ for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
+ maxlen = MIN(cbsz, in.dbcnt);
+ if ((t = ctab) != NULL)
+ for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n';
+ ++cnt)
+ *outp++ = t[ch];
+ else
+ for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n';
+ ++cnt)
+ *outp++ = ch;
+ /*
+ * Check for short record without a newline. Reassemble the
+ * input block.
+ */
+ if (ch != '\n' && in.dbcnt < cbsz) {
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ break;
+ }
+
+ /* Adjust the input buffer numbers. */
+ in.dbcnt -= cnt;
+ if (ch == '\n')
+ --in.dbcnt;
+
+ /* Pad short records with spaces. */
+ if (cnt < cbsz)
+ (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
+ else {
+ /*
+ * If the next character wouldn't have ended the
+ * block, it's a truncation.
+ */
+ if (!in.dbcnt || *inp != '\n')
+ ++st.trunc;
+
+ /* Toss characters to a newline. */
+ for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
+ if (!in.dbcnt)
+ intrunc = 1;
+ else
+ --in.dbcnt;
+ }
+
+ /* Adjust output buffer numbers. */
+ out.dbp += cbsz;
+ if ((out.dbcnt += cbsz) >= out.dbsz)
+ dd_out(0);
+ outp = out.dbp;
+ }
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+block_close(void)
+{
+ /*
+ * Copy any remaining data into the output buffer and pad to a record.
+ * Don't worry about truncation or translation, the input buffer is
+ * always empty when truncating, and no characters have been added for
+ * translation. The bottom line is that anything left in the input
+ * buffer is a truncated record. Anything left in the output buffer
+ * just wasn't big enough.
+ */
+ if (in.dbcnt) {
+ ++st.trunc;
+ (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
+ (void)memset(out.dbp + in.dbcnt, ctab ? ctab[' '] : ' ',
+ cbsz - in.dbcnt);
+ out.dbcnt += cbsz;
+ }
+}
+
+/*
+ * Convert fixed length (cbsz) records to variable length. Deletes any
+ * trailing blanks and appends a newline.
+ *
+ * max in buffer: MAX(ibs, cbsz) + cbsz
+ * max out buffer: obs + cbsz
+ */
+void
+unblock(void)
+{
+ u_char *inp;
+ const u_char *t;
+ size_t cnt;
+
+ /* Translation and case conversion. */
+ if ((t = ctab) != NULL)
+ for (cnt = in.dbrcnt, inp = in.dbp; cnt--;) {
+ *inp = t[*inp];
+ --inp;
+ }
+ /*
+ * Copy records (max cbsz size chunks) into the output buffer. The
+ * translation has to already be done or we might not recognize the
+ * spaces.
+ */
+ for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
+ for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t)
+ ;
+ if (t >= inp) {
+ cnt = t - inp + 1;
+ (void)memmove(out.dbp, inp, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ *out.dbp++ = '\n';
+ if (++out.dbcnt >= out.dbsz)
+ dd_out(0);
+ }
+ if (in.dbcnt)
+ (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+ in.dbp = in.db + in.dbcnt;
+}
+
+void
+unblock_close(void)
+{
+ u_char *t;
+ size_t cnt;
+
+ if (in.dbcnt) {
+ warnx("%s: short input record", in.name);
+ for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t)
+ ;
+ if (t >= in.db) {
+ cnt = t - in.db + 1;
+ (void)memmove(out.dbp, in.db, cnt);
+ out.dbp += cnt;
+ out.dbcnt += cnt;
+ }
+ ++out.dbcnt;
+ *out.dbp++ = '\n';
+ }
+}
diff --git a/file_cmds/dd/conv_tab.c b/file_cmds/dd/conv_tab.c
new file mode 100644
index 0000000..a9f091e
--- /dev/null
+++ b/file_cmds/dd/conv_tab.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/conv_tab.c,v 1.10 1999/09/12 16:51:53 green Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+/*
+ * There are currently six tables:
+ *
+ * ebcdic -> ascii 32V conv=oldascii
+ * ascii -> ebcdic 32V conv=oldebcdic
+ * ascii -> ibm ebcdic 32V conv=oldibm
+ *
+ * ebcdic -> ascii POSIX/S5 conv=ascii
+ * ascii -> ebcdic POSIX/S5 conv=ebcdic
+ * ascii -> ibm ebcdic POSIX/S5 conv=ibm
+ *
+ * Other tables are built from these if multiple conversions are being
+ * done.
+ *
+ * Tables used for conversions to/from IBM and EBCDIC to support an extension
+ * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted
+ * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were
+ * constructed by running against a file with all possible byte values.
+ *
+ * More information can be obtained in "Correspondences of 8-Bit and Hollerith
+ * Codes for Computer Environments-A USASI Tutorial", Communications of the
+ * ACM, Volume 11, Number 11, November 1968, pp. 783-789.
+ */
+
+u_char casetab[256];
+
+/* EBCDIC to ASCII -- 32V compatible. */
+const u_char e2a_32V[] = {
+ 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */
+ 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */
+ 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */
+ 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */
+ 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */
+ 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */
+ 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */
+ 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */
+ 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */
+ 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */
+ 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */
+ 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */
+ 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */
+ 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */
+ 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */
+ 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */
+ 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */
+ 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */
+ 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */
+ 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */
+ 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */
+ 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */
+ 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */
+ 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */
+ 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */
+ 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */
+ 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */
+ 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
+
+/* ASCII to EBCDIC -- 32V compatible. */
+const u_char a2e_32V[] = {
+ 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */
+ 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */
+ 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */
+ 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */
+ 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */
+ 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */
+ 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */
+ 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */
+ 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */
+ 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */
+ 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */
+ 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */
+ 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */
+ 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */
+ 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */
+ 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */
+ 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */
+ 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */
+ 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */
+ 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */
+ 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */
+ 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */
+ 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */
+ 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */
+ 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */
+ 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */
+ 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */
+ 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
+
+/* ASCII to IBM EBCDIC -- 32V compatible. */
+const u_char a2ibm_32V[] = {
+ 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */
+ 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */
+ 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */
+ 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */
+ 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */
+ 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */
+ 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */
+ 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */
+ 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */
+ 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */
+ 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */
+ 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */
+ 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */
+ 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */
+ 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */
+ 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */
+ 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */
+ 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */
+ 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */
+ 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */
+ 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */
+ 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */
+ 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */
+ 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */
+ 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */
+ 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */
+ 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */
+ 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
+
+/* EBCDIC to ASCII -- POSIX and System V compatible. */
+const u_char e2a_POSIX[] = {
+ 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */
+ 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */
+ 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */
+ 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */
+ 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */
+ 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */
+ 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */
+ 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */
+ 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */
+ 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */
+ 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */
+ 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */
+ 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */
+ 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */
+ 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */
+ 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */
+ 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */
+ 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */
+ 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */
+ 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */
+ 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */
+ 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */
+ 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */
+ 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */
+ 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */
+ 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */
+ 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */
+ 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */
+ 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
+
+/* ASCII to EBCDIC -- POSIX and System V compatible. */
+const u_char a2e_POSIX[] = {
+ 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */
+ 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */
+ 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */
+ 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */
+ 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */
+ 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */
+ 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */
+ 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */
+ 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */
+ 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */
+ 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */
+ 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */
+ 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */
+ 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */
+ 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */
+ 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */
+ 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */
+ 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */
+ 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */
+ 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */
+ 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */
+ 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */
+ 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */
+ 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */
+ 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */
+ 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */
+ 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */
+ 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */
+ 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
+
+/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */
+const u_char a2ibm_POSIX[] = {
+ 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */
+ 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */
+ 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */
+ 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */
+ 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */
+ 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */
+ 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */
+ 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */
+ 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */
+ 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */
+ 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */
+ 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */
+ 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */
+ 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */
+ 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */
+ 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */
+ 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */
+ 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */
+ 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */
+ 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */
+ 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */
+ 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */
+ 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */
+ 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */
+ 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */
+ 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */
+ 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */
+ 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */
+ 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */
+};
diff --git a/file_cmds/dd/dd.1 b/file_cmds/dd/dd.1
new file mode 100644
index 0000000..8460813
--- /dev/null
+++ b/file_cmds/dd/dd.1
@@ -0,0 +1,382 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Keith Muller of the University of California, San Diego.
+.\"
+.\" 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.
+.\"
+.\" @(#)dd.1 8.2 (Berkeley) 1/13/94
+.\" $FreeBSD: src/bin/dd/dd.1,v 1.19 2002/03/31 20:49:37 keramida Exp $
+.\"
+.Dd January 13, 1994
+.Dt DD 1
+.Os
+.Sh NAME
+.Nm dd
+.Nd convert and copy a file
+.Sh SYNOPSIS
+.Nm
+.Op Ar operands ...
+.Sh DESCRIPTION
+The
+.Nm
+utility copies the standard input to the standard output.
+Input data is read and written in 512-byte blocks.
+If input reads are short, input from multiple reads are aggregated
+to form the output block.
+When finished,
+.Nm
+displays the number of complete and partial input and output blocks
+and truncated input records to the standard error output.
+.Pp
+The following operands are available:
+.\" XXX
+.Bl -tag -width of=file
+.It Cm bs Ns = Ns Ar n
+Set both input and output block size to
+.Ar n
+bytes, superseding the
+.Cm ibs
+and
+.Cm obs
+operands.
+If no conversion values other than
+.Cm noerror ,
+.Cm notrunc
+or
+.Cm sync
+are specified, then each input block is copied to the output as a
+single block without any aggregation of short blocks.
+.It Cm cbs Ns = Ns Ar n
+Set the conversion record size to
+.Ar n
+bytes.
+The conversion record size is required by the record oriented conversion
+values.
+.It Cm count Ns = Ns Ar n
+Copy only
+.Ar n
+input blocks.
+.It Cm files Ns = Ns Ar n
+Copy
+.Ar n
+input files before terminating.
+This operand is only applicable when the input device is a tape.
+.It Cm ibs Ns = Ns Ar n
+Set the input block size to
+.Ar n
+bytes instead of the default 512.
+.It Cm if Ns = Ns Ar file
+Read input from
+.Ar file
+instead of the standard input.
+.It Cm iseek Ns = Ns Ar n
+Seek on the input file
+.Ar n
+blocks.
+This is synonymous with
+.Cm skip Ns = Ns Ar n .
+.It Cm obs Ns = Ns Ar n
+Set the output block size to
+.Ar n
+bytes instead of the default 512.
+.It Cm of Ns = Ns Ar file
+Write output to
+.Ar file
+instead of the standard output.
+Any regular output file is truncated unless the
+.Cm notrunc
+conversion value is specified.
+If an initial portion of the output file is seeked past (see the
+.Cm oseek
+operand),
+the output file is truncated at that point.
+.It Cm oseek Ns = Ns Ar n
+Seek on the output file
+.Ar n
+blocks.
+This is synonymous with
+.Cm seek Ns = Ns Ar n .
+.It Cm seek Ns = Ns Ar n
+Seek
+.Ar n
+blocks from the beginning of the output before copying.
+On non-tape devices, an
+.Xr lseek 2
+operation is used.
+Otherwise, existing blocks are read and the data discarded.
+If the user does not have read permission for the tape, it is positioned
+using the tape
+.Xr ioctl 2
+function calls.
+If the seek operation is past the end of file, space from the current
+end of file to the specified offset is filled with blocks of
+.Dv NUL
+bytes.
+.It Cm skip Ns = Ns Ar n
+Skip
+.Ar n
+blocks from the beginning of the input before copying.
+On input which supports seeks, an
+.Xr lseek 2
+operation is used.
+Otherwise, input data is read and discarded.
+For pipes, the correct number of bytes is read.
+For all other devices, the correct number of blocks is read without
+distinguishing between a partial or complete block being read.
+.It Cm conv Ns = Ns Ar value Ns Op , Ns Ar value ...
+Where
+.Cm value
+is one of the symbols from the following list.
+.Bl -tag -width ".Cm unblock"
+.It Cm ascii , oldascii
+The same as the
+.Cm unblock
+value except that characters are translated from
+.Tn EBCDIC
+to
+.Tn ASCII
+before the
+records are converted.
+(These values imply
+.Cm unblock
+if the operand
+.Cm cbs
+is also specified.)
+There are two conversion maps for
+.Tn ASCII .
+The value
+.Cm ascii
+specifies the recommended one which is compatible with
+.At V .
+The value
+.Cm oldascii
+specifies the one used in historic
+.At
+and
+.No pre- Ns Bx 4.3 reno
+systems.
+.It Cm block
+Treats the input as a sequence of newline or end-of-file terminated variable
+length records independent of input and output block boundaries.
+Any trailing newline character is discarded.
+Each input record is converted to a fixed length output record where the
+length is specified by the
+.Cm cbs
+operand.
+Input records shorter than the conversion record size are padded with spaces.
+Input records longer than the conversion record size are truncated.
+The number of truncated input records, if any, are reported to the standard
+error output at the completion of the copy.
+.It Cm ebcdic , ibm , oldebcdic , oldibm
+The same as the
+.Cm block
+value except that characters are translated from
+.Tn ASCII
+to
+.Tn EBCDIC
+after the
+records are converted.
+(These values imply
+.Cm block
+if the operand
+.Cm cbs
+is also specified.)
+There are four conversion maps for
+.Tn EBCDIC .
+The value
+.Cm ebcdic
+specifies the recommended one which is compatible with
+.At V .
+The value
+.Cm ibm
+is a slightly different mapping, which is compatible with the
+.At V
+.Cm ibm
+value.
+The values
+.Cm oldebcdic
+and
+.Cm oldibm
+are maps used in historic
+.At
+and
+.No pre- Ns Bx 4.3 reno
+systems.
+.It Cm lcase
+Transform uppercase characters into lowercase characters.
+.It Cm noerror
+Do not stop processing on an input error.
+When an input error occurs, a diagnostic message followed by the current
+input and output block counts will be written to the standard error output
+in the same format as the standard completion message.
+If the
+.Cm sync
+conversion is also specified, any missing input data will be replaced
+with
+.Dv NUL
+bytes (or with spaces if a block oriented conversion value was
+specified) and processed as a normal input buffer.
+If the
+.Cm sync
+conversion is not specified, the input block is omitted from the output.
+On input files which are not tapes or pipes, the file offset
+will be positioned past the block in which the error occurred using
+.Xr lseek 2 .
+.It Cm notrunc
+Do not truncate the output file.
+This will preserve any blocks in the output file not explicitly written
+by
+.Nm .
+The
+.Cm notrunc
+value is not supported for tapes.
+.It Cm osync
+Pad the final output block to the full output block size.
+If the input file is not a multiple of the output block size
+after conversion, this conversion forces the final output block
+to be the same size as preceding blocks for use on devices that require
+regularly sized blocks to be written.
+This option is incompatible with use of the
+.Cm bs Ns = Ns Ar n
+block size specification.
+.It Cm sparse
+If one or more output blocks would consist solely of
+.Dv NUL
+bytes, try to seek the output file by the required space instead of
+filling them with
+.Dv NUL Ns s ,
+resulting in a sparse file.
+.It Cm swab
+Swap every pair of input bytes.
+If an input buffer has an odd number of bytes, the last byte will be
+ignored during swapping.
+.It Cm sync
+Pad every input block to the input buffer size.
+Spaces are used for pad bytes if a block oriented conversion value is
+specified, otherwise
+.Dv NUL
+bytes are used.
+.It Cm ucase
+Transform lowercase characters into uppercase characters.
+.It Cm unblock
+Treats the input as a sequence of fixed length records independent of input
+and output block boundaries.
+The length of the input records is specified by the
+.Cm cbs
+operand.
+Any trailing space characters are discarded and a newline character is
+appended.
+.El
+.El
+.Pp
+Where sizes are specified, a decimal, octal, or hexadecimal number of
+bytes is expected.
+If the number ends with a
+.Dq Li b ,
+.Dq Li k ,
+.Dq Li m ,
+.Dq Li g ,
+or
+.Dq Li w ,
+the
+number is multiplied by 512, 1024 (1K), 1048576 (1M), 1073741824 (1G)
+or the number of bytes in an integer, respectively.
+Two or more numbers may be separated by an
+.Dq Li x
+to indicate a product.
+.Pp
+When finished,
+.Nm
+displays the number of complete and partial input and output blocks,
+truncated input records and odd-length byte-swapping blocks to the
+standard error output.
+A partial input block is one where less than the input block size
+was read.
+A partial output block is one where less than the output block size
+was written.
+Partial output blocks to tape devices are considered fatal errors.
+Otherwise, the rest of the block will be written.
+Partial output blocks to character devices will produce a warning message.
+A truncated input block is one where a variable length record oriented
+conversion value was specified and the input line was too long to
+fit in the conversion record or was not newline terminated.
+.Pp
+Normally, data resulting from input or conversion or both are aggregated
+into output blocks of the specified size.
+After the end of input is reached, any remaining output is written as
+a block.
+This means that the final output block may be shorter than the output
+block size.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+(see the
+.Cm status
+argument for
+.Xr stty 1 )
+signal, the current input and output block counts will
+be written to the standard error output
+in the same format as the standard completion message.
+If
+.Nm
+receives a
+.Dv SIGINT
+signal, the current input and output block counts will
+be written to the standard error output
+in the same format as the standard completion message and
+.Nm
+will exit.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr cp 1 ,
+.Xr tr 1
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2
+standard.
+The
+.Cm files
+operand and the
+.Cm ascii ,
+.Cm ebcdic ,
+.Cm ibm ,
+.Cm oldascii ,
+.Cm oldebcdic
+and
+.Cm oldibm
+values are extensions to the
+\*[Px]
+standard.
diff --git a/file_cmds/dd/dd.c b/file_cmds/dd/dd.c
new file mode 100644
index 0000000..5731186
--- /dev/null
+++ b/file_cmds/dd/dd.c
@@ -0,0 +1,481 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/dd.c,v 1.36 2002/03/07 14:00:33 markm Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/filio.h>
+#include <sys/time.h>
+
+#ifdef __APPLE__
+#include <sys/ioctl.h>
+#else
+#include <sys/disklabel.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static void dd_close(void);
+static void dd_in(void);
+static void getfdtype(IO *);
+static void setup(void);
+
+IO in, out; /* input/output state */
+STAT st; /* statistics */
+void (*cfunc)(void); /* conversion function */
+quad_t cpy_cnt; /* # of blocks to copy */
+off_t pending = 0; /* pending seek if sparse */
+u_int ddflags; /* conversion options */
+size_t cbsz; /* conversion block size */
+quad_t files_cnt = 1; /* # of files to copy */
+const u_char *ctab; /* conversion table */
+
+int
+main(int argc, char *argv[])
+{
+ (void)setlocale(LC_CTYPE, "");
+ jcl(argv);
+ setup();
+
+ (void)signal(SIGINFO, summaryx);
+ (void)signal(SIGINT, terminate);
+
+ atexit(summary);
+
+ while (files_cnt--)
+ dd_in();
+
+ dd_close();
+ exit(0);
+}
+
+static void
+setup(void)
+{
+ u_int cnt;
+ struct timeval tv;
+
+ if (in.name == NULL) {
+ in.name = "stdin";
+ in.fd = STDIN_FILENO;
+ } else {
+ in.fd = open(in.name, O_RDONLY, 0);
+ if (in.fd == -1)
+ err(1, "%s", in.name);
+ }
+
+ getfdtype(&in);
+
+ if (files_cnt > 1 && !(in.flags & ISTAPE))
+ errx(1, "files is not supported for non-tape devices");
+
+ if (out.name == NULL) {
+ /* No way to check for read access here. */
+ out.fd = STDOUT_FILENO;
+ out.name = "stdout";
+ } else {
+#define OFLAGS \
+ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+ out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
+ /*
+ * May not have read access, so try again with write only.
+ * Without read we may have a problem if output also does
+ * not support seeks.
+ */
+ if (out.fd == -1) {
+ out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
+ out.flags |= NOREAD;
+ }
+ if (out.fd == -1)
+ err(1, "%s", out.name);
+ }
+
+ getfdtype(&out);
+
+ /*
+ * Allocate space for the input and output buffers. If not doing
+ * record oriented I/O, only need a single buffer.
+ */
+ if (!(ddflags & (C_BLOCK | C_UNBLOCK))) {
+ if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
+ err(1, "input buffer");
+ out.db = in.db;
+ } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
+ (out.db = malloc(out.dbsz + cbsz)) == NULL)
+ err(1, "output buffer");
+ in.dbp = in.db;
+ out.dbp = out.db;
+
+ /* Position the input/output streams. */
+ if (in.offset)
+ pos_in();
+ if (out.offset)
+ pos_out();
+
+ /*
+ * Truncate the output file. If it fails on a type of output file
+ * that it should _not_ fail on, error out.
+ */
+ if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) &&
+ out.flags & ISTRUNC)
+ if (ftruncate(out.fd, out.offset * out.dbsz) == -1)
+ err(1, "truncating %s", out.name);
+
+ /*
+ * If converting case at the same time as another conversion, build a
+ * table that does both at once. If just converting case, use the
+ * built-in tables.
+ */
+ if (ddflags & (C_LCASE | C_UCASE)) {
+ if (ddflags & (C_ASCII | C_EBCDIC)) {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt <= 0377; ++cnt)
+ casetab[cnt] = tolower(ctab[cnt]);
+ } else {
+ for (cnt = 0; cnt <= 0377; ++cnt)
+ casetab[cnt] = toupper(ctab[cnt]);
+ }
+ } else {
+ if (ddflags & C_LCASE) {
+ for (cnt = 0; cnt <= 0377; ++cnt)
+ casetab[cnt] = tolower((int)cnt);
+ } else {
+ for (cnt = 0; cnt <= 0377; ++cnt)
+ casetab[cnt] = toupper((int)cnt);
+ }
+ }
+ ctab = casetab;
+ }
+
+ (void)gettimeofday(&tv, (struct timezone *)NULL);
+ st.start = tv.tv_sec + tv.tv_usec * 1e-6;
+}
+
+static void
+getfdtype(IO *io)
+{
+ struct stat sb;
+ int type;
+
+ if (fstat(io->fd, &sb) == -1)
+ err(1, "%s", io->name);
+ if (S_ISREG(sb.st_mode))
+ io->flags |= ISTRUNC;
+ if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
+ 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;
+ else if (type & (D_DISK | D_MEM)) {
+ if (type & D_DISK) {
+ const int one = 1;
+
+ (void)ioctl(io->fd, DIOCWLABEL, &one);
+ }
+#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;
+ }
+ errno = 0;
+ if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+ io->flags |= ISPIPE;
+ else
+ io->flags |= ISSEEK;
+}
+
+static void
+dd_in(void)
+{
+ ssize_t n;
+
+ for (;;) {
+ switch (cpy_cnt) {
+ case -1: /* count=0 was specified */
+ return;
+ case 0:
+ break;
+ default:
+ if (st.in_full + st.in_part >= (u_quad_t)cpy_cnt)
+ return;
+ break;
+ }
+
+ /*
+ * Zero the buffer first if sync; if doing block operations,
+ * use spaces.
+ */
+ if (ddflags & C_SYNC) {
+ if (ddflags & (C_BLOCK | C_UNBLOCK))
+ memset(in.dbp, ' ', in.dbsz);
+ else
+ memset(in.dbp, 0, in.dbsz);
+ }
+
+ n = read(in.fd, in.dbp, in.dbsz);
+ if (n == 0) {
+ in.dbrcnt = 0;
+ return;
+ }
+
+ /* Read error. */
+ if (n == -1) {
+ /*
+ * If noerror not specified, die. POSIX requires that
+ * the warning message be followed by an I/O display.
+ */
+ if (!(ddflags & C_NOERROR))
+ err(1, "%s", in.name);
+ warn("%s", in.name);
+ summary();
+
+ /*
+ * If it's a seekable file descriptor, seek past the
+ * error. If your OS doesn't do the right thing for
+ * raw disks this section should be modified to re-read
+ * in sector size chunks.
+ */
+ if (in.flags & ISSEEK &&
+ lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+ warn("%s", in.name);
+
+ /* If sync not specified, omit block and continue. */
+ if (!(ddflags & C_SYNC))
+ continue;
+
+ /* Read errors count as full blocks. */
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ ++st.in_full;
+
+ /* Handle full input blocks. */
+ } else if ((size_t)n == in.dbsz) {
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_full;
+
+ /* Handle partial input blocks. */
+ } else {
+ /* If sync, use the entire block. */
+ if (ddflags & C_SYNC)
+ in.dbcnt += in.dbrcnt = in.dbsz;
+ else
+ in.dbcnt += in.dbrcnt = n;
+ ++st.in_part;
+ }
+
+ /*
+ * POSIX states that if bs is set and no other conversions
+ * than noerror, notrunc or sync are specified, the block
+ * is output without buffering as it is read.
+ */
+ if (ddflags & C_BS) {
+ out.dbcnt = in.dbcnt;
+ dd_out(1);
+ in.dbcnt = 0;
+ continue;
+ }
+
+ if (ddflags & C_SWAB) {
+ if ((n = in.dbrcnt) & 1) {
+ ++st.swab;
+ --n;
+ }
+ swab(in.dbp, in.dbp, (size_t)n);
+ }
+
+ in.dbp += in.dbrcnt;
+ (*cfunc)();
+ }
+}
+
+/*
+ * Clean up any remaining I/O and flush output. If necessary, the output file
+ * is truncated.
+ */
+static void
+dd_close(void)
+{
+ if (cfunc == def)
+ def_close();
+ else if (cfunc == block)
+ block_close();
+ else if (cfunc == unblock)
+ unblock_close();
+ if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
+ if (ddflags & (C_BLOCK | C_UNBLOCK))
+ memset(out.dbp, ' ', out.dbsz - out.dbcnt);
+ else
+ memset(out.dbp, 0, out.dbsz - out.dbcnt);
+ out.dbcnt = out.dbsz;
+ }
+ if (out.dbcnt || pending)
+ dd_out(1);
+}
+
+void
+dd_out(int force)
+{
+ u_char *outp;
+ size_t cnt, i, n;
+ ssize_t nw;
+ static int warned;
+ int sparse;
+
+ /*
+ * Write one or more blocks out. The common case is writing a full
+ * output block in a single write; increment the full block stats.
+ * Otherwise, we're into partial block writes. If a partial write,
+ * and it's a character device, just warn. If a tape device, quit.
+ *
+ * The partial writes represent two cases. 1: Where the input block
+ * was less than expected so the output block was less than expected.
+ * 2: Where the input block was the right size but we were forced to
+ * write the block in multiple chunks. The original versions of dd(1)
+ * never wrote a block in more than a single write, so the latter case
+ * never happened.
+ *
+ * One special case is if we're forced to do the write -- in that case
+ * we play games with the buffer size, and it's usually a partial write.
+ */
+ outp = out.db;
+ for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+ for (cnt = n;; cnt -= nw) {
+ sparse = 0;
+ if (ddflags & C_SPARSE) {
+ sparse = 1; /* Is buffer sparse? */
+ for (i = 0; i < cnt; i++)
+ if (outp[i] != 0) {
+ sparse = 0;
+ break;
+ }
+ }
+ if (sparse && !force) {
+ pending += cnt;
+ nw = cnt;
+ } else {
+ if (pending != 0) {
+ if (force)
+ pending--;
+ if (lseek(out.fd, pending, SEEK_CUR) ==
+ -1)
+ err(2, "%s: seek error creating sparse file",
+ out.name);
+ if (force)
+ write(out.fd, outp, 1);
+ pending = 0;
+ }
+ if (cnt)
+ nw = write(out.fd, outp, cnt);
+ else
+ return;
+ }
+
+ if (nw <= 0) {
+ if (nw == 0)
+ errx(1, "%s: end of device", out.name);
+ if (errno != EINTR)
+ err(1, "%s", out.name);
+ nw = 0;
+ }
+ outp += nw;
+ st.bytes += nw;
+ if ((size_t)nw == n) {
+ if (n != out.dbsz)
+ ++st.out_part;
+ else
+ ++st.out_full;
+ break;
+ }
+ ++st.out_part;
+ if ((size_t)nw == cnt)
+ break;
+ if (out.flags & ISTAPE)
+ errx(1, "%s: short write on tape device",
+ out.name);
+ if (out.flags & ISCHR && !warned) {
+ warned = 1;
+ warnx("%s: short write on character device",
+ out.name);
+ }
+ }
+ if ((out.dbcnt -= n) < out.dbsz)
+ break;
+ }
+
+ /* Reassemble the output block. */
+ if (out.dbcnt)
+ (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+ out.dbp = out.db + out.dbcnt;
+}
diff --git a/file_cmds/dd/dd.entitlements b/file_cmds/dd/dd.entitlements
new file mode 100644
index 0000000..abb8bc5
--- /dev/null
+++ b/file_cmds/dd/dd.entitlements
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
+
diff --git a/file_cmds/dd/dd.h b/file_cmds/dd/dd.h
new file mode 100644
index 0000000..c516ad2
--- /dev/null
+++ b/file_cmds/dd/dd.h
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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.
+ *
+ * @(#)dd.h 8.3 (Berkeley) 4/2/94
+ * $FreeBSD: src/bin/dd/dd.h,v 1.17 2002/02/22 20:51:00 markm Exp $
+ */
+
+#ifndef _DD_H_
+#define _DD_H_
+
+/* Input/output stream state. */
+typedef struct {
+ u_char *db; /* buffer address */
+ u_char *dbp; /* current buffer I/O address */
+ /* XXX ssize_t? */
+ size_t dbcnt; /* current buffer byte count */
+ size_t dbrcnt; /* last read byte count */
+ size_t dbsz; /* buffer size */
+
+#define ISCHR 0x01 /* character device (warn on short) */
+#define ISPIPE 0x02 /* pipe-like (see position.c) */
+#define ISTAPE 0x04 /* tape */
+#define ISSEEK 0x08 /* valid to seek on */
+#define NOREAD 0x10 /* not readable */
+#define ISTRUNC 0x20 /* valid to ftruncate() */
+ u_int flags;
+
+ const char *name; /* name */
+ int fd; /* file descriptor */
+ off_t offset; /* # of blocks to skip */
+
+} IO;
+
+typedef struct {
+ u_quad_t in_full; /* # of full input blocks */
+ u_quad_t in_part; /* # of partial input blocks */
+ u_quad_t out_full; /* # of full output blocks */
+ u_quad_t out_part; /* # of partial output blocks */
+ u_quad_t trunc; /* # of truncated records */
+ u_quad_t swab; /* # of odd-length swab blocks */
+ u_quad_t bytes; /* # of bytes written */
+ double start; /* start time of dd */
+} STAT;
+
+/* Flags (in ddflags). */
+#define C_ASCII 0x00001
+#define C_BLOCK 0x00002
+#define C_BS 0x00004
+#define C_CBS 0x00008
+#define C_COUNT 0x00010
+#define C_EBCDIC 0x00020
+#define C_FILES 0x00040
+#define C_IBS 0x00080
+#define C_IF 0x00100
+#define C_LCASE 0x00200
+#define C_NOERROR 0x00400
+#define C_NOTRUNC 0x00800
+#define C_OBS 0x01000
+#define C_OF 0x02000
+#define C_SEEK 0x04000
+#define C_SKIP 0x08000
+#define C_SWAB 0x10000
+#define C_SYNC 0x20000
+#define C_UCASE 0x40000
+#define C_UNBLOCK 0x80000
+#define C_OSYNC 0x100000
+#define C_SPARSE 0x200000
+
+#endif /* _DD_H_ */
diff --git a/file_cmds/dd/extern.h b/file_cmds/dd/extern.h
new file mode 100644
index 0000000..0ae6330
--- /dev/null
+++ b/file_cmds/dd/extern.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ * $FreeBSD: src/bin/dd/extern.h,v 1.12 2002/02/02 06:24:12 imp Exp $
+ */
+
+#ifndef _DD_EXTERN_H_
+#define _DD_EXTERN_H_
+
+#include <sys/cdefs.h>
+
+void block(void);
+void block_close(void);
+void dd_out(int);
+void def(void);
+void def_close(void);
+void jcl(char **);
+void pos_in(void);
+void pos_out(void);
+void summary(void);
+void summaryx(int);
+void terminate(int);
+void unblock(void);
+void unblock_close(void);
+
+extern IO in, out;
+extern STAT st;
+extern void (*cfunc)(void);
+extern quad_t cpy_cnt;
+extern size_t cbsz;
+extern u_int ddflags;
+extern quad_t files_cnt;
+extern const u_char *ctab;
+extern const u_char a2e_32V[], a2e_POSIX[];
+extern const u_char e2a_32V[], e2a_POSIX[];
+extern const u_char a2ibm_32V[], a2ibm_POSIX[];
+extern u_char casetab[];
+
+#endif /* _DD_EXTERN_H_ */
diff --git a/file_cmds/dd/install_symlink.sh b/file_cmds/dd/install_symlink.sh
new file mode 100644
index 0000000..7e8665f
--- /dev/null
+++ b/file_cmds/dd/install_symlink.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -e
+set -x
+
+case "$PLATFORM_NAME" in
+iphoneos|appletvos|watchos|bridgeos)
+ ln -hfs /usr/local/bin/dd "$DSTROOT"/bin/dd
+ ;;
+macosx)
+ ;;
+*)
+ echo "Unsupported platform: $PLATFORM_NAME"
+ exit 1
+ ;;
+esac
+
diff --git a/file_cmds/dd/misc.c b/file_cmds/dd/misc.c
new file mode 100644
index 0000000..b220334
--- /dev/null
+++ b/file_cmds/dd/misc.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/misc.c,v 1.23 2002/02/02 06:24:12 imp Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+void
+summary(void)
+{
+ struct timeval tv;
+ double secs;
+ char buf[100];
+
+ (void)gettimeofday(&tv, (struct timezone *)NULL);
+ secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start;
+ if (secs < 1e-6)
+ secs = 1e-6;
+ /* Use snprintf(3) so that we don't reenter stdio(3). */
+ (void)snprintf(buf, sizeof(buf),
+ "%qu+%qu records in\n%qu+%qu records out\n",
+ st.in_full, st.in_part, st.out_full, st.out_part);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ if (st.swab) {
+ (void)snprintf(buf, sizeof(buf), "%qu odd length swab %s\n",
+ st.swab, (st.swab == 1) ? "block" : "blocks");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ if (st.trunc) {
+ (void)snprintf(buf, sizeof(buf), "%qu truncated %s\n",
+ st.trunc, (st.trunc == 1) ? "record" : "records");
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+ }
+ (void)snprintf(buf, sizeof(buf),
+ "%qu bytes transferred in %.6f secs (%.0f bytes/sec)\n",
+ st.bytes, secs, st.bytes / secs);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+/* ARGSUSED */
+void
+summaryx(int notused)
+{
+ int save_errno = errno;
+
+ summary();
+ errno = save_errno;
+}
+
+/* ARGSUSED */
+void
+terminate(int sig)
+{
+
+ summary();
+ _exit(sig == 0 ? 0 : 1);
+}
diff --git a/file_cmds/dd/position.c b/file_cmds/dd/position.c
new file mode 100644
index 0000000..c110cd3
--- /dev/null
+++ b/file_cmds/dd/position.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/dd/position.c,v 1.20 2002/02/02 06:24:12 imp Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#ifdef __APPLE__
+#include <sys/ioctl.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+/*
+ * Position input/output data streams before starting the copy. Device type
+ * dependent. Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void
+pos_in(void)
+{
+ off_t cnt;
+ int warned;
+ ssize_t nr;
+ size_t bcnt;
+
+ /* If known to be seekable, try to seek on it. */
+ if (in.flags & ISSEEK) {
+ errno = 0;
+ if (lseek(in.fd, in.offset * in.dbsz, SEEK_CUR) == -1 &&
+ errno != 0)
+ err(1, "%s", in.name);
+ return;
+ }
+
+ /* Don't try to read a really weird amount (like negative). */
+ if (in.offset < 0)
+ errx(1, "%s: illegal offset", "iseek/skip");
+
+ /*
+ * Read the data. If a pipe, read until satisfy the number of bytes
+ * being skipped. No differentiation for reading complete and partial
+ * blocks for other devices.
+ */
+ for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+ if ((nr = read(in.fd, in.db, bcnt)) > 0) {
+ if (in.flags & ISPIPE) {
+ if (!(bcnt -= nr)) {
+ bcnt = in.dbsz;
+ --cnt;
+ }
+ } else
+ --cnt;
+ continue;
+ }
+
+ if (nr == 0) {
+ if (files_cnt > 1) {
+ --files_cnt;
+ continue;
+ }
+ errx(1, "skip reached end of input");
+ }
+
+ /*
+ * Input error -- either EOF with no more files, or I/O error.
+ * If noerror not set die. POSIX requires that the warning
+ * message be followed by an I/O display.
+ */
+ if (ddflags & C_NOERROR) {
+ if (!warned) {
+ warn("%s", in.name);
+ warned = 1;
+ summary();
+ }
+ continue;
+ }
+ err(1, "%s", in.name);
+ }
+}
+
+void
+pos_out(void)
+{
+ off_t cnt;
+ ssize_t n;
+
+ /*
+ * If not a tape, try seeking on the file. Seeking on a pipe is
+ * going to fail, but don't protect the user -- they shouldn't
+ * have specified the seek operand.
+ */
+ if (out.flags & (ISSEEK | ISPIPE)) {
+ errno = 0;
+ if (lseek(out.fd, out.offset * out.dbsz, SEEK_CUR) == -1 &&
+ errno != 0)
+ err(1, "%s", out.name);
+ return;
+ }
+
+ /* Don't try to read a really weird amount (like negative). */
+ if (out.offset < 0)
+ errx(1, "%s: illegal offset", "oseek/seek");
+
+ /* Read it. */
+ for (cnt = 0; cnt < out.offset; ++cnt) {
+ if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+ continue;
+
+ if (n == -1)
+ err(1, "%s", out.name);
+
+ while (cnt++ < out.offset) {
+ n = write(out.fd, out.db, out.dbsz);
+ if (n == -1)
+ err(1, "%s", out.name);
+ if ((size_t)n != out.dbsz)
+ errx(1, "%s: write failure", out.name);
+ }
+ break;
+ }
+}
diff --git a/file_cmds/df/df.1 b/file_cmds/df/df.1
new file mode 100644
index 0000000..4560ab9
--- /dev/null
+++ b/file_cmds/df/df.1
@@ -0,0 +1,217 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)df.1 8.3 (Berkeley) 5/8/95
+.\" $FreeBSD: src/bin/df/df.1,v 1.18.2.7 2002/04/22 22:17:36 keramida Exp $
+.\"
+.Dd May 8, 1995
+.Dt DF 1
+.Os
+.Sh NAME
+.Nm df
+.Nd display free disk space
+.Sh SYNOPSIS
+.Nm df
+.Oo
+.Fl b | h | H | k |
+.Fl m | g | P
+.Oc
+.Op Fl ailn
+.Op Fl t
+.Op Fl T Ar type
+.Op Ar file | filesystem ...
+.Sh LEGACY SYNOPSIS
+.Nm df
+.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
+.Nm df
+utility
+displays statistics about the amount of free disk space on the specified
+.Ar filesystem
+or on the filesystem of which
+.Ar file
+is a part.
+Values are displayed in 512-byte per block counts.
+If neither a file or a filesystem operand is specified,
+statistics for all mounted filesystems are displayed
+(subject to the
+.Fl t
+option below).
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Show all mount points, including those that were mounted with the MNT_IGNORE
+flag.
+.It Fl b
+Use (the default) 512-byte blocks.
+This is only useful as a way to override an
+.Ev BLOCKSIZE
+specification from the environment.
+.It Fl g
+Use 1073741824-byte (1-Gbyte) blocks rather than the default.
+Note that this overrides the
+.Ev BLOCKSIZE
+specification from the environment.
+.It Fl H
+"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte,
+Gigabyte, Terabyte and Petabyte in order to reduce the number of
+digits to three or less using base 10 for sizes.
+.It Fl h
+"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte,
+Gigabyte, Terabyte and Petabyte in order to reduce the number of
+digits to three or less using base 2 for sizes.
+.It Fl i
+Include statistics on the number of free inodes. This option is now the default to conform to
+.St -susv3
+Use
+.Fl P
+to suppress this output.
+.It Fl k
+Use 1024-byte (1-Kbyte) blocks, rather than the default.
+Note that this overrides the
+.Ev BLOCKSIZE
+specification from the environment.
+.It Fl l
+Only display information about locally-mounted filesystems.
+.It Fl m
+Use 1048576-byte (1-Mbyte) blocks rather than the default. Note that
+this overrides the
+.Ev BLOCKSIZE
+specification from the environment.
+.It Fl n
+Print out the previously obtained statistics from the filesystems.
+This option should be used if it is possible that one or more
+filesystems are in a state such that they will not be able to provide
+statistics without a long delay.
+When this option is specified,
+.Nm df
+will not request new statistics from the filesystems, but will respond
+with the possibly stale statistics that were previously obtained.
+.It Fl P
+Use (the default) 512-byte blocks.
+This is only useful as a way to override an
+.Ev BLOCKSIZE
+specification from the environment.
+.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
+.Dq no
+to specify the filesystem types for which action should
+.Em not
+be taken.
+For example, the
+.Nm df
+command:
+.Bd -literal -offset indent
+df -T nonfs,mfs
+.Ed
+.Pp
+lists all filesystems except those of type
+.Tn NFS
+and
+.Tn MFS .
+The
+.Xr lsvfs 1
+command can be used to find out the types of filesystems
+that are available on the system.
+.It Fl t
+If used with no arguments,
+this option is a no-op
+(Mac OS X already prints the total allocated-space figures).
+If used with an argument, it acts like
+.Fl T ,
+but this usage is deprecated and should not be relied upon.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, the block counts will be displayed in units of that size block.
+.El
+.Sh BUGS
+The
+.Fl n
+and
+.Fl t
+flags are ignored if a file or filesystem is specified.
+.Sh LEGACY DESCRIPTION
+The "capacity" percentage is normally rounded up to the next higher integer.
+In legacy mode, it is rounded down to the next lower integer.
+.Pp
+When the
+.Fl P
+option and the
+.Fl k
+option are used together,
+sizes are reported in 1024-blocks.
+In legacy mode, when the
+.Fl P
+option and
+.Fl k
+option are used together,
+the last option specified dictates the reported block size.
+.Pp
+The
+.Fl t
+option is normally a no-op
+(Mac OS X already prints the total allocated-space figures).
+In legacy mode, it is equivalent to
+.Fl T .
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr lsvfs 1 ,
+.Xr quota 1 ,
+.Xr fstatfs 2 ,
+.Xr getfsstat 2 ,
+.Xr statfs 2 ,
+.Xr getmntinfo 3 ,
+.Xr compat 5 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr quot 8
+.Sh HISTORY
+A
+.Nm df
+command appeared in
+.At v1 .
diff --git a/file_cmds/df/df.c b/file_cmds/df/df.c
new file mode 100644
index 0000000..7e576d7
--- /dev/null
+++ b/file_cmds/df/df.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 1980, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95";
+#else
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $";
+#endif
+#endif /* not lint */
+
+#ifdef __APPLE__
+#define MNT_IGNORE 0
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <libutil.h>
+
+#ifdef __APPLE__
+#include "get_compat.h"
+#else
+#define COMPAT_MODE(func, mode) 1
+#endif
+
+#define UNITS_SI 1
+#define UNITS_2 2
+
+#define KILO_SZ(n) (n)
+#define MEGA_SZ(n) ((n) * (n))
+#define GIGA_SZ(n) ((n) * (n) * (n))
+#define TERA_SZ(n) ((n) * (n) * (n) * (n))
+#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
+
+#define KILO_2_SZ (KILO_SZ(1024ULL))
+#define MEGA_2_SZ (MEGA_SZ(1024ULL))
+#define GIGA_2_SZ (GIGA_SZ(1024ULL))
+#define TERA_2_SZ (TERA_SZ(1024ULL))
+#define PETA_2_SZ (PETA_SZ(1024ULL))
+
+#define KILO_SI_SZ (KILO_SZ(1000ULL))
+#define MEGA_SI_SZ (MEGA_SZ(1000ULL))
+#define GIGA_SI_SZ (GIGA_SZ(1000ULL))
+#define TERA_SI_SZ (TERA_SZ(1000ULL))
+#define PETA_SI_SZ (PETA_SZ(1000ULL))
+
+/* Maximum widths of various fields. */
+struct maxwidths {
+ int mntfrom;
+ int total;
+ int used;
+ int avail;
+ int iused;
+ int ifree;
+};
+
+unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
+unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
+unsigned long long *valp;
+
+typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
+
+unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
+
+int bread(off_t, void *, int);
+int checkvfsname(const char *, char **);
+char *getmntpt(char *);
+int int64width(int64_t);
+char *makenetvfslist(void);
+char **makevfslist(const char *);
+void prthuman(struct statfs *, uint64_t);
+void prthumanval(int64_t);
+void prtstat(struct statfs *, struct maxwidths *);
+long regetmntinfo(struct statfs **, long, char **);
+unit_t unit_adjust(double *);
+void update_maxwidths(struct maxwidths *, struct statfs *);
+void usage(void);
+
+int aflag = 0, hflag, iflag, nflag;
+
+static __inline int imax(int a, int b)
+{
+ return (a > b ? a : b);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct stat stbuf;
+ struct statfs statfsbuf, *mntbuf;
+ struct maxwidths maxwidths;
+ char *mntpt, **vfslist;
+ long mntsize;
+ int ch, i, rv, tflag = 0, kludge_tflag = 0;
+ int kflag = 0;
+ const char *options = "abgHhiklmnPt:T:";
+ if (COMPAT_MODE("bin/df", "unix2003")) {
+ /* Unix2003 requires -t be "include total capacity". which df
+ already does, but it conflicts 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:";
+ iflag = 1;
+ }
+
+ vfslist = NULL;
+ while ((ch = getopt(argc, argv, options)) != -1)
+ switch (ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'b':
+ /* FALLTHROUGH */
+ case 'P':
+ if (COMPAT_MODE("bin/df", "unix2003")) {
+ if (!kflag) {
+ /* -k overrides -P */
+ putenv("BLOCKSIZE=512");
+ }
+ iflag = 0;
+ } else {
+ putenv("BLOCKSIZE=512");
+ }
+ hflag = 0;
+ break;
+ case 'g':
+ putenv("BLOCKSIZE=1g");
+ hflag = 0;
+ break;
+ case 'H':
+ hflag = UNITS_SI;
+ valp = vals_si;
+ break;
+ case 'h':
+ hflag = UNITS_2;
+ valp = vals_base2;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'k':
+ if (COMPAT_MODE("bin/df", "unix2003")) {
+ putenv("BLOCKSIZE=1024");
+ } else {
+ putenv("BLOCKSIZE=1k");
+ }
+ kflag = 1;
+ hflag = 0;
+ break;
+ case 'l':
+ if (tflag)
+ errx(1, "-l and -T are mutually exclusive.");
+ if (vfslist != NULL)
+ break;
+ vfslist = makevfslist(makenetvfslist());
+ break;
+ case 'm':
+ putenv("BLOCKSIZE=1m");
+ hflag = 0;
+ break;
+ case 'n':
+ 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 -%c option may be specified", ch);
+ else
+ errx(1, "-l and -%c are mutually exclusive.", ch);
+ }
+ tflag++;
+ vfslist = makevfslist(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ 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) {
+ vfslist = makevfslist(*argv++);
+ }
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ bzero(&maxwidths, sizeof(maxwidths));
+ for (i = 0; i < mntsize; i++)
+ update_maxwidths(&maxwidths, &mntbuf[i]);
+
+ rv = 0;
+ if (!*argv) {
+ mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
+ bzero(&maxwidths, sizeof(maxwidths));
+ for (i = 0; i < mntsize; i++)
+ update_maxwidths(&maxwidths, &mntbuf[i]);
+ for (i = 0; i < mntsize; i++) {
+ if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0)
+ prtstat(&mntbuf[i], &maxwidths);
+ }
+ exit(rv);
+ }
+
+ for (; *argv; argv++) {
+ if (stat(*argv, &stbuf) < 0) {
+ if ((mntpt = getmntpt(*argv)) == 0) {
+ warn("%s", *argv);
+ rv = 1;
+ continue;
+ }
+ } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) {
+ warnx("%s: Raw devices not supported", *argv);
+ rv = 1;
+ continue;
+ } else
+ mntpt = *argv;
+ /*
+ * Statfs does not take a `wait' flag, so we cannot
+ * implement nflag here.
+ */
+ if (statfs(mntpt, &statfsbuf) < 0) {
+ warn("%s", mntpt);
+ rv = 1;
+ continue;
+ }
+ /* Check to make sure the arguments we've been
+ * given are satisfied. Return an error if we
+ * have been asked to list a mount point that does
+ * not match the other args we've been given (-l, -t, etc.)
+ */
+ if (checkvfsname(statfsbuf.f_fstypename, vfslist)) {
+ rv++;
+ continue;
+ }
+
+ if (argc == 1) {
+ bzero(&maxwidths, sizeof(maxwidths));
+ update_maxwidths(&maxwidths, &statfsbuf);
+ }
+ prtstat(&statfsbuf, &maxwidths);
+ }
+ return (rv);
+}
+
+char *
+getmntpt(char *name)
+{
+ long mntsize, i;
+ struct statfs *mntbuf;
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ if (!strcmp(mntbuf[i].f_mntfromname, name))
+ return (mntbuf[i].f_mntonname);
+ }
+ return (0);
+}
+
+/*
+ * Make a pass over the filesystem info in ``mntbuf'' filtering out
+ * filesystem types not in vfslist and possibly re-stating to get
+ * current (not cached) info. Returns the new count of valid statfs bufs.
+ */
+long
+regetmntinfo(struct statfs **mntbufp, long mntsize, char **vfslist)
+{
+ int i, j;
+ struct statfs *mntbuf;
+
+ if (vfslist == NULL)
+ return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
+
+ mntbuf = *mntbufp;
+ for (j = 0, i = 0; i < mntsize; i++) {
+ if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+ continue;
+ if (!nflag)
+ (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
+ else if (i != j)
+ mntbuf[j] = mntbuf[i];
+ j++;
+ }
+ return (j);
+}
+
+/*
+ * Output in "human-readable" format. Uses 3 digits max and puts
+ * unit suffixes at the end. Makes output compact and easy to read,
+ * especially on huge disks.
+ *
+ */
+unit_t
+unit_adjust(double *val)
+{
+ double abval;
+ unit_t unit;
+ unsigned int unit_sz;
+
+ abval = fabs(*val);
+
+ unit_sz = abval ? ilogb(abval) / 10 : 0;
+
+ if (unit_sz >= UNIT_MAX) {
+ unit = NONE;
+ } else {
+ unit = unitp[unit_sz];
+ *val /= (double)valp[unit_sz];
+ }
+
+ return (unit);
+}
+
+void
+prthuman(struct statfs *sfsp, uint64_t used)
+{
+ int64_t value;
+
+ value = sfsp->f_blocks;
+ value *= sfsp->f_bsize;
+ prthumanval(value);
+ value = used;
+ value *= sfsp->f_bsize;
+ prthumanval(value);
+ value = sfsp->f_bavail;
+ value *= sfsp->f_bsize;
+ prthumanval(value);
+}
+
+void
+prthumanval(int64_t bytes)
+{
+ char buf[6];
+ int flags;
+
+ flags = HN_B | HN_NOSPACE | HN_DECIMAL;
+ if (hflag == UNITS_SI)
+ flags |= HN_DIVISOR_1000;
+
+ humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
+ bytes, "", HN_AUTOSCALE, flags);
+
+ if (hflag == UNITS_SI)
+ (void)printf(" %6s", buf);
+ else
+ (void)printf("%6si", buf);
+
+}
+
+/*
+ * Convert statfs returned filesystem size into BLOCKSIZE units.
+ * Attempts to avoid overflow for large filesystems.
+ */
+static intmax_t fsbtoblk(int64_t num, uint64_t fsbs, u_long bs, char *fs)
+{
+ if (num < 0) {
+ warnx("negative filesystem block count/size from fs %s", fs);
+ return 0;
+ } else if ((fsbs != 0) && (fsbs < bs)) {
+ return (num / (intmax_t) (bs / fsbs));
+ } else {
+ return (num * (intmax_t) (fsbs / bs));
+ }
+}
+
+/*
+ * Print out status about a filesystem.
+ */
+void
+prtstat(struct statfs *sfsp, struct maxwidths *mwp)
+{
+ static long blocksize;
+ static int headerlen, timesthrough;
+ static const char *header;
+ uint64_t used, availblks, inodes;
+ char * avail_str;
+
+ if (++timesthrough == 1) {
+ mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem"));
+ if (hflag) {
+ header = " Size";
+ mwp->total = mwp->used = mwp->avail = (int)strlen(header);
+ } else {
+ header = getbsize(&headerlen, &blocksize);
+ mwp->total = imax(mwp->total, headerlen);
+ }
+ mwp->used = imax(mwp->used, (int)strlen("Used"));
+ if (COMPAT_MODE("bin/df", "unix2003") && !hflag) {
+ avail_str = "Available";
+ } else {
+ avail_str = "Avail";
+ }
+ mwp->avail = imax(mwp->avail, (int)strlen(avail_str));
+
+ (void)printf("%-*s %*s %*s %*s Capacity", mwp->mntfrom,
+ "Filesystem", mwp->total, header, mwp->used, "Used",
+ mwp->avail, avail_str);
+ if (iflag) {
+ mwp->iused = imax(mwp->iused, (int)strlen(" iused"));
+ mwp->ifree = imax(mwp->ifree, (int)strlen("ifree"));
+ (void)printf(" %*s %*s %%iused", mwp->iused - 2,
+ "iused", mwp->ifree, "ifree");
+ }
+ (void)printf(" Mounted on\n");
+ }
+
+ (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname);
+ if (sfsp->f_blocks > sfsp->f_bfree)
+ used = sfsp->f_blocks - sfsp->f_bfree;
+ else
+ used = 0;
+ availblks = sfsp->f_bavail + used;
+ if (hflag) {
+ prthuman(sfsp, used);
+ } else {
+ (void)printf(" %*jd %*jd %*jd", mwp->total,
+ fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize, sfsp->f_mntonname),
+ mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize, sfsp->f_mntonname),
+ mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize, sfsp->f_mntonname));
+ }
+ if (COMPAT_MODE("bin/df", "unix2003")) {
+ /* Standard says percentage must be rounded UP to next
+ integer value, not truncated */
+ double value;
+ if (availblks == 0)
+ value = 100.0;
+ else {
+ value = (double)used / (double)availblks * 100.0;
+ if ((value-(int)value) > 0.0) value = value + 1.0;
+ }
+ (void)printf(" %5.0f%%", trunc(value));
+ } else {
+ (void)printf(" %5.0f%%",
+ availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
+ }
+ if (iflag) {
+ inodes = sfsp->f_files;
+ used = inodes - sfsp->f_ffree;
+ (void)printf(" %*llu %*llu %4.0f%% ", mwp->iused, used,
+ mwp->ifree, sfsp->f_ffree, inodes == 0 ? 100.0 :
+ (double)used / (double)inodes * 100.0);
+ } else
+ (void)printf(" ");
+ (void)printf(" %s\n", sfsp->f_mntonname);
+}
+
+/*
+ * Update the maximum field-width information in `mwp' based on
+ * the filesystem specified by `sfsp'.
+ */
+void
+update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp)
+{
+ static long blocksize;
+ int dummy;
+
+ if (blocksize == 0)
+ getbsize(&dummy, &blocksize);
+
+ mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname));
+ mwp->total = imax(mwp->total, int64width(fsbtoblk(sfsp->f_blocks,
+ sfsp->f_bsize, blocksize, sfsp->f_mntonname)));
+ if (sfsp->f_blocks >= sfsp->f_bfree)
+ mwp->used = imax(mwp->used, int64width(fsbtoblk(sfsp->f_blocks -
+ sfsp->f_bfree, sfsp->f_bsize, blocksize, sfsp->f_mntonname)));
+ mwp->avail = imax(mwp->avail, int64width(fsbtoblk(sfsp->f_bavail,
+ sfsp->f_bsize, blocksize, sfsp->f_mntonname)));
+ mwp->iused = imax(mwp->iused, int64width(sfsp->f_files - sfsp->f_ffree));
+ mwp->ifree = imax(mwp->ifree, int64width(sfsp->f_ffree));
+}
+
+/* Return the width in characters of the specified long. */
+int
+int64width(int64_t val)
+{
+ int len;
+
+ len = 0;
+ /* Negative or zero values require one extra digit. */
+ if (val <= 0) {
+ val = -val;
+ len++;
+ }
+ while (val > 0) {
+ len++;
+ val /= 10;
+ }
+
+ return (len);
+}
+
+void
+usage(void)
+{
+
+ char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]";
+ (void)fprintf(stderr,
+ "usage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] %s [filesystem ...]\n", t_flag);
+ exit(EX_USAGE);
+}
+
+char *
+makenetvfslist(void)
+{
+ char *str, *strptr, **listptr;
+#ifndef __APPLE__
+ int mib[3], maxvfsconf, cnt=0, i;
+ size_t miblen;
+ struct ovfsconf *ptr;
+#else
+ int mib[4], maxvfsconf, cnt=0, i;
+ size_t miblen;
+ struct vfsconf vfc;
+#endif
+
+ mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
+ miblen=sizeof(maxvfsconf);
+ if (sysctl(mib, 3,
+ &maxvfsconf, &miblen, NULL, 0)) {
+ warn("sysctl failed");
+ return (NULL);
+ }
+
+ if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
+ warnx("malloc failed");
+ return (NULL);
+ }
+
+#ifndef __APPLE__
+ for (ptr = getvfsent(); ptr; ptr = getvfsent())
+ if (ptr->vfc_flags & VFCF_NETWORK) {
+ listptr[cnt++] = strdup(ptr->vfc_name);
+ if (listptr[cnt-1] == NULL) {
+ warnx("malloc failed");
+ return (NULL);
+ }
+ }
+#else
+ miblen = sizeof (struct vfsconf);
+ mib[2] = VFS_CONF;
+ for (i = 0; i < maxvfsconf; i++) {
+ mib[3] = i;
+ if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) {
+ if (!(vfc.vfc_flags & MNT_LOCAL)) {
+ listptr[cnt++] = strdup(vfc.vfc_name);
+ if (listptr[cnt-1] == NULL) {
+ free(listptr);
+ warnx("malloc failed");
+ return (NULL);
+ }
+ }
+ }
+ }
+#endif
+
+ if (cnt == 0 ||
+ (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) {
+ if (cnt > 0)
+ warnx("malloc failed");
+ free(listptr);
+ return (NULL);
+ }
+
+ *str = 'n'; *(str + 1) = 'o';
+ for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
+ strncpy(strptr, listptr[i], 32);
+ strptr += strlen(listptr[i]);
+ *strptr = ',';
+ free(listptr[i]);
+ }
+ *(--strptr) = '\0';
+
+ free(listptr);
+ return (str);
+}
diff --git a/file_cmds/df/vfslist.c b/file_cmds/df/vfslist.c
new file mode 100644
index 0000000..36b789b
--- /dev/null
+++ b/file_cmds/df/vfslist.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1995
+ * 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 <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/sbin/mount/vfslist.c,v 1.4 1999/08/28 00:13:27 peter Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __APPLE__
+#include "extern.h"
+#endif
+
+static int skipvfs;
+
+int checkvfsname(const char *, const char **);
+const char **makevfslist(const char *);
+
+int
+checkvfsname(vfsname, vfslist)
+ const char *vfsname;
+ const char **vfslist;
+{
+
+ if (vfslist == NULL)
+ return (0);
+ while (*vfslist != NULL) {
+ if (strcmp(vfsname, *vfslist) == 0)
+ return (skipvfs);
+ ++vfslist;
+ }
+ return (!skipvfs);
+}
+
+const char **
+makevfslist(fslist)
+ const char *fslist;
+{
+ const char **av;
+ int i;
+ const char *cnextcp;
+ char *nextcp;
+
+ if (fslist == NULL)
+ return (NULL);
+ if (fslist[0] == 'n' && fslist[1] == 'o') {
+ fslist += 2;
+ skipvfs = 1;
+ }
+ for (i = 0, cnextcp = fslist; *cnextcp; cnextcp++)
+ if (*cnextcp == ',')
+ i++;
+ if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
+ warnx("malloc failed");
+ return (NULL);
+ }
+ nextcp = strdup(fslist);
+ i = 0;
+ av[i++] = nextcp;
+ while ((nextcp = strchr(nextcp, ',')) != NULL) {
+ *nextcp++ = '\0';
+ av[i++] = nextcp;
+ }
+ av[i++] = NULL;
+ return (av);
+}
diff --git a/file_cmds/du/du.1 b/file_cmds/du/du.1
new file mode 100644
index 0000000..1bc6b2b
--- /dev/null
+++ b/file_cmds/du/du.1
@@ -0,0 +1,172 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)du.1 8.2 (Berkeley) 4/1/94
+.\" $FreeBSD: src/usr.bin/du/du.1,v 1.30 2005/05/21 09:55:05 ru Exp $
+.\"
+.Dd June 2, 2004
+.Dt DU 1
+.Os
+.Sh NAME
+.Nm du
+.Nd display disk usage statistics
+.Sh SYNOPSIS
+.Nm du
+.Op Fl H | L | P
+.Op Fl a | s | d Ar depth
+.Op Fl c
+.Op Fl h | k | m | g
+.Op Fl x
+.Op Fl I Ar mask
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm du
+utility displays the file system block usage for each file argument
+and for each directory in the file hierarchy rooted in each directory
+argument.
+If no file is specified, the block usage of the hierarchy rooted in
+the current directory is displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Display an entry for each file in a file hierarchy.
+.It Fl c
+Display a grand total.
+.It Fl d Ar depth
+Display an entry for all files and directories
+.Ar depth
+directories deep.
+.It Fl H
+Symbolic links on the command line are followed, symbolic links in file
+hierarchies are not followed.
+.It Fl h
+"Human-readable" output.
+Use unit suffixes: Byte, Kilobyte, Megabyte,
+Gigabyte, Terabyte and Petabyte.
+.It Fl I Ar mask
+Ignore files and directories matching the specified
+.Ar mask .
+.It Fl g
+Display block counts in 1073741824-byte (1-Gbyte) blocks.
+.It Fl k
+Display block counts in 1024-byte (1-Kbyte) blocks.
+.It Fl L
+Symbolic links on the command line and in file hierarchies are followed.
+.It Fl m
+Display block counts in 1048576-byte (1-Mbyte) blocks.
+.It Fl P
+No symbolic links are followed.
+This is the default.
+.It Fl r
+Generate messages about directories that cannot be read, files
+that cannot be opened, and so on.
+This is the default case.
+This option exists solely for conformance with
+.St -xpg4 .
+.It Fl s
+Display an entry for each specified file.
+(Equivalent to
+.Fl d Li 0 )
+.It Fl x
+File system mount points are not traversed.
+.El
+.Pp
+The
+.Nm du
+utility counts the storage used by symbolic links and not the files they
+reference unless the
+.Fl H
+or
+.Fl L
+option is specified.
+If either the
+.Fl H
+or
+.Fl L
+options are specified, storage used by any symbolic links which are
+followed is not counted or displayed.
+If more than one of the
+.Fl H ,
+.Fl L ,
+and
+.Fl P
+options is specified, the last one given is used.
+.Pp
+Files having multiple hard links are counted (and displayed) a single
+time per
+.Nm du
+execution.
+Directories having multiple hard links (typically Time Machine backups) are
+counted a single time per
+.Nm du
+execution.
+.Sh ENVIRONMENT
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Fl k
+option is not specified, the block counts will be displayed in units of that
+size block.
+If
+.Ev BLOCKSIZE
+is not set, and the
+.Fl k
+option is not specified, the block counts will be displayed in 512-byte blocks.
+.El
+.Sh LEGACY DESCRIPTION
+In legacy mode, only one of the
+.Fl H ,
+.Fl L ,
+or
+.Fl P
+options may be specified.
+.Pp
+The command will detect and report a SYMLOOP error
+(loop involving symbolic links).
+In legacy mode, this is not the case.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr fts 3 ,
+.Xr compat 5 ,
+.Xr symlink 7 ,
+.Xr quot 8
+.Sh HISTORY
+A
+.Nm du
+command appeared in
+.At v1 .
diff --git a/file_cmds/du/du.c b/file_cmds/du/du.c
new file mode 100644
index 0000000..9aea790
--- /dev/null
+++ b/file_cmds/du/du.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $");
+
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/attr.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <locale.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#include <sys/sysctl.h>
+#else
+#define COMPAT_MODE(func, mode) (1)
+#endif
+
+#define KILO_SZ(n) (n)
+#define MEGA_SZ(n) ((n) * (n))
+#define GIGA_SZ(n) ((n) * (n) * (n))
+#define TERA_SZ(n) ((n) * (n) * (n) * (n))
+#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
+
+#define KILO_2_SZ (KILO_SZ(1024ULL))
+#define MEGA_2_SZ (MEGA_SZ(1024ULL))
+#define GIGA_2_SZ (GIGA_SZ(1024ULL))
+#define TERA_2_SZ (TERA_SZ(1024ULL))
+#define PETA_2_SZ (PETA_SZ(1024ULL))
+
+#define KILO_SI_SZ (KILO_SZ(1000ULL))
+#define MEGA_SI_SZ (MEGA_SZ(1000ULL))
+#define GIGA_SI_SZ (GIGA_SZ(1000ULL))
+#define TERA_SI_SZ (TERA_SZ(1000ULL))
+#define PETA_SI_SZ (PETA_SZ(1000ULL))
+
+unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
+unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
+unsigned long long *valp;
+
+typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
+
+int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
+
+SLIST_HEAD(ignhead, ignentry) ignores;
+struct ignentry {
+ char *mask;
+ SLIST_ENTRY(ignentry) next;
+};
+
+static int linkchk(FTSENT *);
+static int dirlinkchk(FTSENT *);
+static void usage(void);
+void prthumanval(double);
+unit_t unit_adjust(double *);
+void ignoreadd(const char *);
+void ignoreclean(void);
+int ignorep(FTSENT *);
+
+int
+main(int argc, char *argv[])
+{
+ FTS *fts;
+ FTSENT *p;
+ off_t savednumber = 0;
+ long blocksize;
+ int ftsoptions;
+ int listall;
+ int depth;
+ int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
+ char **save;
+ static char dot[] = ".";
+ off_t *ftsnum, *ftsparnum;
+
+ setlocale(LC_ALL, "");
+
+ Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
+
+ save = argv;
+ ftsoptions = FTS_NOCHDIR;
+ depth = INT_MAX;
+ SLIST_INIT(&ignores);
+
+ while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1)
+ switch (ch) {
+ case 'H':
+ Lflag = Pflag = 0;
+ Hflag = 1;
+ break;
+ case 'I':
+ ignoreadd(optarg);
+ break;
+ case 'L':
+ Hflag = Pflag = 0;
+ Lflag = 1;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ Pflag = 1;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ errno = 0;
+ depth = atoi(optarg);
+ if (errno == ERANGE || depth < 0) {
+ warnx("invalid argument to option d: %s", optarg);
+ usage();
+ }
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'h':
+ putenv("BLOCKSIZE=512");
+ hflag = 1;
+ valp = vals_base2;
+ break;
+ case 'k':
+ hflag = 0;
+ putenv("BLOCKSIZE=1024");
+ break;
+ case 'm':
+ hflag = 0;
+ putenv("BLOCKSIZE=1048576");
+ break;
+ case 'g':
+ hflag = 0;
+ putenv("BLOCKSIZE=1g");
+ break;
+ case 'r': /* Compatibility. */
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+// argc -= optind;
+ argv += optind;
+
+ /*
+ * XXX
+ * Because of the way that fts(3) works, logical walks will not count
+ * the blocks actually used by symbolic links. We rationalize this by
+ * noting that users computing logical sizes are likely to do logical
+ * copies, so not counting the links is correct. The real reason is
+ * that we'd have to re-implement the kernel's symbolic link traversing
+ * algorithm to get this right. If, for example, you have relative
+ * symbolic links referencing other relative symbolic links, it gets
+ * very nasty, very fast. The bottom line is that it's documented in
+ * the man page, so it's a feature.
+ */
+
+ if (Hflag + Lflag + Pflag > 1)
+ usage();
+
+ if (Hflag + Lflag + Pflag == 0)
+ Pflag = 1; /* -P (physical) is default */
+
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+
+ if (Lflag)
+ ftsoptions |= FTS_LOGICAL;
+
+ if (Pflag)
+ ftsoptions |= FTS_PHYSICAL;
+
+ listall = 0;
+
+ if (aflag) {
+ if (sflag || dflag)
+ usage();
+ listall = 1;
+ } else if (sflag) {
+ if (dflag)
+ usage();
+ depth = 0;
+ }
+
+ if (!*argv) {
+ argv = save;
+ argv[0] = dot;
+ argv[1] = NULL;
+ }
+
+ (void) getbsize(&notused, &blocksize);
+ blocksize /= 512;
+
+#ifdef __APPLE__
+ // "du" should not have any side effect on disk usage,
+ // so prevent materializing dataless directories upon traversal
+ rval = 1;
+ (void) sysctlbyname("vfs.nspace.prevent_materialization", NULL, NULL, &rval, sizeof(rval));
+#endif /* __APPLE__ */
+
+ rval = 0;
+
+ if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err(1, "fts_open");
+
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (ignorep(p) || dirlinkchk(p))
+ fts_set(fts, p, FTS_SKIP);
+ break;
+ case FTS_DP:
+ if (ignorep(p))
+ break;
+
+ ftsparnum = (off_t *)&p->fts_parent->fts_number;
+ ftsnum = (off_t *)&p->fts_number;
+ ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks;
+
+ if (p->fts_level <= depth) {
+ if (hflag) {
+ (void) prthumanval(howmany(*ftsnum, blocksize));
+ (void) printf("\t%s\n", p->fts_path);
+ } else {
+ (void) printf("%jd\t%s\n",
+ (intmax_t)howmany(*ftsnum, blocksize),
+ p->fts_path);
+ }
+ }
+ 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:
+ case FTS_NS:
+ 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;
+
+ if (p->fts_statp->st_nlink > 1 && linkchk(p))
+ break;
+
+ if (listall || p->fts_level == 0) {
+ if (hflag) {
+ (void) prthumanval(howmany(p->fts_statp->st_blocks,
+ blocksize));
+ (void) printf("\t%s\n", p->fts_path);
+ } else {
+ (void) printf("%jd\t%s\n",
+ (intmax_t)howmany(p->fts_statp->st_blocks, blocksize),
+ p->fts_path);
+ }
+ }
+
+ ftsparnum = (off_t *)&p->fts_parent->fts_number;
+ ftsparnum[0] += p->fts_statp->st_blocks;
+ }
+ savednumber = ((off_t *)&p->fts_parent->fts_number)[0];
+ }
+
+ if (errno)
+ err(1, "fts_read");
+
+ if (cflag) {
+ if (hflag) {
+ (void) prthumanval(howmany(savednumber, blocksize));
+ (void) printf("\ttotal\n");
+ } else {
+ (void) printf("%jd\ttotal\n", (intmax_t)howmany(savednumber, blocksize));
+ }
+ }
+
+ ignoreclean();
+ exit(rval);
+}
+
+static int
+linkchk(FTSENT *p)
+{
+ struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ int links;
+ dev_t dev;
+ ino_t ino;
+ };
+ static const size_t links_hash_initial_size = 8192;
+ static struct links_entry **buckets;
+ static struct links_entry *free_list;
+ static size_t number_buckets;
+ static unsigned long number_entries;
+ static char stop_allocating;
+ struct links_entry *le, **new_buckets;
+ struct stat *st;
+ size_t i, new_size;
+ int hash;
+
+ st = p->fts_statp;
+
+ /* If necessary, initialize the hash table. */
+ if (buckets == NULL) {
+ number_buckets = links_hash_initial_size;
+ buckets = malloc(number_buckets * sizeof(buckets[0]));
+ if (buckets == NULL)
+ errx(1, "No memory for hardlink detection");
+ for (i = 0; i < number_buckets; i++)
+ buckets[i] = NULL;
+ }
+
+ /* If the hash table is getting too full, enlarge it. */
+ if (number_entries > number_buckets * 10 && !stop_allocating) {
+ new_size = number_buckets * 2;
+ new_buckets = malloc(new_size * sizeof(struct links_entry *));
+
+ /* Try releasing the free list to see if that helps. */
+ if (new_buckets == NULL && free_list != NULL) {
+ while (free_list != NULL) {
+ le = free_list;
+ free_list = le->next;
+ free(le);
+ }
+ new_buckets = malloc(new_size * sizeof(new_buckets[0]));
+ }
+
+ if (new_buckets == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking hard links");
+ } else {
+ memset(new_buckets, 0,
+ new_size * sizeof(struct links_entry *));
+ for (i = 0; i < number_buckets; i++) {
+ while (buckets[i] != NULL) {
+ /* Remove entry from old bucket. */
+ le = buckets[i];
+ buckets[i] = le->next;
+
+ /* Add entry to new bucket. */
+ hash = (le->dev ^ le->ino) % new_size;
+
+ if (new_buckets[hash] != NULL)
+ new_buckets[hash]->previous =
+ le;
+ le->next = new_buckets[hash];
+ le->previous = NULL;
+ new_buckets[hash] = le;
+ }
+ }
+ free(buckets);
+ buckets = new_buckets;
+ number_buckets = new_size;
+ }
+ }
+
+ /* Try to locate this entry in the hash table. */
+ hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
+ for (le = buckets[hash]; le != NULL; le = le->next) {
+ if (le->dev == st->st_dev && le->ino == st->st_ino) {
+ /*
+ * Save memory by releasing an entry when we've seen
+ * all of it's links.
+ */
+ if (--le->links <= 0) {
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (buckets[hash] == le)
+ buckets[hash] = le->next;
+ number_entries--;
+ /* Recycle this node through the free list */
+ if (stop_allocating) {
+ free(le);
+ } else {
+ le->next = free_list;
+ free_list = le;
+ }
+ }
+ return (1);
+ }
+ }
+
+ if (stop_allocating)
+ return (0);
+
+ /* Add this entry to the links cache. */
+ if (free_list != NULL) {
+ /* Pull a node from the free list if we can. */
+ le = free_list;
+ free_list = le->next;
+ } else
+ /* Malloc one if we have to. */
+ le = malloc(sizeof(struct links_entry));
+ if (le == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking hard links");
+ return (0);
+ }
+ le->dev = st->st_dev;
+ le->ino = st->st_ino;
+ le->links = st->st_nlink - 1;
+ number_entries++;
+ le->next = buckets[hash];
+ le->previous = NULL;
+ if (buckets[hash] != NULL)
+ buckets[hash]->previous = le;
+ buckets[hash] = le;
+ return (0);
+}
+
+static int
+dirlinkchk(FTSENT *p)
+{
+ struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ int links;
+ dev_t dev;
+ ino_t ino;
+ };
+ static const size_t links_hash_initial_size = 8192;
+ static struct links_entry **buckets;
+ static struct links_entry *free_list;
+ static size_t number_buckets;
+ static unsigned long number_entries;
+ static char stop_allocating;
+ struct links_entry *le, **new_buckets;
+ struct stat *st;
+ size_t i, new_size;
+ int hash;
+ struct attrbuf {
+ int size;
+ int linkcount;
+ } buf;
+ struct attrlist attrList;
+
+ memset(&attrList, 0, sizeof(attrList));
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.dirattr = ATTR_DIR_LINKCOUNT;
+ if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0))
+ return 0;
+ if (buf.linkcount == 1)
+ return 0;
+ st = p->fts_statp;
+
+ /* If necessary, initialize the hash table. */
+ if (buckets == NULL) {
+ number_buckets = links_hash_initial_size;
+ buckets = malloc(number_buckets * sizeof(buckets[0]));
+ if (buckets == NULL)
+ errx(1, "No memory for directory hardlink detection");
+ for (i = 0; i < number_buckets; i++)
+ buckets[i] = NULL;
+ }
+
+ /* If the hash table is getting too full, enlarge it. */
+ if (number_entries > number_buckets * 10 && !stop_allocating) {
+ new_size = number_buckets * 2;
+ new_buckets = malloc(new_size * sizeof(struct links_entry *));
+
+ /* Try releasing the free list to see if that helps. */
+ if (new_buckets == NULL && free_list != NULL) {
+ while (free_list != NULL) {
+ le = free_list;
+ free_list = le->next;
+ free(le);
+ }
+ new_buckets = malloc(new_size * sizeof(new_buckets[0]));
+ }
+
+ if (new_buckets == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking directory hard links");
+ } else {
+ memset(new_buckets, 0,
+ new_size * sizeof(struct links_entry *));
+ for (i = 0; i < number_buckets; i++) {
+ while (buckets[i] != NULL) {
+ /* Remove entry from old bucket. */
+ le = buckets[i];
+ buckets[i] = le->next;
+
+ /* Add entry to new bucket. */
+ hash = (le->dev ^ le->ino) % new_size;
+
+ if (new_buckets[hash] != NULL)
+ new_buckets[hash]->previous =
+ le;
+ le->next = new_buckets[hash];
+ le->previous = NULL;
+ new_buckets[hash] = le;
+ }
+ }
+ free(buckets);
+ buckets = new_buckets;
+ number_buckets = new_size;
+ }
+ }
+
+ /* Try to locate this entry in the hash table. */
+ hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
+ for (le = buckets[hash]; le != NULL; le = le->next) {
+ if (le->dev == st->st_dev && le->ino == st->st_ino) {
+ /*
+ * Save memory by releasing an entry when we've seen
+ * all of it's links.
+ */
+ if (--le->links <= 0) {
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (buckets[hash] == le)
+ buckets[hash] = le->next;
+ number_entries--;
+ /* Recycle this node through the free list */
+ if (stop_allocating) {
+ free(le);
+ } else {
+ le->next = free_list;
+ free_list = le;
+ }
+ }
+ return (1);
+ }
+ }
+
+ if (stop_allocating)
+ return (0);
+ /* Add this entry to the links cache. */
+ if (free_list != NULL) {
+ /* Pull a node from the free list if we can. */
+ le = free_list;
+ free_list = le->next;
+ } else
+ /* Malloc one if we have to. */
+ le = malloc(sizeof(struct links_entry));
+ if (le == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking hard links");
+ return (0);
+ }
+ le->dev = st->st_dev;
+ le->ino = st->st_ino;
+ le->links = buf.linkcount - 1;
+ number_entries++;
+ le->next = buckets[hash];
+ le->previous = NULL;
+ if (buckets[hash] != NULL)
+ buckets[hash]->previous = le;
+ buckets[hash] = le;
+ return (0);
+}
+
+/*
+ * Output in "human-readable" format. Uses 3 digits max and puts
+ * unit suffixes at the end. Makes output compact and easy to read,
+ * especially on huge disks.
+ *
+ */
+unit_t
+unit_adjust(double *val)
+{
+ double abval;
+ unit_t unit;
+ unsigned int unit_sz;
+
+ abval = fabs(*val);
+
+ unit_sz = abval ? ilogb(abval) / 10 : 0;
+
+ if (unit_sz >= UNIT_MAX) {
+ unit = NONE;
+ } else {
+ unit = unitp[unit_sz];
+ *val /= (double)valp[unit_sz];
+ }
+
+ return (unit);
+}
+
+void
+prthumanval(double bytes)
+{
+ unit_t unit;
+
+ bytes *= 512;
+ unit = unit_adjust(&bytes);
+
+ if (bytes == 0)
+ (void)printf(" 0B");
+ else if (bytes > 10)
+ (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
+ else
+ (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n");
+ exit(EX_USAGE);
+}
+
+void
+ignoreadd(const char *mask)
+{
+ struct ignentry *ign;
+
+ ign = calloc(1, sizeof(*ign));
+ if (ign == NULL)
+ errx(1, "cannot allocate memory");
+ ign->mask = strdup(mask);
+ if (ign->mask == NULL)
+ errx(1, "cannot allocate memory");
+ SLIST_INSERT_HEAD(&ignores, ign, next);
+}
+
+void
+ignoreclean(void)
+{
+ struct ignentry *ign;
+
+ while (!SLIST_EMPTY(&ignores)) {
+ ign = SLIST_FIRST(&ignores);
+ SLIST_REMOVE_HEAD(&ignores, next);
+ free(ign->mask);
+ free(ign);
+ }
+}
+
+int
+ignorep(FTSENT *ent)
+{
+ struct ignentry *ign;
+
+#ifdef __APPLE__
+ 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("devfs", 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;
+ }
+ }
+#endif /* __APPLE__ */
+ SLIST_FOREACH(ign, &ignores, next)
+ if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
+ return 1;
+ return 0;
+}
diff --git a/file_cmds/file_cmds.xcodeproj/project.pbxproj b/file_cmds/file_cmds.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..e4e94ec
--- /dev/null
+++ b/file_cmds/file_cmds.xcodeproj/project.pbxproj
@@ -0,0 +1,3984 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 3E966CE71FB2211F0019F7A1 /* tests */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 3E966CE91FB2211F0019F7A1 /* Build configuration list for PBXAggregateTarget "tests" */;
+ buildPhases = (
+ 3E966CED1FB2215C0019F7A1 /* CopyFiles */,
+ 3E966CEF1FB221740019F7A1 /* CopyFiles */,
+ );
+ dependencies = (
+ );
+ name = tests;
+ productName = tests;
+ };
+ 729D07252347EC4D000716E5 /* macos_host_tools */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 729D07682347EC4D000716E5 /* Build configuration list for PBXAggregateTarget "macos_host_tools" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 729D074E2347EC4D000716E5 /* PBXTargetDependency */,
+ );
+ name = macos_host_tools;
+ productName = macos_host_tools;
+ };
+ FC8A8C3C14B64A9D001B97AD /* shar */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C3D14B64A9D001B97AD /* Build configuration list for PBXAggregateTarget "shar" */;
+ buildPhases = (
+ FC8A8CC714B65CEB001B97AD /* ShellScript */,
+ FC8A8C3F14B64AA8001B97AD /* CopyFiles */,
+ );
+ dependencies = (
+ );
+ name = shar;
+ productName = shar;
+ };
+ FC8A8C4614B64DCD001B97AD /* readlink */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C4714B64DCE001B97AD /* Build configuration list for PBXAggregateTarget "readlink" */;
+ buildPhases = (
+ FC8A8C4B14B64DEA001B97AD /* Copy Files */,
+ FC8A8C4C14B64DF9001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */,
+ );
+ name = readlink;
+ productName = readlink;
+ };
+ FC8A8C5014B650CF001B97AD /* unlink */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C5614B650CF001B97AD /* Build configuration list for PBXAggregateTarget "unlink" */;
+ buildPhases = (
+ FC8A8C5314B650CF001B97AD /* CopyFiles */,
+ FC8A8C5514B650CF001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8C5914B65238001B97AD /* PBXTargetDependency */,
+ );
+ name = unlink;
+ productName = readlink;
+ };
+ FC8A8C5B14B652E1001B97AD /* chgrp */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C6114B652E1001B97AD /* Build configuration list for PBXAggregateTarget "chgrp" */;
+ buildPhases = (
+ FC8A8C5E14B652E1001B97AD /* CopyFiles */,
+ FC8A8C6014B652E1001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8C6414B652FA001B97AD /* PBXTargetDependency */,
+ );
+ name = chgrp;
+ productName = readlink;
+ };
+ FC8A8C6714B6536D001B97AD /* sum */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C6D14B6536D001B97AD /* Build configuration list for PBXAggregateTarget "sum" */;
+ buildPhases = (
+ FC8A8C6A14B6536D001B97AD /* CopyFiles */,
+ FC8A8C6C14B6536D001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8C7014B6537C001B97AD /* PBXTargetDependency */,
+ );
+ name = sum;
+ productName = readlink;
+ };
+ FC8A8C7314B6554E001B97AD /* link */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C7914B6554E001B97AD /* Build configuration list for PBXAggregateTarget "link" */;
+ buildPhases = (
+ FC8A8C7614B6554E001B97AD /* CopyFiles */,
+ FC8A8C7814B6554E001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8C7C14B65562001B97AD /* PBXTargetDependency */,
+ );
+ name = link;
+ productName = readlink;
+ };
+ FC8A8C8014B655ED001B97AD /* executables */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8C8114B655ED001B97AD /* Build configuration list for PBXAggregateTarget "executables" */;
+ buildPhases = (
+ );
+ dependencies = (
+ FC8A8C8414B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C8614B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C8814B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C8A14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C8C14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C8E14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9014B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9214B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9414B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9614B655FD001B97AD /* PBXTargetDependency */,
+ FDAD94AA1808BDAA00B4D5A0 /* PBXTargetDependency */,
+ FC8A8C9814B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9A14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9C14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8C9E14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CA014B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CA214B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CA414B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CA614B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CA814B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CAA14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CAC14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CAE14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CB014B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CB214B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CB414B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CB614B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CB814B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CBA14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CBC14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CBE14B655FD001B97AD /* PBXTargetDependency */,
+ FC8A8CD314B67BFD001B97AD /* PBXTargetDependency */,
+ FC8A8CC014B655FD001B97AD /* PBXTargetDependency */,
+ );
+ name = executables;
+ productName = executables;
+ };
+ FC8A8CC814B65F92001B97AD /* uncompress */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FC8A8CCE14B65F92001B97AD /* Build configuration list for PBXAggregateTarget "uncompress" */;
+ buildPhases = (
+ FC8A8CCB14B65F92001B97AD /* CopyFiles */,
+ FC8A8CCD14B65F92001B97AD /* ShellScript */,
+ );
+ dependencies = (
+ FC8A8CC914B65F92001B97AD /* PBXTargetDependency */,
+ );
+ name = uncompress;
+ productName = readlink;
+ };
+ FDFF0C501811BA2F00BFC477 /* eOS */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FDFF0C931811BA2F00BFC477 /* Build configuration list for PBXAggregateTarget "eOS" */;
+ buildPhases = (
+ );
+ dependencies = (
+ FDFF0C511811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C531811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C551811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C571811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C591811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C5B1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C5D1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C5F1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C611811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C631811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C671811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C691811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C6B1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C6D1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C6F1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C711811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C731811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C751811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C771811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C791811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C7B1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C7D1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C7F1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C811811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C831811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C851811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C871811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C891811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C8B1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C8D1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C8F1811BA2F00BFC477 /* PBXTargetDependency */,
+ FDFF0C911811BA2F00BFC477 /* PBXTargetDependency */,
+ );
+ name = eOS;
+ productName = executables;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 3E59B9311D4A767600D3128C /* futimens.c in Sources */ = {isa = PBXBuildFile; fileRef = 3E59B9301D4A767600D3128C /* futimens.c */; };
+ 3E966CEE1FB2216F0019F7A1 /* file_cmds.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */; };
+ 3E966CF01FB2218A0019F7A1 /* chgrp.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */; };
+ 729D06D8230B5E42000716E5 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729D06D7230B5E42000716E5 /* CoreFoundation.framework */; };
+ 7D0A20EA2499364700F0F6D7 /* metrics.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D0A20E92499364700F0F6D7 /* metrics.c */; };
+ FC8A8A2814B6486E001B97AD /* chflags.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDCC14B6460C0070FACB /* chflags.c */; };
+ FC8A8BE414B6494B001B97AD /* chflags.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDCB14B6460C0070FACB /* chflags.1 */; };
+ FC8A8BE514B64958001B97AD /* chmod.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD014B6460C0070FACB /* chmod.c */; };
+ FC8A8BE614B6495B001B97AD /* chmod_acl.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD114B6460C0070FACB /* chmod_acl.c */; };
+ FC8A8BE714B6495D001B97AD /* chmod.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDCF14B6460C0070FACB /* chmod.1 */; };
+ FC8A8BE814B64962001B97AD /* chown.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD714B6460C0070FACB /* chown.c */; };
+ FC8A8BEA14B6496E001B97AD /* cksum.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDDA14B6460C0070FACB /* cksum.1 */; };
+ FC8A8BEB14B64970001B97AD /* cksum.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDB14B6460C0070FACB /* cksum.c */; };
+ FC8A8BEC14B64972001B97AD /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDC14B6460C0070FACB /* crc.c */; };
+ FC8A8BED14B64975001B97AD /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDD14B6460C0070FACB /* crc32.c */; };
+ FC8A8BEE14B64977001B97AD /* print.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE014B6460C0070FACB /* print.c */; };
+ FC8A8BEF14B6497A001B97AD /* sum1.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE114B6460C0070FACB /* sum1.c */; };
+ FC8A8BF014B6497D001B97AD /* sum2.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE214B6460C0070FACB /* sum2.c */; };
+ FC8A8BF114B64982001B97AD /* compress.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDE414B6460C0070FACB /* compress.1 */; };
+ FC8A8BF314B64988001B97AD /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE514B6460C0070FACB /* compress.c */; };
+ FC8A8BF414B6498A001B97AD /* zopen.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDEE14B6460C0070FACB /* zopen.c */; };
+ FC8A8BF514B64995001B97AD /* cp.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF214B6460C0070FACB /* cp.c */; };
+ FC8A8BF614B64998001B97AD /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF514B6460C0070FACB /* utils.c */; };
+ FC8A8BF714B6499A001B97AD /* cp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDF114B6460C0070FACB /* cp.1 */; };
+ FC8A8BF814B649A4001B97AD /* args.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF914B6460C0070FACB /* args.c */; };
+ FC8A8BF914B649A5001B97AD /* conv.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFA14B6460C0070FACB /* conv.c */; };
+ FC8A8BFA14B649A7001B97AD /* conv_tab.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFB14B6460C0070FACB /* conv_tab.c */; };
+ FC8A8BFB14B649A9001B97AD /* dd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDFC14B6460C0070FACB /* dd.1 */; };
+ FC8A8BFC14B649AC001B97AD /* dd.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFD14B6460C0070FACB /* dd.c */; };
+ FC8A8BFD14B649AE001B97AD /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0114B6460C0070FACB /* misc.c */; };
+ FC8A8BFE14B649B1001B97AD /* position.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0214B6460C0070FACB /* position.c */; };
+ FC8A8BFF14B649B4001B97AD /* df.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0414B6460C0070FACB /* df.1 */; };
+ FC8A8C0114B649D1001B97AD /* df.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0514B6460C0070FACB /* df.c */; };
+ FC8A8C0214B649D4001B97AD /* du.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0A14B6460C0070FACB /* du.c */; };
+ FC8A8C0314B649D8001B97AD /* du.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0914B6460C0070FACB /* du.1 */; };
+ FC8A8C0414B649DD001B97AD /* xinstall.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1014B6460C0070FACB /* xinstall.c */; };
+ FC8A8C0514B649DF001B97AD /* install.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0D14B6460C0070FACB /* install.1 */; };
+ FC8A8C0614B649E2001B97AD /* ipcrm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1214B6460C0070FACB /* ipcrm.1 */; };
+ FC8A8C0714B649E3001B97AD /* ipcrm.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1314B6460C0070FACB /* ipcrm.c */; };
+ FC8A8C0814B649E8001B97AD /* ipcs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1714B6460C0070FACB /* ipcs.c */; };
+ FC8A8C0914B649EA001B97AD /* ipcs.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1614B6460C0070FACB /* ipcs.1 */; };
+ FC8A8C0A14B649ED001B97AD /* ln.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1A14B6460C0070FACB /* ln.1 */; };
+ FC8A8C0B14B649EF001B97AD /* ln.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1B14B6460C0070FACB /* ln.c */; };
+ FC8A8C0C14B649F5001B97AD /* ls.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2114B6460C0070FACB /* ls.1 */; };
+ FC8A8C0D14B649F7001B97AD /* cmp.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1F14B6460C0070FACB /* cmp.c */; };
+ FC8A8C0E14B649F9001B97AD /* ls.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2214B6460C0070FACB /* ls.c */; };
+ FC8A8C0F14B649FC001B97AD /* print.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2514B6460C0070FACB /* print.c */; };
+ FC8A8C1014B649FE001B97AD /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2614B6460C0070FACB /* util.c */; };
+ FC8A8C1114B64A04001B97AD /* mkdir.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2B14B6460C0070FACB /* mkdir.c */; };
+ FC8A8C1214B64A06001B97AD /* mkdir.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2A14B6460C0070FACB /* mkdir.1 */; };
+ FC8A8C1314B64A09001B97AD /* mkfifo.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2E14B6460C0070FACB /* mkfifo.1 */; };
+ FC8A8C1414B64A0A001B97AD /* mkfifo.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2F14B6460C0070FACB /* mkfifo.c */; };
+ FC8A8C1514B64A0D001B97AD /* mknod.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE3214B6460C0070FACB /* mknod.8 */; };
+ FC8A8C1614B64A0F001B97AD /* mknod.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3314B6460C0070FACB /* mknod.c */; };
+ FC8A8C1714B64A14001B97AD /* commoncrypto.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3514B6460C0070FACB /* commoncrypto.c */; };
+ FC8A8C1814B64A17001B97AD /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3714B6460C0070FACB /* compare.c */; };
+ FC8A8C1914B64A1A001B97AD /* create.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3814B6460C0070FACB /* create.c */; };
+ FC8A8C1A14B64A22001B97AD /* excludes.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3914B6460C0070FACB /* excludes.c */; };
+ FC8A8C1B14B64A27001B97AD /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3C14B6460C0070FACB /* misc.c */; };
+ FC8A8C1C14B64A2D001B97AD /* mtree.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3E14B6460C0070FACB /* mtree.c */; };
+ FC8A8C1D14B64A31001B97AD /* spec.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4014B6460C0070FACB /* spec.c */; };
+ FC8A8C1E14B64A34001B97AD /* specspec.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4114B6460C0070FACB /* specspec.c */; };
+ FC8A8C1F14B64A38001B97AD /* verify.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4814B6460C0070FACB /* verify.c */; };
+ FC8A8C2014B64A40001B97AD /* mtree.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE3D14B6460C0070FACB /* mtree.8 */; };
+ FC8A8C2114B64A49001B97AD /* mv.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE4B14B6460C0070FACB /* mv.1 */; };
+ FC8A8C2214B64A4B001B97AD /* mv.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4C14B6460C0070FACB /* mv.c */; };
+ FC8A8C2314B64A4F001B97AD /* pathchk.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE5014B6460C0070FACB /* pathchk.1 */; };
+ FC8A8C2414B64A53001B97AD /* pathchk.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5114B6460C0070FACB /* pathchk.c */; };
+ FC8A8C2514B64A59001B97AD /* pax.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE6614B6460C0070FACB /* pax.1 */; };
+ FC8A8C2714B64A73001B97AD /* ar_io.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5314B6460C0070FACB /* ar_io.c */; };
+ FC8A8C2814B64A73001B97AD /* ar_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5414B6460C0070FACB /* ar_subs.c */; };
+ FC8A8C2914B64A73001B97AD /* buf_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5514B6460C0070FACB /* buf_subs.c */; };
+ FC8A8C2A14B64A73001B97AD /* cache.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5614B6460C0070FACB /* cache.c */; };
+ FC8A8C2B14B64A73001B97AD /* cpio.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5914B6460C0070FACB /* cpio.c */; };
+ FC8A8C2C14B64A73001B97AD /* file_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5C14B6460C0070FACB /* file_subs.c */; };
+ FC8A8C2D14B64A73001B97AD /* ftree.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5D14B6460C0070FACB /* ftree.c */; };
+ FC8A8C2E14B64A73001B97AD /* gen_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5F14B6460C0070FACB /* gen_subs.c */; };
+ FC8A8C2F14B64A73001B97AD /* getoldopt.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6014B6460C0070FACB /* getoldopt.c */; };
+ FC8A8C3014B64A73001B97AD /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6214B6460C0070FACB /* options.c */; };
+ FC8A8C3114B64A73001B97AD /* pat_rep.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6414B6460C0070FACB /* pat_rep.c */; };
+ FC8A8C3214B64A73001B97AD /* pax.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6714B6460C0070FACB /* pax.c */; };
+ FC8A8C3314B64A73001B97AD /* pax_format.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6914B6460C0070FACB /* pax_format.c */; };
+ FC8A8C3414B64A73001B97AD /* sel_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6B14B6460C0070FACB /* sel_subs.c */; };
+ FC8A8C3514B64A73001B97AD /* tables.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6D14B6460C0070FACB /* tables.c */; };
+ FC8A8C3614B64A73001B97AD /* tar.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6F14B6460C0070FACB /* tar.c */; };
+ FC8A8C3714B64A73001B97AD /* tty_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7114B6460C0070FACB /* tty_subs.c */; };
+ FC8A8C3814B64A7C001B97AD /* rm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE7414B6460C0070FACB /* rm.1 */; };
+ FC8A8C3914B64A7E001B97AD /* rm.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7514B6460C0070FACB /* rm.c */; };
+ FC8A8C3A14B64A85001B97AD /* rmdir.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE7814B6460C0070FACB /* rmdir.1 */; };
+ FC8A8C3B14B64A88001B97AD /* rmdir.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7914B6460C0070FACB /* rmdir.c */; };
+ FC8A8C4014B64AAB001B97AD /* shar.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8014B6460C0070FACB /* shar.1 */; };
+ FC8A8C4114B64AC0001B97AD /* stat.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8414B6460C0070FACB /* stat.1 */; };
+ FC8A8C4214B64AC3001B97AD /* stat.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE8514B6460C0070FACB /* stat.c */; };
+ FC8A8C4314B64AC7001B97AD /* touch.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE8914B6460C0070FACB /* touch.c */; };
+ FC8A8C4514B64AD7001B97AD /* touch.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8814B6460C0070FACB /* touch.1 */; };
+ FC8A8C4E14B64EAE001B97AD /* readlink.1 in Copy Files */ = {isa = PBXBuildFile; fileRef = FC8A8C4D14B64EA8001B97AD /* readlink.1 */; };
+ FC8A8C5A14B6525A001B97AD /* unlink.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C4F14B650C3001B97AD /* unlink.1 */; };
+ FC8A8C6514B65307001B97AD /* chgrp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDD514B6460C0070FACB /* chgrp.1 */; };
+ FC8A8C7114B65389001B97AD /* sum.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C6614B6535B001B97AD /* sum.1 */; };
+ FC8A8C7D14B65575001B97AD /* link.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C7214B65547001B97AD /* link.1 */; };
+ FC8A8C7F14B65586001B97AD /* symlink.7 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1D14B6460C0070FACB /* symlink.7 */; };
+ FC8A8CC214B658D7001B97AD /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC114B658D6001B97AD /* libutil.dylib */; };
+ FC8A8CC314B6598F001B97AD /* vfslist.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0714B6460C0070FACB /* vfslist.c */; };
+ FC8A8CC514B65C3D001B97AD /* libcurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC414B65C3D001B97AD /* libcurses.dylib */; };
+ FC8A8CC614B65C46001B97AD /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC114B658D6001B97AD /* libutil.dylib */; };
+ FC8A8CD014B65F9B001B97AD /* uncompress.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDEB14B6460C0070FACB /* uncompress.1 */; };
+ FC8A8CD114B66E10001B97AD /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDC14B6460C0070FACB /* crc.c */; };
+ FC8A8CD414B67D60001B97AD /* chown.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDD614B6460C0070FACB /* chown.8 */; };
+ FDAD949F1808BBB900B4D5A0 /* gzip.c in Sources */ = {isa = PBXBuildFile; fileRef = FDAD94831808BB3A00B4D5A0 /* gzip.c */; };
+ FDAD94A21808BC9100B4D5A0 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */; };
+ FDAD94A41808BC9700B4D5A0 /* liblzma.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */; };
+ FDAD94A61808BC9B00B4D5A0 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A51808BC9B00B4D5A0 /* libz.dylib */; };
+ FDAD94A81808BD0600B4D5A0 /* gzip.1 in Install Man Page */ = {isa = PBXBuildFile; fileRef = FDAD94821808BB3A00B4D5A0 /* gzip.1 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 729D074F2347EC4D000716E5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B8C14B648ED001B97AD;
+ remoteInfo = mtree;
+ };
+ FC8A8C4914B64DE1001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BC414B648EF001B97AD;
+ remoteInfo = stat;
+ };
+ FC8A8C5814B65238001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BAC14B648EF001B97AD;
+ remoteInfo = rm;
+ };
+ FC8A8C6314B652FA001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1414B648E0001B97AD;
+ remoteInfo = chown;
+ };
+ FC8A8C6F14B6537C001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1C14B648E3001B97AD;
+ remoteInfo = cksum;
+ };
+ FC8A8C7B14B65562001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B6414B648EC001B97AD;
+ remoteInfo = ln;
+ };
+ FC8A8C8314B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FCB1BDB714B645D10070FACB;
+ remoteInfo = chflags;
+ };
+ FC8A8C8514B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C5B14B652E1001B97AD;
+ remoteInfo = chgrp;
+ };
+ FC8A8C8714B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B0B14B648D7001B97AD;
+ remoteInfo = chmod;
+ };
+ FC8A8C8914B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1414B648E0001B97AD;
+ remoteInfo = chown;
+ };
+ FC8A8C8B14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1C14B648E3001B97AD;
+ remoteInfo = cksum;
+ };
+ FC8A8C8D14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B2414B648E5001B97AD;
+ remoteInfo = compress;
+ };
+ FC8A8C8F14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B2C14B648E7001B97AD;
+ remoteInfo = cp;
+ };
+ FC8A8C9114B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B3414B648EA001B97AD;
+ remoteInfo = dd;
+ };
+ FC8A8C9314B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B3C14B648EA001B97AD;
+ remoteInfo = df;
+ };
+ FC8A8C9514B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B4414B648EB001B97AD;
+ remoteInfo = du;
+ };
+ FC8A8C9714B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B4C14B648EB001B97AD;
+ remoteInfo = install;
+ };
+ FC8A8C9914B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B5414B648EB001B97AD;
+ remoteInfo = ipcrm;
+ };
+ FC8A8C9B14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B5C14B648EC001B97AD;
+ remoteInfo = ipcs;
+ };
+ FC8A8C9D14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C7314B6554E001B97AD;
+ remoteInfo = link;
+ };
+ FC8A8C9F14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B6414B648EC001B97AD;
+ remoteInfo = ln;
+ };
+ FC8A8CA114B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B6C14B648ED001B97AD;
+ remoteInfo = ls;
+ };
+ FC8A8CA314B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B7414B648ED001B97AD;
+ remoteInfo = mkdir;
+ };
+ FC8A8CA514B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B7C14B648ED001B97AD;
+ remoteInfo = mkfifo;
+ };
+ FC8A8CA714B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B8414B648ED001B97AD;
+ remoteInfo = mknod;
+ };
+ FC8A8CA914B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B8C14B648ED001B97AD;
+ remoteInfo = mtree;
+ };
+ FC8A8CAB14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B9414B648EE001B97AD;
+ remoteInfo = mv;
+ };
+ FC8A8CAD14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B9C14B648EE001B97AD;
+ remoteInfo = pathchk;
+ };
+ FC8A8CAF14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BA414B648EE001B97AD;
+ remoteInfo = pax;
+ };
+ FC8A8CB114B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C4614B64DCD001B97AD;
+ remoteInfo = readlink;
+ };
+ FC8A8CB314B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BAC14B648EF001B97AD;
+ remoteInfo = rm;
+ };
+ FC8A8CB514B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BB414B648EF001B97AD;
+ remoteInfo = rmdir;
+ };
+ FC8A8CB714B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C3C14B64A9D001B97AD;
+ remoteInfo = shar;
+ };
+ FC8A8CB914B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BC414B648EF001B97AD;
+ remoteInfo = stat;
+ };
+ FC8A8CBB14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C6714B6536D001B97AD;
+ remoteInfo = sum;
+ };
+ FC8A8CBD14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BCC14B648F0001B97AD;
+ remoteInfo = touch;
+ };
+ FC8A8CBF14B655FD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C5014B650CF001B97AD;
+ remoteInfo = unlink;
+ };
+ FC8A8CCA14B65F92001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BAC14B648EF001B97AD;
+ remoteInfo = rm;
+ };
+ FC8A8CD214B67BFD001B97AD /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8CC814B65F92001B97AD;
+ remoteInfo = uncompress;
+ };
+ FDAD94A91808BDAA00B4D5A0 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FDAD94961808BB6D00B4D5A0;
+ remoteInfo = gzip;
+ };
+ FDFF0C521811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FCB1BDB714B645D10070FACB;
+ remoteInfo = chflags;
+ };
+ FDFF0C541811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C5B14B652E1001B97AD;
+ remoteInfo = chgrp;
+ };
+ FDFF0C561811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B0B14B648D7001B97AD;
+ remoteInfo = chmod;
+ };
+ FDFF0C581811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1414B648E0001B97AD;
+ remoteInfo = chown;
+ };
+ FDFF0C5A1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B1C14B648E3001B97AD;
+ remoteInfo = cksum;
+ };
+ FDFF0C5C1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B2414B648E5001B97AD;
+ remoteInfo = compress;
+ };
+ FDFF0C5E1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B2C14B648E7001B97AD;
+ remoteInfo = cp;
+ };
+ FDFF0C601811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B3414B648EA001B97AD;
+ remoteInfo = dd;
+ };
+ FDFF0C621811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B3C14B648EA001B97AD;
+ remoteInfo = df;
+ };
+ FDFF0C641811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B4414B648EB001B97AD;
+ remoteInfo = du;
+ };
+ FDFF0C681811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B4C14B648EB001B97AD;
+ remoteInfo = install;
+ };
+ FDFF0C6A1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B5414B648EB001B97AD;
+ remoteInfo = ipcrm;
+ };
+ FDFF0C6C1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B5C14B648EC001B97AD;
+ remoteInfo = ipcs;
+ };
+ FDFF0C6E1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C7314B6554E001B97AD;
+ remoteInfo = link;
+ };
+ FDFF0C701811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B6414B648EC001B97AD;
+ remoteInfo = ln;
+ };
+ FDFF0C721811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B6C14B648ED001B97AD;
+ remoteInfo = ls;
+ };
+ FDFF0C741811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B7414B648ED001B97AD;
+ remoteInfo = mkdir;
+ };
+ FDFF0C761811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B7C14B648ED001B97AD;
+ remoteInfo = mkfifo;
+ };
+ FDFF0C781811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B8414B648ED001B97AD;
+ remoteInfo = mknod;
+ };
+ FDFF0C7A1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B8C14B648ED001B97AD;
+ remoteInfo = mtree;
+ };
+ FDFF0C7C1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B9414B648EE001B97AD;
+ remoteInfo = mv;
+ };
+ FDFF0C7E1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8B9C14B648EE001B97AD;
+ remoteInfo = pathchk;
+ };
+ FDFF0C801811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BA414B648EE001B97AD;
+ remoteInfo = pax;
+ };
+ FDFF0C821811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C4614B64DCD001B97AD;
+ remoteInfo = readlink;
+ };
+ FDFF0C841811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BAC14B648EF001B97AD;
+ remoteInfo = rm;
+ };
+ FDFF0C861811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BB414B648EF001B97AD;
+ remoteInfo = rmdir;
+ };
+ FDFF0C881811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C3C14B64A9D001B97AD;
+ remoteInfo = shar;
+ };
+ FDFF0C8A1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BC414B648EF001B97AD;
+ remoteInfo = stat;
+ };
+ FDFF0C8C1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C6714B6536D001B97AD;
+ remoteInfo = sum;
+ };
+ FDFF0C8E1811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8BCC14B648F0001B97AD;
+ remoteInfo = touch;
+ };
+ FDFF0C901811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8CC814B65F92001B97AD;
+ remoteInfo = uncompress;
+ };
+ FDFF0C921811BA2F00BFC477 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FCB1BDAF14B645D00070FACB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FC8A8C5014B650CF001B97AD;
+ remoteInfo = unlink;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 3E966CED1FB2215C0019F7A1 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /AppleInternal/CoreOS/BATS/unit_tests;
+ dstSubfolderSpec = 0;
+ files = (
+ 3E966CEE1FB2216F0019F7A1 /* file_cmds.plist in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3E966CEF1FB221740019F7A1 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /AppleInternal/Tests/file_cmds;
+ dstSubfolderSpec = 0;
+ files = (
+ 3E966CF01FB2218A0019F7A1 /* chgrp.sh in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B0F14B648D7001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BE714B6495D001B97AD /* chmod.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B1714B648E0001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man8/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8CD414B67D60001B97AD /* chown.8 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B1F14B648E3001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BEA14B6496E001B97AD /* cksum.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B2714B648E5001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BF114B64982001B97AD /* compress.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B2F14B648E7001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BF714B6499A001B97AD /* cp.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B3714B648EA001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BFB14B649A9001B97AD /* dd.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B3F14B648EA001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BFF14B649B4001B97AD /* df.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B4714B648EB001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0314B649D8001B97AD /* du.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B4F14B648EB001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0514B649DF001B97AD /* install.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B5714B648EB001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0614B649E2001B97AD /* ipcrm.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B5F14B648EC001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0914B649EA001B97AD /* ipcs.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B6714B648EC001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0A14B649ED001B97AD /* ln.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B6F14B648ED001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C0C14B649F5001B97AD /* ls.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B7714B648ED001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C1214B64A06001B97AD /* mkdir.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B7F14B648ED001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C1314B64A09001B97AD /* mkfifo.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B8714B648ED001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man8/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C1514B64A0D001B97AD /* mknod.8 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B8F14B648ED001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man8/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C2014B64A40001B97AD /* mtree.8 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B9714B648EE001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C2114B64A49001B97AD /* mv.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8B9F14B648EE001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C2314B64A4F001B97AD /* pathchk.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8BA714B648EE001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C2514B64A59001B97AD /* pax.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8BAF14B648EF001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C3814B64A7C001B97AD /* rm.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8BB714B648EF001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C3A14B64A85001B97AD /* rmdir.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8BC714B648EF001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C4114B64AC0001B97AD /* stat.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8BCF14B648F0001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C4514B64AD7001B97AD /* touch.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C3F14B64AA8001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C4014B64AAB001B97AD /* shar.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C4B14B64DEA001B97AD /* Copy Files */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C4E14B64EAE001B97AD /* readlink.1 in Copy Files */,
+ );
+ name = "Copy Files";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C5314B650CF001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C5A14B6525A001B97AD /* unlink.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C5E14B652E1001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C6514B65307001B97AD /* chgrp.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C6A14B6536D001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C7114B65389001B97AD /* sum.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C7614B6554E001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C7D14B65575001B97AD /* link.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8C7E14B6557E001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man7/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8C7F14B65586001B97AD /* symlink.7 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FC8A8CCB14B65F92001B97AD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8CD014B65F9B001B97AD /* uncompress.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FCB1BDB614B645D10070FACB /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ FC8A8BE414B6494B001B97AD /* chflags.1 in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ FDAD94951808BB6D00B4D5A0 /* Install Man Page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(GZIP_PREFIX)/share/man/man1";
+ dstSubfolderSpec = 0;
+ files = (
+ FDAD94A81808BD0600B4D5A0 /* gzip.1 in Install Man Page */,
+ );
+ name = "Install Man Page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 0773099A1A3A4DFE00E9B4EA /* dd.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = dd.entitlements; sourceTree = "<group>"; };
+ 3E59B9301D4A767600D3128C /* futimens.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = futimens.c; sourceTree = "<group>"; };
+ 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = chgrp.sh; sourceTree = "<group>"; };
+ 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = file_cmds.plist; sourceTree = "<group>"; };
+ 729D06D7230B5E42000716E5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 7D0A20E82499364700F0F6D7 /* metrics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metrics.h; sourceTree = "<group>"; };
+ 7D0A20E92499364700F0F6D7 /* metrics.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = metrics.c; sourceTree = "<group>"; };
+ FC8A8B1214B648D7001B97AD /* chmod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chmod; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B1A14B648E0001B97AD /* chown */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chown; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B2214B648E3001B97AD /* cksum */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cksum; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B2A14B648E5001B97AD /* compress */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = compress; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B3214B648E7001B97AD /* cp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cp; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B3A14B648EA001B97AD /* dd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dd; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B4214B648EA001B97AD /* df */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = df; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B4A14B648EB001B97AD /* du */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = du; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B5214B648EB001B97AD /* install */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = install; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B5A14B648EB001B97AD /* ipcrm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ipcrm; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B6214B648EC001B97AD /* ipcs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ipcs; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B6A14B648EC001B97AD /* ln */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ln; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B7214B648ED001B97AD /* ls */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ls; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B7A14B648ED001B97AD /* mkdir */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mkdir; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B8214B648ED001B97AD /* mkfifo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mkfifo; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B8A14B648ED001B97AD /* mknod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mknod; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B9214B648ED001B97AD /* mtree */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mtree; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8B9A14B648EE001B97AD /* mv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mv; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BA214B648EE001B97AD /* pathchk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pathchk; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BAA14B648EE001B97AD /* pax */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pax; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BB214B648EF001B97AD /* rm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rm; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BBA14B648EF001B97AD /* rmdir */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rmdir; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BCA14B648EF001B97AD /* stat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = stat; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8BD214B648F0001B97AD /* touch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = touch; sourceTree = BUILT_PRODUCTS_DIR; };
+ FC8A8C4D14B64EA8001B97AD /* readlink.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = readlink.1; sourceTree = "<group>"; };
+ FC8A8C4F14B650C3001B97AD /* unlink.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = unlink.1; sourceTree = "<group>"; };
+ FC8A8C6614B6535B001B97AD /* sum.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = sum.1; sourceTree = "<group>"; };
+ FC8A8C7214B65547001B97AD /* link.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = link.1; sourceTree = "<group>"; };
+ FC8A8CC114B658D6001B97AD /* libutil.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libutil.dylib; path = /usr/lib/libutil.dylib; sourceTree = "<absolute>"; };
+ FC8A8CC414B65C3D001B97AD /* libcurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurses.dylib; path = /usr/lib/libcurses.dylib; sourceTree = "<absolute>"; };
+ FCB1BDB814B645D10070FACB /* chflags */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chflags; sourceTree = BUILT_PRODUCTS_DIR; };
+ FCB1BDCB14B6460C0070FACB /* chflags.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chflags.1; sourceTree = "<group>"; };
+ FCB1BDCC14B6460C0070FACB /* chflags.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chflags.c; sourceTree = "<group>"; };
+ FCB1BDCF14B6460C0070FACB /* chmod.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chmod.1; sourceTree = "<group>"; };
+ FCB1BDD014B6460C0070FACB /* chmod.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chmod.c; sourceTree = "<group>"; };
+ FCB1BDD114B6460C0070FACB /* chmod_acl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chmod_acl.c; sourceTree = "<group>"; };
+ FCB1BDD214B6460C0070FACB /* chmod_acl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chmod_acl.h; sourceTree = "<group>"; };
+ FCB1BDD514B6460C0070FACB /* chgrp.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chgrp.1; sourceTree = "<group>"; };
+ FCB1BDD614B6460C0070FACB /* chown.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = chown.8; sourceTree = "<group>"; };
+ FCB1BDD714B6460C0070FACB /* chown.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chown.c; sourceTree = "<group>"; };
+ FCB1BDDA14B6460C0070FACB /* cksum.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cksum.1; sourceTree = "<group>"; };
+ FCB1BDDB14B6460C0070FACB /* cksum.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cksum.c; sourceTree = "<group>"; };
+ FCB1BDDC14B6460C0070FACB /* crc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = crc.c; sourceTree = "<group>"; };
+ FCB1BDDD14B6460C0070FACB /* crc32.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = "<group>"; };
+ FCB1BDDE14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BDE014B6460C0070FACB /* print.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = "<group>"; };
+ FCB1BDE114B6460C0070FACB /* sum1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sum1.c; sourceTree = "<group>"; };
+ FCB1BDE214B6460C0070FACB /* sum2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sum2.c; sourceTree = "<group>"; usesTabs = 1; };
+ FCB1BDE414B6460C0070FACB /* compress.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = compress.1; sourceTree = "<group>"; };
+ FCB1BDE514B6460C0070FACB /* compress.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = "<group>"; };
+ FCB1BDE714B6460C0070FACB /* NOTES */ = {isa = PBXFileReference; lastKnownFileType = text; path = NOTES; sourceTree = "<group>"; };
+ FCB1BDE814B6460C0070FACB /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+ FCB1BDE914B6460C0070FACB /* revision.log */ = {isa = PBXFileReference; lastKnownFileType = text; path = revision.log; sourceTree = "<group>"; };
+ FCB1BDEB14B6460C0070FACB /* uncompress.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uncompress.1; sourceTree = "<group>"; };
+ FCB1BDEC14B6460C0070FACB /* zcat.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zcat.sh; sourceTree = "<group>"; };
+ FCB1BDED14B6460C0070FACB /* zopen.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = zopen.3; sourceTree = "<group>"; };
+ FCB1BDEE14B6460C0070FACB /* zopen.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zopen.c; sourceTree = "<group>"; };
+ FCB1BDEF14B6460C0070FACB /* zopen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zopen.h; sourceTree = "<group>"; };
+ FCB1BDF114B6460C0070FACB /* cp.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cp.1; sourceTree = "<group>"; };
+ FCB1BDF214B6460C0070FACB /* cp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cp.c; sourceTree = "<group>"; };
+ FCB1BDF314B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BDF514B6460C0070FACB /* utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
+ FCB1BDF714B6460C0070FACB /* strpct.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = strpct.c; sourceTree = "<group>"; };
+ FCB1BDF914B6460C0070FACB /* args.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = args.c; sourceTree = "<group>"; };
+ FCB1BDFA14B6460C0070FACB /* conv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv.c; sourceTree = "<group>"; };
+ FCB1BDFB14B6460C0070FACB /* conv_tab.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv_tab.c; sourceTree = "<group>"; };
+ FCB1BDFC14B6460C0070FACB /* dd.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = dd.1; sourceTree = "<group>"; };
+ FCB1BDFD14B6460C0070FACB /* dd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dd.c; sourceTree = "<group>"; };
+ FCB1BDFE14B6460C0070FACB /* dd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dd.h; sourceTree = "<group>"; };
+ FCB1BDFF14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BE0114B6460C0070FACB /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; };
+ FCB1BE0214B6460C0070FACB /* position.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = position.c; sourceTree = "<group>"; };
+ FCB1BE0414B6460C0070FACB /* df.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = df.1; sourceTree = "<group>"; };
+ FCB1BE0514B6460C0070FACB /* df.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = df.c; sourceTree = "<group>"; };
+ FCB1BE0714B6460C0070FACB /* vfslist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vfslist.c; sourceTree = "<group>"; };
+ FCB1BE0914B6460C0070FACB /* du.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = du.1; sourceTree = "<group>"; };
+ FCB1BE0A14B6460C0070FACB /* du.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = du.c; sourceTree = "<group>"; };
+ FCB1BE0D14B6460C0070FACB /* install.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = install.1; sourceTree = "<group>"; };
+ FCB1BE0F14B6460C0070FACB /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; };
+ FCB1BE1014B6460C0070FACB /* xinstall.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = xinstall.c; sourceTree = "<group>"; };
+ FCB1BE1214B6460C0070FACB /* ipcrm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ipcrm.1; sourceTree = "<group>"; };
+ FCB1BE1314B6460C0070FACB /* ipcrm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ipcrm.c; sourceTree = "<group>"; };
+ FCB1BE1614B6460C0070FACB /* ipcs.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ipcs.1; sourceTree = "<group>"; };
+ FCB1BE1714B6460C0070FACB /* ipcs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ipcs.c; sourceTree = "<group>"; };
+ FCB1BE1A14B6460C0070FACB /* ln.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ln.1; sourceTree = "<group>"; };
+ FCB1BE1B14B6460C0070FACB /* ln.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ln.c; sourceTree = "<group>"; };
+ FCB1BE1D14B6460C0070FACB /* symlink.7 */ = {isa = PBXFileReference; lastKnownFileType = text; path = symlink.7; sourceTree = "<group>"; };
+ FCB1BE1F14B6460C0070FACB /* cmp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmp.c; sourceTree = "<group>"; };
+ FCB1BE2014B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BE2114B6460C0070FACB /* ls.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; lineEnding = 0; path = ls.1; sourceTree = "<group>"; };
+ FCB1BE2214B6460C0070FACB /* ls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ls.c; sourceTree = "<group>"; };
+ FCB1BE2314B6460C0070FACB /* ls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ls.h; sourceTree = "<group>"; };
+ FCB1BE2514B6460C0070FACB /* print.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = "<group>"; };
+ FCB1BE2614B6460C0070FACB /* util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = "<group>"; };
+ FCB1BE2A14B6460C0070FACB /* mkdir.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mkdir.1; sourceTree = "<group>"; };
+ FCB1BE2B14B6460C0070FACB /* mkdir.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mkdir.c; sourceTree = "<group>"; };
+ FCB1BE2E14B6460C0070FACB /* mkfifo.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mkfifo.1; sourceTree = "<group>"; };
+ FCB1BE2F14B6460C0070FACB /* mkfifo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mkfifo.c; sourceTree = "<group>"; };
+ FCB1BE3214B6460C0070FACB /* mknod.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = mknod.8; sourceTree = "<group>"; };
+ FCB1BE3314B6460C0070FACB /* mknod.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mknod.c; sourceTree = "<group>"; };
+ FCB1BE3514B6460C0070FACB /* commoncrypto.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = "<group>"; };
+ FCB1BE3614B6460C0070FACB /* commoncrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = "<group>"; };
+ FCB1BE3714B6460C0070FACB /* compare.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compare.c; sourceTree = "<group>"; };
+ FCB1BE3814B6460C0070FACB /* create.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = create.c; sourceTree = "<group>"; };
+ FCB1BE3914B6460C0070FACB /* excludes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = excludes.c; sourceTree = "<group>"; };
+ FCB1BE3A14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BE3C14B6460C0070FACB /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; };
+ FCB1BE3D14B6460C0070FACB /* mtree.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = mtree.8; sourceTree = "<group>"; };
+ FCB1BE3E14B6460C0070FACB /* mtree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mtree.c; sourceTree = "<group>"; };
+ FCB1BE3F14B6460C0070FACB /* mtree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mtree.h; sourceTree = "<group>"; };
+ FCB1BE4014B6460C0070FACB /* spec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = spec.c; sourceTree = "<group>"; };
+ FCB1BE4114B6460C0070FACB /* specspec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = specspec.c; sourceTree = "<group>"; };
+ FCB1BE4314B6460C0070FACB /* test00.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test00.sh; sourceTree = "<group>"; };
+ FCB1BE4414B6460C0070FACB /* test01.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test01.sh; sourceTree = "<group>"; };
+ FCB1BE4514B6460C0070FACB /* test02.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test02.sh; sourceTree = "<group>"; };
+ FCB1BE4614B6460C0070FACB /* test03.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test03.sh; sourceTree = "<group>"; };
+ FCB1BE4714B6460C0070FACB /* test04.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test04.sh; sourceTree = "<group>"; };
+ FCB1BE4814B6460C0070FACB /* verify.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = verify.c; sourceTree = "<group>"; };
+ FCB1BE4B14B6460C0070FACB /* mv.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mv.1; sourceTree = "<group>"; };
+ FCB1BE4C14B6460C0070FACB /* mv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mv.c; sourceTree = "<group>"; };
+ FCB1BE4D14B6460C0070FACB /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; };
+ FCB1BE5014B6460C0070FACB /* pathchk.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pathchk.1; sourceTree = "<group>"; };
+ FCB1BE5114B6460C0070FACB /* pathchk.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pathchk.c; sourceTree = "<group>"; };
+ FCB1BE5314B6460C0070FACB /* ar_io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ar_io.c; sourceTree = "<group>"; };
+ FCB1BE5414B6460C0070FACB /* ar_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ar_subs.c; sourceTree = "<group>"; };
+ FCB1BE5514B6460C0070FACB /* buf_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = buf_subs.c; sourceTree = "<group>"; };
+ FCB1BE5614B6460C0070FACB /* cache.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cache.c; sourceTree = "<group>"; };
+ FCB1BE5714B6460C0070FACB /* cache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cache.h; sourceTree = "<group>"; };
+ FCB1BE5814B6460C0070FACB /* cpio.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cpio.1; sourceTree = "<group>"; };
+ FCB1BE5914B6460C0070FACB /* cpio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cpio.c; sourceTree = "<group>"; };
+ FCB1BE5A14B6460C0070FACB /* cpio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cpio.h; sourceTree = "<group>"; };
+ FCB1BE5B14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; };
+ FCB1BE5C14B6460C0070FACB /* file_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = file_subs.c; sourceTree = "<group>"; };
+ FCB1BE5D14B6460C0070FACB /* ftree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ftree.c; sourceTree = "<group>"; };
+ FCB1BE5E14B6460C0070FACB /* ftree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftree.h; sourceTree = "<group>"; };
+ FCB1BE5F14B6460C0070FACB /* gen_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gen_subs.c; sourceTree = "<group>"; };
+ FCB1BE6014B6460C0070FACB /* getoldopt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = getoldopt.c; sourceTree = "<group>"; };
+ FCB1BE6214B6460C0070FACB /* options.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = options.c; sourceTree = "<group>"; };
+ FCB1BE6314B6460C0070FACB /* options.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = "<group>"; };
+ FCB1BE6414B6460C0070FACB /* pat_rep.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pat_rep.c; sourceTree = "<group>"; };
+ FCB1BE6514B6460C0070FACB /* pat_rep.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pat_rep.h; sourceTree = "<group>"; };
+ FCB1BE6614B6460C0070FACB /* pax.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pax.1; sourceTree = "<group>"; };
+ FCB1BE6714B6460C0070FACB /* pax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pax.c; sourceTree = "<group>"; };
+ FCB1BE6814B6460C0070FACB /* pax.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pax.h; sourceTree = "<group>"; };
+ FCB1BE6914B6460C0070FACB /* pax_format.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pax_format.c; sourceTree = "<group>"; };
+ FCB1BE6A14B6460C0070FACB /* pax_format.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pax_format.h; sourceTree = "<group>"; };
+ FCB1BE6B14B6460C0070FACB /* sel_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sel_subs.c; sourceTree = "<group>"; };
+ FCB1BE6C14B6460C0070FACB /* sel_subs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sel_subs.h; sourceTree = "<group>"; };
+ FCB1BE6D14B6460C0070FACB /* tables.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tables.c; sourceTree = "<group>"; };
+ FCB1BE6E14B6460C0070FACB /* tables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tables.h; sourceTree = "<group>"; };
+ FCB1BE6F14B6460C0070FACB /* tar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tar.c; sourceTree = "<group>"; };
+ FCB1BE7014B6460C0070FACB /* tar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tar.h; sourceTree = "<group>"; };
+ FCB1BE7114B6460C0070FACB /* tty_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tty_subs.c; sourceTree = "<group>"; };
+ FCB1BE7414B6460C0070FACB /* rm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rm.1; sourceTree = "<group>"; };
+ FCB1BE7514B6460C0070FACB /* rm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rm.c; sourceTree = "<group>"; };
+ FCB1BE7814B6460C0070FACB /* rmdir.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rmdir.1; sourceTree = "<group>"; };
+ FCB1BE7914B6460C0070FACB /* rmdir.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rmdir.c; sourceTree = "<group>"; };
+ FCB1BE7C14B6460C0070FACB /* rmt.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = rmt.8; sourceTree = "<group>"; };
+ FCB1BE7D14B6460C0070FACB /* rmt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rmt.c; sourceTree = "<group>"; };
+ FCB1BE8014B6460C0070FACB /* shar.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = shar.1; sourceTree = "<group>"; };
+ FCB1BE8114B6460C0070FACB /* shar.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = shar.sh; sourceTree = "<group>"; };
+ FCB1BE8414B6460C0070FACB /* stat.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = stat.1; sourceTree = "<group>"; };
+ FCB1BE8514B6460C0070FACB /* stat.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = stat.c; sourceTree = "<group>"; };
+ FCB1BE8814B6460C0070FACB /* touch.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = touch.1; sourceTree = "<group>"; };
+ FCB1BE8914B6460C0070FACB /* touch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = touch.c; sourceTree = "<group>"; };
+ FDAD94801808BB3A00B4D5A0 /* gzexe */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = gzexe; sourceTree = "<group>"; };
+ FDAD94811808BB3A00B4D5A0 /* gzexe.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = gzexe.1; sourceTree = "<group>"; };
+ FDAD94821808BB3A00B4D5A0 /* gzip.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = gzip.1; sourceTree = "<group>"; };
+ FDAD94831808BB3A00B4D5A0 /* gzip.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gzip.c; sourceTree = "<group>"; };
+ FDAD94841808BB3A00B4D5A0 /* gzip.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = gzip.plist; sourceTree = "<group>"; };
+ FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = gzip.xcconfig; sourceTree = "<group>"; };
+ FDAD94861808BB3A00B4D5A0 /* install_scripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = install_scripts.sh; sourceTree = "<group>"; };
+ FDAD94871808BB3A00B4D5A0 /* unbzip2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unbzip2.c; sourceTree = "<group>"; };
+ FDAD94881808BB3A00B4D5A0 /* unpack.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = "<group>"; };
+ FDAD94891808BB3A00B4D5A0 /* unxz.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unxz.c; sourceTree = "<group>"; };
+ FDAD948A1808BB3A00B4D5A0 /* zdiff */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zdiff; sourceTree = "<group>"; };
+ FDAD948B1808BB3A00B4D5A0 /* zdiff.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zdiff.1; sourceTree = "<group>"; };
+ FDAD948C1808BB3A00B4D5A0 /* zforce */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zforce; sourceTree = "<group>"; };
+ FDAD948D1808BB3A00B4D5A0 /* zforce.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zforce.1; sourceTree = "<group>"; };
+ FDAD948E1808BB3A00B4D5A0 /* zmore */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zmore; sourceTree = "<group>"; };
+ FDAD948F1808BB3A00B4D5A0 /* zmore.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zmore.1; sourceTree = "<group>"; };
+ FDAD94901808BB3A00B4D5A0 /* znew */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = znew; sourceTree = "<group>"; };
+ FDAD94911808BB3A00B4D5A0 /* znew.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = znew.1; sourceTree = "<group>"; };
+ FDAD94921808BB3A00B4D5A0 /* zuncompress.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zuncompress.c; sourceTree = "<group>"; };
+ FDAD94971808BB6D00B4D5A0 /* gzip */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gzip; sourceTree = BUILT_PRODUCTS_DIR; };
+ FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = usr/lib/libbz2.dylib; sourceTree = SDKROOT; };
+ FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = liblzma.dylib; path = usr/lib/liblzma.dylib; sourceTree = SDKROOT; };
+ FDAD94A51808BC9B00B4D5A0 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ FC8A8B0E14B648D7001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B1614B648E0001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B1E14B648E3001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B2614B648E5001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B2E14B648E7001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B3614B648EA001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B3E14B648EA001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8CC214B658D7001B97AD /* libutil.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B4614B648EB001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B4E14B648EB001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B5614B648EB001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B5E14B648EC001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B6614B648EC001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B6E14B648ED001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8CC614B65C46001B97AD /* libutil.dylib in Frameworks */,
+ FC8A8CC514B65C3D001B97AD /* libcurses.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B7614B648ED001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B7E14B648ED001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B8614B648ED001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B8E14B648ED001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 729D06D8230B5E42000716E5 /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B9614B648EE001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B9E14B648EE001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BA614B648EE001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BAE14B648EF001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BB614B648EF001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BC614B648EF001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BCE14B648F0001B97AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FCB1BDB514B645D10070FACB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FDAD94941808BB6D00B4D5A0 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FDAD94A21808BC9100B4D5A0 /* libbz2.dylib in Frameworks */,
+ FDAD94A41808BC9700B4D5A0 /* liblzma.dylib in Frameworks */,
+ FDAD94A61808BC9B00B4D5A0 /* libz.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 3E966CEA1FB221330019F7A1 /* tests */ = {
+ isa = PBXGroup;
+ children = (
+ 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */,
+ 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */,
+ );
+ path = tests;
+ sourceTree = "<group>";
+ };
+ 729D06D6230B5E42000716E5 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 729D06D7230B5E42000716E5 /* CoreFoundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ FCB1BDAD14B645D00070FACB = {
+ isa = PBXGroup;
+ children = (
+ 3E966CEA1FB221330019F7A1 /* tests */,
+ FCB1BDCA14B6460C0070FACB /* chflags */,
+ FCB1BDCE14B6460C0070FACB /* chmod */,
+ FCB1BDD414B6460C0070FACB /* chown */,
+ FCB1BDD914B6460C0070FACB /* cksum */,
+ FCB1BDE314B6460C0070FACB /* compress */,
+ FCB1BDF014B6460C0070FACB /* cp */,
+ FCB1BDF614B6460C0070FACB /* csh */,
+ FCB1BDF814B6460C0070FACB /* dd */,
+ FCB1BE0314B6460C0070FACB /* df */,
+ FCB1BE0814B6460C0070FACB /* du */,
+ FDAD947F1808BB3A00B4D5A0 /* gzip */,
+ FCB1BE0C14B6460C0070FACB /* install */,
+ FCB1BE1114B6460C0070FACB /* ipcrm */,
+ FCB1BE1514B6460C0070FACB /* ipcs */,
+ FCB1BE1914B6460C0070FACB /* ln */,
+ FCB1BE1E14B6460C0070FACB /* ls */,
+ FCB1BE2814B6460C0070FACB /* mkdir */,
+ FCB1BE2C14B6460C0070FACB /* mkfifo */,
+ FCB1BE3014B6460C0070FACB /* mknod */,
+ FCB1BE3414B6460C0070FACB /* mtree */,
+ FCB1BE4914B6460C0070FACB /* mv */,
+ FCB1BE4E14B6460C0070FACB /* pathchk */,
+ FCB1BE5214B6460C0070FACB /* pax */,
+ FCB1BE7214B6460C0070FACB /* rm */,
+ FCB1BE7614B6460C0070FACB /* rmdir */,
+ FCB1BE7A14B6460C0070FACB /* rmt */,
+ FCB1BE7E14B6460C0070FACB /* shar */,
+ FCB1BE8214B6460C0070FACB /* stat */,
+ FCB1BE8614B6460C0070FACB /* touch */,
+ FDAD94A71808BCB700B4D5A0 /* Libraries */,
+ FCB1BDB914B645D10070FACB /* Products */,
+ 729D06D6230B5E42000716E5 /* Frameworks */,
+ );
+ indentWidth = 8;
+ sourceTree = "<group>";
+ tabWidth = 8;
+ usesTabs = 1;
+ };
+ FCB1BDB914B645D10070FACB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDB814B645D10070FACB /* chflags */,
+ FC8A8B1214B648D7001B97AD /* chmod */,
+ FC8A8B1A14B648E0001B97AD /* chown */,
+ FC8A8B2214B648E3001B97AD /* cksum */,
+ FC8A8B2A14B648E5001B97AD /* compress */,
+ FC8A8B3214B648E7001B97AD /* cp */,
+ FC8A8B3A14B648EA001B97AD /* dd */,
+ FC8A8B4214B648EA001B97AD /* df */,
+ FC8A8B4A14B648EB001B97AD /* du */,
+ FC8A8B5214B648EB001B97AD /* install */,
+ FC8A8B5A14B648EB001B97AD /* ipcrm */,
+ FC8A8B6214B648EC001B97AD /* ipcs */,
+ FC8A8B6A14B648EC001B97AD /* ln */,
+ FC8A8B7214B648ED001B97AD /* ls */,
+ FC8A8B7A14B648ED001B97AD /* mkdir */,
+ FC8A8B8214B648ED001B97AD /* mkfifo */,
+ FC8A8B8A14B648ED001B97AD /* mknod */,
+ FC8A8B9214B648ED001B97AD /* mtree */,
+ FC8A8B9A14B648EE001B97AD /* mv */,
+ FC8A8BA214B648EE001B97AD /* pathchk */,
+ FC8A8BAA14B648EE001B97AD /* pax */,
+ FC8A8BB214B648EF001B97AD /* rm */,
+ FC8A8BBA14B648EF001B97AD /* rmdir */,
+ FC8A8BCA14B648EF001B97AD /* stat */,
+ FC8A8BD214B648F0001B97AD /* touch */,
+ FDAD94971808BB6D00B4D5A0 /* gzip */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ FCB1BDCA14B6460C0070FACB /* chflags */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDCB14B6460C0070FACB /* chflags.1 */,
+ FCB1BDCC14B6460C0070FACB /* chflags.c */,
+ );
+ path = chflags;
+ sourceTree = "<group>";
+ };
+ FCB1BDCE14B6460C0070FACB /* chmod */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDCF14B6460C0070FACB /* chmod.1 */,
+ FCB1BDD014B6460C0070FACB /* chmod.c */,
+ FCB1BDD114B6460C0070FACB /* chmod_acl.c */,
+ FCB1BDD214B6460C0070FACB /* chmod_acl.h */,
+ );
+ path = chmod;
+ sourceTree = "<group>";
+ };
+ FCB1BDD414B6460C0070FACB /* chown */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDD514B6460C0070FACB /* chgrp.1 */,
+ FCB1BDD614B6460C0070FACB /* chown.8 */,
+ FCB1BDD714B6460C0070FACB /* chown.c */,
+ );
+ path = chown;
+ sourceTree = "<group>";
+ };
+ FCB1BDD914B6460C0070FACB /* cksum */ = {
+ isa = PBXGroup;
+ children = (
+ FC8A8C6614B6535B001B97AD /* sum.1 */,
+ FCB1BDDA14B6460C0070FACB /* cksum.1 */,
+ FCB1BDDB14B6460C0070FACB /* cksum.c */,
+ FCB1BDDC14B6460C0070FACB /* crc.c */,
+ FCB1BDDD14B6460C0070FACB /* crc32.c */,
+ FCB1BDDE14B6460C0070FACB /* extern.h */,
+ FCB1BDE014B6460C0070FACB /* print.c */,
+ FCB1BDE114B6460C0070FACB /* sum1.c */,
+ FCB1BDE214B6460C0070FACB /* sum2.c */,
+ );
+ path = cksum;
+ sourceTree = "<group>";
+ };
+ FCB1BDE314B6460C0070FACB /* compress */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDE414B6460C0070FACB /* compress.1 */,
+ FCB1BDE514B6460C0070FACB /* compress.c */,
+ FCB1BDE614B6460C0070FACB /* doc */,
+ FCB1BDEB14B6460C0070FACB /* uncompress.1 */,
+ FCB1BDEC14B6460C0070FACB /* zcat.sh */,
+ FCB1BDED14B6460C0070FACB /* zopen.3 */,
+ FCB1BDEE14B6460C0070FACB /* zopen.c */,
+ FCB1BDEF14B6460C0070FACB /* zopen.h */,
+ );
+ path = compress;
+ sourceTree = "<group>";
+ };
+ FCB1BDE614B6460C0070FACB /* doc */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDE714B6460C0070FACB /* NOTES */,
+ FCB1BDE814B6460C0070FACB /* README */,
+ FCB1BDE914B6460C0070FACB /* revision.log */,
+ );
+ path = doc;
+ sourceTree = "<group>";
+ };
+ FCB1BDF014B6460C0070FACB /* cp */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDF114B6460C0070FACB /* cp.1 */,
+ FCB1BDF214B6460C0070FACB /* cp.c */,
+ FCB1BDF314B6460C0070FACB /* extern.h */,
+ FCB1BDF514B6460C0070FACB /* utils.c */,
+ );
+ path = cp;
+ sourceTree = "<group>";
+ };
+ FCB1BDF614B6460C0070FACB /* csh */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BDF714B6460C0070FACB /* strpct.c */,
+ );
+ path = csh;
+ sourceTree = "<group>";
+ };
+ FCB1BDF814B6460C0070FACB /* dd */ = {
+ isa = PBXGroup;
+ children = (
+ 0773099A1A3A4DFE00E9B4EA /* dd.entitlements */,
+ FCB1BDF914B6460C0070FACB /* args.c */,
+ FCB1BDFA14B6460C0070FACB /* conv.c */,
+ FCB1BDFB14B6460C0070FACB /* conv_tab.c */,
+ FCB1BDFC14B6460C0070FACB /* dd.1 */,
+ FCB1BDFD14B6460C0070FACB /* dd.c */,
+ FCB1BDFE14B6460C0070FACB /* dd.h */,
+ FCB1BDFF14B6460C0070FACB /* extern.h */,
+ FCB1BE0114B6460C0070FACB /* misc.c */,
+ FCB1BE0214B6460C0070FACB /* position.c */,
+ );
+ path = dd;
+ sourceTree = "<group>";
+ };
+ FCB1BE0314B6460C0070FACB /* df */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE0414B6460C0070FACB /* df.1 */,
+ FCB1BE0514B6460C0070FACB /* df.c */,
+ FCB1BE0714B6460C0070FACB /* vfslist.c */,
+ );
+ path = df;
+ sourceTree = "<group>";
+ };
+ FCB1BE0814B6460C0070FACB /* du */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE0914B6460C0070FACB /* du.1 */,
+ FCB1BE0A14B6460C0070FACB /* du.c */,
+ );
+ path = du;
+ sourceTree = "<group>";
+ };
+ FCB1BE0C14B6460C0070FACB /* install */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE0D14B6460C0070FACB /* install.1 */,
+ FCB1BE0F14B6460C0070FACB /* pathnames.h */,
+ FCB1BE1014B6460C0070FACB /* xinstall.c */,
+ );
+ path = install;
+ sourceTree = "<group>";
+ };
+ FCB1BE1114B6460C0070FACB /* ipcrm */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE1214B6460C0070FACB /* ipcrm.1 */,
+ FCB1BE1314B6460C0070FACB /* ipcrm.c */,
+ );
+ path = ipcrm;
+ sourceTree = "<group>";
+ };
+ FCB1BE1514B6460C0070FACB /* ipcs */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE1614B6460C0070FACB /* ipcs.1 */,
+ FCB1BE1714B6460C0070FACB /* ipcs.c */,
+ );
+ path = ipcs;
+ sourceTree = "<group>";
+ };
+ FCB1BE1914B6460C0070FACB /* ln */ = {
+ isa = PBXGroup;
+ children = (
+ FC8A8C7214B65547001B97AD /* link.1 */,
+ FCB1BE1A14B6460C0070FACB /* ln.1 */,
+ FCB1BE1B14B6460C0070FACB /* ln.c */,
+ FCB1BE1D14B6460C0070FACB /* symlink.7 */,
+ );
+ path = ln;
+ sourceTree = "<group>";
+ };
+ FCB1BE1E14B6460C0070FACB /* ls */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE1F14B6460C0070FACB /* cmp.c */,
+ FCB1BE2014B6460C0070FACB /* extern.h */,
+ FCB1BE2114B6460C0070FACB /* ls.1 */,
+ FCB1BE2214B6460C0070FACB /* ls.c */,
+ FCB1BE2314B6460C0070FACB /* ls.h */,
+ FCB1BE2514B6460C0070FACB /* print.c */,
+ FCB1BE2614B6460C0070FACB /* util.c */,
+ FC8A8CC414B65C3D001B97AD /* libcurses.dylib */,
+ FC8A8CC114B658D6001B97AD /* libutil.dylib */,
+ );
+ path = ls;
+ sourceTree = "<group>";
+ };
+ FCB1BE2814B6460C0070FACB /* mkdir */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE2A14B6460C0070FACB /* mkdir.1 */,
+ FCB1BE2B14B6460C0070FACB /* mkdir.c */,
+ );
+ path = mkdir;
+ sourceTree = "<group>";
+ };
+ FCB1BE2C14B6460C0070FACB /* mkfifo */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE2E14B6460C0070FACB /* mkfifo.1 */,
+ FCB1BE2F14B6460C0070FACB /* mkfifo.c */,
+ );
+ path = mkfifo;
+ sourceTree = "<group>";
+ };
+ FCB1BE3014B6460C0070FACB /* mknod */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE3214B6460C0070FACB /* mknod.8 */,
+ FCB1BE3314B6460C0070FACB /* mknod.c */,
+ );
+ path = mknod;
+ sourceTree = "<group>";
+ };
+ FCB1BE3414B6460C0070FACB /* mtree */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE3514B6460C0070FACB /* commoncrypto.c */,
+ FCB1BE3614B6460C0070FACB /* commoncrypto.h */,
+ FCB1BE3714B6460C0070FACB /* compare.c */,
+ FCB1BE3814B6460C0070FACB /* create.c */,
+ FCB1BE3914B6460C0070FACB /* excludes.c */,
+ FCB1BE3A14B6460C0070FACB /* extern.h */,
+ FCB1BE3C14B6460C0070FACB /* misc.c */,
+ FCB1BE3D14B6460C0070FACB /* mtree.8 */,
+ FCB1BE3E14B6460C0070FACB /* mtree.c */,
+ FCB1BE3F14B6460C0070FACB /* mtree.h */,
+ FCB1BE4014B6460C0070FACB /* spec.c */,
+ FCB1BE4114B6460C0070FACB /* specspec.c */,
+ FCB1BE4214B6460C0070FACB /* test */,
+ FCB1BE4814B6460C0070FACB /* verify.c */,
+ 7D0A20E82499364700F0F6D7 /* metrics.h */,
+ 7D0A20E92499364700F0F6D7 /* metrics.c */,
+ );
+ path = mtree;
+ sourceTree = "<group>";
+ };
+ FCB1BE4214B6460C0070FACB /* test */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE4314B6460C0070FACB /* test00.sh */,
+ FCB1BE4414B6460C0070FACB /* test01.sh */,
+ FCB1BE4514B6460C0070FACB /* test02.sh */,
+ FCB1BE4614B6460C0070FACB /* test03.sh */,
+ FCB1BE4714B6460C0070FACB /* test04.sh */,
+ );
+ path = test;
+ sourceTree = "<group>";
+ };
+ FCB1BE4914B6460C0070FACB /* mv */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE4B14B6460C0070FACB /* mv.1 */,
+ FCB1BE4C14B6460C0070FACB /* mv.c */,
+ FCB1BE4D14B6460C0070FACB /* pathnames.h */,
+ );
+ path = mv;
+ sourceTree = "<group>";
+ };
+ FCB1BE4E14B6460C0070FACB /* pathchk */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE5014B6460C0070FACB /* pathchk.1 */,
+ FCB1BE5114B6460C0070FACB /* pathchk.c */,
+ );
+ path = pathchk;
+ sourceTree = "<group>";
+ };
+ FCB1BE5214B6460C0070FACB /* pax */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE5314B6460C0070FACB /* ar_io.c */,
+ FCB1BE5414B6460C0070FACB /* ar_subs.c */,
+ FCB1BE5514B6460C0070FACB /* buf_subs.c */,
+ FCB1BE5614B6460C0070FACB /* cache.c */,
+ FCB1BE5714B6460C0070FACB /* cache.h */,
+ FCB1BE5814B6460C0070FACB /* cpio.1 */,
+ FCB1BE5914B6460C0070FACB /* cpio.c */,
+ FCB1BE5A14B6460C0070FACB /* cpio.h */,
+ FCB1BE5B14B6460C0070FACB /* extern.h */,
+ FCB1BE5C14B6460C0070FACB /* file_subs.c */,
+ FCB1BE5D14B6460C0070FACB /* ftree.c */,
+ FCB1BE5E14B6460C0070FACB /* ftree.h */,
+ FCB1BE5F14B6460C0070FACB /* gen_subs.c */,
+ FCB1BE6014B6460C0070FACB /* getoldopt.c */,
+ FCB1BE6214B6460C0070FACB /* options.c */,
+ FCB1BE6314B6460C0070FACB /* options.h */,
+ FCB1BE6414B6460C0070FACB /* pat_rep.c */,
+ FCB1BE6514B6460C0070FACB /* pat_rep.h */,
+ FCB1BE6614B6460C0070FACB /* pax.1 */,
+ FCB1BE6714B6460C0070FACB /* pax.c */,
+ FCB1BE6814B6460C0070FACB /* pax.h */,
+ FCB1BE6914B6460C0070FACB /* pax_format.c */,
+ FCB1BE6A14B6460C0070FACB /* pax_format.h */,
+ FCB1BE6B14B6460C0070FACB /* sel_subs.c */,
+ FCB1BE6C14B6460C0070FACB /* sel_subs.h */,
+ FCB1BE6D14B6460C0070FACB /* tables.c */,
+ FCB1BE6E14B6460C0070FACB /* tables.h */,
+ FCB1BE6F14B6460C0070FACB /* tar.c */,
+ FCB1BE7014B6460C0070FACB /* tar.h */,
+ FCB1BE7114B6460C0070FACB /* tty_subs.c */,
+ );
+ path = pax;
+ sourceTree = "<group>";
+ };
+ FCB1BE7214B6460C0070FACB /* rm */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE7414B6460C0070FACB /* rm.1 */,
+ FCB1BE7514B6460C0070FACB /* rm.c */,
+ FC8A8C4F14B650C3001B97AD /* unlink.1 */,
+ );
+ path = rm;
+ sourceTree = "<group>";
+ };
+ FCB1BE7614B6460C0070FACB /* rmdir */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE7814B6460C0070FACB /* rmdir.1 */,
+ FCB1BE7914B6460C0070FACB /* rmdir.c */,
+ );
+ path = rmdir;
+ sourceTree = "<group>";
+ };
+ FCB1BE7A14B6460C0070FACB /* rmt */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE7C14B6460C0070FACB /* rmt.8 */,
+ FCB1BE7D14B6460C0070FACB /* rmt.c */,
+ );
+ path = rmt;
+ sourceTree = "<group>";
+ };
+ FCB1BE7E14B6460C0070FACB /* shar */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE8014B6460C0070FACB /* shar.1 */,
+ FCB1BE8114B6460C0070FACB /* shar.sh */,
+ );
+ path = shar;
+ sourceTree = "<group>";
+ };
+ FCB1BE8214B6460C0070FACB /* stat */ = {
+ isa = PBXGroup;
+ children = (
+ FC8A8C4D14B64EA8001B97AD /* readlink.1 */,
+ FCB1BE8414B6460C0070FACB /* stat.1 */,
+ FCB1BE8514B6460C0070FACB /* stat.c */,
+ );
+ path = stat;
+ sourceTree = "<group>";
+ };
+ FCB1BE8614B6460C0070FACB /* touch */ = {
+ isa = PBXGroup;
+ children = (
+ FCB1BE8814B6460C0070FACB /* touch.1 */,
+ FCB1BE8914B6460C0070FACB /* touch.c */,
+ );
+ path = touch;
+ sourceTree = "<group>";
+ };
+ FDAD947F1808BB3A00B4D5A0 /* gzip */ = {
+ isa = PBXGroup;
+ children = (
+ 3E59B9301D4A767600D3128C /* futimens.c */,
+ FDAD94801808BB3A00B4D5A0 /* gzexe */,
+ FDAD94811808BB3A00B4D5A0 /* gzexe.1 */,
+ FDAD94821808BB3A00B4D5A0 /* gzip.1 */,
+ FDAD94831808BB3A00B4D5A0 /* gzip.c */,
+ FDAD94841808BB3A00B4D5A0 /* gzip.plist */,
+ FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */,
+ FDAD94861808BB3A00B4D5A0 /* install_scripts.sh */,
+ FDAD94871808BB3A00B4D5A0 /* unbzip2.c */,
+ FDAD94881808BB3A00B4D5A0 /* unpack.c */,
+ FDAD94891808BB3A00B4D5A0 /* unxz.c */,
+ FDAD948A1808BB3A00B4D5A0 /* zdiff */,
+ FDAD948B1808BB3A00B4D5A0 /* zdiff.1 */,
+ FDAD948C1808BB3A00B4D5A0 /* zforce */,
+ FDAD948D1808BB3A00B4D5A0 /* zforce.1 */,
+ FDAD948E1808BB3A00B4D5A0 /* zmore */,
+ FDAD948F1808BB3A00B4D5A0 /* zmore.1 */,
+ FDAD94901808BB3A00B4D5A0 /* znew */,
+ FDAD94911808BB3A00B4D5A0 /* znew.1 */,
+ FDAD94921808BB3A00B4D5A0 /* zuncompress.c */,
+ );
+ path = gzip;
+ sourceTree = "<group>";
+ };
+ FDAD94A71808BCB700B4D5A0 /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */,
+ FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */,
+ FDAD94A51808BC9B00B4D5A0 /* libz.dylib */,
+ );
+ name = Libraries;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ FC8A8B0B14B648D7001B97AD /* chmod */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B1014B648D7001B97AD /* Build configuration list for PBXNativeTarget "chmod" */;
+ buildPhases = (
+ FC8A8B0C14B648D7001B97AD /* Sources */,
+ FC8A8B0E14B648D7001B97AD /* Frameworks */,
+ FC8A8B0F14B648D7001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = chmod;
+ productName = file_cmds;
+ productReference = FC8A8B1214B648D7001B97AD /* chmod */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B1414B648E0001B97AD /* chown */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B1814B648E0001B97AD /* Build configuration list for PBXNativeTarget "chown" */;
+ buildPhases = (
+ FC8A8B1514B648E0001B97AD /* Sources */,
+ FC8A8B1614B648E0001B97AD /* Frameworks */,
+ FC8A8B1714B648E0001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = chown;
+ productName = file_cmds;
+ productReference = FC8A8B1A14B648E0001B97AD /* chown */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B1C14B648E3001B97AD /* cksum */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B2014B648E3001B97AD /* Build configuration list for PBXNativeTarget "cksum" */;
+ buildPhases = (
+ FC8A8B1D14B648E3001B97AD /* Sources */,
+ FC8A8B1E14B648E3001B97AD /* Frameworks */,
+ FC8A8B1F14B648E3001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = cksum;
+ productName = file_cmds;
+ productReference = FC8A8B2214B648E3001B97AD /* cksum */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B2414B648E5001B97AD /* compress */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B2814B648E5001B97AD /* Build configuration list for PBXNativeTarget "compress" */;
+ buildPhases = (
+ FC8A8B2514B648E5001B97AD /* Sources */,
+ FC8A8B2614B648E5001B97AD /* Frameworks */,
+ FC8A8B2714B648E5001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = compress;
+ productName = file_cmds;
+ productReference = FC8A8B2A14B648E5001B97AD /* compress */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B2C14B648E7001B97AD /* cp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B3014B648E7001B97AD /* Build configuration list for PBXNativeTarget "cp" */;
+ buildPhases = (
+ FC8A8B2D14B648E7001B97AD /* Sources */,
+ FC8A8B2E14B648E7001B97AD /* Frameworks */,
+ FC8A8B2F14B648E7001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = cp;
+ productName = file_cmds;
+ productReference = FC8A8B3214B648E7001B97AD /* cp */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B3414B648EA001B97AD /* dd */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B3814B648EA001B97AD /* Build configuration list for PBXNativeTarget "dd" */;
+ buildPhases = (
+ FC8A8B3514B648EA001B97AD /* Sources */,
+ FC8A8B3614B648EA001B97AD /* Frameworks */,
+ FC8A8B3714B648EA001B97AD /* CopyFiles */,
+ 72E62BA81A3A62960015FC8E /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dd;
+ productName = file_cmds;
+ productReference = FC8A8B3A14B648EA001B97AD /* dd */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B3C14B648EA001B97AD /* df */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B4014B648EA001B97AD /* Build configuration list for PBXNativeTarget "df" */;
+ buildPhases = (
+ FC8A8B3D14B648EA001B97AD /* Sources */,
+ FC8A8B3E14B648EA001B97AD /* Frameworks */,
+ FC8A8B3F14B648EA001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = df;
+ productName = file_cmds;
+ productReference = FC8A8B4214B648EA001B97AD /* df */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B4414B648EB001B97AD /* du */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B4814B648EB001B97AD /* Build configuration list for PBXNativeTarget "du" */;
+ buildPhases = (
+ FC8A8B4514B648EB001B97AD /* Sources */,
+ FC8A8B4614B648EB001B97AD /* Frameworks */,
+ FC8A8B4714B648EB001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = du;
+ productName = file_cmds;
+ productReference = FC8A8B4A14B648EB001B97AD /* du */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B4C14B648EB001B97AD /* install */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B5014B648EB001B97AD /* Build configuration list for PBXNativeTarget "install" */;
+ buildPhases = (
+ FC8A8B4D14B648EB001B97AD /* Sources */,
+ FC8A8B4E14B648EB001B97AD /* Frameworks */,
+ FC8A8B4F14B648EB001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = install;
+ productName = file_cmds;
+ productReference = FC8A8B5214B648EB001B97AD /* install */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B5414B648EB001B97AD /* ipcrm */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B5814B648EB001B97AD /* Build configuration list for PBXNativeTarget "ipcrm" */;
+ buildPhases = (
+ FC8A8B5514B648EB001B97AD /* Sources */,
+ FC8A8B5614B648EB001B97AD /* Frameworks */,
+ FC8A8B5714B648EB001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ipcrm;
+ productName = file_cmds;
+ productReference = FC8A8B5A14B648EB001B97AD /* ipcrm */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B5C14B648EC001B97AD /* ipcs */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B6014B648EC001B97AD /* Build configuration list for PBXNativeTarget "ipcs" */;
+ buildPhases = (
+ FC8A8B5D14B648EC001B97AD /* Sources */,
+ FC8A8B5E14B648EC001B97AD /* Frameworks */,
+ FC8A8B5F14B648EC001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ipcs;
+ productName = file_cmds;
+ productReference = FC8A8B6214B648EC001B97AD /* ipcs */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B6414B648EC001B97AD /* ln */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B6814B648EC001B97AD /* Build configuration list for PBXNativeTarget "ln" */;
+ buildPhases = (
+ FC8A8B6514B648EC001B97AD /* Sources */,
+ FC8A8B6614B648EC001B97AD /* Frameworks */,
+ FC8A8B6714B648EC001B97AD /* CopyFiles */,
+ FC8A8C7E14B6557E001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ln;
+ productName = file_cmds;
+ productReference = FC8A8B6A14B648EC001B97AD /* ln */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B6C14B648ED001B97AD /* ls */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B7014B648ED001B97AD /* Build configuration list for PBXNativeTarget "ls" */;
+ buildPhases = (
+ FC8A8B6D14B648ED001B97AD /* Sources */,
+ FC8A8B6E14B648ED001B97AD /* Frameworks */,
+ FC8A8B6F14B648ED001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ls;
+ productName = file_cmds;
+ productReference = FC8A8B7214B648ED001B97AD /* ls */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B7414B648ED001B97AD /* mkdir */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B7814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkdir" */;
+ buildPhases = (
+ FC8A8B7514B648ED001B97AD /* Sources */,
+ FC8A8B7614B648ED001B97AD /* Frameworks */,
+ FC8A8B7714B648ED001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mkdir;
+ productName = file_cmds;
+ productReference = FC8A8B7A14B648ED001B97AD /* mkdir */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B7C14B648ED001B97AD /* mkfifo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B8014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkfifo" */;
+ buildPhases = (
+ FC8A8B7D14B648ED001B97AD /* Sources */,
+ FC8A8B7E14B648ED001B97AD /* Frameworks */,
+ FC8A8B7F14B648ED001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mkfifo;
+ productName = file_cmds;
+ productReference = FC8A8B8214B648ED001B97AD /* mkfifo */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B8414B648ED001B97AD /* mknod */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B8814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mknod" */;
+ buildPhases = (
+ FC8A8B8514B648ED001B97AD /* Sources */,
+ FC8A8B8614B648ED001B97AD /* Frameworks */,
+ FC8A8B8714B648ED001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mknod;
+ productName = file_cmds;
+ productReference = FC8A8B8A14B648ED001B97AD /* mknod */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B8C14B648ED001B97AD /* mtree */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B9014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mtree" */;
+ buildPhases = (
+ FC8A8B8D14B648ED001B97AD /* Sources */,
+ FC8A8B8E14B648ED001B97AD /* Frameworks */,
+ FC8A8B8F14B648ED001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mtree;
+ productName = file_cmds;
+ productReference = FC8A8B9214B648ED001B97AD /* mtree */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B9414B648EE001B97AD /* mv */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8B9814B648EE001B97AD /* Build configuration list for PBXNativeTarget "mv" */;
+ buildPhases = (
+ FC8A8B9514B648EE001B97AD /* Sources */,
+ FC8A8B9614B648EE001B97AD /* Frameworks */,
+ FC8A8B9714B648EE001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = mv;
+ productName = file_cmds;
+ productReference = FC8A8B9A14B648EE001B97AD /* mv */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8B9C14B648EE001B97AD /* pathchk */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BA014B648EE001B97AD /* Build configuration list for PBXNativeTarget "pathchk" */;
+ buildPhases = (
+ FC8A8B9D14B648EE001B97AD /* Sources */,
+ FC8A8B9E14B648EE001B97AD /* Frameworks */,
+ FC8A8B9F14B648EE001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = pathchk;
+ productName = file_cmds;
+ productReference = FC8A8BA214B648EE001B97AD /* pathchk */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8BA414B648EE001B97AD /* pax */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BA814B648EE001B97AD /* Build configuration list for PBXNativeTarget "pax" */;
+ buildPhases = (
+ FC8A8BA514B648EE001B97AD /* Sources */,
+ FC8A8BA614B648EE001B97AD /* Frameworks */,
+ FC8A8BA714B648EE001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = pax;
+ productName = file_cmds;
+ productReference = FC8A8BAA14B648EE001B97AD /* pax */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8BAC14B648EF001B97AD /* rm */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BB014B648EF001B97AD /* Build configuration list for PBXNativeTarget "rm" */;
+ buildPhases = (
+ FC8A8BAD14B648EF001B97AD /* Sources */,
+ FC8A8BAE14B648EF001B97AD /* Frameworks */,
+ FC8A8BAF14B648EF001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = rm;
+ productName = file_cmds;
+ productReference = FC8A8BB214B648EF001B97AD /* rm */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8BB414B648EF001B97AD /* rmdir */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BB814B648EF001B97AD /* Build configuration list for PBXNativeTarget "rmdir" */;
+ buildPhases = (
+ FC8A8BB514B648EF001B97AD /* Sources */,
+ FC8A8BB614B648EF001B97AD /* Frameworks */,
+ FC8A8BB714B648EF001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = rmdir;
+ productName = file_cmds;
+ productReference = FC8A8BBA14B648EF001B97AD /* rmdir */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8BC414B648EF001B97AD /* stat */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BC814B648EF001B97AD /* Build configuration list for PBXNativeTarget "stat" */;
+ buildPhases = (
+ FC8A8BC514B648EF001B97AD /* Sources */,
+ FC8A8BC614B648EF001B97AD /* Frameworks */,
+ FC8A8BC714B648EF001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = stat;
+ productName = file_cmds;
+ productReference = FC8A8BCA14B648EF001B97AD /* stat */;
+ productType = "com.apple.product-type.tool";
+ };
+ FC8A8BCC14B648F0001B97AD /* touch */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FC8A8BD014B648F0001B97AD /* Build configuration list for PBXNativeTarget "touch" */;
+ buildPhases = (
+ FC8A8BCD14B648F0001B97AD /* Sources */,
+ FC8A8BCE14B648F0001B97AD /* Frameworks */,
+ FC8A8BCF14B648F0001B97AD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = touch;
+ productName = file_cmds;
+ productReference = FC8A8BD214B648F0001B97AD /* touch */;
+ productType = "com.apple.product-type.tool";
+ };
+ FCB1BDB714B645D10070FACB /* chflags */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FCB1BDC214B645D10070FACB /* Build configuration list for PBXNativeTarget "chflags" */;
+ buildPhases = (
+ FCB1BDB414B645D10070FACB /* Sources */,
+ FCB1BDB514B645D10070FACB /* Frameworks */,
+ 590112FE18284E58006881A1 /* ShellScript */,
+ FCB1BDB614B645D10070FACB /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = chflags;
+ productName = file_cmds;
+ productReference = FCB1BDB814B645D10070FACB /* chflags */;
+ productType = "com.apple.product-type.tool";
+ };
+ FDAD94961808BB6D00B4D5A0 /* gzip */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = FDAD949D1808BB6D00B4D5A0 /* Build configuration list for PBXNativeTarget "gzip" */;
+ buildPhases = (
+ FDAD94931808BB6D00B4D5A0 /* Sources */,
+ FDAD94941808BB6D00B4D5A0 /* Frameworks */,
+ FDAD94951808BB6D00B4D5A0 /* Install Man Page */,
+ FDAD94A01808BBF100B4D5A0 /* Install Scripts */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = gzip;
+ productName = gzip;
+ productReference = FDAD94971808BB6D00B4D5A0 /* gzip */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ FCB1BDAF14B645D00070FACB /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0610;
+ ORGANIZATIONNAME = "Apple Inc.";
+ TargetAttributes = {
+ 3E966CE71FB2211F0019F7A1 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = FCB1BDB214B645D00070FACB /* Build configuration list for PBXProject "file_cmds" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ English,
+ en,
+ );
+ mainGroup = FCB1BDAD14B645D00070FACB;
+ productRefGroup = FCB1BDB914B645D10070FACB /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ FC8A8C8014B655ED001B97AD /* executables */,
+ FDFF0C501811BA2F00BFC477 /* eOS */,
+ FCB1BDB714B645D10070FACB /* chflags */,
+ FC8A8C5B14B652E1001B97AD /* chgrp */,
+ FC8A8B0B14B648D7001B97AD /* chmod */,
+ FC8A8B1414B648E0001B97AD /* chown */,
+ FC8A8B1C14B648E3001B97AD /* cksum */,
+ FC8A8B2414B648E5001B97AD /* compress */,
+ FC8A8B2C14B648E7001B97AD /* cp */,
+ FC8A8B3414B648EA001B97AD /* dd */,
+ FC8A8B3C14B648EA001B97AD /* df */,
+ FC8A8B4414B648EB001B97AD /* du */,
+ FDAD94961808BB6D00B4D5A0 /* gzip */,
+ FC8A8B4C14B648EB001B97AD /* install */,
+ FC8A8B5414B648EB001B97AD /* ipcrm */,
+ FC8A8B5C14B648EC001B97AD /* ipcs */,
+ FC8A8C7314B6554E001B97AD /* link */,
+ FC8A8B6414B648EC001B97AD /* ln */,
+ FC8A8B6C14B648ED001B97AD /* ls */,
+ FC8A8B7414B648ED001B97AD /* mkdir */,
+ FC8A8B7C14B648ED001B97AD /* mkfifo */,
+ FC8A8B8414B648ED001B97AD /* mknod */,
+ FC8A8B8C14B648ED001B97AD /* mtree */,
+ FC8A8B9414B648EE001B97AD /* mv */,
+ FC8A8B9C14B648EE001B97AD /* pathchk */,
+ FC8A8BA414B648EE001B97AD /* pax */,
+ FC8A8C4614B64DCD001B97AD /* readlink */,
+ FC8A8BAC14B648EF001B97AD /* rm */,
+ FC8A8BB414B648EF001B97AD /* rmdir */,
+ FC8A8C3C14B64A9D001B97AD /* shar */,
+ FC8A8BC414B648EF001B97AD /* stat */,
+ FC8A8C6714B6536D001B97AD /* sum */,
+ FC8A8BCC14B648F0001B97AD /* touch */,
+ FC8A8CC814B65F92001B97AD /* uncompress */,
+ FC8A8C5014B650CF001B97AD /* unlink */,
+ 3E966CE71FB2211F0019F7A1 /* tests */,
+ 729D07252347EC4D000716E5 /* macos_host_tools */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 590112FE18284E58006881A1 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = "/bin/sh -ex";
+ shellScript = "install -d -g ${GROUP} -o ${USER} -m 0755 ${INSTALL_DIR}\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share/man\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share/man/man{1,7,8}";
+ showEnvVarsInLog = 0;
+ };
+ 72E62BA81A3A62960015FC8E /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". ${SRCROOT}/dd/install_symlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8C4C14B64DF9001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/stat",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8C5514B650CF001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/rm",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8C6014B652E1001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/usr/sbin/chown",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8C6C14B6536D001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/cksum",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8C7814B6554E001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/ln",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8CC714B65CEB001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "install -d -o root -g wheel -m 0755 \"$DSTROOT\"/usr/bin\ninstall -c -o root -g wheel -m 0755 \"$PROJECT_DIR\"/shar/shar.sh \"$DSTROOT\"/usr/bin/shar";
+ showEnvVarsInLog = 0;
+ };
+ FC8A8CCD14B65F92001B97AD /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/compress",
+ );
+ outputPaths = (
+ "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh";
+ showEnvVarsInLog = 0;
+ };
+ FDAD94A01808BBF100B4D5A0 /* Install Scripts */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Install Scripts";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = ". ${SRCROOT}/gzip/install_scripts.sh";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ FC8A8B0C14B648D7001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BE614B6495B001B97AD /* chmod_acl.c in Sources */,
+ FC8A8BE514B64958001B97AD /* chmod.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B1514B648E0001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BE814B64962001B97AD /* chown.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B1D14B648E3001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BF014B6497D001B97AD /* sum2.c in Sources */,
+ FC8A8BEF14B6497A001B97AD /* sum1.c in Sources */,
+ FC8A8BEE14B64977001B97AD /* print.c in Sources */,
+ FC8A8BED14B64975001B97AD /* crc32.c in Sources */,
+ FC8A8BEC14B64972001B97AD /* crc.c in Sources */,
+ FC8A8BEB14B64970001B97AD /* cksum.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B2514B648E5001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BF414B6498A001B97AD /* zopen.c in Sources */,
+ FC8A8BF314B64988001B97AD /* compress.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B2D14B648E7001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BF614B64998001B97AD /* utils.c in Sources */,
+ FC8A8BF514B64995001B97AD /* cp.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B3514B648EA001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8BFE14B649B1001B97AD /* position.c in Sources */,
+ FC8A8BFD14B649AE001B97AD /* misc.c in Sources */,
+ FC8A8BFC14B649AC001B97AD /* dd.c in Sources */,
+ FC8A8BFA14B649A7001B97AD /* conv_tab.c in Sources */,
+ FC8A8BF914B649A5001B97AD /* conv.c in Sources */,
+ FC8A8BF814B649A4001B97AD /* args.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B3D14B648EA001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0114B649D1001B97AD /* df.c in Sources */,
+ FC8A8CC314B6598F001B97AD /* vfslist.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B4514B648EB001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0214B649D4001B97AD /* du.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B4D14B648EB001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0414B649DD001B97AD /* xinstall.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B5514B648EB001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0714B649E3001B97AD /* ipcrm.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B5D14B648EC001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0814B649E8001B97AD /* ipcs.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B6514B648EC001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0B14B649EF001B97AD /* ln.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B6D14B648ED001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C0D14B649F7001B97AD /* cmp.c in Sources */,
+ FC8A8C0E14B649F9001B97AD /* ls.c in Sources */,
+ FC8A8C0F14B649FC001B97AD /* print.c in Sources */,
+ FC8A8C1014B649FE001B97AD /* util.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B7514B648ED001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C1114B64A04001B97AD /* mkdir.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B7D14B648ED001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C1414B64A0A001B97AD /* mkfifo.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B8514B648ED001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C1614B64A0F001B97AD /* mknod.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B8D14B648ED001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8CD114B66E10001B97AD /* crc.c in Sources */,
+ FC8A8C1714B64A14001B97AD /* commoncrypto.c in Sources */,
+ FC8A8C1814B64A17001B97AD /* compare.c in Sources */,
+ FC8A8C1914B64A1A001B97AD /* create.c in Sources */,
+ FC8A8C1A14B64A22001B97AD /* excludes.c in Sources */,
+ FC8A8C1B14B64A27001B97AD /* misc.c in Sources */,
+ 7D0A20EA2499364700F0F6D7 /* metrics.c in Sources */,
+ FC8A8C1C14B64A2D001B97AD /* mtree.c in Sources */,
+ FC8A8C1D14B64A31001B97AD /* spec.c in Sources */,
+ FC8A8C1E14B64A34001B97AD /* specspec.c in Sources */,
+ FC8A8C1F14B64A38001B97AD /* verify.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B9514B648EE001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C2214B64A4B001B97AD /* mv.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8B9D14B648EE001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C2414B64A53001B97AD /* pathchk.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BA514B648EE001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C2714B64A73001B97AD /* ar_io.c in Sources */,
+ FC8A8C2814B64A73001B97AD /* ar_subs.c in Sources */,
+ FC8A8C2914B64A73001B97AD /* buf_subs.c in Sources */,
+ FC8A8C2A14B64A73001B97AD /* cache.c in Sources */,
+ FC8A8C2B14B64A73001B97AD /* cpio.c in Sources */,
+ FC8A8C2C14B64A73001B97AD /* file_subs.c in Sources */,
+ FC8A8C2D14B64A73001B97AD /* ftree.c in Sources */,
+ FC8A8C2E14B64A73001B97AD /* gen_subs.c in Sources */,
+ FC8A8C2F14B64A73001B97AD /* getoldopt.c in Sources */,
+ FC8A8C3014B64A73001B97AD /* options.c in Sources */,
+ FC8A8C3114B64A73001B97AD /* pat_rep.c in Sources */,
+ FC8A8C3214B64A73001B97AD /* pax.c in Sources */,
+ FC8A8C3314B64A73001B97AD /* pax_format.c in Sources */,
+ FC8A8C3414B64A73001B97AD /* sel_subs.c in Sources */,
+ FC8A8C3514B64A73001B97AD /* tables.c in Sources */,
+ FC8A8C3614B64A73001B97AD /* tar.c in Sources */,
+ FC8A8C3714B64A73001B97AD /* tty_subs.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BAD14B648EF001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C3914B64A7E001B97AD /* rm.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BB514B648EF001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C3B14B64A88001B97AD /* rmdir.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BC514B648EF001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C4214B64AC3001B97AD /* stat.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FC8A8BCD14B648F0001B97AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8C4314B64AC7001B97AD /* touch.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FCB1BDB414B645D10070FACB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ FC8A8A2814B6486E001B97AD /* chflags.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FDAD94931808BB6D00B4D5A0 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3E59B9311D4A767600D3128C /* futimens.c in Sources */,
+ FDAD949F1808BBB900B4D5A0 /* gzip.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 729D074E2347EC4D000716E5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8C14B648ED001B97AD /* mtree */;
+ targetProxy = 729D074F2347EC4D000716E5 /* PBXContainerItemProxy */;
+ };
+ FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BC414B648EF001B97AD /* stat */;
+ targetProxy = FC8A8C4914B64DE1001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C5914B65238001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BAC14B648EF001B97AD /* rm */;
+ targetProxy = FC8A8C5814B65238001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C6414B652FA001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1414B648E0001B97AD /* chown */;
+ targetProxy = FC8A8C6314B652FA001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C7014B6537C001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1C14B648E3001B97AD /* cksum */;
+ targetProxy = FC8A8C6F14B6537C001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C7C14B65562001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B6414B648EC001B97AD /* ln */;
+ targetProxy = FC8A8C7B14B65562001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8414B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FCB1BDB714B645D10070FACB /* chflags */;
+ targetProxy = FC8A8C8314B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8614B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C5B14B652E1001B97AD /* chgrp */;
+ targetProxy = FC8A8C8514B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8814B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B0B14B648D7001B97AD /* chmod */;
+ targetProxy = FC8A8C8714B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8A14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1414B648E0001B97AD /* chown */;
+ targetProxy = FC8A8C8914B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8C14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1C14B648E3001B97AD /* cksum */;
+ targetProxy = FC8A8C8B14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C8E14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B2414B648E5001B97AD /* compress */;
+ targetProxy = FC8A8C8D14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9014B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B2C14B648E7001B97AD /* cp */;
+ targetProxy = FC8A8C8F14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9214B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B3414B648EA001B97AD /* dd */;
+ targetProxy = FC8A8C9114B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9414B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B3C14B648EA001B97AD /* df */;
+ targetProxy = FC8A8C9314B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9614B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B4414B648EB001B97AD /* du */;
+ targetProxy = FC8A8C9514B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9814B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B4C14B648EB001B97AD /* install */;
+ targetProxy = FC8A8C9714B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9A14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B5414B648EB001B97AD /* ipcrm */;
+ targetProxy = FC8A8C9914B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9C14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B5C14B648EC001B97AD /* ipcs */;
+ targetProxy = FC8A8C9B14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8C9E14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C7314B6554E001B97AD /* link */;
+ targetProxy = FC8A8C9D14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CA014B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B6414B648EC001B97AD /* ln */;
+ targetProxy = FC8A8C9F14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CA214B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B6C14B648ED001B97AD /* ls */;
+ targetProxy = FC8A8CA114B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CA414B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B7414B648ED001B97AD /* mkdir */;
+ targetProxy = FC8A8CA314B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CA614B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B7C14B648ED001B97AD /* mkfifo */;
+ targetProxy = FC8A8CA514B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CA814B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8414B648ED001B97AD /* mknod */;
+ targetProxy = FC8A8CA714B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CAA14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8C14B648ED001B97AD /* mtree */;
+ targetProxy = FC8A8CA914B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CAC14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B9414B648EE001B97AD /* mv */;
+ targetProxy = FC8A8CAB14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CAE14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B9C14B648EE001B97AD /* pathchk */;
+ targetProxy = FC8A8CAD14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CB014B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BA414B648EE001B97AD /* pax */;
+ targetProxy = FC8A8CAF14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CB214B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C4614B64DCD001B97AD /* readlink */;
+ targetProxy = FC8A8CB114B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CB414B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BAC14B648EF001B97AD /* rm */;
+ targetProxy = FC8A8CB314B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CB614B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BB414B648EF001B97AD /* rmdir */;
+ targetProxy = FC8A8CB514B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CB814B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C3C14B64A9D001B97AD /* shar */;
+ targetProxy = FC8A8CB714B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CBA14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BC414B648EF001B97AD /* stat */;
+ targetProxy = FC8A8CB914B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CBC14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C6714B6536D001B97AD /* sum */;
+ targetProxy = FC8A8CBB14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CBE14B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BCC14B648F0001B97AD /* touch */;
+ targetProxy = FC8A8CBD14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CC014B655FD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C5014B650CF001B97AD /* unlink */;
+ targetProxy = FC8A8CBF14B655FD001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CC914B65F92001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BAC14B648EF001B97AD /* rm */;
+ targetProxy = FC8A8CCA14B65F92001B97AD /* PBXContainerItemProxy */;
+ };
+ FC8A8CD314B67BFD001B97AD /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8CC814B65F92001B97AD /* uncompress */;
+ targetProxy = FC8A8CD214B67BFD001B97AD /* PBXContainerItemProxy */;
+ };
+ FDAD94AA1808BDAA00B4D5A0 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FDAD94961808BB6D00B4D5A0 /* gzip */;
+ targetProxy = FDAD94A91808BDAA00B4D5A0 /* PBXContainerItemProxy */;
+ };
+ FDFF0C511811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FCB1BDB714B645D10070FACB /* chflags */;
+ targetProxy = FDFF0C521811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C531811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C5B14B652E1001B97AD /* chgrp */;
+ targetProxy = FDFF0C541811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C551811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B0B14B648D7001B97AD /* chmod */;
+ targetProxy = FDFF0C561811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C571811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1414B648E0001B97AD /* chown */;
+ targetProxy = FDFF0C581811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C591811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B1C14B648E3001B97AD /* cksum */;
+ targetProxy = FDFF0C5A1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C5B1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B2414B648E5001B97AD /* compress */;
+ targetProxy = FDFF0C5C1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C5D1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B2C14B648E7001B97AD /* cp */;
+ targetProxy = FDFF0C5E1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C5F1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B3414B648EA001B97AD /* dd */;
+ targetProxy = FDFF0C601811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C611811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B3C14B648EA001B97AD /* df */;
+ targetProxy = FDFF0C621811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C631811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B4414B648EB001B97AD /* du */;
+ targetProxy = FDFF0C641811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C671811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B4C14B648EB001B97AD /* install */;
+ targetProxy = FDFF0C681811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C691811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B5414B648EB001B97AD /* ipcrm */;
+ targetProxy = FDFF0C6A1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C6B1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B5C14B648EC001B97AD /* ipcs */;
+ targetProxy = FDFF0C6C1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C6D1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C7314B6554E001B97AD /* link */;
+ targetProxy = FDFF0C6E1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C6F1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B6414B648EC001B97AD /* ln */;
+ targetProxy = FDFF0C701811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C711811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B6C14B648ED001B97AD /* ls */;
+ targetProxy = FDFF0C721811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C731811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B7414B648ED001B97AD /* mkdir */;
+ targetProxy = FDFF0C741811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C751811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B7C14B648ED001B97AD /* mkfifo */;
+ targetProxy = FDFF0C761811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C771811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8414B648ED001B97AD /* mknod */;
+ targetProxy = FDFF0C781811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C791811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8C14B648ED001B97AD /* mtree */;
+ targetProxy = FDFF0C7A1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C7B1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B9414B648EE001B97AD /* mv */;
+ targetProxy = FDFF0C7C1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C7D1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B9C14B648EE001B97AD /* pathchk */;
+ targetProxy = FDFF0C7E1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C7F1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BA414B648EE001B97AD /* pax */;
+ targetProxy = FDFF0C801811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C811811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C4614B64DCD001B97AD /* readlink */;
+ targetProxy = FDFF0C821811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C831811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BAC14B648EF001B97AD /* rm */;
+ targetProxy = FDFF0C841811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C851811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BB414B648EF001B97AD /* rmdir */;
+ targetProxy = FDFF0C861811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C871811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C3C14B64A9D001B97AD /* shar */;
+ targetProxy = FDFF0C881811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C891811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BC414B648EF001B97AD /* stat */;
+ targetProxy = FDFF0C8A1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C8B1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C6714B6536D001B97AD /* sum */;
+ targetProxy = FDFF0C8C1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C8D1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8BCC14B648F0001B97AD /* touch */;
+ targetProxy = FDFF0C8E1811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C8F1811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8CC814B65F92001B97AD /* uncompress */;
+ targetProxy = FDFF0C901811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+ FDFF0C911811BA2F00BFC477 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8C5014B650CF001B97AD /* unlink */;
+ targetProxy = FDFF0C921811BA2F00BFC477 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 3E966CE81FB2211F0019F7A1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ 729D07692347EC4D000716E5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /usr/bin;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ FC8A8B1114B648D7001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B1914B648E0001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /usr/sbin;
+ };
+ name = Release;
+ };
+ FC8A8B2114B648E3001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8B2914B648E5001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ };
+ name = Release;
+ };
+ FC8A8B3114B648E7001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B3914B648EA001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = dd/dd.entitlements;
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "";
+ CODE_SIGN_IDENTITY = "-";
+ INSTALL_PATH = /bin;
+ "INSTALL_PATH[sdk=appletvos]" = /usr/local/bin;
+ "INSTALL_PATH[sdk=iphoneos*]" = /usr/local/bin;
+ };
+ name = Release;
+ };
+ FC8A8B4114B648EA001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B4914B648EB001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ };
+ name = Release;
+ };
+ FC8A8B5114B648EB001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ };
+ name = Release;
+ };
+ FC8A8B5914B648EB001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8B6114B648EC001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ OTHER_CFLAGS = (
+ "-iquote",
+ "$(SDKROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders",
+ "-iquote",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ WARNING_CFLAGS = "";
+ };
+ name = Release;
+ };
+ FC8A8B6914B648EC001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B7114B648ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "__FBSDID=__RCSID",
+ _DARWIN_USE_64_BIT_INODE,
+ COLORLS,
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B7914B648ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8B8114B648ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8B8914B648ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /sbin;
+ };
+ name = Release;
+ };
+ FC8A8B9114B648ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "__FBSDID=__RCSID",
+ _DARWIN_USE_64_BIT_INODE,
+ ENABLE_MD5,
+ ENABLE_RMD160,
+ ENABLE_SHA1,
+ ENABLE_SHA256,
+ );
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
+ INSTALL_PATH = /usr/sbin;
+ "OTHER_LDFLAGS[sdk=macosx*]" = "-lCrashReporterClient";
+ };
+ name = Release;
+ };
+ FC8A8B9914B648EE001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8BA114B648EE001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8BA914B648EE001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8BB114B648EF001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8BB914B648EF001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8BC914B648EF001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "HAVE_CONFIG_H=0",
+ );
+ };
+ name = Release;
+ };
+ FC8A8BD114B648F0001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8C3E14B64A9D001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8C4814B64DCE001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8C5714B650CF001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8C6214B652E1001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8C6E14B6536D001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8C7A14B6554E001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /bin;
+ };
+ name = Release;
+ };
+ FC8A8C8214B655ED001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FC8A8CCF14B65F92001B97AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FCB1BDC114B645D10070FACB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "__FBSDID=__RCSID",
+ _DARWIN_USE_64_BIT_INODE,
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = /usr/bin;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ VERSIONING_SYSTEM = "apple-generic";
+ WARNING_CFLAGS = (
+ "-Wall",
+ "-Werror",
+ "-Wundef",
+ );
+ };
+ name = Release;
+ };
+ FCB1BDC414B645D10070FACB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FDAD949E1808BB6D00B4D5A0 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ FDFF0C941811BA2F00BFC477 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 3E966CE91FB2211F0019F7A1 /* Build configuration list for PBXAggregateTarget "tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3E966CE81FB2211F0019F7A1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 729D07682347EC4D000716E5 /* Build configuration list for PBXAggregateTarget "macos_host_tools" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 729D07692347EC4D000716E5 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B1014B648D7001B97AD /* Build configuration list for PBXNativeTarget "chmod" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B1114B648D7001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B1814B648E0001B97AD /* Build configuration list for PBXNativeTarget "chown" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B1914B648E0001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B2014B648E3001B97AD /* Build configuration list for PBXNativeTarget "cksum" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B2114B648E3001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B2814B648E5001B97AD /* Build configuration list for PBXNativeTarget "compress" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B2914B648E5001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B3014B648E7001B97AD /* Build configuration list for PBXNativeTarget "cp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B3114B648E7001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B3814B648EA001B97AD /* Build configuration list for PBXNativeTarget "dd" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B3914B648EA001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B4014B648EA001B97AD /* Build configuration list for PBXNativeTarget "df" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B4114B648EA001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B4814B648EB001B97AD /* Build configuration list for PBXNativeTarget "du" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B4914B648EB001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B5014B648EB001B97AD /* Build configuration list for PBXNativeTarget "install" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B5114B648EB001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B5814B648EB001B97AD /* Build configuration list for PBXNativeTarget "ipcrm" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B5914B648EB001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B6014B648EC001B97AD /* Build configuration list for PBXNativeTarget "ipcs" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B6114B648EC001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B6814B648EC001B97AD /* Build configuration list for PBXNativeTarget "ln" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B6914B648EC001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B7014B648ED001B97AD /* Build configuration list for PBXNativeTarget "ls" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B7114B648ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B7814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkdir" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B7914B648ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B8014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkfifo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B8114B648ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B8814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mknod" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B8914B648ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B9014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mtree" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B9114B648ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8B9814B648EE001B97AD /* Build configuration list for PBXNativeTarget "mv" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8B9914B648EE001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BA014B648EE001B97AD /* Build configuration list for PBXNativeTarget "pathchk" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BA114B648EE001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BA814B648EE001B97AD /* Build configuration list for PBXNativeTarget "pax" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BA914B648EE001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BB014B648EF001B97AD /* Build configuration list for PBXNativeTarget "rm" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BB114B648EF001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BB814B648EF001B97AD /* Build configuration list for PBXNativeTarget "rmdir" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BB914B648EF001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BC814B648EF001B97AD /* Build configuration list for PBXNativeTarget "stat" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BC914B648EF001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8BD014B648F0001B97AD /* Build configuration list for PBXNativeTarget "touch" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8BD114B648F0001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C3D14B64A9D001B97AD /* Build configuration list for PBXAggregateTarget "shar" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C3E14B64A9D001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C4714B64DCE001B97AD /* Build configuration list for PBXAggregateTarget "readlink" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C4814B64DCE001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C5614B650CF001B97AD /* Build configuration list for PBXAggregateTarget "unlink" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C5714B650CF001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C6114B652E1001B97AD /* Build configuration list for PBXAggregateTarget "chgrp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C6214B652E1001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C6D14B6536D001B97AD /* Build configuration list for PBXAggregateTarget "sum" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C6E14B6536D001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C7914B6554E001B97AD /* Build configuration list for PBXAggregateTarget "link" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C7A14B6554E001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8C8114B655ED001B97AD /* Build configuration list for PBXAggregateTarget "executables" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8C8214B655ED001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FC8A8CCE14B65F92001B97AD /* Build configuration list for PBXAggregateTarget "uncompress" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FC8A8CCF14B65F92001B97AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FCB1BDB214B645D00070FACB /* Build configuration list for PBXProject "file_cmds" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FCB1BDC114B645D10070FACB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FCB1BDC214B645D10070FACB /* Build configuration list for PBXNativeTarget "chflags" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FCB1BDC414B645D10070FACB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FDAD949D1808BB6D00B4D5A0 /* Build configuration list for PBXNativeTarget "gzip" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FDAD949E1808BB6D00B4D5A0 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ FDFF0C931811BA2F00BFC477 /* Build configuration list for PBXAggregateTarget "eOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ FDFF0C941811BA2F00BFC477 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = FCB1BDAF14B645D00070FACB /* Project object */;
+}
diff --git a/file_cmds/gzip/futimens.c b/file_cmds/gzip/futimens.c
new file mode 100644
index 0000000..08345c3
--- /dev/null
+++ b/file_cmds/gzip/futimens.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2015 Jilles Tjoelker
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+
+#ifndef UTIME_NOW
+#define UTIME_NOW -1
+#endif
+
+#ifndef UTIME_OMIT
+#define UTIME_OMIT -2
+#endif
+int futimens(int fd, const struct timespec times[2]);
+
+int
+futimens(int fd, const struct timespec times[2])
+{
+ struct timeval now, tv[2], *tvp;
+ struct stat sb;
+
+ if (times == NULL || (times[0].tv_nsec == UTIME_NOW &&
+ times[1].tv_nsec == UTIME_NOW))
+ tvp = NULL;
+ else if (times[0].tv_nsec == UTIME_OMIT &&
+ times[1].tv_nsec == UTIME_OMIT)
+ return (0);
+ else {
+ if ((times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999) &&
+ times[0].tv_nsec != UTIME_NOW &&
+ times[0].tv_nsec != UTIME_OMIT) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999) &&
+ times[1].tv_nsec != UTIME_NOW &&
+ times[1].tv_nsec != UTIME_OMIT) {
+ errno = EINVAL;
+ return (-1);
+ }
+ tv[0].tv_sec = times[0].tv_sec;
+ tv[0].tv_usec = times[0].tv_nsec / 1000;
+ tv[1].tv_sec = times[1].tv_sec;
+ tv[1].tv_usec = times[1].tv_nsec / 1000;
+ tvp = tv;
+ if (times[0].tv_nsec == UTIME_OMIT ||
+ times[1].tv_nsec == UTIME_OMIT) {
+ if (fstat(fd, &sb) == -1)
+ return (-1);
+ if (times[0].tv_nsec == UTIME_OMIT) {
+ tv[0].tv_sec = sb.st_atimespec.tv_sec;
+ tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
+ }
+ if (times[1].tv_nsec == UTIME_OMIT) {
+ tv[1].tv_sec = sb.st_mtimespec.tv_sec;
+ tv[1].tv_usec = sb.st_mtimespec.tv_nsec / 1000;
+ }
+ }
+ if (times[0].tv_nsec == UTIME_NOW ||
+ times[1].tv_nsec == UTIME_NOW) {
+ if (gettimeofday(&now, NULL) == -1)
+ return (-1);
+ if (times[0].tv_nsec == UTIME_NOW)
+ tv[0] = now;
+ if (times[1].tv_nsec == UTIME_NOW)
+ tv[1] = now;
+ }
+ }
+ return (futimes(fd, tvp));
+}
diff --git a/file_cmds/gzip/gzexe b/file_cmds/gzip/gzexe
new file mode 100644
index 0000000..5809133
--- /dev/null
+++ b/file_cmds/gzip/gzexe
@@ -0,0 +1,179 @@
+#!/bin/sh -
+#
+# $NetBSD: gzexe,v 1.3 2004/05/01 08:22:41 wiz Exp $
+# $OpenBSD: gzexe,v 1.3 2003/08/05 18:22:17 deraadt Exp $
+#
+#-
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# $FreeBSD: src/usr.bin/gzip/gzexe,v 1.1 2007/01/26 10:19:07 delphij Exp $
+
+# The number of lines plus one in the on-the-fly decompression script
+lines=19
+
+# A simple string to recognize already compressed files
+magic="# compressed by gzexe"
+
+# Write the decompression script to stdout
+header () {
+ # first section needs variable expansion, second not
+ cat <<- EOF
+ #!/bin/sh -
+ $magic
+ lines=$lines
+ EOF
+ cat <<- 'EOF'
+ prog=`/usr/bin/basename "$0"`
+ tmp=`/usr/bin/mktemp -d /tmp/gzexeXXXXXXXXXX` || {
+ /bin/echo "$prog: cannot create tmp dir"; exit 1
+ }
+ trap '/bin/rm -rf "$tmp"' 0
+ if /usr/bin/tail +$lines "$0" |
+ /usr/bin/gzip -dc > "$tmp/$prog" 2> /dev/null; then
+ /bin/chmod u+x "$tmp/$prog"
+ "$tmp/$prog" ${1+"$@"}
+ ret=$?
+ else
+ /bin/echo "$prog: cannot decompress $0"
+ ret=1
+ fi
+ exit $ret
+ EOF
+}
+
+# Test if a file is compressed by checking the magic line
+compressed () {
+ test "X`sed -n 2p "$1" 2> /dev/null`" = "X$magic"
+}
+
+# Decompress a file
+decompress () {
+ tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ if ! cp "$1" "$tmp"; then
+ echo "$prog: cannot copy $1 to $tmp"
+ rm -f "$tmp"
+ return 1
+ fi
+ if ! tail +$lines "$tmp" | gzip -vdc > "$1"; then
+ echo "$prog: cannot decompress $1"
+ cp "$tmp" "$1"
+ rm -f "$tmp"
+ return 1
+ fi
+}
+
+# Perform some sanity checks on the file
+check () {
+ if test ! -e "$1"; then
+ echo "$prog: cannot compress non-existing file $1"
+ return 1
+ fi
+
+ if test ! -f "$1"; then
+ echo "$prog: cannot compress non-regular file $1"
+ return 1
+ fi
+
+ case `basename "$1"` in
+ sh | mktemp | rm | echo | tail | gzip | chmod)
+ echo "$prog: cannot compress $1, I depend on it"
+ return 1
+ esac
+
+ if test ! -x "$1"; then
+ echo "$prog: cannot compress $1, it is not executable"
+ return 1
+ fi
+
+ if test -u "$1" -o -g "$1"; then
+ echo "$prog: cannot compress $1, it has an s bit set"
+ return 1
+ fi
+}
+
+# Compress a file
+compress () {
+ tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ if ! cp "$1" "$tmp"; then
+ echo "$prog: cannot copy $1 to $tmp"
+ rm -f "$tmp"
+ return 1
+ fi
+ if ! cp "$1" "$1"~; then
+ echo "$prog: cannot create backup copy $1~"
+ rm -f "$1"~ "$tmp"
+ return 1
+ fi
+
+ # Use cp to overwrite the existing file preserving mode and owner
+ # if possible. If the file is not writable, this will produce an
+ # error.
+
+ if header "$1" > "$tmp" && gzip -vc "$1" >> "$tmp"; then
+ if ! cp "$tmp" "$1"; then
+ echo "$prog: cannot copy $tmp to $1"
+ rm -f "$tmp"
+ return 1
+ fi
+ else
+ echo "$prog: cannot compress $1"
+ rm -f "$1"~ "$tmp"
+ return 1
+ fi
+}
+
+# Is the -d flag specified?
+dflag=
+
+# Return value
+rc=0
+
+if test "X$1" = X-d; then
+ dflag=1
+ shift
+fi
+
+prog=`basename "$0"`
+USAGE="usage: $prog [-d] file ..."
+if test $# -eq 0; then
+ echo $USAGE
+ exit 1
+fi
+
+while test $# -ne 0; do
+ if test $dflag; then
+ if ! compressed "$1"; then
+ echo "$prog: $1 is not compressed"
+ rc=1;
+ elif ! decompress "$1"; then
+ rc=$?
+ fi
+ else
+ if compressed "$1"; then
+ echo "$prog: $1 is already compressed"
+ rc=1;
+ elif ! check "$1" || ! compress "$1"; then
+ rc=$?
+ fi
+ fi
+ shift
+done
+exit $rc
diff --git a/file_cmds/gzip/gzexe.1 b/file_cmds/gzip/gzexe.1
new file mode 100644
index 0000000..0c514c4
--- /dev/null
+++ b/file_cmds/gzip/gzexe.1
@@ -0,0 +1,73 @@
+.\" $NetBSD: gzexe.1,v 1.3 2003/12/28 12:49:41 wiz Exp $
+.\" $OpenBSD: gzexe.1,v 1.1 2003/07/31 07:32:47 otto Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $FreeBSD: src/usr.bin/gzip/gzexe.1,v 1.1 2007/01/26 10:19:07 delphij Exp $
+.Dd January 26, 2007
+.Dt GZEXE 1
+.Os
+.Sh NAME
+.Nm gzexe
+.Nd create auto-decompressing executables
+.Sh SYNOPSIS
+.Nm gzexe
+.Op Fl d
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility uses
+.Xr gzip 1
+to compress executables, producing executables that decompress on-the-fly
+when executed.
+This saves disk space, at the cost of slower execution times.
+The original executables are saved by copying each of them to a file with
+the same name with a
+.Sq ~
+suffix appended.
+After verifying that the compressed executables work as expected, the backup
+files can be removed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Decompress executables previously compressed by
+.Nm .
+.El
+.Pp
+The
+.Nm
+program refuses to compress non-regular or non-executable files,
+files with a setuid or setgid bit set, files that are already
+compressed using
+.Nm
+or programs it needs to perform on-the-fly decompression:
+.Xr sh 1 ,
+.Xr mktemp 1 ,
+.Xr rm 1 ,
+.Xr echo 1 ,
+.Xr tail 1 ,
+.Xr gzip 1 ,
+and
+.Xr chmod 1 .
+.Sh SEE ALSO
+.Xr gzip 1
+.Sh CAVEATS
+The
+.Nm
+utility replaces files by overwriting them with the generated
+compressed executable.
+To be able to do this, it is required that the original files are writable.
diff --git a/file_cmds/gzip/gzip.1 b/file_cmds/gzip/gzip.1
new file mode 100644
index 0000000..98e0ea2
--- /dev/null
+++ b/file_cmds/gzip/gzip.1
@@ -0,0 +1,234 @@
+.\" $NetBSD: gzip.1,v 1.26 2015/10/27 07:36:18 mrg Exp $
+.\"
+.\" Copyright (c) 1997, 2003, 2004 Matthew R. Green
+.\" 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 ``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: head/usr.bin/gzip/gzip.1 290073 2015-10-27 21:26:05Z delphij $
+.Dd October 26, 2015
+.Dt GZIP 1
+.Os
+.Sh NAME
+.Nm gzip
+.Nd compression/decompression tool using Lempel-Ziv coding (LZ77)
+.Sh SYNOPSIS
+.Nm
+.Op Fl cdfhkLlNnqrtVv
+.Op Fl S Ar suffix
+.Ar file
+.Oo
+.Ar file Oo ...
+.Oc
+.Oc
+.Nm gunzip
+.Op Fl cfhkLNqrtVv
+.Op Fl S Ar suffix
+.Ar file
+.Oo
+.Ar file Oo ...
+.Oc
+.Oc
+.Nm zcat
+.Op Fl fhV
+.Ar file
+.Oo
+.Ar file Oo ...
+.Oc
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+program compresses and decompresses files using Lempel-Ziv coding
+(LZ77).
+If no
+.Ar files
+are specified,
+.Nm
+will compress from standard input, or decompress to standard output.
+When in compression mode, each
+.Ar file
+will be replaced with another file with the suffix, set by the
+.Fl S Ar suffix
+option, added, if possible.
+.Pp
+In decompression mode, each
+.Ar file
+will be checked for existence, as will the file with the suffix
+added.
+Each
+.Ar file
+argument must contain a separate complete archive;
+when multiple
+.Ar files
+are indicated, each is decompressed in turn.
+.Pp
+In the case of
+.Nm gzcat
+the resulting data is then concatenated in the manner of
+.Xr cat 1 .
+.Pp
+If invoked as
+.Nm gunzip
+then the
+.Fl d
+option is enabled.
+If invoked as
+.Nm zcat
+or
+.Nm gzcat
+then both the
+.Fl c
+and
+.Fl d
+options are enabled.
+.Pp
+This version of
+.Nm
+is also capable of decompressing files compressed using
+.Xr compress 1 ,
+.Xr bzip2 1 ,
+or
+.Xr xz 1 .
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width XXrXXXrecursiveX
+.It Fl 1 , -fast
+.It Fl 2 , 3 , 4 , 5 , 6 , 7 , 8
+.It Fl 9 , -best
+These options change the compression level used, with the
+.Fl 1
+option being the fastest, with less compression, and the
+.Fl 9
+option being the slowest, with optimal compression.
+The default compression level is 6.
+.It Fl c , -stdout , -to-stdout
+This option specifies that output will go to the standard output
+stream, leaving files intact.
+.It Fl d , -decompress , -uncompress
+This option selects decompression rather than compression.
+.It Fl f , -force
+This option turns on force mode.
+This allows files with multiple links, symbolic links to regular files,
+overwriting of pre-existing files, reading from or writing to a terminal,
+and when combined with the
+.Fl c
+option, allowing non-compressed data to pass through unchanged.
+.It Fl h , -help
+This option prints a usage summary and exits.
+.It Fl k , -keep
+Keep (do not delete) input files during compression
+or decompression.
+.It Fl L , -license
+This option prints
+.Nm
+license.
+.It Fl l , -list
+This option displays information about the file's compressed and
+uncompressed size, ratio, uncompressed name.
+With the
+.Fl v
+option, it also displays the compression method, CRC, date and time
+embedded in the file.
+.It Fl N , -name
+This option causes the stored filename in the input file to be used
+as the output file.
+.It Fl n , -no-name
+This option stops the filename and timestamp from being stored in
+the output file.
+.It Fl q , -quiet
+With this option, no warnings or errors are printed.
+.It Fl r , -recursive
+This option is used to
+.Nm
+the files in a directory tree individually, using the
+.Xr fts 3
+library.
+.It Fl S Ar suffix , Fl -suffix Ar suffix
+This option changes the default suffix from .gz to
+.Ar suffix .
+.It Fl t , -test
+This option will test compressed files for integrity.
+.It Fl V , -version
+This option prints the version of the
+.Nm
+program.
+.It Fl v , -verbose
+This option turns on verbose mode, which prints the compression
+ratio for each file compressed.
+.El
+.Sh ENVIRONMENT
+If the environment variable
+.Ev GZIP
+is set, it is parsed as a white-space separated list of options
+handled before any options on the command line.
+Options on the command line will override anything in
+.Ev GZIP .
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success,
+1 on errors,
+and 2 if a warning occurs.
+.Sh SEE ALSO
+.Xr bzip2 1 ,
+.Xr compress 1 ,
+.Xr xz 1 ,
+.Xr fts 3 ,
+.Xr zlib 3
+.Sh HISTORY
+The
+.Nm
+program was originally written by Jean-loup Gailly, licensed under
+the GNU Public Licence.
+Matthew R. Green wrote a simple front end for
+.Nx 1.3
+distribution media, based on the freely re-distributable zlib library.
+It was enhanced to be mostly feature-compatible with the original
+GNU
+.Nm
+program for
+.Nx 2.0 .
+.Pp
+This implementation of
+.Nm
+was ported based on the
+.Nx
+.Nm ,
+and first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+.An -nosplit
+This implementation of
+.Nm
+was written by
+.An Matthew R. Green Aq Mt mrg@eterna.com.au
+with unpack support written by
+.An Xin LI Aq Mt delphij@FreeBSD.org .
+.Sh BUGS
+According to RFC 1952, the recorded file size is stored in a 32-bit
+integer, therefore, it cannot represent files larger than 4GB.
+This limitation also applies to
+.Fl l
+option of
+.Nm
+utility.
diff --git a/file_cmds/gzip/gzip.c b/file_cmds/gzip/gzip.c
new file mode 100644
index 0000000..3d27266
--- /dev/null
+++ b/file_cmds/gzip/gzip.c
@@ -0,0 +1,2256 @@
+/* $NetBSD: gzip.c,v 1.109 2015/10/27 07:36:18 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
+ * 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 ``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>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\
+ Matthew R. Green. All rights reserved.");
+__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 290073 2015-10-27 21:26:05Z delphij $");
+#endif /* not lint */
+
+/*
+ * gzip.c -- GPL free gzip using zlib.
+ *
+ * RFC 1950 covers the zlib format
+ * RFC 1951 covers the deflate format
+ * RFC 1952 covers the gzip format
+ *
+ * TODO:
+ * - use mmap where possible
+ * - make bzip2/compress -v/-t/-l support work as well as possible
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <zlib.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <time.h>
+
+#ifdef __APPLE__
+#include <sys/attr.h>
+#include <copyfile.h>
+#include <get_compat.h>
+int futimens(int fd, const struct timespec times[2]);
+#endif /* __APPLE__ */
+
+/* what type of file are we dealing with */
+enum filetype {
+ FT_GZIP,
+#ifndef NO_BZIP2_SUPPORT
+ FT_BZIP2,
+#endif
+#ifndef NO_COMPRESS_SUPPORT
+ FT_Z,
+#endif
+#ifndef NO_PACK_SUPPORT
+ FT_PACK,
+#endif
+#ifndef NO_XZ_SUPPORT
+ FT_XZ,
+#endif
+ FT_LAST,
+ FT_UNKNOWN
+};
+
+#ifndef NO_BZIP2_SUPPORT
+#include <bzlib.h>
+
+#define BZ2_SUFFIX ".bz2"
+#define BZIP2_MAGIC "\102\132\150"
+#endif
+
+#ifndef NO_COMPRESS_SUPPORT
+#define Z_SUFFIX ".Z"
+#define Z_MAGIC "\037\235"
+#endif
+
+#ifndef NO_PACK_SUPPORT
+#define PACK_MAGIC "\037\036"
+#endif
+
+#ifndef NO_XZ_SUPPORT
+#include <lzma.h>
+#define XZ_SUFFIX ".xz"
+#define XZ_MAGIC "\3757zXZ"
+#endif
+
+#define GZ_SUFFIX ".gz"
+
+#define BUFLEN (64 * 1024)
+
+#define GZIP_MAGIC0 0x1F
+#define GZIP_MAGIC1 0x8B
+#define GZIP_OMAGIC1 0x9E
+
+#define GZIP_TIMESTAMP (off_t)4
+#define GZIP_ORIGNAME (off_t)10
+
+#define HEAD_CRC 0x02
+#define EXTRA_FIELD 0x04
+#define ORIG_NAME 0x08
+#define COMMENT 0x10
+
+#define OS_CODE 3 /* Unix */
+
+typedef struct {
+ const char *zipped;
+ int ziplen;
+ const char *normal; /* for unzip - must not be longer than zipped */
+} suffixes_t;
+static suffixes_t suffixes[] = {
+#define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
+ SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
+#ifndef SMALL
+ SUFFIX(GZ_SUFFIX, ""),
+ SUFFIX(".z", ""),
+ SUFFIX("-gz", ""),
+ SUFFIX("-z", ""),
+ SUFFIX("_z", ""),
+ SUFFIX(".taz", ".tar"),
+ SUFFIX(".tgz", ".tar"),
+#ifndef NO_BZIP2_SUPPORT
+ SUFFIX(BZ2_SUFFIX, ""),
+ SUFFIX(".tbz", ".tar"),
+ SUFFIX(".tbz2", ".tar"),
+#endif
+#ifndef NO_COMPRESS_SUPPORT
+ SUFFIX(Z_SUFFIX, ""),
+#endif
+#ifndef NO_XZ_SUPPORT
+ SUFFIX(XZ_SUFFIX, ""),
+#endif
+ SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
+#endif /* SMALL */
+#undef SUFFIX
+};
+#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
+#define SUFFIX_MAXLEN 30
+
+#ifdef __APPLE__
+static const char gzip_version[] = "Apple gzip " GZIP_APPLE_VERSION;
+#else
+static const char gzip_version[] = "FreeBSD gzip 20150413";
+#endif
+
+#ifndef SMALL
+static const char gzip_copyright[] = \
+" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
+" All rights reserved.\n"
+"\n"
+" Redistribution and use in source and binary forms, with or without\n"
+" modification, are permitted provided that the following conditions\n"
+" are met:\n"
+" 1. Redistributions of source code must retain the above copyright\n"
+" notice, this list of conditions and the following disclaimer.\n"
+" 2. Redistributions in binary form must reproduce the above copyright\n"
+" notice, this list of conditions and the following disclaimer in the\n"
+" documentation and/or other materials provided with the distribution.\n"
+"\n"
+" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
+" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
+" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
+" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
+" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
+" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
+" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
+" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
+" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
+" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
+" SUCH DAMAGE.";
+#endif
+
+static int cflag; /* stdout mode */
+static int dflag; /* decompress mode */
+static int lflag; /* list mode */
+static int numflag = 6; /* gzip -1..-9 value */
+
+#ifndef SMALL
+static int fflag; /* force mode */
+static int kflag; /* don't delete input files */
+static int nflag; /* don't save name/timestamp */
+static int Nflag; /* don't restore name/timestamp */
+static int qflag; /* quiet mode */
+static int rflag; /* recursive mode */
+static int tflag; /* test */
+static int vflag; /* verbose mode */
+static const char *remove_file = NULL; /* file to be removed upon SIGINT */
+#else
+#define qflag 0
+#define tflag 0
+#endif
+
+static int exit_value = 0; /* exit value */
+
+static char *infile; /* name of file coming in */
+
+#ifdef __APPLE__
+static bool zcat;
+#endif
+
+static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
+#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
+ !defined(NO_XZ_SUPPORT)
+static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
+#endif
+static void maybe_warn(const char *fmt, ...) __printflike(1, 2);
+static void maybe_warnx(const char *fmt, ...) __printflike(1, 2);
+static enum filetype file_gettype(u_char *);
+#ifdef SMALL
+#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
+#endif
+static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
+static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
+static off_t file_compress(char *, char *, size_t);
+static off_t file_uncompress(char *, char *, size_t);
+static void handle_pathname(char *);
+static void handle_file(char *, struct stat *);
+static void handle_stdin(void);
+static void handle_stdout(void);
+static void print_ratio(off_t, off_t, FILE *);
+static void print_list(int fd, off_t, const char *, time_t);
+static void usage(void) __dead2;
+static void display_version(void) __dead2;
+#ifndef SMALL
+static void display_license(void);
+static void sigint_handler(int);
+#endif
+static const suffixes_t *check_suffix(char *, int);
+static ssize_t read_retry(int, void *, size_t);
+
+#ifdef SMALL
+#define unlink_input(f, sb) unlink(f)
+#else
+static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
+static void prepend_gzip(char *, int *, char ***);
+static void handle_dir(char *);
+static void print_verbage(const char *, const char *, off_t, off_t);
+static void print_test(const char *, int);
+static void copymodes(int fd, const struct stat *, const char *file);
+static int check_outfile(const char *outfile);
+#endif
+
+#ifndef NO_BZIP2_SUPPORT
+static off_t unbzip2(int, int, char *, size_t, off_t *);
+#endif
+
+#ifndef NO_COMPRESS_SUPPORT
+static FILE *zdopen(int);
+static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
+#endif
+
+#ifndef NO_PACK_SUPPORT
+static off_t unpack(int, int, char *, size_t, off_t *);
+#endif
+
+#ifndef NO_XZ_SUPPORT
+static off_t unxz(int, int, char *, size_t, off_t *);
+#endif
+
+#ifdef SMALL
+#define getopt_long(a,b,c,d,e) getopt(a,b,c)
+#else
+static const struct option longopts[] = {
+ { "stdout", no_argument, 0, 'c' },
+ { "to-stdout", no_argument, 0, 'c' },
+ { "decompress", no_argument, 0, 'd' },
+ { "uncompress", no_argument, 0, 'd' },
+ { "force", no_argument, 0, 'f' },
+ { "help", no_argument, 0, 'h' },
+ { "keep", no_argument, 0, 'k' },
+ { "list", no_argument, 0, 'l' },
+ { "no-name", no_argument, 0, 'n' },
+ { "name", no_argument, 0, 'N' },
+ { "quiet", no_argument, 0, 'q' },
+ { "recursive", no_argument, 0, 'r' },
+ { "suffix", required_argument, 0, 'S' },
+ { "test", no_argument, 0, 't' },
+ { "verbose", no_argument, 0, 'v' },
+ { "version", no_argument, 0, 'V' },
+ { "fast", no_argument, 0, '1' },
+ { "best", no_argument, 0, '9' },
+ { "ascii", no_argument, 0, 'a' },
+ { "license", no_argument, 0, 'L' },
+ { NULL, no_argument, 0, 0 },
+};
+#endif
+
+int
+main(int argc, char **argv)
+{
+ const char *progname = getprogname();
+#ifndef SMALL
+ char *gzip;
+ int len;
+#endif
+ int ch;
+
+#ifndef SMALL
+ if ((gzip = getenv("GZIP")) != NULL)
+ prepend_gzip(gzip, &argc, &argv);
+ signal(SIGINT, sigint_handler);
+#endif
+
+ /*
+ * XXX
+ * handle being called `gunzip', `zcat' and `gzcat'
+ */
+ if (strcmp(progname, "gunzip") == 0)
+ dflag = 1;
+ else if (strcmp(progname, "zcat") == 0 ||
+ strcmp(progname, "gzcat") == 0)
+ dflag = cflag = 1;
+
+#ifdef __APPLE__
+ if (strcmp(progname, "zcat") == 0) {
+ zcat = true;
+ }
+#endif
+
+#ifdef SMALL
+#define OPT_LIST "123456789cdhlV"
+#else
+#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
+#endif
+
+ while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
+ switch (ch) {
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ numflag = ch - '0';
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ dflag = 1;
+ break;
+ case 'V':
+ display_version();
+ /* NOTREACHED */
+#ifndef SMALL
+ case 'a':
+ fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'L':
+ display_license();
+ /* NOT REACHED */
+ case 'N':
+ nflag = 0;
+ Nflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ Nflag = 0;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'S':
+ len = strlen(optarg);
+ if (len != 0) {
+ if (len > SUFFIX_MAXLEN)
+ errx(1, "incorrect suffix: '%s': too long", optarg);
+ suffixes[0].zipped = optarg;
+ suffixes[0].ziplen = len;
+ } else {
+ suffixes[NUM_SUFFIXES - 1].zipped = "";
+ suffixes[NUM_SUFFIXES - 1].ziplen = 0;
+ }
+ break;
+ case 't':
+ cflag = 1;
+ tflag = 1;
+ dflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+#endif
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0) {
+ if (dflag) /* stdin mode */
+ handle_stdin();
+ else /* stdout mode */
+ handle_stdout();
+ } else {
+ do {
+ handle_pathname(argv[0]);
+ } while (*++argv);
+ }
+#ifndef SMALL
+ if (qflag == 0 && lflag && argc > 1)
+ print_list(-1, 0, "(totals)", 0);
+#endif
+ exit(exit_value);
+}
+
+/* maybe print a warning */
+void
+maybe_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (qflag == 0) {
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+ }
+ if (exit_value == 0)
+ exit_value = 1;
+}
+
+/* ... without an errno. */
+void
+maybe_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (qflag == 0) {
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ }
+ if (exit_value == 0)
+ exit_value = 1;
+}
+
+/* maybe print an error */
+void
+maybe_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (qflag == 0) {
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+ }
+ exit(2);
+}
+
+#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
+ !defined(NO_XZ_SUPPORT)
+/* ... without an errno. */
+void
+maybe_errx(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (qflag == 0) {
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ }
+ exit(2);
+}
+#endif
+
+#ifndef SMALL
+/* split up $GZIP and prepend it to the argument list */
+static void
+prepend_gzip(char *gzip, int *argc, char ***argv)
+{
+ char *s, **nargv, **ac;
+ int nenvarg = 0, i;
+
+ /* scan how many arguments there are */
+ for (s = gzip;;) {
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == 0)
+ goto count_done;
+ nenvarg++;
+ while (*s != ' ' && *s != '\t')
+ if (*s++ == 0)
+ goto count_done;
+ }
+count_done:
+ /* punt early */
+ if (nenvarg == 0)
+ return;
+
+ *argc += nenvarg;
+ ac = *argv;
+
+ nargv = (char **)malloc((*argc + 1) * sizeof(char *));
+ if (nargv == NULL)
+ maybe_err("malloc");
+
+ /* stash this away */
+ *argv = nargv;
+
+ /* copy the program name first */
+ i = 0;
+ nargv[i++] = *(ac++);
+
+ s = gzip;
+ for (;;) {
+ /* Skip whitespaces. */
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == 0) {
+ goto copy_done;
+ }
+ nargv[i++] = s;
+ /* Find the end of this argument. */
+ while (*s != ' ' && *s != '\t')
+ if (*s++ == 0)
+ /* Argument followed by NUL. */
+ goto copy_done;
+ /* copy any unterminated args */
+ nargv[i-1] = strndup(nargv[i-1], s-nargv[i-1]);
+ if (nargv[i-1] == NULL)
+ maybe_err("strndup");
+ s++;
+ }
+copy_done:
+
+ /* copy the original arguments and a NULL */
+ while (*ac)
+ nargv[i++] = *(ac++);
+ nargv[i] = NULL;
+}
+#endif
+
+/* compress input to output. Return bytes read, -1 on error */
+static off_t
+gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
+{
+ z_stream z;
+ char *outbufp, *inbufp;
+ off_t in_tot = 0, out_tot = 0;
+ ssize_t in_size;
+ int i, error;
+ uLong crc;
+#ifdef SMALL
+ static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
+ 0, 0, 0, 0,
+ 0, OS_CODE };
+#endif
+
+ outbufp = malloc(BUFLEN);
+ inbufp = malloc(BUFLEN);
+ if (outbufp == NULL || inbufp == NULL) {
+ maybe_err("malloc failed");
+ goto out;
+ }
+
+ memset(&z, 0, sizeof z);
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = 0;
+
+#ifdef SMALL
+ memcpy(outbufp, header, sizeof header);
+ i = sizeof header;
+#else
+ if (nflag != 0) {
+ mtime = 0;
+ origname = "";
+ }
+
+ i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
+ GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
+ *origname ? ORIG_NAME : 0,
+ mtime & 0xff,
+ (mtime >> 8) & 0xff,
+ (mtime >> 16) & 0xff,
+ (mtime >> 24) & 0xff,
+ numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
+ OS_CODE, origname);
+ if (i >= BUFLEN)
+ /* this need PATH_MAX > BUFLEN ... */
+ maybe_err("snprintf");
+ if (*origname)
+ i++;
+#endif
+
+ z.next_out = (unsigned char *)outbufp + i;
+ z.avail_out = BUFLEN - i;
+
+ error = deflateInit2(&z, numflag, Z_DEFLATED,
+ (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
+ if (error != Z_OK) {
+ maybe_warnx("deflateInit2 failed");
+ in_tot = -1;
+ goto out;
+ }
+
+ crc = crc32(0L, Z_NULL, 0);
+ for (;;) {
+ if (z.avail_out == 0) {
+ if (write(out, outbufp, BUFLEN) != BUFLEN) {
+ maybe_warn("write");
+ out_tot = -1;
+ goto out;
+ }
+
+ out_tot += BUFLEN;
+ z.next_out = (unsigned char *)outbufp;
+ z.avail_out = BUFLEN;
+ }
+
+ if (z.avail_in == 0) {
+ in_size = read(in, inbufp, BUFLEN);
+ if (in_size < 0) {
+ maybe_warn("read");
+ in_tot = -1;
+ goto out;
+ }
+ if (in_size == 0)
+ break;
+
+ crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
+ in_tot += in_size;
+ z.next_in = (unsigned char *)inbufp;
+ z.avail_in = in_size;
+ }
+
+ error = deflate(&z, Z_NO_FLUSH);
+ if (error != Z_OK && error != Z_STREAM_END) {
+ maybe_warnx("deflate failed");
+ in_tot = -1;
+ goto out;
+ }
+ }
+
+ /* clean up */
+ for (;;) {
+ size_t len;
+ ssize_t w;
+
+ error = deflate(&z, Z_FINISH);
+ if (error != Z_OK && error != Z_STREAM_END) {
+ maybe_warnx("deflate failed");
+ in_tot = -1;
+ goto out;
+ }
+
+ len = (char *)z.next_out - outbufp;
+
+ w = write(out, outbufp, len);
+ if (w == -1 || (size_t)w != len) {
+ maybe_warn("write");
+ out_tot = -1;
+ goto out;
+ }
+ out_tot += len;
+ z.next_out = (unsigned char *)outbufp;
+ z.avail_out = BUFLEN;
+
+ if (error == Z_STREAM_END)
+ break;
+ }
+
+ if (deflateEnd(&z) != Z_OK) {
+ maybe_warnx("deflateEnd failed");
+ in_tot = -1;
+ goto out;
+ }
+
+ i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
+ (int)crc & 0xff,
+ (int)(crc >> 8) & 0xff,
+ (int)(crc >> 16) & 0xff,
+ (int)(crc >> 24) & 0xff,
+ (int)in_tot & 0xff,
+ (int)(in_tot >> 8) & 0xff,
+ (int)(in_tot >> 16) & 0xff,
+ (int)(in_tot >> 24) & 0xff);
+ if (i != 8)
+ maybe_err("snprintf");
+ if (write(out, outbufp, i) != i) {
+ maybe_warn("write");
+ in_tot = -1;
+ } else
+ out_tot += i;
+
+out:
+ if (inbufp != NULL)
+ free(inbufp);
+ if (outbufp != NULL)
+ free(outbufp);
+ if (gsizep)
+ *gsizep = out_tot;
+ return in_tot;
+}
+
+/*
+ * uncompress input to output then close the input. return the
+ * uncompressed size written, and put the compressed sized read
+ * into `*gsizep'.
+ */
+static off_t
+gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
+ const char *filename)
+{
+ z_stream z;
+ char *outbufp, *inbufp;
+ off_t out_tot = -1, in_tot = 0;
+ uint32_t out_sub_tot = 0;
+ enum {
+ GZSTATE_MAGIC0,
+ GZSTATE_MAGIC1,
+ GZSTATE_METHOD,
+ GZSTATE_FLAGS,
+ GZSTATE_SKIPPING,
+ GZSTATE_EXTRA,
+ GZSTATE_EXTRA2,
+ GZSTATE_EXTRA3,
+ GZSTATE_ORIGNAME,
+ GZSTATE_COMMENT,
+ GZSTATE_HEAD_CRC1,
+ GZSTATE_HEAD_CRC2,
+ GZSTATE_INIT,
+ GZSTATE_READ,
+ GZSTATE_CRC,
+ GZSTATE_LEN,
+ } state = GZSTATE_MAGIC0;
+ int flags = 0, skip_count = 0;
+ int error = Z_STREAM_ERROR, done_reading = 0;
+ uLong crc = 0;
+ ssize_t wr;
+ int needmore = 0;
+
+#define ADVANCE() { z.next_in++; z.avail_in--; }
+
+ if ((outbufp = malloc(BUFLEN)) == NULL) {
+ maybe_err("malloc failed");
+ goto out2;
+ }
+ if ((inbufp = malloc(BUFLEN)) == NULL) {
+ maybe_err("malloc failed");
+ goto out1;
+ }
+
+ memset(&z, 0, sizeof z);
+ z.avail_in = prelen;
+ z.next_in = (unsigned char *)pre;
+ z.avail_out = BUFLEN;
+ z.next_out = (unsigned char *)outbufp;
+ z.zalloc = NULL;
+ z.zfree = NULL;
+ z.opaque = 0;
+
+ in_tot = prelen;
+ out_tot = 0;
+
+ for (;;) {
+ if ((z.avail_in == 0 || needmore) && done_reading == 0) {
+ ssize_t in_size;
+
+ if (z.avail_in > 0) {
+ memmove(inbufp, z.next_in, z.avail_in);
+ }
+ z.next_in = (unsigned char *)inbufp;
+ in_size = read(in, z.next_in + z.avail_in,
+ BUFLEN - z.avail_in);
+
+ if (in_size == -1) {
+ maybe_warn("failed to read stdin");
+ goto stop_and_fail;
+ } else if (in_size == 0) {
+ done_reading = 1;
+ }
+
+ z.avail_in += in_size;
+ needmore = 0;
+
+ in_tot += in_size;
+ }
+ if (z.avail_in == 0) {
+ if (done_reading && state != GZSTATE_MAGIC0) {
+ maybe_warnx("%s: unexpected end of file",
+ filename);
+ goto stop_and_fail;
+ }
+ goto stop;
+ }
+ switch (state) {
+ case GZSTATE_MAGIC0:
+ if (*z.next_in != GZIP_MAGIC0) {
+ if (in_tot > 0) {
+ maybe_warnx("%s: trailing garbage "
+ "ignored", filename);
+ exit_value = 2;
+ goto stop;
+ }
+ maybe_warnx("input not gziped (MAGIC0)");
+ goto stop_and_fail;
+ }
+ ADVANCE();
+ state++;
+ out_sub_tot = 0;
+ crc = crc32(0L, Z_NULL, 0);
+ break;
+
+ case GZSTATE_MAGIC1:
+ if (*z.next_in != GZIP_MAGIC1 &&
+ *z.next_in != GZIP_OMAGIC1) {
+ maybe_warnx("input not gziped (MAGIC1)");
+ goto stop_and_fail;
+ }
+ ADVANCE();
+ state++;
+ break;
+
+ case GZSTATE_METHOD:
+ if (*z.next_in != Z_DEFLATED) {
+ maybe_warnx("unknown compression method");
+ goto stop_and_fail;
+ }
+ ADVANCE();
+ state++;
+ break;
+
+ case GZSTATE_FLAGS:
+ flags = *z.next_in;
+ ADVANCE();
+ skip_count = 6;
+ state++;
+ break;
+
+ case GZSTATE_SKIPPING:
+ if (skip_count > 0) {
+ skip_count--;
+ ADVANCE();
+ } else
+ state++;
+ break;
+
+ case GZSTATE_EXTRA:
+ if ((flags & EXTRA_FIELD) == 0) {
+ state = GZSTATE_ORIGNAME;
+ break;
+ }
+ skip_count = *z.next_in;
+ ADVANCE();
+ state++;
+ break;
+
+ case GZSTATE_EXTRA2:
+ skip_count |= ((*z.next_in) << 8);
+ ADVANCE();
+ state++;
+ break;
+
+ case GZSTATE_EXTRA3:
+ if (skip_count > 0) {
+ skip_count--;
+ ADVANCE();
+ } else
+ state++;
+ break;
+
+ case GZSTATE_ORIGNAME:
+ if ((flags & ORIG_NAME) == 0) {
+ state++;
+ break;
+ }
+ if (*z.next_in == 0)
+ state++;
+ ADVANCE();
+ break;
+
+ case GZSTATE_COMMENT:
+ if ((flags & COMMENT) == 0) {
+ state++;
+ break;
+ }
+ if (*z.next_in == 0)
+ state++;
+ ADVANCE();
+ break;
+
+ case GZSTATE_HEAD_CRC1:
+ if (flags & HEAD_CRC)
+ skip_count = 2;
+ else
+ skip_count = 0;
+ state++;
+ break;
+
+ case GZSTATE_HEAD_CRC2:
+ if (skip_count > 0) {
+ skip_count--;
+ ADVANCE();
+ } else
+ state++;
+ break;
+
+ case GZSTATE_INIT:
+ if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
+ maybe_warnx("failed to inflateInit");
+ goto stop_and_fail;
+ }
+ state++;
+ break;
+
+ case GZSTATE_READ:
+ error = inflate(&z, Z_FINISH);
+ switch (error) {
+ /* Z_BUF_ERROR goes with Z_FINISH... */
+ case Z_BUF_ERROR:
+ if (z.avail_out > 0 && !done_reading)
+ continue;
+
+ case Z_STREAM_END:
+ case Z_OK:
+ break;
+
+ case Z_NEED_DICT:
+ maybe_warnx("Z_NEED_DICT error");
+ goto stop_and_fail;
+ case Z_DATA_ERROR:
+ maybe_warnx("data stream error");
+ goto stop_and_fail;
+ case Z_STREAM_ERROR:
+ maybe_warnx("internal stream error");
+ goto stop_and_fail;
+ case Z_MEM_ERROR:
+ maybe_warnx("memory allocation error");
+ goto stop_and_fail;
+
+ default:
+ maybe_warn("unknown error from inflate(): %d",
+ error);
+ }
+ wr = BUFLEN - z.avail_out;
+
+ if (wr != 0) {
+ crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
+ if (
+#ifndef SMALL
+ /* don't write anything with -t */
+ tflag == 0 &&
+#endif
+ write(out, outbufp, wr) != wr) {
+ maybe_warn("error writing to output");
+ goto stop_and_fail;
+ }
+
+ out_tot += wr;
+ out_sub_tot += wr;
+ }
+
+ if (error == Z_STREAM_END) {
+ inflateEnd(&z);
+ state++;
+ }
+
+ z.next_out = (unsigned char *)outbufp;
+ z.avail_out = BUFLEN;
+
+ break;
+ case GZSTATE_CRC:
+ {
+ uLong origcrc;
+
+ if (z.avail_in < 4) {
+ if (!done_reading) {
+ needmore = 1;
+ continue;
+ }
+ maybe_warnx("truncated input");
+ goto stop_and_fail;
+ }
+ origcrc = ((unsigned)z.next_in[0] & 0xff) |
+ ((unsigned)z.next_in[1] & 0xff) << 8 |
+ ((unsigned)z.next_in[2] & 0xff) << 16 |
+ ((unsigned)z.next_in[3] & 0xff) << 24;
+ if (origcrc != crc) {
+ maybe_warnx("invalid compressed"
+ " data--crc error");
+ goto stop_and_fail;
+ }
+ }
+
+ z.avail_in -= 4;
+ z.next_in += 4;
+
+ if (!z.avail_in && done_reading) {
+ goto stop;
+ }
+ state++;
+ break;
+ case GZSTATE_LEN:
+ {
+ uLong origlen;
+
+ if (z.avail_in < 4) {
+ if (!done_reading) {
+ needmore = 1;
+ continue;
+ }
+ maybe_warnx("truncated input");
+ goto stop_and_fail;
+ }
+ origlen = ((unsigned)z.next_in[0] & 0xff) |
+ ((unsigned)z.next_in[1] & 0xff) << 8 |
+ ((unsigned)z.next_in[2] & 0xff) << 16 |
+ ((unsigned)z.next_in[3] & 0xff) << 24;
+
+ if (origlen != out_sub_tot) {
+ maybe_warnx("invalid compressed"
+ " data--length error");
+ goto stop_and_fail;
+ }
+ }
+
+ z.avail_in -= 4;
+ z.next_in += 4;
+
+ if (error < 0) {
+ maybe_warnx("decompression error");
+ goto stop_and_fail;
+ }
+ state = GZSTATE_MAGIC0;
+ break;
+ }
+ continue;
+stop_and_fail:
+ out_tot = -1;
+stop:
+ break;
+ }
+ if (state > GZSTATE_INIT)
+ inflateEnd(&z);
+
+ free(inbufp);
+out1:
+ free(outbufp);
+out2:
+ if (gsizep)
+ *gsizep = in_tot;
+ return (out_tot);
+}
+
+#ifndef SMALL
+/*
+ * set the owner, mode, flags & utimes using the given file descriptor.
+ * file is only used in possible warning messages.
+ */
+static void
+copymodes(int fd, const struct stat *sbp, const char *file)
+{
+ struct timespec times[2];
+ struct stat sb;
+
+ /*
+ * If we have no info on the input, give this file some
+ * default values and return..
+ */
+ if (sbp == NULL) {
+ mode_t mask = umask(022);
+
+ (void)fchmod(fd, DEFFILEMODE & ~mask);
+ (void)umask(mask);
+ return;
+ }
+ sb = *sbp;
+
+ /* if the chown fails, remove set-id bits as-per compress(1) */
+ if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
+ if (errno != EPERM)
+ maybe_warn("couldn't fchown: %s", file);
+ sb.st_mode &= ~(S_ISUID|S_ISGID);
+ }
+
+ /* we only allow set-id and the 9 normal permission bits */
+ sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
+ if (fchmod(fd, sb.st_mode) < 0)
+ maybe_warn("couldn't fchmod: %s", file);
+
+#ifdef __APPLE__
+ times[0] = sb.st_atimespec;
+ times[1] = sb.st_mtimespec;
+#else
+ times[0] = sb.st_atim;
+ times[1] = sb.st_mtim;
+#endif
+ if (futimens(fd, times) < 0)
+ maybe_warn("couldn't futimens: %s", file);
+
+ /* only try flags if they exist already */
+ if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
+ maybe_warn("couldn't fchflags: %s", file);
+}
+#endif
+
+/* what sort of file is this? */
+static enum filetype
+file_gettype(u_char *buf)
+{
+
+ if (buf[0] == GZIP_MAGIC0 &&
+ (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
+ return FT_GZIP;
+ else
+#ifndef NO_BZIP2_SUPPORT
+ if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
+ buf[3] >= '0' && buf[3] <= '9')
+ return FT_BZIP2;
+ else
+#endif
+#ifndef NO_COMPRESS_SUPPORT
+ if (memcmp(buf, Z_MAGIC, 2) == 0)
+ return FT_Z;
+ else
+#endif
+#ifndef NO_PACK_SUPPORT
+ if (memcmp(buf, PACK_MAGIC, 2) == 0)
+ return FT_PACK;
+ else
+#endif
+#ifndef NO_XZ_SUPPORT
+ if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */
+ return FT_XZ;
+ else
+#endif
+ return FT_UNKNOWN;
+}
+
+#ifndef SMALL
+/* check the outfile is OK. */
+static int
+check_outfile(const char *outfile)
+{
+ struct stat sb;
+ int ok = 1;
+
+ if (lflag == 0 && stat(outfile, &sb) == 0) {
+ if (fflag)
+ unlink(outfile);
+ else if (isatty(STDIN_FILENO)) {
+ char ans[10] = { 'n', '\0' }; /* default */
+
+ fprintf(stderr, "%s already exists -- do you wish to "
+ "overwrite (y or n)? " , outfile);
+ (void)fgets(ans, sizeof(ans) - 1, stdin);
+ if (ans[0] != 'y' && ans[0] != 'Y') {
+ fprintf(stderr, "\tnot overwriting\n");
+ ok = 0;
+ } else
+ unlink(outfile);
+ } else {
+ maybe_warnx("%s already exists -- skipping", outfile);
+ ok = 0;
+ }
+ }
+ return ok;
+}
+
+static void
+unlink_input(const char *file, const struct stat *sb)
+{
+ struct stat nsb;
+
+ if (kflag)
+ return;
+ bzero(&nsb, sizeof(nsb));
+ if (stat(file, &nsb) != 0)
+ /* Must be gone already */
+ return;
+ if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
+ /* Definitely a different file */
+ return;
+ unlink(file);
+}
+
+static void
+sigint_handler(int signo __unused)
+{
+
+ if (remove_file != NULL)
+ unlink(remove_file);
+ _exit(2);
+}
+#endif
+
+static const suffixes_t *
+check_suffix(char *file, int xlate)
+{
+ const suffixes_t *s;
+ int len = strlen(file);
+ char *sp;
+
+ for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
+ /* if it doesn't fit in "a.suf", don't bother */
+ if (s->ziplen >= len)
+ continue;
+ sp = file + len - s->ziplen;
+ if (strcmp(s->zipped, sp) != 0)
+ continue;
+ if (xlate)
+ strcpy(sp, s->normal);
+ return s;
+ }
+ return NULL;
+}
+
+#ifdef __APPLE__
+static void
+clear_type_and_creator(int fd)
+{
+ struct attrlist alist;
+ struct {
+ u_int32_t length;
+ char info[32];
+ } abuf;
+
+ memset(&alist, 0, sizeof(alist));
+ alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+ alist.commonattr = ATTR_CMN_FNDRINFO;
+
+ if (!fgetattrlist(fd, &alist, &abuf, sizeof(abuf), 0) && abuf.length == sizeof(abuf)) {
+ memset(abuf.info, 0, 8);
+ fsetattrlist(fd, &alist, abuf.info, sizeof(abuf.info), 0);
+ }
+}
+#endif /* __APPLE__ */
+
+/*
+ * compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+static off_t
+file_compress(char *file, char *outfile, size_t outsize)
+{
+ int in;
+ int out;
+ off_t size, insize;
+#ifndef SMALL
+ struct stat isb, osb;
+ const suffixes_t *suff;
+#endif
+
+ in = open(file, O_RDONLY);
+ if (in == -1) {
+ maybe_warn("can't open %s", file);
+ return (-1);
+ }
+
+#ifndef SMALL
+ bzero(&isb, sizeof(isb));
+ if (fstat(in, &isb) != 0) {
+ maybe_warn("couldn't stat: %s", file);
+ close(in);
+ return (-1);
+ }
+#endif
+
+ if (cflag == 0) {
+#ifndef SMALL
+ if (isb.st_nlink > 1 && fflag == 0) {
+ maybe_warnx("%s has %d other link%s -- skipping",
+ file, isb.st_nlink - 1,
+ (isb.st_nlink - 1) == 1 ? "" : "s");
+ close(in);
+ return (-1);
+ }
+
+ if (fflag == 0 && (suff = check_suffix(file, 0)) &&
+ suff->zipped[0] != 0) {
+ maybe_warnx("%s already has %s suffix -- unchanged",
+ file, suff->zipped);
+ close(in);
+ return (-1);
+ }
+#endif
+
+ /* Add (usually) .gz to filename */
+ if ((size_t)snprintf(outfile, outsize, "%s%s",
+ file, suffixes[0].zipped) >= outsize)
+ memcpy(outfile + outsize - suffixes[0].ziplen - 1,
+ suffixes[0].zipped, suffixes[0].ziplen + 1);
+
+#ifndef SMALL
+ if (check_outfile(outfile) == 0) {
+ close(in);
+ return (-1);
+ }
+#endif
+ }
+
+ if (cflag == 0) {
+ out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (out == -1) {
+ maybe_warn("could not create output: %s", outfile);
+ fclose(stdin);
+ return (-1);
+ }
+#ifndef SMALL
+ remove_file = outfile;
+#endif
+ } else
+ out = STDOUT_FILENO;
+
+ insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
+
+#ifndef __APPLE__
+ (void)close(in);
+#endif /* !__APPLE__ */
+
+ /*
+ * If there was an error, insize will be -1.
+ * If we compressed to stdout, just return the size.
+ * Otherwise stat the file and check it is the correct size.
+ * We only blow away the file if we can stat the output and it
+ * has the expected size.
+ */
+ if (cflag != 0)
+ return (insize == -1 ? -1 : size);
+
+#ifndef SMALL
+ if (fstat(out, &osb) != 0) {
+ maybe_warn("couldn't stat: %s", outfile);
+ goto bad_outfile;
+ }
+
+ if (osb.st_size != size) {
+ maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
+ outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
+ goto bad_outfile;
+ }
+
+#ifdef __APPLE__
+ fcopyfile(in, out, 0, COPYFILE_ACL | COPYFILE_XATTR);
+ clear_type_and_creator(out);
+#endif /* __APPLE__ */
+ copymodes(out, &isb, outfile);
+ remove_file = NULL;
+#endif
+#ifdef __APPLE__
+ (void)close(in);
+#endif /* __APPLE__ */
+ if (close(out) == -1)
+ maybe_warn("couldn't close output");
+
+ /* output is good, ok to delete input */
+ unlink_input(file, &isb);
+ return (size);
+
+#ifndef SMALL
+ bad_outfile:
+ if (close(out) == -1)
+ maybe_warn("couldn't close output");
+
+ maybe_warnx("leaving original %s", file);
+ unlink(outfile);
+ return (size);
+#endif
+}
+
+/* uncompress the given file and remove the original */
+static off_t
+file_uncompress(char *file, char *outfile, size_t outsize)
+{
+ struct stat isb, osb;
+ off_t size;
+ ssize_t rbytes;
+ unsigned char header1[4];
+ enum filetype method;
+ int fd, ofd, zfd = -1;
+#ifndef SMALL
+ ssize_t rv;
+ time_t timestamp = 0;
+ char name[PATH_MAX + 1];
+#endif
+
+ /* gather the old name info */
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ maybe_warn("can't open %s", file);
+ goto lose;
+ }
+
+ strlcpy(outfile, file, outsize);
+ if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
+ maybe_warnx("%s: unknown suffix -- ignored", file);
+ goto lose;
+ }
+
+ rbytes = read(fd, header1, sizeof header1);
+ if (rbytes != sizeof header1) {
+ /* we don't want to fail here. */
+#ifndef SMALL
+ if (fflag)
+ goto lose;
+#endif
+ if (rbytes == -1)
+ maybe_warn("can't read %s", file);
+ else
+ goto unexpected_EOF;
+ goto lose;
+ }
+
+ method = file_gettype(header1);
+#ifndef SMALL
+ if (fflag == 0 && method == FT_UNKNOWN) {
+ maybe_warnx("%s: not in gzip format", file);
+ goto lose;
+ }
+
+#endif
+
+#ifndef SMALL
+ if (method == FT_GZIP && Nflag) {
+ unsigned char ts[4]; /* timestamp */
+
+ rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
+ if (rv >= 0 && rv < (ssize_t)(sizeof ts))
+ goto unexpected_EOF;
+ if (rv == -1) {
+ if (!fflag)
+ maybe_warn("can't read %s", file);
+ goto lose;
+ }
+ timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
+
+ if (header1[3] & ORIG_NAME) {
+ rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
+ if (rbytes < 0) {
+ maybe_warn("can't read %s", file);
+ goto lose;
+ }
+ if (name[0] != '\0') {
+ char *dp, *nf;
+
+ /* Make sure that name is NUL-terminated */
+ name[rbytes] = '\0';
+
+ /* strip saved directory name */
+ nf = strrchr(name, '/');
+ if (nf == NULL)
+ nf = name;
+ else
+ nf++;
+
+ /* preserve original directory name */
+ dp = strrchr(file, '/');
+ if (dp == NULL)
+ dp = file;
+ else
+ dp++;
+ snprintf(outfile, outsize, "%.*s%.*s",
+ (int) (dp - file),
+ file, (int) rbytes, nf);
+ }
+ }
+ }
+#endif
+ lseek(fd, 0, SEEK_SET);
+ bzero(&isb, sizeof(isb));
+ if (cflag == 0 || lflag) {
+ if (fstat(fd, &isb) != 0)
+ goto lose;
+#ifndef SMALL
+ if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
+ maybe_warnx("%s has %d other links -- skipping",
+ file, isb.st_nlink - 1);
+ goto lose;
+ }
+ if (nflag == 0 && timestamp)
+ isb.st_mtime = timestamp;
+ if (check_outfile(outfile) == 0)
+ goto lose;
+#endif
+ }
+
+ if (cflag == 0 && lflag == 0) {
+ zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ if (zfd == STDOUT_FILENO) {
+ /* We won't close STDOUT_FILENO later... */
+ zfd = dup(zfd);
+ close(STDOUT_FILENO);
+ }
+ if (zfd == -1) {
+ maybe_warn("can't open %s", outfile);
+ goto lose;
+ }
+#ifndef SMALL
+ remove_file = outfile;
+#endif
+ } else
+ zfd = STDOUT_FILENO;
+
+ switch (method) {
+#ifndef NO_BZIP2_SUPPORT
+ case FT_BZIP2:
+ /* XXX */
+ if (lflag) {
+ maybe_warnx("no -l with bzip2 files");
+ goto lose;
+ }
+
+ size = unbzip2(fd, zfd, NULL, 0, NULL);
+ break;
+#endif
+
+#ifndef NO_COMPRESS_SUPPORT
+ case FT_Z: {
+ FILE *in, *out;
+
+ /* XXX */
+ if (lflag) {
+ maybe_warnx("no -l with Lempel-Ziv files");
+ goto lose;
+ }
+
+ if ((in = zdopen(fd)) == NULL) {
+ maybe_warn("zdopen for read: %s", file);
+ goto lose;
+ }
+
+ out = fdopen(dup(zfd), "w");
+ if (out == NULL) {
+ maybe_warn("fdopen for write: %s", outfile);
+ fclose(in);
+ goto lose;
+ }
+
+ size = zuncompress(in, out, NULL, 0, NULL);
+ /* need to fclose() if ferror() is true... */
+ if (ferror(in) | fclose(in)) {
+ maybe_warn("failed infile fclose");
+ unlink(outfile);
+ (void)fclose(out);
+ }
+ if (fclose(out) != 0) {
+ maybe_warn("failed outfile fclose");
+ unlink(outfile);
+ goto lose;
+ }
+ break;
+ }
+#endif
+
+#ifndef NO_PACK_SUPPORT
+ case FT_PACK:
+ if (lflag) {
+ maybe_warnx("no -l with packed files");
+ goto lose;
+ }
+
+ size = unpack(fd, zfd, NULL, 0, NULL);
+ break;
+#endif
+
+#ifndef NO_XZ_SUPPORT
+ case FT_XZ:
+ if (lflag) {
+ maybe_warnx("no -l with xz files");
+ goto lose;
+ }
+
+ size = unxz(fd, zfd, NULL, 0, NULL);
+ break;
+#endif
+
+#ifndef SMALL
+ case FT_UNKNOWN:
+ if (lflag) {
+ maybe_warnx("no -l for unknown filetypes");
+ goto lose;
+ }
+ size = cat_fd(NULL, 0, NULL, fd);
+ break;
+#endif
+ default:
+ if (lflag) {
+ print_list(fd, isb.st_size, outfile, isb.st_mtime);
+ close(fd);
+ return -1; /* XXX */
+ }
+
+ size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
+ break;
+ }
+
+ if (close(fd) != 0)
+ maybe_warn("couldn't close input");
+ if (zfd != STDOUT_FILENO && close(zfd) != 0)
+ maybe_warn("couldn't close output");
+
+ if (size == -1) {
+ if (cflag == 0)
+ unlink(outfile);
+ maybe_warnx("%s: uncompress failed", file);
+ return -1;
+ }
+
+ /* if testing, or we uncompressed to stdout, this is all we need */
+#ifndef SMALL
+ if (tflag)
+ return size;
+#endif
+ /* if we are uncompressing to stdin, don't remove the file. */
+ if (cflag)
+ return size;
+
+ /*
+ * if we create a file...
+ */
+ /*
+ * if we can't stat the file don't remove the file.
+ */
+
+ ofd = open(outfile, O_RDWR, 0);
+ if (ofd == -1) {
+ maybe_warn("couldn't open (leaving original): %s",
+ outfile);
+ return -1;
+ }
+ if (fstat(ofd, &osb) != 0) {
+ maybe_warn("couldn't stat (leaving original): %s",
+ outfile);
+ close(ofd);
+ return -1;
+ }
+ if (osb.st_size != size) {
+ maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
+ (uintmax_t)size, (uintmax_t)osb.st_size);
+ close(ofd);
+ unlink(outfile);
+ return -1;
+ }
+#ifndef SMALL
+ copymodes(ofd, &isb, outfile);
+ remove_file = NULL;
+#endif
+ close(ofd);
+ unlink_input(file, &isb);
+ return size;
+
+ unexpected_EOF:
+ maybe_warnx("%s: unexpected end of file", file);
+ lose:
+ if (fd != -1)
+ close(fd);
+ if (zfd != -1 && zfd != STDOUT_FILENO)
+ close(fd);
+ return -1;
+}
+
+#ifndef SMALL
+static off_t
+cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
+{
+ char buf[BUFLEN];
+ off_t in_tot;
+ ssize_t w;
+
+ in_tot = count;
+ w = write(STDOUT_FILENO, prepend, count);
+ if (w == -1 || (size_t)w != count) {
+ maybe_warn("write to stdout");
+ return -1;
+ }
+ for (;;) {
+ ssize_t rv;
+
+ rv = read(fd, buf, sizeof buf);
+ if (rv == 0)
+ break;
+ if (rv < 0) {
+ maybe_warn("read from fd %d", fd);
+ break;
+ }
+
+ if (write(STDOUT_FILENO, buf, rv) != rv) {
+ maybe_warn("write to stdout");
+ break;
+ }
+ in_tot += rv;
+ }
+
+ if (gsizep)
+ *gsizep = in_tot;
+ return (in_tot);
+}
+#endif
+
+static void
+handle_stdin(void)
+{
+ unsigned char header1[4];
+ off_t usize, gsize;
+ enum filetype method;
+ ssize_t bytes_read;
+#ifndef NO_COMPRESS_SUPPORT
+ FILE *in;
+#endif
+
+#ifndef SMALL
+ if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
+ maybe_warnx("standard input is a terminal -- ignoring");
+ return;
+ }
+#endif
+
+ if (lflag) {
+ struct stat isb;
+
+ /* XXX could read the whole file, etc. */
+ if (fstat(STDIN_FILENO, &isb) < 0) {
+ maybe_warn("fstat");
+ return;
+ }
+ print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
+ return;
+ }
+
+ bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
+ if (bytes_read == -1) {
+ maybe_warn("can't read stdin");
+ return;
+ } else if (bytes_read != sizeof(header1)) {
+ maybe_warnx("(stdin): unexpected end of file");
+ return;
+ }
+
+ method = file_gettype(header1);
+ switch (method) {
+ default:
+#ifndef SMALL
+ if (fflag == 0) {
+ maybe_warnx("unknown compression format");
+ return;
+ }
+ usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
+ break;
+#endif
+ case FT_GZIP:
+ usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
+ (char *)header1, sizeof header1, &gsize, "(stdin)");
+ break;
+#ifndef NO_BZIP2_SUPPORT
+ case FT_BZIP2:
+ usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
+ (char *)header1, sizeof header1, &gsize);
+ break;
+#endif
+#ifndef NO_COMPRESS_SUPPORT
+ case FT_Z:
+ if ((in = zdopen(STDIN_FILENO)) == NULL) {
+ maybe_warnx("zopen of stdin");
+ return;
+ }
+
+ usize = zuncompress(in, stdout, (char *)header1,
+ sizeof header1, &gsize);
+ fclose(in);
+ break;
+#endif
+#ifndef NO_PACK_SUPPORT
+ case FT_PACK:
+ usize = unpack(STDIN_FILENO, STDOUT_FILENO,
+ (char *)header1, sizeof header1, &gsize);
+ break;
+#endif
+#ifndef NO_XZ_SUPPORT
+ case FT_XZ:
+ usize = unxz(STDIN_FILENO, STDOUT_FILENO,
+ (char *)header1, sizeof header1, &gsize);
+ break;
+#endif
+ }
+
+#ifndef SMALL
+ if (vflag && !tflag && usize != -1 && gsize != -1)
+ print_verbage(NULL, NULL, usize, gsize);
+ if (vflag && tflag)
+ print_test("(stdin)", usize != -1);
+#endif
+
+}
+
+static void
+handle_stdout(void)
+{
+ off_t gsize, usize;
+ struct stat sb;
+ time_t systime;
+ uint32_t mtime;
+ int ret;
+
+#ifndef SMALL
+ if (fflag == 0 && isatty(STDOUT_FILENO)) {
+ maybe_warnx("standard output is a terminal -- ignoring");
+ return;
+ }
+#endif
+ /* If stdin is a file use its mtime, otherwise use current time */
+ ret = fstat(STDIN_FILENO, &sb);
+
+#ifndef SMALL
+ if (ret < 0) {
+ maybe_warn("Can't stat stdin");
+ return;
+ }
+#endif
+
+ if (S_ISREG(sb.st_mode))
+ mtime = (uint32_t)sb.st_mtime;
+ else {
+ systime = time(NULL);
+#ifndef SMALL
+ if (systime == -1) {
+ maybe_warn("time");
+ return;
+ }
+#endif
+ mtime = (uint32_t)systime;
+ }
+
+ usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
+#ifndef SMALL
+ if (vflag && !tflag && usize != -1 && gsize != -1)
+ print_verbage(NULL, NULL, usize, gsize);
+#endif
+}
+
+/* do what is asked for, for the path name */
+static void
+handle_pathname(char *path)
+{
+ char *opath = path, *s = NULL;
+ ssize_t len;
+ int slen;
+ struct stat sb;
+
+ /* check for stdout/stdin */
+ if (path[0] == '-' && path[1] == '\0') {
+ if (dflag)
+ handle_stdin();
+ else
+ handle_stdout();
+ return;
+ }
+
+#ifdef __APPLE__
+ if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) {
+ char *suffix = strrchr(path, '.');
+ if (suffix == NULL || strcmp(suffix, Z_SUFFIX) != 0) {
+ len = strlen(path);
+ slen = sizeof(Z_SUFFIX) - 1;
+ s = malloc(len + slen + 1);
+ memcpy(s, path, len);
+ memcpy(s + len, Z_SUFFIX, slen + 1);
+ path = s;
+ }
+ }
+#endif
+
+retry:
+ if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
+ lstat(path, &sb) != 0)) {
+ /* lets try <path>.gz if we're decompressing */
+ if (dflag && s == NULL && errno == ENOENT) {
+ len = strlen(path);
+ slen = suffixes[0].ziplen;
+ s = malloc(len + slen + 1);
+ if (s == NULL)
+ maybe_err("malloc");
+ memcpy(s, path, len);
+ memcpy(s + len, suffixes[0].zipped, slen + 1);
+ path = s;
+ goto retry;
+ }
+#ifdef __APPLE__
+ /* Include actual path for clarity. */
+ maybe_warn("can't stat: %s (%s)", opath, path);
+#else
+ maybe_warn("can't stat: %s", opath);
+#endif
+ goto out;
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+#ifndef SMALL
+ if (rflag)
+ handle_dir(path);
+ else
+#endif
+ maybe_warnx("%s is a directory", path);
+ goto out;
+ }
+
+ if (S_ISREG(sb.st_mode))
+ handle_file(path, &sb);
+ else
+ maybe_warnx("%s is not a regular file", path);
+
+out:
+ if (s)
+ free(s);
+}
+
+/* compress/decompress a file */
+static void
+handle_file(char *file, struct stat *sbp)
+{
+ off_t usize, gsize;
+ char outfile[PATH_MAX];
+
+ infile = file;
+ if (dflag) {
+ usize = file_uncompress(file, outfile, sizeof(outfile));
+#ifndef SMALL
+ if (vflag && tflag)
+ print_test(file, usize != -1);
+#endif
+ if (usize == -1)
+ return;
+ gsize = sbp->st_size;
+ } else {
+ gsize = file_compress(file, outfile, sizeof(outfile));
+ if (gsize == -1)
+ return;
+ usize = sbp->st_size;
+ }
+
+
+#ifndef SMALL
+ if (vflag && !tflag)
+ print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
+#endif
+}
+
+#ifndef SMALL
+/* this is used with -r to recursively descend directories */
+static void
+handle_dir(char *dir)
+{
+ char *path_argv[2];
+ FTS *fts;
+ FTSENT *entry;
+
+ path_argv[0] = dir;
+ path_argv[1] = 0;
+ fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL) {
+ warn("couldn't fts_open %s", dir);
+ return;
+ }
+
+ while ((entry = fts_read(fts))) {
+ switch(entry->fts_info) {
+ case FTS_D:
+ case FTS_DP:
+ continue;
+
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ maybe_warn("%s", entry->fts_path);
+ continue;
+ case FTS_F:
+ handle_file(entry->fts_path, entry->fts_statp);
+ }
+ }
+ (void)fts_close(fts);
+}
+#endif
+
+/* print a ratio - size reduction as a fraction of uncompressed size */
+static void
+print_ratio(off_t in, off_t out, FILE *where)
+{
+ int percent10; /* 10 * percent */
+ off_t diff;
+ char buff[8];
+ int len;
+
+ diff = in - out/2;
+ if (diff <= 0)
+ /*
+ * Output is more than double size of input! print -99.9%
+ * Quite possibly we've failed to get the original size.
+ */
+ percent10 = -999;
+ else {
+ /*
+ * We only need 12 bits of result from the final division,
+ * so reduce the values until a 32bit division will suffice.
+ */
+ while (in > 0x100000) {
+ diff >>= 1;
+ in >>= 1;
+ }
+ if (in != 0)
+ percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
+ else
+ percent10 = 0;
+ }
+
+ len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
+ /* Move the '.' to before the last digit */
+ buff[len - 1] = buff[len - 2];
+ buff[len - 2] = '.';
+ fprintf(where, "%5s%%", buff);
+}
+
+#ifndef SMALL
+/* print compression statistics, and the new name (if there is one!) */
+static void
+print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
+{
+ if (file)
+ fprintf(stderr, "%s:%s ", file,
+ strlen(file) < 7 ? "\t\t" : "\t");
+ print_ratio(usize, gsize, stderr);
+ if (nfile)
+ fprintf(stderr, " -- replaced with %s", nfile);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+/* print test results */
+static void
+print_test(const char *file, int ok)
+{
+
+ if (exit_value == 0 && ok == 0)
+ exit_value = 1;
+ fprintf(stderr, "%s:%s %s\n", file,
+ strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
+ fflush(stderr);
+}
+#endif
+
+/* print a file's info ala --list */
+/* eg:
+ compressed uncompressed ratio uncompressed_name
+ 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
+*/
+static void
+print_list(int fd, off_t out, const char *outfile, time_t ts)
+{
+ static int first = 1;
+#ifndef SMALL
+ static off_t in_tot, out_tot;
+ uint32_t crc = 0;
+#endif
+ off_t in = 0, rv;
+
+ if (first) {
+#ifndef SMALL
+ if (vflag)
+ printf("method crc date time ");
+#endif
+ if (qflag == 0)
+ printf(" compressed uncompressed "
+ "ratio uncompressed_name\n");
+ }
+ first = 0;
+
+ /* print totals? */
+#ifndef SMALL
+ if (fd == -1) {
+ in = in_tot;
+ out = out_tot;
+ } else
+#endif
+ {
+ /* read the last 4 bytes - this is the uncompressed size */
+ rv = lseek(fd, (off_t)(-8), SEEK_END);
+ if (rv != -1) {
+ unsigned char buf[8];
+ uint32_t usize;
+
+ rv = read(fd, (char *)buf, sizeof(buf));
+ if (rv == -1)
+ maybe_warn("read of uncompressed size");
+ else if (rv != sizeof(buf))
+ maybe_warnx("read of uncompressed size");
+
+ else {
+ usize = buf[4] | buf[5] << 8 |
+ buf[6] << 16 | buf[7] << 24;
+ in = (off_t)usize;
+#ifndef SMALL
+ crc = buf[0] | buf[1] << 8 |
+ buf[2] << 16 | buf[3] << 24;
+#endif
+ }
+ }
+ }
+
+#ifndef SMALL
+ if (vflag && fd == -1)
+ printf(" ");
+ else if (vflag) {
+ char *date = ctime(&ts);
+
+ /* skip the day, 1/100th second, and year */
+ date += 4;
+ date[12] = 0;
+ printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
+ }
+ in_tot += in;
+ out_tot += out;
+#else
+ (void)&ts; /* XXX */
+#endif
+ printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
+ print_ratio(in, out, stdout);
+ printf(" %s\n", outfile);
+}
+
+/* display the usage of NetBSD gzip */
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n", gzip_version);
+ fprintf(stderr,
+#ifdef SMALL
+ "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
+#else
+ "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
+ " -1 --fast fastest (worst) compression\n"
+ " -2 .. -8 set compression level\n"
+ " -9 --best best (slowest) compression\n"
+ " -c --stdout write to stdout, keep original files\n"
+ " --to-stdout\n"
+ " -d --decompress uncompress files\n"
+ " --uncompress\n"
+ " -f --force force overwriting & compress links\n"
+ " -h --help display this help\n"
+ " -k --keep don't delete input files during operation\n"
+ " -l --list list compressed file contents\n"
+ " -N --name save or restore original file name and time stamp\n"
+ " -n --no-name don't save original file name or time stamp\n"
+ " -q --quiet output no warnings\n"
+ " -r --recursive recursively compress files in directories\n"
+ " -S .suf use suffix .suf instead of .gz\n"
+ " --suffix .suf\n"
+ " -t --test test compressed file\n"
+ " -V --version display program version\n"
+ " -v --verbose print extra statistics\n",
+#endif
+ getprogname());
+ exit(0);
+}
+
+#ifndef SMALL
+/* display the license information of FreeBSD gzip */
+static void
+display_license(void)
+{
+
+#ifdef __APPLE__
+ fprintf(stderr, "%s (based on FreeBSD gzip 20150113)\n", gzip_version);
+#else
+ fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
+#endif
+ fprintf(stderr, "%s\n", gzip_copyright);
+ exit(0);
+}
+#endif
+
+/* display the version of NetBSD gzip */
+static void
+display_version(void)
+{
+
+ fprintf(stderr, "%s\n", gzip_version);
+ exit(0);
+}
+
+#ifndef NO_BZIP2_SUPPORT
+#include "unbzip2.c"
+#endif
+#ifndef NO_COMPRESS_SUPPORT
+#include "zuncompress.c"
+#endif
+#ifndef NO_PACK_SUPPORT
+#include "unpack.c"
+#endif
+#ifndef NO_XZ_SUPPORT
+#include "unxz.c"
+#endif
+
+static ssize_t
+read_retry(int fd, void *buf, size_t sz)
+{
+ char *cp = buf;
+ size_t left = MIN(sz, (size_t) SSIZE_MAX);
+
+ while (left > 0) {
+ ssize_t ret;
+
+ ret = read(fd, cp, left);
+ if (ret == -1) {
+ return ret;
+ } else if (ret == 0) {
+ break; /* EOF */
+ }
+ cp += ret;
+ left -= ret;
+ }
+
+ return sz - left;
+}
diff --git a/file_cmds/gzip/gzip.plist b/file_cmds/gzip/gzip.plist
new file mode 100644
index 0000000..a0190fd
--- /dev/null
+++ b/file_cmds/gzip/gzip.plist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>OpenSourceImportDate</key>
+ <string>2012-10-25</string>
+ <key>OpenSourceLicense</key>
+ <string>bsd</string>
+ <key>OpenSourceLicenseFile</key>
+ <string>gzip.txt</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>gzip.c: adjust --version output</string>
+ <string>gzip.c: timespec portability fixes</string>
+ <string>gzip.c: preserve ACLs/EAs (3753276)</string>
+ <string>gzip.c: clear type/creator (4123341)</string>
+ <string>gzip.c, gzip.1: zcat conformance (3675040)</string>
+ <string>zmore: work as zless or zmore</string>
+ </array>
+ <key>OpenSourceProject</key>
+ <string>gzip</string>
+ <key>OpenSourceSCM</key>
+ <string>cvs -d :pserver:anoncvs@anoncvs.fr.FreeBSD.org:/home/ncvs co src/usr.bin/gzip</string>
+ <key>OpenSourceVersion</key>
+ <string>2012-10-19</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://svnweb.freebsd.org/base/head/usr.bin/gzip/</string>
+</dict>
+</plist>
diff --git a/file_cmds/gzip/gzip.xcconfig b/file_cmds/gzip/gzip.xcconfig
new file mode 100644
index 0000000..382cbbf
--- /dev/null
+++ b/file_cmds/gzip/gzip.xcconfig
@@ -0,0 +1,49 @@
+PRODUCT_NAME = $(TARGET_NAME)
+GZIP_PREFIX = /usr/local
+GZIP_PREFIX[sdk=macosx*] = /usr
+INSTALL_PATH = $(GZIP_PREFIX)/bin
+
+GCC_PREPROCESSOR_DEFINITIONS = GZIP_APPLE_VERSION=\"$(RC_ProjectSourceVersion)\"
+
+// Make it build in the GUI
+SUPPORTED_PLATFORMS = iphoneos macosx
+ARCHS[sdk=macosx*] = x86_64
+RUN_CLANG_STATIC_ANALYZER = YES
+
+// Versioning
+CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion)
+VERSIONING_SYSTEM = apple-generic
+VERSION_INFO_PREFIX = __
+
+// Other Stuff
+ALWAYS_SEARCH_USER_PATHS = NO
+APPLY_RULES_IN_COPY_FILES = YES
+CODE_SIGN_IDENTITY = -
+DEAD_CODE_STRIPPING = YES
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
+STRIP_INSTALLED_PRODUCT = YES
+USE_HEADERMAP = NO
+
+// Warnings
+//CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
+//CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
+GCC_TREAT_WARNINGS_AS_ERRORS = YES
+//GCC_WARN_64_TO_32_BIT_CONVERSION = YES
+GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
+GCC_WARN_ABOUT_RETURN_TYPE = YES
+GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES
+GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
+GCC_WARN_SHADOW = YES
+GCC_WARN_SIGN_COMPARE = YES
+GCC_WARN_UNINITIALIZED_AUTOS = YES
+GCC_WARN_UNKNOWN_PRAGMAS = YES
+GCC_WARN_UNUSED_FUNCTION = YES
+GCC_WARN_UNUSED_LABEL = YES
+GCC_WARN_UNUSED_PARAMETER = YES
+GCC_WARN_UNUSED_VARIABLE = YES
+
+// Used by install_scripts.sh
+GZIP_SCRIPTS = gzexe zdiff zforce zmore znew
+GZIP_LINKS = gzip gunzip gzip gzcat gzip zcat zdiff zcmp zmore zless
diff --git a/file_cmds/gzip/install_scripts.sh b/file_cmds/gzip/install_scripts.sh
new file mode 100644
index 0000000..6e8e550
--- /dev/null
+++ b/file_cmds/gzip/install_scripts.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+set -e
+
+INSTALL_MAN1_DIR=${INSTALL_ROOT}${GZIP_PREFIX}/share/man/man1
+
+install -d -m 0755 "${INSTALL_MAN1_DIR}"
+for script in ${GZIP_SCRIPTS}; do
+ printf "Installing ${script} ...\n"
+ install -m 0755 ${SRCROOT}/gzip/${script} ${INSTALL_DIR}/${script}
+ install -m 0644 ${SRCROOT}/gzip/${script}.1 ${INSTALL_MAN1_DIR}/${script}.1
+done
+
+set ${GZIP_LINKS}
+while [ $# -ge 2 ]; do
+ l=$1
+ shift
+ t=$1
+ shift
+ printf "Creating link: ${t} -> ${l} ...\n"
+ ln -f ${INSTALL_DIR}/${l} ${INSTALL_DIR}/${t}
+ ln -f ${INSTALL_MAN1_DIR}/${l}.1 ${INSTALL_MAN1_DIR}/${t}.1
+done
diff --git a/file_cmds/gzip/unbzip2.c b/file_cmds/gzip/unbzip2.c
new file mode 100644
index 0000000..4192e9b
--- /dev/null
+++ b/file_cmds/gzip/unbzip2.c
@@ -0,0 +1,141 @@
+/* $NetBSD: unbzip2.c,v 1.13 2009/12/05 03:23:37 mrg Exp $ */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Simon Burge.
+ *
+ * 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 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/gzip/unbzip2.c,v 1.5 2010/04/07 22:54:53 delphij Exp $
+ */
+
+/* This file is #included by gzip.c */
+
+static off_t
+unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in)
+{
+ int ret, end_of_file, cold = 0;
+ off_t bytes_out = 0;
+ bz_stream bzs;
+ static char *inbuf, *outbuf;
+
+ if (inbuf == NULL)
+ inbuf = malloc(BUFLEN);
+ if (outbuf == NULL)
+ outbuf = malloc(BUFLEN);
+ if (inbuf == NULL || outbuf == NULL)
+ maybe_err("malloc");
+
+ bzs.bzalloc = NULL;
+ bzs.bzfree = NULL;
+ bzs.opaque = NULL;
+
+ end_of_file = 0;
+ ret = BZ2_bzDecompressInit(&bzs, 0, 0);
+ if (ret != BZ_OK)
+ maybe_errx("bzip2 init");
+
+ /* Prepend. */
+ bzs.avail_in = prelen;
+ bzs.next_in = pre;
+
+ if (bytes_in)
+ *bytes_in = prelen;
+
+ while (ret == BZ_OK) {
+ if (bzs.avail_in == 0 && !end_of_file) {
+ ssize_t n;
+
+ n = read(in, inbuf, BUFLEN);
+ if (n < 0)
+ maybe_err("read");
+ if (n == 0)
+ end_of_file = 1;
+ bzs.next_in = inbuf;
+ bzs.avail_in = n;
+ if (bytes_in)
+ *bytes_in += n;
+ }
+
+ bzs.next_out = outbuf;
+ bzs.avail_out = BUFLEN;
+ ret = BZ2_bzDecompress(&bzs);
+
+ switch (ret) {
+ case BZ_STREAM_END:
+ case BZ_OK:
+ if (ret == BZ_OK && end_of_file) {
+ /*
+ * If we hit this after a stream end, consider
+ * it as the end of the whole file and don't
+ * bail out.
+ */
+ if (cold == 1)
+ ret = BZ_STREAM_END;
+ else
+ maybe_errx("truncated file");
+ }
+ cold = 0;
+ if (!tflag && bzs.avail_out != BUFLEN) {
+ ssize_t n;
+
+ n = write(out, outbuf, BUFLEN - bzs.avail_out);
+ if (n < 0)
+ maybe_err("write");
+ bytes_out += n;
+ }
+ if (ret == BZ_STREAM_END && !end_of_file) {
+ if (BZ2_bzDecompressEnd(&bzs) != BZ_OK ||
+ BZ2_bzDecompressInit(&bzs, 0, 0) != BZ_OK)
+ maybe_errx("bzip2 re-init");
+ cold = 1;
+ ret = BZ_OK;
+ }
+ break;
+
+ case BZ_DATA_ERROR:
+ maybe_warnx("bzip2 data integrity error");
+ break;
+
+ case BZ_DATA_ERROR_MAGIC:
+ maybe_warnx("bzip2 magic number error");
+ break;
+
+ case BZ_MEM_ERROR:
+ maybe_warnx("bzip2 out of memory");
+ break;
+
+ default:
+ maybe_warnx("unknown bzip2 error: %d", ret);
+ break;
+ }
+ }
+
+ if (ret != BZ_STREAM_END || BZ2_bzDecompressEnd(&bzs) != BZ_OK)
+ return (-1);
+
+ return (bytes_out);
+}
+
diff --git a/file_cmds/gzip/unpack.c b/file_cmds/gzip/unpack.c
new file mode 100644
index 0000000..dbad588
--- /dev/null
+++ b/file_cmds/gzip/unpack.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 2009 Xin LI <delphij@FreeBSD.org>
+ * 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.
+ *
+ * $FreeBSD: src/usr.bin/gzip/unpack.c,v 1.3 2010/10/16 15:24:04 bcr Exp $
+ */
+
+/* This file is #included by gzip.c */
+
+/*
+ * pack(1) file format:
+ *
+ * The first 7 bytes is the header:
+ * 00, 01 - Signature (US, RS), we already validated it earlier.
+ * 02..05 - Uncompressed size
+ * 06 - Level for the huffman tree (<=24)
+ *
+ * pack(1) will then store symbols (leaf) nodes count in each huffman
+ * tree levels, each level would consume 1 byte (See [1]).
+ *
+ * After the symbol count table, there is the symbol table, storing
+ * symbols represented by corresponding leaf node. EOB is not being
+ * explicitly transmitted (not necessary anyway) in the symbol table.
+ *
+ * Compressed data goes after the symbol table.
+ *
+ * NOTES
+ *
+ * [1] If we count EOB into the symbols, that would mean that we will
+ * have at most 256 symbols in the huffman tree. pack(1) rejects empty
+ * file and files that just repeats one character, which means that we
+ * will have at least 2 symbols. Therefore, pack(1) would reduce the
+ * last level symbol count by 2 which makes it a number in
+ * range [0..254], so all levels' symbol count would fit into 1 byte.
+ */
+
+#define PACK_HEADER_LENGTH 7
+#define HTREE_MAXLEVEL 24
+
+/*
+ * unpack descriptor
+ *
+ * Represent the huffman tree in a similar way that pack(1) would
+ * store in a packed file. We store all symbols in a linear table,
+ * and store pointers to each level's first symbol. In addition to
+ * that, maintain two counts for each level: inner nodes count and
+ * leaf nodes count.
+ */
+typedef struct {
+ int symbol_size; /* Size of the symbol table */
+ int treelevels; /* Levels for the huffman tree */
+
+ int *symbolsin; /* Table of leaf symbols count in
+ each level */
+ int *inodesin; /* Table of internal nodes count in
+ each level */
+
+ char *symbol; /* The symbol table */
+ char *symbol_eob; /* Pointer to the EOB symbol */
+ char **tree; /* Decoding huffman tree (pointers to
+ first symbol of each tree level */
+
+ off_t uncompressed_size; /* Uncompressed size */
+ FILE *fpIn; /* Input stream */
+ FILE *fpOut; /* Output stream */
+} unpack_descriptor_t;
+
+/*
+ * Release resource allocated to an unpack descriptor.
+ *
+ * Caller is responsible to make sure that all of these pointers are
+ * initialized (in our case, they all point to valid memory block).
+ * We don't zero out pointers here because nobody else would ever
+ * reference the memory block without scrubbing them.
+ */
+static void
+unpack_descriptor_fini(unpack_descriptor_t *unpackd)
+{
+
+ free(unpackd->symbolsin);
+ free(unpackd->inodesin);
+ free(unpackd->symbol);
+ free(unpackd->tree);
+
+ fclose(unpackd->fpIn);
+ fclose(unpackd->fpOut);
+}
+
+/*
+ * Recursively fill the internal node count table
+ */
+static void
+unpackd_fill_inodesin(const unpack_descriptor_t *unpackd, int level)
+{
+
+ /*
+ * The internal nodes would be 1/2 of total internal nodes and
+ * leaf nodes in the next level. For the last level there
+ * would be no internal node by definition.
+ */
+ if (level < unpackd->treelevels) {
+ unpackd_fill_inodesin(unpackd, level + 1);
+ unpackd->inodesin[level] = (unpackd->inodesin[level + 1] +
+ unpackd->symbolsin[level + 1]) / 2;
+ } else
+ unpackd->inodesin[level] = 0;
+}
+
+/*
+ * Update counter for accepted bytes
+ */
+static void
+accepted_bytes(off_t *bytes_in, off_t newbytes)
+{
+
+ if (bytes_in != NULL)
+ (*bytes_in) += newbytes;
+}
+
+/*
+ * Read file header and construct the tree. Also, prepare the buffered I/O
+ * for decode routine.
+ *
+ * Return value is uncompressed size.
+ */
+static void
+unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in,
+ unpack_descriptor_t *unpackd)
+{
+ unsigned char hdr[PACK_HEADER_LENGTH]; /* buffer for header */
+ ssize_t bytesread; /* Bytes read from the file */
+ int i, j, thisbyte;
+
+ /* Prepend the header buffer if we already read some data */
+ if (prelen != 0)
+ memcpy(hdr, pre, prelen);
+
+ /* Read in and fill the rest bytes of header */
+ bytesread = read(in, hdr + prelen, PACK_HEADER_LENGTH - prelen);
+ if (bytesread < 0)
+ maybe_err("Error reading pack header");
+
+ accepted_bytes(bytes_in, PACK_HEADER_LENGTH);
+
+ /* Obtain uncompressed length (bytes 2,3,4,5)*/
+ unpackd->uncompressed_size = 0;
+ for (i = 2; i <= 5; i++) {
+ unpackd->uncompressed_size <<= 8;
+ unpackd->uncompressed_size |= hdr[i];
+ }
+
+ /* Get the levels of the tree */
+ unpackd->treelevels = hdr[6];
+ if (unpackd->treelevels > HTREE_MAXLEVEL || unpackd->treelevels < 1)
+ maybe_errx("Huffman tree has insane levels");
+
+ /* Let libc take care for buffering from now on */
+ if ((unpackd->fpIn = fdopen(in, "r")) == NULL)
+ maybe_err("Can not fdopen() input stream");
+ if ((unpackd->fpOut = fdopen(out, "w")) == NULL)
+ maybe_err("Can not fdopen() output stream");
+
+ /* Allocate for the tables of bounds and the tree itself */
+ unpackd->inodesin =
+ calloc(unpackd->treelevels, sizeof(*(unpackd->inodesin)));
+ unpackd->symbolsin =
+ calloc(unpackd->treelevels, sizeof(*(unpackd->symbolsin)));
+ unpackd->tree =
+ calloc(unpackd->treelevels, (sizeof (*(unpackd->tree))));
+ if (unpackd->inodesin == NULL || unpackd->symbolsin == NULL ||
+ unpackd->tree == NULL)
+ maybe_err("calloc");
+
+ /* We count from 0 so adjust to match array upper bound */
+ unpackd->treelevels--;
+
+ /* Read the levels symbol count table and calculate total */
+ unpackd->symbol_size = 1; /* EOB */
+ for (i = 0; i <= unpackd->treelevels; i++) {
+ if ((thisbyte = fgetc(unpackd->fpIn)) == EOF)
+ maybe_err("File appears to be truncated");
+ unpackd->symbolsin[i] = (unsigned char)thisbyte;
+ unpackd->symbol_size += unpackd->symbolsin[i];
+ }
+ accepted_bytes(bytes_in, unpackd->treelevels);
+ if (unpackd->symbol_size > 256)
+ maybe_errx("Bad symbol table");
+
+ /* Allocate for the symbol table, point symbol_eob at the beginning */
+ unpackd->symbol_eob = unpackd->symbol = calloc(1, unpackd->symbol_size);
+ if (unpackd->symbol == NULL)
+ maybe_err("calloc");
+
+ /*
+ * Read in the symbol table, which contain [2, 256] symbols.
+ * In order to fit the count in one byte, pack(1) would offset
+ * it by reducing 2 from the actual number from the last level.
+ *
+ * We adjust the last level's symbol count by 1 here, because
+ * the EOB symbol is not being transmitted explicitly. Another
+ * adjustment would be done later afterward.
+ */
+ unpackd->symbolsin[unpackd->treelevels]++;
+ for (i = 0; i <= unpackd->treelevels; i++) {
+ unpackd->tree[i] = unpackd->symbol_eob;
+ for (j = 0; j < unpackd->symbolsin[i]; j++) {
+ if ((thisbyte = fgetc(unpackd->fpIn)) == EOF)
+ maybe_errx("Symbol table truncated");
+ *unpackd->symbol_eob++ = (char)thisbyte;
+ }
+ accepted_bytes(bytes_in, unpackd->symbolsin[i]);
+ }
+
+ /* Now, take account for the EOB symbol as well */
+ unpackd->symbolsin[unpackd->treelevels]++;
+
+ /*
+ * The symbolsin table has been constructed now.
+ * Calculate the internal nodes count table based on it.
+ */
+ unpackd_fill_inodesin(unpackd, 0);
+}
+
+/*
+ * Decode huffman stream, based on the huffman tree.
+ */
+static void
+unpack_decode(const unpack_descriptor_t *unpackd, off_t *bytes_in)
+{
+ int thislevel, thiscode, thisbyte, inlevelindex;
+ int i;
+ off_t bytes_out = 0;
+ const char *thissymbol; /* The symbol pointer decoded from stream */
+
+ /*
+ * Decode huffman. Fetch every bytes from the file, get it
+ * into 'thiscode' bit-by-bit, then output the symbol we got
+ * when one has been found.
+ *
+ * Assumption: sizeof(int) > ((max tree levels + 1) / 8).
+ * bad things could happen if not.
+ */
+ thislevel = 0;
+ thiscode = thisbyte = 0;
+
+ while ((thisbyte = fgetc(unpackd->fpIn)) != EOF) {
+ accepted_bytes(bytes_in, 1);
+
+ /*
+ * Split one bit from thisbyte, from highest to lowest,
+ * feed the bit into thiscode, until we got a symbol from
+ * the tree.
+ */
+ for (i = 7; i >= 0; i--) {
+ thiscode = (thiscode << 1) | ((thisbyte >> i) & 1);
+
+ /* Did we got a symbol? (referencing leaf node) */
+ if (thiscode >= unpackd->inodesin[thislevel]) {
+ inlevelindex =
+ thiscode - unpackd->inodesin[thislevel];
+ if (inlevelindex > unpackd->symbolsin[thislevel])
+ maybe_errx("File corrupt");
+
+ thissymbol =
+ &(unpackd->tree[thislevel][inlevelindex]);
+ if ((thissymbol == unpackd->symbol_eob) &&
+ (bytes_out == unpackd->uncompressed_size))
+ goto finished;
+
+ fputc((*thissymbol), unpackd->fpOut);
+ bytes_out++;
+
+ /* Prepare for next input */
+ thislevel = 0; thiscode = 0;
+ } else {
+ thislevel++;
+ if (thislevel > unpackd->treelevels)
+ maybe_errx("File corrupt");
+ }
+ }
+ }
+
+finished:
+ if (bytes_out != unpackd->uncompressed_size)
+ maybe_errx("Premature EOF");
+}
+
+/* Handler for pack(1)'ed file */
+static off_t
+unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in)
+{
+ unpack_descriptor_t unpackd;
+
+ in = dup(in);
+ if (in == -1)
+ maybe_err("dup");
+ out = dup(out);
+ if (out == -1)
+ maybe_err("dup");
+
+ unpack_parse_header(in, out, pre, prelen, bytes_in, &unpackd);
+ unpack_decode(&unpackd, bytes_in);
+ unpack_descriptor_fini(&unpackd);
+
+ /* If we reached here, the unpack was successful */
+ return (unpackd.uncompressed_size);
+}
+
diff --git a/file_cmds/gzip/unxz.c b/file_cmds/gzip/unxz.c
new file mode 100644
index 0000000..df48ae3
--- /dev/null
+++ b/file_cmds/gzip/unxz.c
@@ -0,0 +1,153 @@
+/* $NetBSD: unxz.c,v 1.5 2011/09/30 01:32:21 christos Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 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>
+__FBSDID("$FreeBSD: src/usr.bin/gzip/unxz.c,v 1.2 2011/10/16 07:35:26 delphij Exp $");
+
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <lzma.h>
+
+static off_t
+unxz(int i, int o, char *pre, size_t prelen, off_t *bytes_in)
+{
+ lzma_stream strm = LZMA_STREAM_INIT;
+ static const int flags = LZMA_TELL_UNSUPPORTED_CHECK|LZMA_CONCATENATED;
+ lzma_ret ret;
+ lzma_action action = LZMA_RUN;
+ off_t bytes_out, bp;
+ uint8_t ibuf[BUFSIZ];
+ uint8_t obuf[BUFSIZ];
+
+ if (bytes_in == NULL)
+ bytes_in = &bp;
+
+ strm.next_in = ibuf;
+ memcpy(ibuf, pre, prelen);
+ strm.avail_in = read(i, ibuf + prelen, sizeof(ibuf) - prelen);
+ if (strm.avail_in == (size_t)-1)
+ maybe_err("read failed");
+ strm.avail_in += prelen;
+ *bytes_in = strm.avail_in;
+
+ if ((ret = lzma_stream_decoder(&strm, UINT64_MAX, flags)) != LZMA_OK)
+ maybe_errx("Can't initialize decoder (%d)", ret);
+
+ strm.next_out = NULL;
+ strm.avail_out = 0;
+ if ((ret = lzma_code(&strm, LZMA_RUN)) != LZMA_OK)
+ maybe_errx("Can't read headers (%d)", ret);
+
+ bytes_out = 0;
+ strm.next_out = obuf;
+ strm.avail_out = sizeof(obuf);
+
+ for (;;) {
+ if (strm.avail_in == 0) {
+ strm.next_in = ibuf;
+ strm.avail_in = read(i, ibuf, sizeof(ibuf));
+ switch (strm.avail_in) {
+ case (size_t)-1:
+ maybe_err("read failed");
+ /*NOTREACHED*/
+ case 0:
+ action = LZMA_FINISH;
+ break;
+ default:
+ *bytes_in += strm.avail_in;
+ break;
+ }
+ }
+
+ ret = lzma_code(&strm, action);
+
+ // Write and check write error before checking decoder error.
+ // This way as much data as possible gets written to output
+ // even if decoder detected an error.
+ if (strm.avail_out == 0 || ret != LZMA_OK) {
+ const size_t write_size = sizeof(obuf) - strm.avail_out;
+
+ if (write(o, obuf, write_size) != (ssize_t)write_size)
+ maybe_err("write failed");
+
+ strm.next_out = obuf;
+ strm.avail_out = sizeof(obuf);
+ bytes_out += write_size;
+ }
+
+ if (ret != LZMA_OK) {
+ if (ret == LZMA_STREAM_END) {
+ // Check that there's no trailing garbage.
+ if (strm.avail_in != 0 || read(i, ibuf, 1))
+ ret = LZMA_DATA_ERROR;
+ else {
+ lzma_end(&strm);
+ return bytes_out;
+ }
+ }
+
+ const char *msg;
+ switch (ret) {
+ case LZMA_MEM_ERROR:
+ msg = strerror(ENOMEM);
+ break;
+
+ case LZMA_FORMAT_ERROR:
+ msg = "File format not recognized";
+ break;
+
+ case LZMA_OPTIONS_ERROR:
+ // FIXME: Better message?
+ msg = "Unsupported compression options";
+ break;
+
+ case LZMA_DATA_ERROR:
+ msg = "File is corrupt";
+ break;
+
+ case LZMA_BUF_ERROR:
+ msg = "Unexpected end of input";
+ break;
+
+ case LZMA_MEMLIMIT_ERROR:
+ msg = "Reached memory limit";
+ break;
+
+ default:
+ maybe_errx("Unknown error (%d)", ret);
+ break;
+ }
+ maybe_errx("%s", msg);
+
+ }
+ }
+}
diff --git a/file_cmds/gzip/zdiff b/file_cmds/gzip/zdiff
new file mode 100644
index 0000000..87863c0
--- /dev/null
+++ b/file_cmds/gzip/zdiff
@@ -0,0 +1,142 @@
+#!/bin/sh -
+#
+# $NetBSD: zdiff,v 1.5 2010/04/14 20:30:28 joerg Exp $
+#
+# $OpenBSD: zdiff,v 1.2 2003/07/29 07:42:44 otto Exp $
+#
+#-
+# Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+# Copyright (c) 2010 Joerg Sonnenberger <joerg@NetBSD.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Sponsored in part by the Defense Advanced Research Projects
+# Agency (DARPA) and Air Force Research Laboratory, Air Force
+# Materiel Command, USAF, under agreement number F39502-99-1-0512.
+#
+# $FreeBSD: src/usr.bin/gzip/zdiff,v 1.2 2011/05/23 09:02:44 delphij Exp $
+
+# Set $prog based on $0
+case $0 in
+ *cmp) prog=cmp
+ ;;
+ *) prog=diff
+ ;;
+esac
+USAGE="usage: $0 [options] file1 [file2]"
+
+check_suffix() {
+ case "$1" in
+ *[._-][Zz])
+ setvar $2 "${1%??}"
+ setvar $3 "gzip -cdqf"
+ ;;
+ *[._-]bz)
+ setvar $2 "${1%???}"
+ setvar $3 "bzip2 -cdqf"
+ ;;
+ *[._-]gz)
+ setvar $2 "${1%???}"
+ setvar $3 "gzip -cdqf"
+ ;;
+ *[._-]xz)
+ setvar $2 "${1%???}"
+ setvar $3 "xz -cdqf"
+ ;;
+ *[._-]bz2)
+ setvar $2 "${1%????}"
+ setvar $3 "bzip2 -cdqf"
+ ;;
+ *[._-]lzma)
+ setvar $2 "${1%?????}"
+ setvar $3 "xz -cdqf"
+ ;;
+ *.t[ag]z)
+ setvar $2 "${1%??}"ar
+ setvar $3 "gzip -cdqf"
+ ;;
+ *.tbz)
+ setvar $2 "${1%??}"ar
+ setvar $3 "bzip2 -cdqf"
+ ;;
+ *.tbz2)
+ setvar $2 "${1%???}"ar
+ setvar $3 "bzip2 -cdqf"
+ ;;
+ *.t[lx]z)
+ setvar $2 "${1%??}"ar
+ setvar $3 "xz -cdqf"
+ ;;
+ *)
+ setvar $2 "$1"
+ setvar $3 ""
+ ;;
+ esac
+}
+
+
+# Pull out any command line flags so we can pass them to diff/cmp
+# XXX - assumes there is no optarg
+flags=
+while test $# -ne 0; do
+ case "$1" in
+ --)
+ shift
+ break
+ ;;
+ -)
+ break
+ ;;
+ -*)
+ flags="$flags $1"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ $# -eq 1 ]; then
+ # One file given, compare compressed to uncompressed
+ files="$1"
+ check_suffix "$1" files filt
+ if [ -z "$filt" ]; then
+ echo "z$prog: unknown suffix" 1>&2
+ exit 1
+ fi
+ $filt -- "$1" | $prog $flags -- - "$files"
+ status=$?
+elif [ $# -eq 2 ]; then
+ # Two files given, compare the two uncompressing as needed
+ check_suffix "$1" files filt
+ check_suffix "$2" files2 filt2
+ if [ -z "$filt" -a -z "$filt2" ]; then
+ $prog $flags -- "$1" "$2"
+ elif [ -z "$filt" -a -n "$filt2" -a "$1" != "-" ]; then
+ $filt2 -- "$2" | $prog $flags -- "$1" -
+ elif [ -n "$filt" -a -z "$filt2" -a "$2" != "-" ]; then
+ $filt -- "$1" | $prog $flags -- - "$2"
+ else
+ tmp=`mktemp -t z$prog.XXXXXXXXXX` || exit 1
+ trap "rm -f $tmp" 0 1 2 3 13 15
+ ${filt2:-cat} -- "$2" > $tmp || exit $?
+ ${filt:-cat} -- "$1" | $prog $flags -- - "$tmp"
+ fi
+ status=$?
+else
+ echo "$USAGE" 1>&2
+ exit 1
+fi
+
+exit $status
diff --git a/file_cmds/gzip/zdiff.1 b/file_cmds/gzip/zdiff.1
new file mode 100644
index 0000000..2e0ebea
--- /dev/null
+++ b/file_cmds/gzip/zdiff.1
@@ -0,0 +1,142 @@
+.\" $NetBSD: zdiff.1,v 1.5 2010/04/14 19:52:05 wiz Exp $
+.\" $OpenBSD: zdiff.1,v 1.2 2003/07/13 17:39:14 millert Exp $
+.\"
+.\" Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+.\" Copyright (c) 2010 Joerg Sonnenberger <joerg@NetBSD.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.\" $FreeBSD: src/usr.bin/gzip/zdiff.1,v 1.2 2011/05/23 09:02:44 delphij Exp $
+.Dd May 23, 2011
+.Dt ZDIFF 1
+.Os
+.Sh NAME
+.Nm zcmp ,
+.Nm zdiff
+.Nd compare compressed files
+.Sh SYNOPSIS
+.Nm zcmp
+.Op Ar options
+.Ar file
+.Op Ar file2
+.Nm zdiff
+.Op Ar options
+.Ar file
+.Op Ar file2
+.Sh DESCRIPTION
+.Nm zcmp
+and
+.Nm zdiff
+are filters that invoke
+.Xr cmp 1
+or
+.Xr diff 1
+respectively to compare compressed files.
+Any
+.Ar options
+that are specified are passed to
+.Xr cmp 1
+or
+.Xr diff 1 .
+.Pp
+If only
+.Ar file1
+is specified, it is compared against a file with the same name, but
+with the extension removed.
+When both
+.Ar file1
+or
+.Ar file2
+are specified, either file may be compressed.
+.Pp
+Extensions handled by
+.Xr gzip 1 :
+.Bl -bullet -compact
+.It
+z, Z,
+.It
+gz,
+.It
+taz,
+.It
+tgz.
+.El
+.Pp
+Extensions handled by
+.Xr bzip2 1 :
+.Bl -bullet -compact
+.It
+bz,
+.It
+bz2,
+.It
+tbz,
+.It
+tbz2.
+.El
+.Pp
+Extensions handled by
+.Xr xz 1 :
+.Bl -bullet -compact
+.It
+lzma,
+.It
+xz,
+.It
+tlz,
+.It
+txz.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width "TMPDIR"
+.It Ev TMPDIR
+Directory in which to place temporary files.
+If unset,
+.Pa /tmp
+is used.
+.El
+.Sh FILES
+.Bl -tag -width "/tmp/zdiff.XXXXXXXXXX" -compact
+.It Pa /tmp/zcmp.XXXXXXXXXX
+Temporary file for
+.Nm zcmp .
+.It Pa /tmp/zdiff.XXXXXXXXXX
+Temporary file for
+.Nm zdiff .
+.El
+.Sh SEE ALSO
+.Xr bzip2 1 ,
+.Xr cmp 1 ,
+.Xr diff 1 ,
+.Xr gzip 1 ,
+.Xr xz 1
+.Sh CAVEATS
+.Nm zcmp
+and
+.Nm zdiff
+rely solely on the file extension to determine what is, or is not,
+a compressed file.
+Consequently, the following are not supported as arguments:
+.Bl -dash
+.It
+directories
+.It
+device special files
+.It
+filenames indicating the standard input
+.Pq Dq \-
+.El
diff --git a/file_cmds/gzip/zforce b/file_cmds/gzip/zforce
new file mode 100644
index 0000000..243cbdf
--- /dev/null
+++ b/file_cmds/gzip/zforce
@@ -0,0 +1,55 @@
+#!/bin/sh -
+#
+# $NetBSD: zforce,v 1.2 2003/12/28 12:43:43 wiz Exp $
+# $OpenBSD: zforce,v 1.2 2003/08/05 18:22:17 deraadt Exp $
+#
+#-
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# $FreeBSD: src/usr.bin/gzip/zforce,v 1.1 2007/01/26 10:19:07 delphij Exp $
+prog=`basename $0`
+USAGE="usage: $prog file ..."
+if test $# -eq 0; then
+ echo $USAGE
+ exit 1
+fi
+
+ret=0
+
+while test $# -ne 0; do
+ case "$1" in
+ *[._-]gz)
+ shift
+ ;;
+ *.t[ag]z)
+ shift
+ ;;
+ *)
+ if file "$1" |
+ grep -q "gzip compressed data" 2> /dev/null
+ then
+ n="$1".gz
+ if mv "$1" "$n" 2> /dev/null; then
+ echo "$1" -- renamed to "$n"
+ else
+ ret=1
+ echo $prog: cannot rename "$1" to "$n"
+ fi
+ fi
+ shift
+ ;;
+ esac
+done
+exit $ret
diff --git a/file_cmds/gzip/zforce.1 b/file_cmds/gzip/zforce.1
new file mode 100644
index 0000000..933d681
--- /dev/null
+++ b/file_cmds/gzip/zforce.1
@@ -0,0 +1,53 @@
+.\" $NetBSD: zforce.1,v 1.2 2003/12/28 12:43:43 wiz Exp $
+.\" $OpenBSD: zforce.1,v 1.1 2003/07/29 11:50:09 otto Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $FreeBSD: src/usr.bin/gzip/zforce.1,v 1.1 2007/01/26 10:19:07 delphij Exp $
+.Dd January 26, 2007
+.Dt ZFORCE 1
+.Os
+.Sh NAME
+.Nm zforce
+.Nd force gzip files to have a .gz suffix
+.Sh SYNOPSIS
+.Nm zforce
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility renames
+.Xr gzip 1
+files to have a
+.Sq .gz
+suffix, so that
+.Xr gzip 1
+will not compress them twice.
+This can be useful if file names were truncated during a file transfer.
+Files that have an existing
+.Sq .gz ,
+.Sq -gz ,
+.Sq _gz ,
+.Sq .tgz
+or
+.Sq .taz
+suffix, or that have not been compressed by
+.Xr gzip 1 ,
+are ignored.
+.Sh SEE ALSO
+.Xr gzip 1
+.Sh CAVEATS
+.Nm
+overwrites existing files without warning.
diff --git a/file_cmds/gzip/zmore b/file_cmds/gzip/zmore
new file mode 100644
index 0000000..dbdbd3f
--- /dev/null
+++ b/file_cmds/gzip/zmore
@@ -0,0 +1,82 @@
+#!/bin/sh -
+#
+# $NetBSD: zmore,v 1.5 2013/12/06 13:33:15 pettai Exp $
+#
+# $OpenBSD: zmore,v 1.6 2008/08/20 09:22:02 mpf Exp $
+#
+#-
+# Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Sponsored in part by the Defense Advanced Research Projects
+# Agency (DARPA) and Air Force Research Laboratory, Air Force
+# Materiel Command, USAF, under agreement number F39502-99-1-0512.
+#
+# $FreeBSD: head/usr.bin/gzip/zmore 273507 2014-10-23 01:22:29Z delphij $
+
+# Pull out any command line flags so we can pass them to more/less
+flags=
+while test $# -ne 0; do
+ case "$1" in
+ --)
+ shift
+ break
+ ;;
+ -*)
+ flags="$flags $1"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ `basename $0` = "zless" ] ; then
+ pager=${PAGER-less}
+else
+ pager=${PAGER-more}
+fi
+
+# No files means read from stdin
+if [ $# -eq 0 ]; then
+ gzip -cdfq 2>&1 | $pager $flags
+ exit 0
+fi
+
+oterm=`stty -g 2>/dev/null`
+while test $# -ne 0; do
+ gzip -cdfq "$1" 2>&1 | $pager $flags
+ prev="$1"
+ shift
+ if tty -s && test -n "$oterm" -a $# -gt 0; then
+ #echo -n "--More--(Next file: $1)"
+ echo -n "$prev (END) - Next: $1 "
+ trap "stty $oterm 2>/dev/null" 0 1 2 3 13 15
+ stty cbreak -echo 2>/dev/null
+ REPLY=`dd bs=1 count=1 2>/dev/null`
+ stty $oterm 2>/dev/null
+ trap - 0 1 2 3 13 15
+ echo
+ case "$REPLY" in
+ s)
+ shift
+ ;;
+ e|q)
+ break
+ ;;
+ esac
+ fi
+done
+exit 0
diff --git a/file_cmds/gzip/zmore.1 b/file_cmds/gzip/zmore.1
new file mode 100644
index 0000000..4723f22
--- /dev/null
+++ b/file_cmds/gzip/zmore.1
@@ -0,0 +1,110 @@
+.\" $NetBSD: zmore.1,v 1.4 2013/11/12 21:58:37 pettai Exp $
+.\" $OpenBSD: zmore.1,v 1.10 2009/08/16 09:41:08 sobrado Exp $
+.\"
+.\" Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.\" $FreeBSD: head/usr.bin/gzip/zmore.1 273507 2014-10-23 01:22:29Z delphij $
+.Dd October 22, 2014
+.Dt ZMORE 1
+.Os
+.Sh NAME
+.Nm zmore ,
+.Nm zless
+.Nd view compressed files
+.Sh SYNOPSIS
+.Nm zmore
+.Op Ar flags
+.Op Ar
+.Nm zless
+.Op Ar flags
+.Op Ar
+.Sh DESCRIPTION
+.Nm
+is a filter that allows the viewing of files compressed with Lempel-Ziv
+encoding.
+Such files generally have a
+.Dq Z
+or
+.Dq gz
+extension (both the
+.Xr compress 1
+and
+.Xr gzip 1
+formats are supported).
+Any
+.Ar flags
+that are specified are passed to the user's preferred
+.Ev PAGER
+(which is
+.Pa /usr/bin/more
+by default).
+.Pp
+.Nm zless
+is equivalent to
+.Nm zmore
+but uses
+.Xr less 1
+as a pager instead of
+.Xr more 1 .
+.Pp
+When multiple files are specified,
+.Nm
+will pause at the end of each file and present the following prompt to the user:
+.Bd -literal -offset indent
+prev_file (END) - Next: next_file
+.Ed
+.Pp
+Where
+.Sy prev_file
+is the file that was just displayed and
+.Sy next_file
+is the next file to be displayed.
+The following keys are recognized at the prompt:
+.Bl -tag -width "e or q" -offset indent
+.It Ic e No or Ic q
+quit
+.Nm zmore .
+.It Ic s
+skip the next file (or exit if the next file is the last).
+.El
+.Pp
+If no files are specified,
+.Nm
+will read from the standard input.
+In this mode
+.Nm
+will assume
+.Xr gzip 1
+style compression since there is no suffix on which to make a decision.
+.Sh ENVIRONMENT
+.Bl -tag -width "PAGER"
+.It Ev PAGER
+Program used to display files.
+If unset,
+.Pa /usr/bin/more
+is used
+.Pq Nm zmore
+or
+.Pa /usr/bin/less
+.Pq Nm zless .
+.El
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr less 1 ,
+.Xr more 1
diff --git a/file_cmds/gzip/znew b/file_cmds/gzip/znew
new file mode 100644
index 0000000..4a84f58
--- /dev/null
+++ b/file_cmds/gzip/znew
@@ -0,0 +1,137 @@
+#!/bin/sh -
+#
+# $NetBSD: znew,v 1.3 2008/04/27 09:07:13 nakayama Exp $
+# $OpenBSD: znew,v 1.2 2003/08/05 18:22:17 deraadt Exp $
+#
+#-
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# $FreeBSD: src/usr.bin/gzip/znew,v 1.3 2008/06/30 23:53:15 delphij Exp $
+
+# Return 0 if the first arg file size is smaller than the second, 1 otherwise.
+smaller () {
+ a=`du -k "$1" | awk '{ print $1 }'`
+ b=`du -k "$2" | awk '{ print $1 }'`
+ test $a -lt $b
+}
+
+# Check gzip integrity if the -t flag is specified
+checkfile () {
+ if test $tflag -eq 1; then
+ gzip -qt < "$1"
+ fi
+}
+
+# Decompress a file and then gzip it
+process () {
+ prefix="${1%.Z}"
+ filez="$prefix".Z
+ filegz="$prefix".gz
+
+ if test ! -e "$filez"; then
+ echo "$prog: $filez does not exist"
+ return 1
+ fi
+ if test ! -f "$filez"; then
+ echo "$prog: $filez is not a regular file"
+ return 1
+ fi
+ if test -e "$filegz" -a $fflag -eq 0; then
+ echo "$prog: $filegz already exists"
+ return 1
+ fi
+
+ tmp=`mktemp /tmp/znewXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ trap 'rm -f "$tmp"; exit 1' HUP INT QUIT PIPE TERM
+
+ # Do the actual work, producing a file "$tmp"
+ if uncompress -f -c < "$filez" | gzip -f -c $gzipflags > "$tmp"; then
+ if test $kflag -eq 1 && smaller "$filez" "$tmp"; then
+ echo -n "$prog: $filez is smaller than $filegz"
+ echo "; keeping it"
+ rm -f "$tmp"
+ return 0
+ fi
+ if ! checkfile "$tmp"; then
+ echo "$prog: integrity check of $tmp failed"
+ rm -f "$tmp"
+ return 1;
+ fi
+
+ # Try to keep the mode of the original file
+ if ! cp -fp "$filez" "$filegz"; then
+ echo "$prog: warning: could not keep mode of $filez"
+ fi
+ if ! cp "$tmp" "$filegz" 2> /dev/null; then
+ echo "$prog: warning: could not keep mode of $filez"
+ if ! cp -f "$tmp" "$filegz" 2> /dev/null; then
+ echo "$prog: could not copy $tmp to $filegz"
+ rm -f "$filegz" "$tmp"
+ return 1
+ fi
+ fi
+ if ! touch -fr "$filez" "$filegz"; then
+ echo -n "$prog: warning: could not keep timestamp of "
+ echo "$filez"
+ fi
+ rm -f "$filez" "$tmp"
+ else
+ echo "$prog: failed to process $filez"
+ rm -f "$tmp"
+ return 1
+ fi
+}
+
+prog=`basename "$0"`
+usage="usage: $prog [-ftv9K] file ..."
+
+fflag=0
+tflag=0
+kflag=0
+gzipflags=
+
+# -P flag is recognized to maintain compatibility, but ignored. Pipe mode is
+# always used
+while getopts :ftv9PK i; do
+ case $i in
+ f) fflag=1;;
+ t) tflag=1;;
+ v) gzipflags="-v $gzipflags";;
+ 9) gzipflags="-9 $gzipflags";;
+ P) ;;
+ K) kflag=1;;
+ \?) echo "$usage"; exit 1;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+if test $# -eq 0; then
+ echo "$usage"
+ exit 1
+fi
+
+rc=0
+
+while test $# -ne 0; do
+ if ! process "$1"; then
+ rc=$?
+ fi
+ shift
+done
+exit $rc
diff --git a/file_cmds/gzip/znew.1 b/file_cmds/gzip/znew.1
new file mode 100644
index 0000000..61060ca
--- /dev/null
+++ b/file_cmds/gzip/znew.1
@@ -0,0 +1,71 @@
+.\" $NetBSD: znew.1,v 1.2 2003/12/28 12:43:43 wiz Exp $
+.\" $OpenBSD: znew.1,v 1.1 2003/08/02 20:52:50 otto Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $FreeBSD: src/usr.bin/gzip/znew.1,v 1.1 2007/01/26 10:19:07 delphij Exp $
+.Dd January 26, 2007
+.Dt ZNEW 1
+.Os
+.Sh NAME
+.Nm znew
+.Nd convert compressed files to gzipped files
+.Sh SYNOPSIS
+.Nm
+.Op Fl ftv9K
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility uncompresses files compressed by
+.Xr compress 1
+and recompresses them with
+.Xr gzip 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f
+Overwrite existing
+.Sq .gz
+files.
+Unless this option is specified,
+.Nm
+refuses to overwrite existing files.
+.It Fl t
+Test integrity of the gzipped file before deleting the original file.
+If the integrity check fails, the original
+.Sq .Z
+file is not removed.
+.It Fl v
+Print a report specifying the achieved compression ratios.
+.It Fl 9
+Use the -9 mode of
+.Xr gzip 1 ,
+achieving better compression at the cost of slower execution.
+.It Fl K
+Keep the original
+.Sq .Z
+file if it uses less disk blocks than the gzipped one.
+A disk block is 1024 bytes.
+.El
+.Sh SEE ALSO
+.Xr gzip 1
+.Sh CAVEATS
+The
+.Nm
+utility tries to maintain the file mode of the original file.
+If the original file is not writable, it is not able to do that and
+.Nm
+will print a warning.
diff --git a/file_cmds/gzip/zuncompress.c b/file_cmds/gzip/zuncompress.c
new file mode 100644
index 0000000..06df597
--- /dev/null
+++ b/file_cmds/gzip/zuncompress.c
@@ -0,0 +1,396 @@
+/* $NetBSD: zuncompress.c,v 1.11 2011/08/16 13:55:02 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: NetBSD: zopen.c,v 1.8 2003/08/07 11:13:29 agc Exp
+ * $FreeBSD: src/usr.bin/gzip/zuncompress.c,v 1.7 2012/10/19 14:49:42 ed Exp $
+ */
+
+/* This file is #included by gzip.c */
+
+static int zread(void *, char *, int);
+
+#define tab_prefixof(i) (zs->zs_codetab[i])
+#define tab_suffixof(i) ((char_type *)(zs->zs_htab))[i]
+#define de_stack ((char_type *)&tab_suffixof(1 << BITS))
+
+#define BITS 16 /* Default bits. */
+#define HSIZE 69001 /* 95% occupancy */ /* XXX may not need HSIZE */
+#define BIT_MASK 0x1f /* Defines for third byte of header. */
+#define BLOCK_MASK 0x80
+#define CHECK_GAP 10000 /* Ratio check interval. */
+#define BUFSIZE (64 * 1024)
+
+/*
+ * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
+ * a fourth header byte (for expansion).
+ */
+#define INIT_BITS 9 /* Initial number of bits/code. */
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+
+#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
+
+typedef long code_int;
+typedef long count_int;
+typedef u_char char_type;
+
+static char_type magic_header[] =
+ {'\037', '\235'}; /* 1F 9D */
+
+static char_type rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static off_t total_compressed_bytes;
+static size_t compressed_prelen;
+static char *compressed_pre;
+
+struct s_zstate {
+ FILE *zs_fp; /* File stream for I/O */
+ char zs_mode; /* r or w */
+ 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. */
+ code_int zs_maxcode; /* Maximum code, given n_bits. */
+ code_int zs_maxmaxcode; /* Should NEVER generate this code. */
+ count_int zs_htab [HSIZE];
+ u_short zs_codetab [HSIZE];
+ code_int zs_hsize; /* For dynamic table sizing. */
+ code_int zs_free_ent; /* First unused entry. */
+ /*
+ * Block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+ int zs_block_compress;
+ int zs_clear_flg;
+ long zs_ratio;
+ count_int zs_checkpoint;
+ 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). */
+ char_type zs_buf[BITS];
+ union {
+ struct {
+ long zs_fcode;
+ code_int zs_ent;
+ code_int zs_hsize_reg;
+ int zs_hshift;
+ } w; /* Write parameters */
+ struct {
+ char_type *zs_stackp;
+ int zs_finchar;
+ code_int zs_code, zs_oldcode, zs_incode;
+ int zs_roffset, zs_size;
+ char_type zs_gbuf[BITS];
+ } r; /* Read parameters */
+ } u;
+};
+
+static code_int getcode(struct s_zstate *zs);
+
+static off_t
+zuncompress(FILE *in, FILE *out, char *pre, size_t prelen,
+ off_t *compressed_bytes)
+{
+ off_t bin, bout = 0;
+ char *buf;
+
+ buf = malloc(BUFSIZE);
+ if (buf == NULL)
+ return -1;
+
+ /* XXX */
+ compressed_prelen = prelen;
+ if (prelen != 0)
+ compressed_pre = pre;
+ else
+ compressed_pre = NULL;
+
+ while ((bin = fread(buf, 1, BUFSIZE, in)) != 0) {
+ if (tflag == 0 && (off_t)fwrite(buf, 1, bin, out) != bin) {
+ free(buf);
+ return -1;
+ }
+ bout += bin;
+ }
+
+ if (compressed_bytes)
+ *compressed_bytes = total_compressed_bytes;
+
+ free(buf);
+ return bout;
+}
+
+static int
+zclose(void *zs)
+{
+ free(zs);
+ /* We leave the caller to close the fd passed to zdopen() */
+ return 0;
+}
+
+FILE *
+zdopen(int fd)
+{
+ struct s_zstate *zs;
+
+ if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL)
+ return (NULL);
+
+ zs->zs_state = S_START;
+
+ /* XXX we can get rid of some of these */
+ zs->zs_hsize = HSIZE; /* For dynamic table sizing. */
+ zs->zs_free_ent = 0; /* First unused entry. */
+ zs->zs_block_compress = BLOCK_MASK;
+ zs->zs_clear_flg = 0; /* XXX we calloc()'d this structure why = 0? */
+ zs->zs_ratio = 0;
+ zs->zs_checkpoint = CHECK_GAP;
+ zs->zs_in_count = 1; /* Length of input. */
+ zs->zs_out_count = 0; /* # of codes output (for debugging). */
+ zs->u.r.zs_roffset = 0;
+ zs->u.r.zs_size = 0;
+
+ /*
+ * Layering compress on top of stdio in order to provide buffering,
+ * and ensure that reads and write work with the data specified.
+ */
+ if ((zs->zs_fp = fdopen(fd, "r")) == NULL) {
+ free(zs);
+ return NULL;
+ }
+
+ return funopen(zs, zread, NULL, NULL, zclose);
+}
+
+/*
+ * Decompress read. This routine adapts to the codes in the file building
+ * the "string" table on-the-fly; requiring no table to be stored in the
+ * compressed file. The tables used herein are shared with those of the
+ * compress() routine. See the definitions above.
+ */
+static int
+zread(void *cookie, char *rbp, int num)
+{
+ u_int count, i;
+ struct s_zstate *zs;
+ u_char *bp, header[3];
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)rbp;
+ switch (zs->zs_state) {
+ case S_START:
+ zs->zs_state = S_MIDDLE;
+ break;
+ case S_MIDDLE:
+ goto middle;
+ case S_EOF:
+ goto eof;
+ }
+
+ /* Check the magic number */
+ for (i = 0; i < 3 && compressed_prelen; i++, compressed_prelen--)
+ header[i] = *compressed_pre++;
+
+ if (fread(header + i, 1, sizeof(header) - i, zs->zs_fp) !=
+ sizeof(header) - i ||
+ memcmp(header, magic_header, sizeof(magic_header)) != 0) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ total_compressed_bytes = 0;
+ zs->zs_maxbits = header[2]; /* Set -b from file. */
+ zs->zs_block_compress = zs->zs_maxbits & BLOCK_MASK;
+ zs->zs_maxbits &= BIT_MASK;
+ zs->zs_maxmaxcode = 1L << zs->zs_maxbits;
+ if (zs->zs_maxbits > BITS || zs->zs_maxbits < 12) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ /* As above, initialize the first 256 entries in the table. */
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS);
+ for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0; zs->u.r.zs_code--) {
+ tab_prefixof(zs->u.r.zs_code) = 0;
+ tab_suffixof(zs->u.r.zs_code) = (char_type) zs->u.r.zs_code;
+ }
+ zs->zs_free_ent = zs->zs_block_compress ? FIRST : 256;
+
+ zs->u.r.zs_oldcode = -1;
+ zs->u.r.zs_stackp = de_stack;
+
+ while ((zs->u.r.zs_code = getcode(zs)) > -1) {
+
+ if ((zs->u.r.zs_code == CLEAR) && zs->zs_block_compress) {
+ for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0;
+ zs->u.r.zs_code--)
+ tab_prefixof(zs->u.r.zs_code) = 0;
+ zs->zs_clear_flg = 1;
+ zs->zs_free_ent = FIRST;
+ zs->u.r.zs_oldcode = -1;
+ continue;
+ }
+ zs->u.r.zs_incode = zs->u.r.zs_code;
+
+ /* Special case for KwKwK string. */
+ if (zs->u.r.zs_code >= zs->zs_free_ent) {
+ if (zs->u.r.zs_code > zs->zs_free_ent ||
+ zs->u.r.zs_oldcode == -1) {
+ /* Bad stream. */
+ errno = EINVAL;
+ return (-1);
+ }
+ *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar;
+ zs->u.r.zs_code = zs->u.r.zs_oldcode;
+ }
+ /*
+ * The above condition ensures that code < free_ent.
+ * The construction of tab_prefixof in turn guarantees that
+ * each iteration decreases code and therefore stack usage is
+ * bound by 1 << BITS - 256.
+ */
+
+ /* Generate output characters in reverse order. */
+ while (zs->u.r.zs_code >= 256) {
+ *zs->u.r.zs_stackp++ = tab_suffixof(zs->u.r.zs_code);
+ zs->u.r.zs_code = tab_prefixof(zs->u.r.zs_code);
+ }
+ *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar = tab_suffixof(zs->u.r.zs_code);
+
+ /* And put them out in forward order. */
+middle: do {
+ if (count-- == 0)
+ return (num);
+ *bp++ = *--zs->u.r.zs_stackp;
+ } while (zs->u.r.zs_stackp > de_stack);
+
+ /* Generate the new entry. */
+ if ((zs->u.r.zs_code = zs->zs_free_ent) < zs->zs_maxmaxcode &&
+ zs->u.r.zs_oldcode != -1) {
+ tab_prefixof(zs->u.r.zs_code) = (u_short) zs->u.r.zs_oldcode;
+ tab_suffixof(zs->u.r.zs_code) = zs->u.r.zs_finchar;
+ zs->zs_free_ent = zs->u.r.zs_code + 1;
+ }
+
+ /* Remember previous code. */
+ zs->u.r.zs_oldcode = zs->u.r.zs_incode;
+ }
+ zs->zs_state = S_EOF;
+eof: return (num - count);
+}
+
+/*-
+ * Read one code from the standard input. If EOF, return -1.
+ * Inputs:
+ * stdin
+ * Outputs:
+ * code or -1 is returned.
+ */
+static code_int
+getcode(struct s_zstate *zs)
+{
+ code_int gcode;
+ int r_off, bits, i;
+ char_type *bp;
+
+ bp = zs->u.r.zs_gbuf;
+ if (zs->zs_clear_flg > 0 || zs->u.r.zs_roffset >= zs->u.r.zs_size ||
+ zs->zs_free_ent > zs->zs_maxcode) {
+ /*
+ * If the next entry will be too big for the current gcode
+ * size, then we must increase the size. This implies reading
+ * a new buffer full, too.
+ */
+ if (zs->zs_free_ent > zs->zs_maxcode) {
+ zs->zs_n_bits++;
+ if (zs->zs_n_bits == zs->zs_maxbits) /* Won't get any bigger now. */
+ zs->zs_maxcode = zs->zs_maxmaxcode;
+ else
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits);
+ }
+ if (zs->zs_clear_flg > 0) {
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS);
+ zs->zs_clear_flg = 0;
+ }
+ /* XXX */
+ for (i = 0; i < zs->zs_n_bits && compressed_prelen; i++, compressed_prelen--)
+ zs->u.r.zs_gbuf[i] = *compressed_pre++;
+ zs->u.r.zs_size = fread(zs->u.r.zs_gbuf + i, 1, zs->zs_n_bits - i, zs->zs_fp);
+ zs->u.r.zs_size += i;
+ if (zs->u.r.zs_size <= 0) /* End of file. */
+ return (-1);
+ zs->u.r.zs_roffset = 0;
+
+ total_compressed_bytes += zs->u.r.zs_size;
+
+ /* Round size down to integral number of codes. */
+ zs->u.r.zs_size = (zs->u.r.zs_size << 3) - (zs->zs_n_bits - 1);
+ }
+ r_off = zs->u.r.zs_roffset;
+ bits = zs->zs_n_bits;
+
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+
+ /* Get first part (low order bits). */
+ gcode = (*bp++ >> r_off);
+ bits -= (8 - r_off);
+ r_off = 8 - r_off; /* Now, roffset into gcode word. */
+
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ gcode |= *bp++ << r_off;
+ r_off += 8;
+ bits -= 8;
+ }
+
+ /* High order bits. */
+ gcode |= (*bp & rmask[bits]) << r_off;
+ zs->u.r.zs_roffset += zs->zs_n_bits;
+
+ return (gcode);
+}
+
diff --git a/file_cmds/install/install.1 b/file_cmds/install/install.1
new file mode 100644
index 0000000..76f3697
--- /dev/null
+++ b/file_cmds/install/install.1
@@ -0,0 +1,253 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)install.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD: src/usr.bin/xinstall/install.1,v 1.21 2001/05/30 09:45:47 ru Exp $
+.\"
+.Dd May 7, 2001
+.Dt INSTALL 1
+.Os
+.Sh NAME
+.Nm install
+.Nd install binaries
+.Sh SYNOPSIS
+.Nm install
+.Op Fl bCcMpSsv
+.Op Fl B Ar suffix
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar file1 file2
+.Nm install
+.Op Fl bCcMpSsv
+.Op Fl B Ar suffix
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar file1 ... fileN directory
+.Nm install
+.Fl d
+.Op Fl v
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar directory ...
+.Sh DESCRIPTION
+The file(s) are copied
+to the target file or directory.
+If the destination is a directory, then the
+.Ar file
+is copied into
+.Ar directory
+with its original filename.
+If the target file already exists, it is
+either renamed to
+.Ar file Ns Pa .old
+if the
+.Fl b
+option is given
+or overwritten
+if permissions allow.
+An alternate backup suffix may be specified via the
+.Fl B
+option's argument.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.\" ==========
+.It Fl B Ar suffix
+Use
+.Ar suffix
+as the backup suffix if
+.Fl b
+is given.
+.\" ==========
+.It Fl b
+Back up any existing files before overwriting them by renaming
+them to
+.Ar file Ns Pa .old .
+See
+.Fl B
+for specifying a different backup suffix.
+.\" ==========
+.It Fl C
+Copy the file.
+If the target file already exists and the files are the same,
+then don't change the modification time of the target.
+.\" ==========
+.It Fl c
+Copy the file.
+This is actually the default.
+The
+.Fl c
+option is only included for backwards compatibility.
+.\" ==========
+.It Fl d
+Create directories.
+Missing parent directories are created as required.
+.\" ==========
+.It Fl f
+Specify the target's file flags; see
+.Xr chflags 1
+for a list of possible flags and their meanings.
+.\" ==========
+.It Fl g
+Specify a group.
+A numeric GID is allowed.
+.\" ==========
+.It Fl M
+Disable all use of
+.Xr mmap 2 .
+.\" ==========
+.It Fl m
+Specify an alternate mode.
+The default mode is set to rwxr-xr-x (0755).
+The specified mode may be either an octal or symbolic value; see
+.Xr chmod 1
+for a description of possible mode values.
+.\" ==========
+.It Fl o
+Specify an owner.
+A numeric UID is allowed.
+.\" ==========
+.It Fl p
+Preserve the modification time.
+Copy the file, as if the
+.Fl C
+(compare and copy) option is specified,
+except if the target file doesn't already exist or is different,
+then preserve the modification time of the file.
+.\" ==========
+.It Fl S
+Safe copy.
+Normally,
+.Nm install
+unlinks an existing target before installing the new file.
+With the
+.Fl S
+flag a temporary file is used and then renamed to be
+the target.
+The reason this is safer is that if the copy or
+rename fails, the existing target is left untouched.
+.\" ==========
+.It Fl s
+.Nm install
+exec's the command
+.Xr strip 1
+to strip binaries so that
+.Nm install
+can be portable over a large
+number of systems and binary types.
+.\" ==========
+.It Fl v
+Causes
+.Nm install
+to show when
+.Fl C
+actually installs something.
+.El
+.Pp
+By default,
+.Nm install
+preserves all file flags, with the exception of the
+.Dq nodump
+flag.
+.Pp
+The
+.Nm install
+utility attempts to prevent moving a file onto itself.
+.Pp
+Installing
+.Pa /dev/null
+creates an empty file.
+.Sh DIAGNOSTICS
+The
+.Nm install
+utility exits 0 on success, and 1 otherwise.
+.Sh FILES
+.Bl -tag -width INS@XXXX -compact
+.It Pa INS@XXXX
+If either
+.Fl S
+option is specified, or the
+.Fl C
+or
+.Fl p
+option is used in conjuction with the
+.Fl s
+option, temporary files named
+.Pa INS@XXXX ,
+where
+.Pa XXXX
+is decided by
+.Xr mkstemp 3 ,
+are created in the target directory.
+.El
+.Sh COMPATIBILITY
+Historically
+.Nm install
+moved files by default.
+The default was changed to copy in
+.Fx 4.4 .
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr mv 1 ,
+.Xr strip 1 ,
+.Xr mmap 2 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm install
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+Temporary files may be left in the target directory if
+.Nm install
+exits abnormally.
+.Pp
+File flags cannot be set by
+.Xr fchflags 2
+over a NFS file system. Other file systems do not have a concept of flags.
+.Nm install
+will only warn when flags could not be set on a file system
+that does not support them.
+.Pp
+.Nm install
+with
+.Fl v
+falsely says a file is copied when
+.Fl C
+snaps hard links.
diff --git a/file_cmds/install/pathnames.h b/file_cmds/install/pathnames.h
new file mode 100644
index 0000000..5099eb0
--- /dev/null
+++ b/file_cmds/install/pathnames.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _INSTALL_PATHNAMES_H_
+#define _INSTALL_PATHNAMES_H_
+
+#define _PATH_STRIP "/usr/bin/strip"
+
+#endif /* _INSTALL_PATHNAMES_H_ */
diff --git a/file_cmds/install/xinstall.c b/file_cmds/install/xinstall.c
new file mode 100644
index 0000000..5e6bcec
--- /dev/null
+++ b/file_cmds/install/xinstall.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";
+#endif
+__used static const char rcsid[] =
+ "$FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.43 2001/05/30 07:08:49 ru Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <utime.h>
+#include <spawn.h>
+
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#include <copyfile.h>
+#endif /* __APPLE__ */
+
+#include "pathnames.h"
+
+/* Bootstrap aid - this doesn't exist in most older releases */
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */
+#endif
+
+#define DIRECTORY 0x01 /* Tell install it's a directory. */
+#define SETFLAGS 0x02 /* Tell install to set flags. */
+#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
+#define BACKUP_SUFFIX ".old"
+
+struct passwd *pp;
+struct group *gp;
+gid_t gid;
+uid_t uid;
+int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose;
+mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+char *suffix = BACKUP_SUFFIX;
+
+void copy __P((int, char *, int, char *, off_t));
+int compare __P((int, const char *, size_t, int, const char *, size_t));
+int create_newfile __P((char *, int, struct stat *));
+int create_tempfile __P((char *, char *, size_t));
+void install __P((char *, char *, u_long, u_int));
+void install_dir __P((char *));
+u_long numeric_id __P((char *, char *));
+void strip __P((char *));
+int trymmap __P((int));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat from_sb, to_sb;
+ mode_t *set;
+ u_long fset = 0;
+ int ch, no_target;
+ u_int iflags;
+ char *flags, *group, *owner, *to_name;
+
+ iflags = 0;
+ group = owner = NULL;
+ while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1)
+ switch((char)ch) {
+ case 'B':
+ suffix = optarg;
+ /* FALLTHROUGH */
+ case 'b':
+ dobackup = 1;
+ break;
+ case 'C':
+ docompare = 1;
+ break;
+ case 'c':
+ /* For backwards compatibility. */
+ break;
+ case 'd':
+ dodir = 1;
+ break;
+ case 'f':
+ flags = optarg;
+ if (strtofflags(&flags, &fset, NULL))
+ errx(EX_USAGE, "%s: invalid flag", flags);
+ iflags |= SETFLAGS;
+ break;
+ case 'g':
+ group = optarg;
+ break;
+ case 'M':
+ nommap = 1;
+ break;
+ case 'm':
+ if (!(set = setmode(optarg)))
+ errx(EX_USAGE, "invalid file mode: %s",
+ optarg);
+ mode = getmode(set, 0);
+ free(set);
+ break;
+ case 'o':
+ owner = optarg;
+ break;
+ case 'p':
+ docompare = dopreserve = 1;
+ break;
+ case 'S':
+ safecopy = 1;
+ break;
+ case 's':
+ dostrip = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* some options make no sense when creating directories */
+ if ((safecopy || dostrip) && dodir)
+ usage();
+
+ /*
+ * Older versions allowed -d -C combo. Issue a warning
+ * for now, but turn this into an error before 4.5-RELEASE.
+ */
+ if (docompare && dodir)
+ warnx("the -d and -C options may not be specified together");
+
+ /* must have at least two arguments, except when creating directories */
+ if (argc < 2 && !dodir)
+ usage();
+
+ /* need to make a temp copy so we can compare stripped version */
+ if (docompare && dostrip)
+ safecopy = 1;
+
+ /* get group and owner id's */
+ if (group != NULL) {
+ if ((gp = getgrnam(group)) != NULL)
+ gid = gp->gr_gid;
+ else
+ gid = (uid_t)numeric_id(group, "group");
+ } else
+ gid = (gid_t)-1;
+
+ if (owner != NULL) {
+ if ((pp = getpwnam(owner)) != NULL)
+ uid = pp->pw_uid;
+ else
+ uid = (uid_t)numeric_id(owner, "user");
+ } else
+ uid = (uid_t)-1;
+
+ if (dodir) {
+ for (; *argv != NULL; ++argv)
+ install_dir(*argv);
+ exit(EX_OK);
+ /* NOTREACHED */
+ }
+
+ no_target = stat(to_name = argv[argc - 1], &to_sb);
+ if (!no_target && S_ISDIR(to_sb.st_mode)) {
+ for (; *argv != to_name; ++argv)
+ install(*argv, to_name, fset, iflags | DIRECTORY);
+ exit(EX_OK);
+ /* NOTREACHED */
+ }
+
+ /* can't do file1 file2 directory/file */
+ if (argc != 2)
+ usage();
+
+ if (!no_target) {
+ if (stat(*argv, &from_sb))
+ err(EX_OSERR, "%s", *argv);
+ if (!S_ISREG(to_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (to_sb.st_dev == from_sb.st_dev &&
+ to_sb.st_ino == from_sb.st_ino)
+ errx(EX_USAGE,
+ "%s and %s are the same file", *argv, to_name);
+ }
+ install(*argv, to_name, fset, iflags);
+ exit(EX_OK);
+ /* NOTREACHED */
+}
+
+u_long
+numeric_id(name, type)
+ char *name, *type;
+{
+ u_long val;
+ char *ep;
+
+ /*
+ * XXX
+ * We know that uid_t's and gid_t's are unsigned longs.
+ */
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno)
+ err(EX_NOUSER, "%s", name);
+ if (*ep != '\0')
+ errx(EX_NOUSER, "unknown %s %s", type, name);
+ return (val);
+}
+
+/*
+ * install --
+ * build a path name and install the file
+ */
+void
+install(from_name, to_name, fset, flags)
+ char *from_name, *to_name;
+ u_long fset;
+ u_int flags;
+{
+ struct stat from_sb, temp_sb, to_sb;
+ struct utimbuf utb;
+ int devnull, files_match, from_fd=0, serrno, target;
+ int tempcopy, temp_fd, to_fd=0;
+ char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN];
+
+ files_match = 0;
+
+ /* If try to install NULL file to a directory, fails. */
+ if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
+ if (stat(from_name, &from_sb))
+ err(EX_OSERR, "%s", from_name);
+ if (!S_ISREG(from_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", from_name);
+ }
+ /* Build the target path. */
+ if (flags & DIRECTORY) {
+ (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+ to_name,
+ (p = strrchr(from_name, '/')) ? ++p : from_name);
+ to_name = pathbuf;
+ }
+ devnull = 0;
+ } else {
+ devnull = 1;
+ }
+
+ target = stat(to_name, &to_sb) == 0;
+
+ /* Only install to regular files. */
+ if (target && !S_ISREG(to_sb.st_mode)) {
+ errno = EFTYPE;
+ warn("%s", to_name);
+ return;
+ }
+
+ /* Only copy safe if the target exists. */
+ tempcopy = safecopy && target;
+
+ if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0)
+ err(EX_OSERR, "%s", from_name);
+
+ /* If we don't strip, we can compare first. */
+ if (docompare && !dostrip && target) {
+ if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+ err(EX_OSERR, "%s", to_name);
+ if (devnull)
+ files_match = to_sb.st_size == 0;
+ else
+ files_match = !(compare(from_fd, from_name,
+ (size_t)from_sb.st_size, to_fd,
+ to_name, (size_t)to_sb.st_size));
+
+ /* Close "to" file unless we match. */
+ if (!files_match)
+ (void)close(to_fd);
+ }
+
+ if (!files_match) {
+ if (tempcopy) {
+ to_fd = create_tempfile(to_name, tempfile,
+ sizeof(tempfile));
+ if (to_fd < 0)
+ err(EX_OSERR, "%s", tempfile);
+ } else {
+ if ((to_fd = create_newfile(to_name, target,
+ &to_sb)) < 0)
+ err(EX_OSERR, "%s", to_name);
+ if (verbose)
+ (void)printf("install: %s -> %s\n",
+ from_name, to_name);
+ }
+ if (!devnull)
+ copy(from_fd, from_name, to_fd,
+ tempcopy ? tempfile : to_name, from_sb.st_size);
+ }
+
+ if (dostrip) {
+ strip(tempcopy ? tempfile : to_name);
+
+ /*
+ * Re-open our fd on the target, in case we used a strip
+ * that does not work in-place -- like GNU binutils strip.
+ */
+ close(to_fd);
+ to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0);
+ if (to_fd < 0)
+ err(EX_OSERR, "stripping %s", to_name);
+ }
+
+ /*
+ * Compare the stripped temp file with the target.
+ */
+ if (docompare && dostrip && target) {
+ temp_fd = to_fd;
+
+ /* Re-open to_fd using the real target name. */
+ if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+ err(EX_OSERR, "%s", to_name);
+
+ if (fstat(temp_fd, &temp_sb)) {
+ serrno = errno;
+ (void)unlink(tempfile);
+ errno = serrno;
+ err(EX_OSERR, "%s", tempfile);
+ }
+
+ if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd,
+ to_name, (size_t)to_sb.st_size) == 0) {
+ /*
+ * If target has more than one link we need to
+ * replace it in order to snap the extra links.
+ * Need to preserve target file times, though.
+ */
+ if (to_sb.st_nlink != 1) {
+ utb.actime = to_sb.st_atime;
+ utb.modtime = to_sb.st_mtime;
+ (void)utime(tempfile, &utb);
+ } else {
+ files_match = 1;
+ (void)unlink(tempfile);
+ }
+ (void) close(temp_fd);
+ }
+ }
+
+ /*
+ * Move the new file into place if doing a safe copy
+ * and the files are different (or just not compared).
+ */
+ if (tempcopy && !files_match) {
+ /* Try to turn off the immutable bits. */
+ if (to_sb.st_flags & NOCHANGEBITS)
+ (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
+ if (dobackup) {
+ if (snprintf(backup, MAXPATHLEN, "%s%s", to_name,
+ suffix) != strlen(to_name) + strlen(suffix)) {
+ unlink(tempfile);
+ errx(EX_OSERR, "%s: backup filename too long",
+ to_name);
+ }
+ if (verbose)
+ (void)printf("install: %s -> %s\n", to_name, backup);
+ if (rename(to_name, backup) < 0) {
+ serrno = errno;
+ unlink(tempfile);
+ errno = serrno;
+ err(EX_OSERR, "rename: %s to %s", to_name,
+ backup);
+ }
+ }
+ if (verbose)
+ (void)printf("install: %s -> %s\n", from_name, to_name);
+ if (rename(tempfile, to_name) < 0) {
+ serrno = errno;
+ unlink(tempfile);
+ errno = serrno;
+ err(EX_OSERR, "rename: %s to %s",
+ tempfile, to_name);
+ }
+
+ /* Re-open to_fd so we aren't hosed by the rename(2). */
+ (void) close(to_fd);
+ if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)
+ err(EX_OSERR, "%s", to_name);
+ }
+
+#ifdef __APPLE__
+ /* in case mtime is modified */
+ if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) &&
+ fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0) {
+ warn("%s: unable to copy extended attributes from %s", to_name, from_name);
+ }
+#endif /* __APPLE__ */
+ /*
+ * Preserve the timestamp of the source file if necessary.
+ */
+ if (dopreserve && !files_match && !devnull) {
+ utb.actime = from_sb.st_atime;
+ utb.modtime = from_sb.st_mtime;
+ (void)utime(to_name, &utb);
+ }
+
+ if (fstat(to_fd, &to_sb) == -1) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s", to_name);
+ }
+
+ /*
+ * Set owner, group, mode for target; do the chown first,
+ * chown may lose the setuid bits.
+ */
+ if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
+ (uid != (uid_t)-1 && uid != to_sb.st_uid) ||
+ (mode != to_sb.st_mode)) {
+ /* Try to turn off the immutable bits. */
+ if (to_sb.st_flags & NOCHANGEBITS)
+ (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS);
+ }
+
+ if ((gid != (gid_t)-1 && gid != to_sb.st_gid) ||
+ (uid != (uid_t)-1 && uid != to_sb.st_uid))
+ if (fchown(to_fd, uid, gid) == -1) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR,"%s: chown/chgrp", to_name);
+ }
+
+ if (mode != to_sb.st_mode)
+ if (fchmod(to_fd, mode)) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s: chmod", to_name);
+ }
+
+ /*
+ * If provided a set of flags, set them, otherwise, preserve the
+ * flags, except for the dump flag.
+ * NFS does not support flags. Ignore ENOTSUP flags if we're just
+ * trying to turn off UF_NODUMP. If we're trying to set real flags,
+ * then warn if the the fs doesn't support it, otherwise fail.
+ */
+ if (!devnull && fchflags(to_fd,
+ flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
+ if (flags & SETFLAGS) {
+ if (errno == ENOTSUP)
+ warn("%s: chflags", to_name);
+ else {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s: chflags", to_name);
+ }
+ }
+ }
+#ifdef __APPLE__
+ /* the ACL could prevent credential/permission system calls later on... */
+ if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) &&
+ (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0)) {
+ warn("%s: unable to copy ACL from %s", to_name, from_name);
+ }
+#endif /* __APPLE__ */
+
+ (void)close(to_fd);
+ if (!devnull)
+ (void)close(from_fd);
+}
+
+/*
+ * compare --
+ * compare two files; non-zero means files differ
+ */
+int
+compare(int from_fd, const char *from_name, size_t from_len,
+ int to_fd, const char *to_name, size_t to_len)
+{
+ char *p, *q;
+ int rv;
+ int done_compare;
+
+ rv = 0;
+ if (from_len != to_len)
+ return 1;
+
+ if (from_len <= 8 * 1024 * 1024) {
+ done_compare = 0;
+ if (trymmap(from_fd) && trymmap(to_fd)) {
+ p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+ if (p == (char *)MAP_FAILED)
+ goto out;
+ q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+ if (q == (char *)MAP_FAILED) {
+ munmap(p, from_len);
+ goto out;
+ }
+
+ rv = memcmp(p, q, from_len);
+ munmap(p, from_len);
+ munmap(q, from_len);
+ done_compare = 1;
+ }
+ out:
+ if (!done_compare) {
+ char buf1[MAXBSIZE];
+ char buf2[MAXBSIZE];
+ int n1, n2;
+
+ rv = 0;
+ lseek(from_fd, 0, SEEK_SET);
+ lseek(to_fd, 0, SEEK_SET);
+ while (rv == 0) {
+ n1 = read(from_fd, buf1, sizeof(buf1));
+ if (n1 == 0)
+ break; /* EOF */
+ else if (n1 > 0) {
+ n2 = read(to_fd, buf2, n1);
+ if (n2 == n1)
+ rv = memcmp(buf1, buf2, n1);
+ else
+ rv = 1; /* out of sync */
+ } else
+ rv = 1; /* read failure */
+ }
+ lseek(from_fd, 0, SEEK_SET);
+ lseek(to_fd, 0, SEEK_SET);
+ }
+ } else
+ rv = 1; /* don't bother in this case */
+
+ return rv;
+}
+
+/*
+ * create_tempfile --
+ * create a temporary file based on path and open it
+ */
+int
+create_tempfile(path, temp, tsize)
+ char *path;
+ char *temp;
+ size_t tsize;
+{
+ char *p;
+
+ (void)strncpy(temp, path, tsize);
+ temp[tsize - 1] = '\0';
+ if ((p = strrchr(temp, '/')) != NULL)
+ p++;
+ else
+ p = temp;
+ (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p);
+ temp[tsize - 1] = '\0';
+ return (mkstemp(temp));
+}
+
+/*
+ * create_newfile --
+ * create a new file, overwriting an existing one if necessary
+ */
+int
+create_newfile(path, target, sbp)
+ char *path;
+ int target;
+ struct stat *sbp;
+{
+ char backup[MAXPATHLEN];
+
+ if (target) {
+ /*
+ * Unlink now... avoid ETXTBSY errors later. Try to turn
+ * off the append/immutable bits -- if we fail, go ahead,
+ * it might work.
+ */
+ if (sbp->st_flags & NOCHANGEBITS)
+ (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS);
+
+ if (dobackup) {
+ if (snprintf(backup, MAXPATHLEN, "%s%s",
+ path, suffix) != strlen(path) + strlen(suffix))
+ errx(EX_OSERR, "%s: backup filename too long",
+ path);
+ (void)snprintf(backup, MAXPATHLEN, "%s%s",
+ path, suffix);
+ if (verbose)
+ (void)printf("install: %s -> %s\n",
+ path, backup);
+ if (rename(path, backup) < 0)
+ err(EX_OSERR, "rename: %s to %s", path, backup);
+ } else
+ (void)unlink(path);
+ }
+
+ return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
+}
+
+/*
+ * copy --
+ * copy from one file to another
+ */
+void
+copy(from_fd, from_name, to_fd, to_name, size)
+ register int from_fd, to_fd;
+ char *from_name, *to_name;
+ off_t size;
+{
+ register int nr, nw;
+ int serrno;
+ char *p;
+ char buf[MAXBSIZE];
+ int done_copy;
+
+ /* Rewind file descriptors. */
+ if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)
+ err(EX_OSERR, "lseek: %s", from_name);
+ if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)
+ err(EX_OSERR, "lseek: %s", to_name);
+
+ /*
+ * Mmap and write if less than 8M (the limit is so we don't totally
+ * trash memory on big files. This is really a minor hack, but it
+ * wins some CPU back.
+ */
+ done_copy = 0;
+ if (size <= 8 * 1048576 && trymmap(from_fd) &&
+ (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED,
+ from_fd, (off_t)0)) != (char *)MAP_FAILED) {
+ if ((nw = write(to_fd, p, size)) != size) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = nw > 0 ? EIO : serrno;
+ err(EX_OSERR, "%s", to_name);
+ }
+ done_copy = 1;
+ }
+ if (!done_copy) {
+ while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
+ if ((nw = write(to_fd, buf, nr)) != nr) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = nw > 0 ? EIO : serrno;
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (nr != 0) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s", from_name);
+ }
+ }
+}
+
+/*
+ * strip --
+ * use strip(1) to strip the target file
+ */
+void
+strip(to_name)
+ char *to_name;
+{
+ pid_t pid;
+ int error;
+ extern char** environ;
+ char *const argv[] = { "xcrun", "strip", "-", to_name, NULL };
+
+ if (0 == (error = posix_spawnp(&pid, "xcrun", NULL, NULL, argv, environ))) {
+ int status = 0;
+ pid_t child = waitpid(pid, &status, 0);
+ if ((child == -1) || status) {
+ unlink(to_name);
+ errx(EX_SOFTWARE, "child process failed: xcrun strip - %s", to_name);
+ }
+ } else {
+ errno = error;
+ err(EX_OSERR, "xcrun strip - %s", to_name);
+ }
+}
+
+/*
+ * install_dir --
+ * build directory heirarchy
+ */
+void
+install_dir(path)
+ char *path;
+{
+ register char *p;
+ struct stat sb;
+ int ch;
+
+ for (p = path;; ++p)
+ if (!*p || (p != path && *p == '/')) {
+ ch = *p;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT || mkdir(path, 0755) < 0) {
+ err(EX_OSERR, "mkdir %s", path);
+ /* NOTREACHED */
+ } else if (verbose)
+ (void)printf("install: mkdir %s\n",
+ path);
+ } else if (!S_ISDIR(sb.st_mode))
+ errx(EX_OSERR, "%s exists but is not a directory", path);
+ if (!(*p = ch))
+ break;
+ }
+
+ if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
+ warn("chown %u:%u %s", uid, gid, path);
+ if (chmod(path, mode))
+ warn("chmod %o %s", mode, path);
+}
+
+/*
+ * usage --
+ * print a usage message and die
+ */
+void
+usage()
+{
+ (void)fprintf(stderr, "\
+usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\
+ [-o owner] file1 file2\n\
+ install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\
+ [-o owner] file1 ... fileN directory\n\
+ install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n");
+ exit(EX_USAGE);
+ /* NOTREACHED */
+}
+
+/*
+ * trymmap --
+ * return true (1) if mmap should be tried, false (0) if not.
+ */
+int
+trymmap(fd)
+ int fd;
+{
+/*
+ * The ifdef is for bootstrapping - f_fstypename doesn't exist in
+ * pre-Lite2-merge systems.
+ */
+#ifdef MFSNAMELEN
+ struct statfs stfs;
+
+ if (nommap || fstatfs(fd, &stfs) != 0)
+ return (0);
+ if (strcmp(stfs.f_fstypename, "ufs") == 0 ||
+ strcmp(stfs.f_fstypename, "cd9660") == 0)
+ return (1);
+#endif
+ return (0);
+}
diff --git a/file_cmds/ipcrm/ipcrm.1 b/file_cmds/ipcrm/ipcrm.1
new file mode 100644
index 0000000..55ff23c
--- /dev/null
+++ b/file_cmds/ipcrm/ipcrm.1
@@ -0,0 +1,84 @@
+.\" 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.3 2005/04/12 23:51:24 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 ipcrm
+.Op Fl M Ar shmkey
+.Op Fl m Ar shmid
+.Op Fl Q Ar msgkey
+.Op Fl q Ar msqid
+.Op Fl S Ar semkey
+.Op Fl s Ar semid
+.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 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 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 Q Ar msgkey
+Remove the message queue associated with key
+.Nm msgkey
+from the system.
+.It Fl q Ar msqid
+Remove the message queue associated with the id
+.Nm msqid
+from the system.
+.It Fl S Ar semkey
+Remove the semaphore set associated with key
+.Nm semkey
+from the system.
+.It Fl s Ar semid
+Removes the semaphore set associated with id
+.Nm semid
+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/file_cmds/ipcrm/ipcrm.c b/file_cmds/ipcrm/ipcrm.c
new file mode 100644
index 0000000..e90aaa1
--- /dev/null
+++ b/file_cmds/ipcrm/ipcrm.c
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char rcsid[] =
+ "$Id: ipcrm.c,v 1.3 2005/02/03 07:31:33 josborne 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>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#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;
+
+static void usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: ipcrm [-q msqid] [-m shmid] [-s semid]",
+ " [-Q msgkey] [-M shmkey] [-S semkey] ...");
+ exit(1);
+}
+
+static int msgrm(key_t key, int id)
+{
+ if (key) {
+ id = msgget(key, 0);
+ if (id == -1)
+ return -1;
+ }
+ return msgctl(id, IPC_RMID, NULL);
+}
+
+static int shmrm(key_t key, int id)
+{
+ if (key) {
+ id = shmget(key, 0, 0);
+ if (id == -1)
+ return -1;
+ }
+ return shmctl(id, IPC_RMID, NULL);
+}
+
+static int semrm(key_t key, int id)
+{
+ if (key) {
+ id = semget(key, 0, 0);
+ if (id == -1)
+ return -1;
+ }
+ return semctl(id, 0, IPC_RMID);
+}
+
+static void not_configured(__unused int unused)
+{
+ signaled++;
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int c, result, errflg, target_id;
+ key_t target_key;
+ char *en;
+
+ 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 = (int)strtol(optarg, &en, 0);
+ if (*en) {
+ warnx("%s: '%s' is not a number",
+ IPC_TO_STRING(toupper(c)), optarg);
+ continue;
+ }
+ 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 = (key_t)strtol(optarg, &en, 0);
+ if (*en) {
+ warnx("%s: '%s' is not a number", IPC_TO_STRING(c), optarg);
+ continue;
+ }
+ 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("%s key(%d): ", IPC_TO_STRING(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/file_cmds/ipcs/ipcs.1 b/file_cmds/ipcs/ipcs.1
new file mode 100644
index 0000000..f0a6df4
--- /dev/null
+++ b/file_cmds/ipcs/ipcs.1
@@ -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 ipcs
+.Op Fl abcMmopQqSsTt
+.Sh DESCRIPTION
+The
+.Nm ipcs
+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 system information about shared memory.
+.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 system information about messages queues.
+.It Fl q
+Display information about active message queues.
+.It Fl S
+Display system information about semaphores.
+.It Fl s
+Display information about active semaphores.
+.It Fl T
+Display system information about shared memory, message queues
+and 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.
+.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 ipcs
+is running; the output of
+.Nm ipcs
+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 ipcs .
+.Sh SEE ALSO
+.Xr ipcrm 1
+.Sh AUTHORS
+.An Thorsten Lockert Aq tholo@sigmasoft.com
diff --git a/file_cmds/ipcs/ipcs.c b/file_cmds/ipcs/ipcs.c
new file mode 100644
index 0000000..986b229
--- /dev/null
+++ b/file_cmds/ipcs/ipcs.c
@@ -0,0 +1,553 @@
+/*
+ * 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 <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 <sysexits.h>
+
+#include "sys/types.h"
+#include <sys/ucred.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <errno.h>
+#include "sys/ipcs.h"
+#define KERNEL 1 /* To get new ipc_perm and __(sem|shm|msg)ds_new */
+#include "sys/ipc.h"
+#include "sys/sem_internal.h"
+#include "sys/shm_internal.h"
+#include "sys/msg.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) << 16L) | (ix & 0xffff))
+#endif
+
+static char *
+fmt_perm(u_short mode, char write_char)
+{
+ static char buffer[100];
+
+ buffer[0] = '-';
+ buffer[1] = '-';
+ buffer[2] = ((mode & 0400) ? 'r' : '-');
+ buffer[3] = ((mode & 0200) ? write_char : '-');
+ buffer[4] = '-';
+ buffer[5] = ((mode & 0040) ? 'r' : '-');
+ buffer[6] = ((mode & 0020) ? write_char : '-');
+ buffer[7] = '-';
+ buffer[8] = ((mode & 0004) ? 'r' : '-');
+ buffer[9] = ((mode & 0002) ? write_char : '-');
+ buffer[10] = '-';
+ buffer[11] = '\0';
+ return (&buffer[0]);
+}
+
+static void
+cvt_time(time_t t, char *buf)
+{
+ struct tm *tm;
+
+ if (t == 0) {
+ strcpy(buf, "no-entry");
+ } else {
+ tm = localtime(&t);
+ if (tm != NULL) {
+ 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
+
+static void
+usage(void)
+{
+ errx(EX_USAGE, "%s","usage: ipcs [-abcmopqstMQST]\n");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int display = 0;
+ int option = 0;
+ int exit_val = 0;
+ time_t now;
+ char datestring[100];
+ 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 == 0)
+ display = SHMINFO | MSGINFO | SEMINFO;
+ now = time(0);
+ struct tm* tm = localtime(&now);
+ if (tm == NULL) {
+ now = 0;
+ tm = localtime(&now);
+ }
+ if (0 == strftime(datestring, sizeof(datestring), "%a %b %e %H:%M:%S %Z %Y", tm))
+ errx(1, "strftime failed\n");
+ printf("IPC status from <running system> as of %s\n", datestring);
+ 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);
+
+ if (sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size)) {
+ if (errno != EPERM) {
+ char buffer[1024];
+ snprintf(buffer, 1024, "sysctlbyname(IPCS_MSG_SYSCTL, op=CONF, &ic, &%ld) datalen=%d",
+ sizeof(ic), ic.ipcs_datalen);
+ perror(buffer);
+ } else
+ perror("sysctlbyname IPCS_MSG_SYSCTL");
+ }
+
+ 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_new ds;
+ struct __msqid_ds_new *msqptr = &ds;
+ size_t ic_size = sizeof(ic);
+
+ 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("\nMessage Queues:\n");
+
+ ic.ipcs_magic = IPCS_MAGIC;
+ ic.ipcs_op = IPCS_MSG_ITER;
+ ic.ipcs_cursor = 0; /* start */
+ ic.ipcs_datalen = sizeof(*msqptr);
+ ic.ipcs_data = msqptr;
+
+ memset(msqptr, 0, sizeof(*msqptr));
+
+ while(!(sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+ ic.ipcs_data = msqptr;
+
+ 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 0x%08x %s %8s %8s",
+ IXSEQ_TO_IPCID((ic.ipcs_cursor-1), msqptr->msg_perm),
+ (int)msqptr->msg_perm._key,
+ fmt_perm(msqptr->msg_perm.mode, 'w'),
+ 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");
+ }
+ memset(msqptr, 0, sizeof(*msqptr));
+ errno = 0;
+ }
+
+ if (errno != ENOENT && errno != ERANGE) {
+ if (errno != EPERM) {
+ errx(1, "sysctlbyname(IPCS_MSG_SYSCTL, op=ITER, &ic, &%ld) datalen=%d failed:%s\n",
+ sizeof(ic), ic.ipcs_datalen, strerror(errno));
+ } else
+ errx(1, "sysctlbyname IPCS_MSG_SYSCTL: %s", strerror(errno));
+ }
+ printf("\n");
+ }
+ } else
+ if (display & (MSGINFO | MSGTOTAL)) {
+ errx(1, "%s", "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);
+
+ if (sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size)) {
+ if (errno != EPERM) {
+ errx(1, "sysctlbyname(IPCS_SHM_SYSCTL, op=CONF, &ic, &%ld) datalen=%d failed: %s\n",
+ sizeof(ic), ic.ipcs_datalen, strerror(errno));
+ } else
+ errx(1, "sysctlbyname: %s", strerror(errno));
+ }
+ printf("shminfo:\n");
+ printf("\tshmmax: %7lld\t(max shared memory segment size)\n",
+ shminfo.shmmax);
+ printf("\tshmmin: %7lld\t(min shared memory segment size)\n",
+ shminfo.shmmin);
+ printf("\tshmmni: %7lld\t(max number of shared memory identifiers)\n",
+ shminfo.shmmni);
+ printf("\tshmseg: %7lld\t(max shared memory segments per process)\n",
+ shminfo.shmseg);
+ printf("\tshmall: %7lld\t(max amount of shared memory in pages)\n\n",
+ shminfo.shmall);
+ }
+ if (display & SHMINFO) {
+ struct IPCS_command ic;
+ struct __shmid_ds_new ds;
+ struct __shmid_ds_new *shmptr = &ds;
+ size_t ic_size = sizeof(ic);
+
+ 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("\nShared Memory:\n");
+ { /* XXX */
+
+ ic.ipcs_magic = IPCS_MAGIC;
+ ic.ipcs_op = IPCS_SHM_ITER;
+ ic.ipcs_cursor = 0; /* start */
+ ic.ipcs_datalen = sizeof(*shmptr);
+ ic.ipcs_data = shmptr;
+ memset(shmptr, 0, sizeof(*shmptr));
+
+ while(!(sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+ ic.ipcs_data = shmptr; /* xnu workaround */
+
+ 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 0x%08x %s %8s %8s",
+ IXSEQ_TO_IPCID((ic.ipcs_cursor-1), shmptr->shm_perm),
+ (int)shmptr->shm_perm._key,
+ fmt_perm(shmptr->shm_perm.mode, 'w'),
+ 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(" %6ld",
+ 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");
+ }
+ memset(shmptr, 0, sizeof(*shmptr));
+ errno = 0;
+ }
+
+ if (errno != ENOENT && errno != ERANGE) {
+ if (errno != EPERM) {
+ errx(1, "sysctlbyname(IPCS_SHM_SYSCTL, op=ITER, &ic, &%ld) datalen=%d failed:%s\n",
+ sizeof(ic), ic.ipcs_datalen, strerror(errno));
+ } else
+ errx(1, "sysctlbyname: %s", strerror(errno));
+ }
+ } /* XXX */
+ printf("\n");
+ }
+ }
+else
+ if (display & (SHMINFO | SHMTOTAL)) {
+ errx(1, "%s", "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);
+
+ if (sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size)) {
+ if (errno != EPERM) {
+ char buffer[1024];
+ snprintf(buffer, 1024, "sysctlbyname(IPCS_SEM_SYSCTL, op=CONF, &ic, &%ld) datalen=%d",
+ sizeof(ic), ic.ipcs_datalen);
+ perror(buffer);
+ } else
+ perror("sysctlbyname IPCS_SEM_SYSCTL/SEM_CONF");
+ }
+
+ 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_new ds;
+ struct __semid_ds_new *semaptr = &ds;
+ size_t ic_size = sizeof(ic);
+
+ 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("\nSemaphores:\n");
+
+ ic.ipcs_magic = IPCS_MAGIC;
+ ic.ipcs_op = IPCS_SEM_ITER;
+ ic.ipcs_cursor = 0; /* start */
+ ic.ipcs_datalen = sizeof(*semaptr);
+ ic.ipcs_data = semaptr;
+
+ memset(semaptr, 0, sizeof(*semaptr));
+
+ while(!(sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size))) {
+ ic.ipcs_data = semaptr; /* xnu workaround */
+
+ 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 0x%08x %s %8s %8s",
+ IXSEQ_TO_IPCID((ic.ipcs_cursor-1), semaptr->sem_perm),
+ (int)semaptr->sem_perm._key,
+ fmt_perm(semaptr->sem_perm.mode, 'a'),
+ 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");
+ }
+ memset(semaptr, 0, sizeof(*semaptr));
+ errno = 0;
+ }
+
+ if (errno != ENOENT && errno != ERANGE) {
+ if (errno != EPERM) {
+ errx(1, "sysctlbyname(IPCS_SEM_SYSCTL/ITER, op=ITER, &ic, &%ld) datalen=%d failed: %s\n",
+ sizeof(ic), ic.ipcs_datalen, strerror(errno));
+ } else
+ errx(1, "sysctlbyname: IPCS_SEM_SYSCTL %s", strerror(errno));
+ }
+ printf("\n");
+ }
+ } else
+ if (display & (SEMINFO | SEMTOTAL)) {
+ errx(1, "%s", "SVID semaphores facility not configured in the system\n");
+ }
+
+ exit(exit_val);
+}
diff --git a/file_cmds/ln/link.1 b/file_cmds/ln/link.1
new file mode 100644
index 0000000..bfb5b6e
--- /dev/null
+++ b/file_cmds/ln/link.1
@@ -0,0 +1 @@
+.so man1/ln.1
diff --git a/file_cmds/ln/ln.1 b/file_cmds/ln/ln.1
new file mode 100644
index 0000000..d9edf00
--- /dev/null
+++ b/file_cmds/ln/ln.1
@@ -0,0 +1,233 @@
+.\"-
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" @(#)ln.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD: src/bin/ln/ln.1,v 1.31 2006/02/14 11:08:05 glebius Exp $
+.\"
+.Dd July 12, 2019
+.Dt LN 1
+.Os
+.Sh NAME
+.Nm link ,
+.Nm ln
+.Nd make links
+.Sh SYNOPSIS
+.Nm ln
+.Op Fl Ffhinsv
+.Ar source_file
+.Op Ar link_name
+.Nm ln
+.Op Fl Ffhinsv
+.Ar source_file ...
+.Ar link_dirname
+.Nm link
+.Ar source_file Ar link_name
+.Sh DESCRIPTION
+The
+.Nm ln
+utility creates a new directory entry (linked file) which has the
+same modes as the original file.
+It is useful for maintaining multiple copies of a file in many places
+at once without using up storage for the
+.Dq copies ;
+instead, a link
+.Dq points
+to the original copy.
+There are two types of links; hard links and symbolic links.
+How a link
+.Dq points
+to a file is one of the differences between a hard and symbolic link.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.\" ==========
+.It Fl F
+If the proposed link (link_name) already exists and is a directory, then remove it
+so that the link may occur.
+The
+.Fl F
+option should be used with either
+.Fl f
+or
+.Fl i
+options.
+If none is specified,
+.Fl f
+is implied.
+The
+.Fl F
+option is a no-op unless
+.Fl s
+option is specified.
+.It Fl h
+If the
+.Ar link_name
+or
+.Ar link_dirname
+is a symbolic link, do not follow it.
+This is most useful with the
+.Fl f
+option, to replace a symlink which may point to a directory.
+.\" ==========
+.It Fl f
+If the proposed link (link_name) already exists,
+then unlink it so that the link may occur.
+(The
+.Fl f
+option overrides any previous
+.Fl i
+options.)
+.\" ==========
+.It Fl i
+Cause
+.Nm ln
+to write a prompt to standard error if the proposed link exists.
+If the response from the standard input begins with the character
+.Sq Li y
+or
+.Sq Li Y ,
+then unlink the proposed link so that the link may occur.
+Otherwise, do not attempt the link.
+(The
+.Fl i
+option overrides any previous
+.Fl f
+options.)
+.\" ==========
+.It Fl n
+Same as
+.Fl h ,
+for compatibility with other
+.Nm ln
+implementations.
+.\" ==========
+.It Fl s
+Create a symbolic link.
+.\" ==========
+.It Fl v
+Cause
+.Nm ln
+to be verbose, showing files as they are processed.
+.El
+.Pp
+By default,
+.Nm ln
+makes
+.Em hard
+links.
+A hard link to a file is indistinguishable from the original directory entry;
+any changes to a file are effectively independent of the name used to reference
+the file.
+Hard links may not normally refer to directories and may not span file systems.
+.Pp
+A symbolic link contains the name of the file to
+which it is linked.
+The referenced file is used when an
+.Xr open 2
+operation is performed on the link.
+A
+.Xr stat 2
+on a symbolic link will return the linked-to file; an
+.Xr lstat 2
+must be done to obtain information about the link.
+The
+.Xr readlink 2
+call may be used to read the contents of a symbolic link.
+Symbolic links may span file systems and may refer to directories.
+.Pp
+Given one or two arguments,
+.Nm ln
+creates a link to an existing file
+.Ar source_file .
+If
+.Ar link_name
+is given, the link has that name;
+.Ar link_name
+may also be a directory in which to place the link;
+otherwise it is placed in the current directory.
+If only the directory is specified, the link will be made
+to the last component of
+.Ar source_file .
+.Pp
+Given more than two arguments,
+.Nm ln
+makes links in
+.Ar link_dirname
+to all the named source files.
+The links made will have the same name as the files being linked to.
+.Pp
+When the utility is called as
+.Nm link ,
+exactly two arguments must be supplied,
+neither of which may specify a directory.
+No options may be supplied in this simple mode of operation,
+which performs a
+.Xr link 2
+operation using the two passed arguments.
+.Sh COMPATIBILITY
+The
+.Fl h ,
+.Fl i ,
+.Fl n
+and
+.Fl v
+options are non-standard and their use in scripts is not recommended.
+They are provided solely for compatibility with other
+.Nm ln
+implementations.
+.Pp
+The
+.Fl F
+option is
+.Fx
+extention and should not be used in portable scripts.
+.Sh SEE ALSO
+.Xr link 2 ,
+.Xr lstat 2 ,
+.Xr readlink 2 ,
+.Xr stat 2 ,
+.Xr symlink 2 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm ln
+utility conforms to
+.St -p1003.2-92 .
+.Pp
+The simplified
+.Nm link
+command conforms to
+.St -susv2 .
+.Sh HISTORY
+An
+.Nm ln
+command appeared in
+.At v1 .
diff --git a/file_cmds/ln/ln.c b/file_cmds/ln/ln.c
new file mode 100644
index 0000000..0455dce
--- /dev/null
+++ b/file_cmds/ln/ln.c
@@ -0,0 +1,272 @@
+/*-
+ * Copyright (c) 1987, 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ln/ln.c,v 1.34 2006/02/14 11:08:05 glebius Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int fflag; /* Unlink existing files. */
+int Fflag; /* Remove empty directories also. */
+int hflag; /* Check new name for symlink first. */
+int iflag; /* Interactive mode. */
+int sflag; /* Symbolic, not hard, link. */
+int vflag; /* Verbose output. */
+ /* System link call. */
+int (*linkf)(const char *, const char *);
+char linkch;
+
+int linkit(const char *, const char *, int);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ char *p, *sourcedir;
+ int ch, exitval;
+
+ if (argc < 1)
+ usage();
+ /*
+ * Test for the special case where the utility is called as
+ * "link", for which the functionality provided is greatly
+ * simplified.
+ */
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ if (strcmp(p, "link") == 0) {
+ while (getopt(argc, argv, "") != -1)
+ usage();
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ usage();
+ linkf = link;
+ /* UNIX conformance requires that both operands be NOT a dir */
+ for (int i = 0; i < 2; i++) {
+ if (stat(argv[i], &sb) == 0 && S_ISDIR(sb.st_mode)){
+ errno = EISDIR;
+ warn("%s", argv[0]);
+ return 1;
+ }
+ }
+
+ exit(linkit(argv[0], argv[1], 0));
+ }
+
+ while ((ch = getopt(argc, argv, "Ffhinsv")) != -1)
+ switch (ch) {
+ case 'F':
+ Fflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ iflag = 0;
+ break;
+ case 'h':
+ case 'n':
+ hflag = 1;
+ break;
+ case 'i':
+ iflag = 1;
+ fflag = 0;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ linkf = sflag ? symlink : link;
+ linkch = sflag ? '-' : '=';
+ if (sflag == 0)
+ Fflag = 0;
+ if (Fflag == 1 && iflag == 0)
+ fflag = 1;
+
+ switch(argc) {
+ case 0:
+ usage();
+ /* NOTREACHED */
+ case 1: /* ln target */
+ exit(linkit(argv[0], ".", 1));
+ case 2: /* ln target source */
+ exit(linkit(argv[0], argv[1], 0));
+ default:
+ ;
+ }
+ /* ln target1 target2 directory */
+ sourcedir = argv[argc - 1];
+ if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
+ /*
+ * We were asked not to follow symlinks, but found one at
+ * the target--simulate "not a directory" error
+ */
+ errno = ENOTDIR;
+ err(1, "%s", sourcedir);
+ }
+ if (stat(sourcedir, &sb))
+ err(1, "%s", sourcedir);
+ if (!S_ISDIR(sb.st_mode))
+ usage();
+ for (exitval = 0; *argv != sourcedir; ++argv)
+ exitval |= linkit(*argv, sourcedir, 1);
+ exit(exitval);
+}
+
+int
+linkit(const char *target, const char *source, int isdir)
+{
+ struct stat sb;
+ const char *p;
+ int ch, exists, first;
+ char path[PATH_MAX];
+ char bbuf[PATH_MAX];
+
+ if (!sflag) {
+ /* If target doesn't exist, quit now. */
+ if (stat(target, &sb)) {
+ warn("%s", target);
+ return (1);
+ }
+ /* Only symbolic links to directories. */
+ if (S_ISDIR(sb.st_mode)) {
+ errno = EISDIR;
+ warn("%s", target);
+ return (1);
+ }
+ }
+
+ /*
+ * If the source is a directory (and not a symlink if hflag),
+ * append the target's name.
+ */
+ if (isdir ||
+ (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
+ (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) {
+ if (strlcpy(bbuf, target, sizeof(bbuf)) >= sizeof(bbuf) ||
+ (p = basename(bbuf)) == NULL ||
+ snprintf(path, sizeof(path), "%s/%s", source, p) >=
+ (ssize_t)sizeof(path)) {
+ errno = ENAMETOOLONG;
+ warn("%s", target);
+ return (1);
+ }
+ source = path;
+ }
+
+ exists = !lstat(source, &sb);
+ /*
+ * If the file exists, then unlink it forcibly if -f was specified
+ * and interactively if -i was specified.
+ */
+ if (fflag && exists) {
+ if (Fflag && S_ISDIR(sb.st_mode)) {
+ if (rmdir(source)) {
+ warn("%s", source);
+ return (1);
+ }
+ } else if (unlink(source)) {
+ warn("%s", source);
+ return (1);
+ }
+ } else if (iflag && exists) {
+ fflush(stdout);
+ fprintf(stderr, "replace %s? ", source);
+
+ first = ch = getchar();
+ while(ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y') {
+ fprintf(stderr, "not replaced\n");
+ return (1);
+ }
+
+ if (Fflag && S_ISDIR(sb.st_mode)) {
+ if (rmdir(source)) {
+ warn("%s", source);
+ return (1);
+ }
+ } else if (unlink(source)) {
+ warn("%s", source);
+ return (1);
+ }
+ }
+
+ /* Attempt the link. */
+ if ((*linkf)(target, source)) {
+ warn("%s", source);
+ return (1);
+ }
+ if (vflag)
+ (void)printf("%s %c> %s\n", source, linkch, target);
+ return (0);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: ln [-Ffhinsv] source_file [link_name]",
+ " ln [-Ffhinsv] source_file ... linkname_dir",
+ " link source_file link_name");
+ exit(1);
+}
diff --git a/file_cmds/ln/symlink.7 b/file_cmds/ln/symlink.7
new file mode 100644
index 0000000..7c6c757
--- /dev/null
+++ b/file_cmds/ln/symlink.7
@@ -0,0 +1,457 @@
+.\"-
+.\" Copyright (c) 1992, 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.
+.\" 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.
+.\"
+.\" @(#)symlink.7 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD: src/bin/ln/symlink.7,v 1.30 2005/02/13 22:25:09 ru Exp $
+.\"
+.Dd March 31, 1994
+.Dt SYMLINK 7
+.Os
+.Sh NAME
+.Nm symlink
+.Nd symbolic link handling
+.Sh SYMBOLIC LINK HANDLING
+Symbolic links are files that act as pointers to other files.
+To understand their behavior, you must first understand how hard links
+work.
+A hard link to a file is indistinguishable from the original file because
+it is a reference to the object underlying the original file name.
+Changes to a file are independent of the name used to reference the
+file.
+Hard links may not refer to directories and may not reference files
+on different file systems.
+A symbolic link contains the name of the file to which it is linked,
+i.e., it is a pointer to another name, and not to an underlying object.
+For this reason, symbolic links may reference directories and may span
+file systems.
+.Pp
+Because a symbolic link and its referenced object coexist in the file system
+name space, confusion can arise in distinguishing between the link itself
+and the referenced object.
+Historically, commands and system calls have adopted their own link
+following conventions in a somewhat ad-hoc fashion.
+Rules for more a uniform approach, as they are implemented in this system,
+are outlined here.
+It is important that local applications conform to these rules, too,
+so that the user interface can be as consistent as possible.
+.Pp
+Symbolic links are handled either by operating on the link itself,
+or by operating on the object referenced by the link.
+In the latter case,
+an application or system call is said to
+.Dq follow
+the link.
+Symbolic links may reference other symbolic links,
+in which case the links are dereferenced until an object that is
+not a symbolic link is found,
+a symbolic link which references a file which does not exist is found,
+or a loop is detected.
+(Loop detection is done by placing an upper limit on the number of
+links that may be followed, and an error results if this limit is
+exceeded.)
+.Pp
+There are three separate areas that need to be discussed.
+They are as follows:
+.Pp
+.Bl -enum -compact -offset indent
+.It
+Symbolic links used as file name arguments for system calls.
+.It
+Symbolic links specified as command line arguments to utilities that
+are not traversing a file tree.
+.It
+Symbolic links encountered by utilities that are traversing a file tree
+(either specified on the command line or encountered as part of the
+file hierarchy walk).
+.El
+.Ss System calls.
+The first area is symbolic links used as file name arguments for
+system calls.
+.Pp
+Except as noted below, all system calls follow symbolic links.
+For example, if there were a symbolic link
+.Dq Li slink
+which pointed to a file named
+.Dq Li afile ,
+the system call
+.Dq Li open("slink" ...\&)
+would return a file descriptor to the file
+.Dq afile .
+.Pp
+There are nine system calls that do not follow links, and which operate
+on the symbolic link itself.
+They are:
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
+.Xr lchown 2 ,
+.Xr lstat 2 ,
+.Xr lutimes 3 ,
+.Xr readlink 2 ,
+.Xr rename 2 ,
+.Xr rmdir 2 ,
+and
+.Xr unlink 2 .
+Because
+.Xr remove 3
+is an alias for
+.Xr unlink 2 ,
+it also does not follow symbolic links.
+When
+.Xr rmdir 2
+is applied to a symbolic link, it fails with the error
+.Er ENOTDIR .
+.Pp
+The owner and group of an existing symbolic link can be changed by
+means of the
+.Xr lchown 2
+system call.
+The flags, access permissions, owner/group and modification time of
+an existing symbolic link can be changed by means of the
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
+.Xr lchown 2 ,
+and
+.Xr lutimes 3
+system calls, respectively.
+Of these, only the flags are used by the system;
+the access permissions and ownership are ignored.
+.Pp
+The
+.Bx 4.4
+system differs from historical
+.Bx 4
+systems in that the system call
+.Xr chown 2
+has been changed to follow symbolic links.
+The
+.Xr lchown 2
+system call was added later when the limitations of the new
+.Xr chown 2
+became apparent.
+.Ss Commands not traversing a file tree.
+The second area is symbolic links, specified as command line file
+name arguments, to commands which are not traversing a file tree.
+.Pp
+Except as noted below, commands follow symbolic links named as command
+line arguments.
+For example, if there were a symbolic link
+.Dq Li slink
+which pointed to a file named
+.Dq Li afile ,
+the command
+.Dq Li cat slink
+would display the contents of the file
+.Dq Li afile .
+.Pp
+It is important to realize that this rule includes commands which may
+optionally traverse file trees, e.g.\& the command
+.Dq Li "chown file"
+is included in this rule, while the command
+.Dq Li "chown -R file"
+is not.
+(The latter is described in the third area, below.)
+.Pp
+If it is explicitly intended that the command operate on the symbolic
+link instead of following the symbolic link, e.g., it is desired that
+.Dq Li "chown slink"
+change the ownership of the file that
+.Dq Li slink
+is, whether it is a symbolic link or not, the
+.Fl h
+option should be used.
+In the above example,
+.Dq Li "chown root slink"
+would change the ownership of the file referenced by
+.Dq Li slink ,
+while
+.Dq Li "chown -h root slink"
+would change the ownership of
+.Dq Li slink
+itself.
+.Pp
+There are four exceptions to this rule.
+The
+.Xr mv 1
+and
+.Xr rm 1
+commands do not follow symbolic links named as arguments,
+but respectively attempt to rename and delete them.
+(Note, if the symbolic link references a file via a relative path,
+moving it to another directory may very well cause it to stop working,
+since the path may no longer be correct.)
+.Pp
+The
+.Xr ls 1
+command is also an exception to this rule.
+For compatibility with historic systems (when
+.Nm ls
+is not doing a tree walk, i.e., the
+.Fl R
+option is not specified),
+the
+.Nm ls
+command follows symbolic links named as arguments if the
+.Fl H
+or
+.Fl L
+option is specified,
+or if the
+.Fl F ,
+.Fl d
+or
+.Fl l
+options are not specified.
+(The
+.Nm ls
+command is the only command where the
+.Fl H
+and
+.Fl L
+options affect its behavior even though it is not doing a walk of
+a file tree.)
+.Pp
+The
+.Xr file 1
+command is also an exception to this rule.
+The
+.Xr file 1
+command does not follow symbolic links named as argument by default.
+The
+.Xr file 1
+command does follow symbolic links named as argument if
+.Fl L
+option is specified.
+.Pp
+The
+.Bx 4.4
+system differs from historical
+.Bx 4
+systems in that the
+.Nm chown
+and
+.Nm chgrp
+commands follow symbolic links specified on the command line.
+.Ss Commands traversing a file tree.
+The following commands either optionally or always traverse file trees:
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr du 1 ,
+.Xr find 1 ,
+.Xr ls 1 ,
+.Xr pax 1 ,
+.Xr rm 1 ,
+.Xr tar 1
+and
+.Xr chown 8 .
+.Pp
+It is important to realize that the following rules apply equally to
+symbolic links encountered during the file tree traversal and symbolic
+links listed as command line arguments.
+.Pp
+The first rule applies to symbolic links that reference files that are
+not of type directory.
+Operations that apply to symbolic links are performed on the links
+themselves, but otherwise the links are ignored.
+.Pp
+The command
+.Dq Li "rm -r slink directory"
+will remove
+.Dq Li slink ,
+as well as any symbolic links encountered in the tree traversal of
+.Dq Li directory ,
+because symbolic links may be removed.
+In no case will
+.Nm rm
+affect the file which
+.Dq Li slink
+references in any way.
+.Pp
+The second rule applies to symbolic links that reference files of type
+directory.
+Symbolic links which reference files of type directory are never
+.Dq followed
+by default.
+This is often referred to as a
+.Dq physical
+walk, as opposed to a
+.Dq logical
+walk (where symbolic links referencing directories are followed).
+.Pp
+As consistently as possible, you can make commands doing a file tree
+walk follow any symbolic links named on the command line, regardless
+of the type of file they reference, by specifying the
+.Fl H
+(for
+.Dq half\-logical )
+flag.
+This flag is intended to make the command line name space look
+like the logical name space.
+(Note, for commands that do not always do file tree traversals, the
+.Fl H
+flag will be ignored if the
+.Fl R
+flag is not also specified.)
+.Pp
+For example, the command
+.Dq Li "chown -HR user slink"
+will traverse the file hierarchy rooted in the file pointed to by
+.Dq Li slink .
+Note, the
+.Fl H
+is not the same as the previously discussed
+.Fl h
+flag.
+The
+.Fl H
+flag causes symbolic links specified on the command line to be
+dereferenced both for the purposes of the action to be performed
+and the tree walk, and it is as if the user had specified the
+name of the file to which the symbolic link pointed.
+.Pp
+As consistently as possible, you can make commands doing a file tree
+walk follow any symbolic links named on the command line, as well as
+any symbolic links encountered during the traversal, regardless of
+the type of file they reference, by specifying the
+.Fl L
+(for
+.Dq logical )
+flag.
+This flag is intended to make the entire name space look like
+the logical name space.
+(Note, for commands that do not always do file tree traversals, the
+.Fl L
+flag will be ignored if the
+.Fl R
+flag is not also specified.)
+.Pp
+For example, the command
+.Dq Li "chown -LR user slink"
+will change the owner of the file referenced by
+.Dq Li slink .
+If
+.Dq Li slink
+references a directory,
+.Nm chown
+will traverse the file hierarchy rooted in the directory that it
+references.
+In addition, if any symbolic links are encountered in any file tree that
+.Nm chown
+traverses, they will be treated in the same fashion as
+.Dq Li slink .
+.Pp
+As consistently as possible, you can specify the default behavior by
+specifying the
+.Fl P
+(for
+.Dq physical )
+flag.
+This flag is intended to make the entire name space look like the
+physical name space.
+.Pp
+For commands that do not by default do file tree traversals, the
+.Fl H ,
+.Fl L
+and
+.Fl P
+flags are ignored if the
+.Fl R
+flag is not also specified.
+In addition, you may specify the
+.Fl H ,
+.Fl L
+and
+.Fl P
+options more than once; the last one specified determines the
+command's behavior.
+This is intended to permit you to alias commands to behave one way
+or the other, and then override that behavior on the command line.
+.Pp
+The
+.Xr ls 1
+and
+.Xr rm 1
+commands have exceptions to these rules.
+The
+.Nm rm
+command operates on the symbolic link, and not the file it references,
+and therefore never follows a symbolic link.
+The
+.Nm rm
+command does not support the
+.Fl H ,
+.Fl L
+or
+.Fl P
+options.
+.Pp
+To maintain compatibility with historic systems,
+the
+.Nm ls
+command acts a little differently.
+If you do not specify the
+.Fl F ,
+.Fl d
+or
+.Fl l
+options,
+.Nm ls
+will follow symbolic links specified on the command line.
+If the
+.Fl L
+flag is specified,
+.Nm ls
+follows all symbolic links,
+regardless of their type,
+whether specified on the command line or encountered in the tree walk.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr du 1 ,
+.Xr find 1 ,
+.Xr ln 1 ,
+.Xr ls 1 ,
+.Xr mv 1 ,
+.Xr pax 1 ,
+.Xr rm 1 ,
+.Xr tar 1 ,
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
+.Xr lchown 2 ,
+.Xr lstat 2 ,
+.Xr lutimes 3 ,
+.Xr readlink 2 ,
+.Xr rename 2 ,
+.Xr symlink 2 ,
+.Xr unlink 2 ,
+.Xr fts 3 ,
+.Xr remove 3 ,
+.Xr chown 8
diff --git a/file_cmds/ls/cmp.c b/file_cmds/ls/cmp.c
new file mode 100644
index 0000000..2ad5edd
--- /dev/null
+++ b/file_cmds/ls/cmp.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/ls/cmp.c,v 1.12 2002/06/30 05:13:54 obrien Exp $");
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fts.h>
+#include <string.h>
+
+#include "ls.h"
+#include "extern.h"
+
+#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \
+ defined(_XOPEN_SOURCE) || defined(__NetBSD__)
+#define ATIMENSEC_CMP(x, op, y) ((x)->st_atimensec op (y)->st_atimensec)
+#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctimensec op (y)->st_ctimensec)
+#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtimensec op (y)->st_mtimensec)
+#define BTIMENSEC_CMP(x, op, y) ((x)->st_birthtimensec op (y)->st_birthtimensec)
+#else
+#define ATIMENSEC_CMP(x, op, y) \
+ ((x)->st_atimespec.tv_nsec op (y)->st_atimespec.tv_nsec)
+#define CTIMENSEC_CMP(x, op, y) \
+ ((x)->st_ctimespec.tv_nsec op (y)->st_ctimespec.tv_nsec)
+#define MTIMENSEC_CMP(x, op, y) \
+ ((x)->st_mtimespec.tv_nsec op (y)->st_mtimespec.tv_nsec)
+#define BTIMENSEC_CMP(x, op, y) \
+ ((x)->st_birthtimespec.tv_nsec op (y)->st_birthtimespec.tv_nsec)
+#endif
+
+int
+namecmp(const FTSENT *a, const FTSENT *b)
+{
+ return (strcoll(a->fts_name, b->fts_name));
+}
+
+int
+revnamecmp(const FTSENT *a, const FTSENT *b)
+{
+ return (strcoll(b->fts_name, a->fts_name));
+}
+
+int
+modcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_mtime > a->fts_statp->st_mtime)
+ return (1);
+ else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime)
+ return (-1);
+ else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revmodcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_mtime > a->fts_statp->st_mtime)
+ return (-1);
+ else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime)
+ return (1);
+ else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+acccmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_atime > a->fts_statp->st_atime)
+ return (1);
+ else if (b->fts_statp->st_atime < a->fts_statp->st_atime)
+ return (-1);
+ else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revacccmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_atime > a->fts_statp->st_atime)
+ return (-1);
+ else if (b->fts_statp->st_atime < a->fts_statp->st_atime)
+ return (1);
+ else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+statcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_ctime > a->fts_statp->st_ctime)
+ return (1);
+ else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime)
+ return (-1);
+ else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revstatcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_ctime > a->fts_statp->st_ctime)
+ return (-1);
+ else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime)
+ return (1);
+ else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+sizecmp(a, b)
+ const FTSENT *a, *b;
+{
+ if (b->fts_statp->st_size > a->fts_statp->st_size)
+ return (1);
+ if (b->fts_statp->st_size < a->fts_statp->st_size)
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revsizecmp(a, b)
+ const FTSENT *a, *b;
+{
+ if (b->fts_statp->st_size > a->fts_statp->st_size)
+ return (-1);
+ if (b->fts_statp->st_size < a->fts_statp->st_size)
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+birthcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime)
+ return (1);
+ else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime)
+ return (-1);
+ else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revbirthcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime)
+ return (-1);
+ else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime)
+ return (1);
+ else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
diff --git a/file_cmds/ls/extern.h b/file_cmds/ls/extern.h
new file mode 100644
index 0000000..a33bd97
--- /dev/null
+++ b/file_cmds/ls/extern.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)extern.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/bin/ls/extern.h,v 1.19 2002/05/19 02:51:36 tjr Exp $
+ */
+
+#ifndef _LS_EXTERN_H_
+#define _LS_EXTERN_H_
+
+int acccmp(const FTSENT *, const FTSENT *);
+int revacccmp(const FTSENT *, const FTSENT *);
+int modcmp(const FTSENT *, const FTSENT *);
+int revmodcmp(const FTSENT *, const FTSENT *);
+int namecmp(const FTSENT *, const FTSENT *);
+int revnamecmp(const FTSENT *, const FTSENT *);
+int statcmp(const FTSENT *, const FTSENT *);
+int revstatcmp(const FTSENT *, const FTSENT *);
+int sizecmp (const FTSENT *, const FTSENT *);
+int revsizecmp (const FTSENT *, const FTSENT *);
+int birthcmp(const FTSENT *, const FTSENT *);
+int revbirthcmp(const FTSENT *, const FTSENT *);
+
+void printcol(DISPLAY *);
+void printlong(DISPLAY *);
+void printscol(DISPLAY *);
+void printstream(DISPLAY *);
+void usage(void);
+int prn_normal(const char *);
+size_t len_octal(const char *, int);
+int prn_octal(const char *);
+int prn_printable(const char *);
+#ifdef COLORLS
+void parsecolors(const char *cs);
+void colorquit(int);
+
+extern char *ansi_fgcol;
+extern char *ansi_bgcol;
+extern char *ansi_coloff;
+extern char *attrs_off;
+extern char *enter_bold;
+#endif
+
+#endif /* _LS_EXTERN_H_ */
diff --git a/file_cmds/ls/ls.1 b/file_cmds/ls/ls.1
new file mode 100644
index 0000000..b75c1ae
--- /dev/null
+++ b/file_cmds/ls/ls.1
@@ -0,0 +1,715 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" 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.
+.\"
+.\" @(#)ls.1 8.7 (Berkeley) 7/29/94
+.\" $FreeBSD: src/bin/ls/ls.1,v 1.69 2002/08/21 17:32:34 trhodes Exp $
+.\"
+.Dd May 19, 2002
+.Dt LS 1
+.Os
+.Sh NAME
+.Nm ls
+.Nd list directory contents
+.Sh SYNOPSIS
+.Nm ls
+.Op Fl ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1%
+.Op Ar
+.Sh DESCRIPTION
+For each operand that names a
+.Ar file
+of a type other than
+directory,
+.Nm ls
+displays its name as well as any requested,
+associated information.
+For each operand that names a
+.Ar file
+of type directory,
+.Nm ls
+displays the names of files contained
+within that directory, as well as any requested, associated
+information.
+.Pp
+If no operands are given, the contents of the current
+directory are displayed.
+If more than one operand is given,
+non-directory operands are displayed first; directory
+and non-directory operands are sorted separately and in
+lexicographical order.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl @
+Display extended attribute keys and sizes in long
+.Pq Fl l
+output.
+.It Fl 1
+(The numeric digit
+.Dq one . )
+Force output to be
+one entry per line.
+This is the default when
+output is not to a terminal.
+.It Fl A
+List all entries except for
+.Pa \&.
+and
+.Pa .. .
+Always set for the super-user.
+.It Fl a
+Include directory entries whose names begin with a
+dot
+.Pq Pa \&. .
+.It Fl B
+Force printing of non-printable characters (as defined by
+.Xr ctype 3
+and current locale settings) in file names as
+.Li \e Ns Va xxx ,
+where
+.Va xxx
+is the numeric value of the character in octal.
+.It Fl b
+As
+.Fl B ,
+but use
+.Tn C
+escape codes whenever possible.
+.It Fl C
+Force multi-column output; this is the default when output is to a terminal.
+.It Fl c
+Use time when file status was last changed for sorting
+.Pq Fl t
+or long printing
+.Pq Fl l .
+.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, in long
+.Pq Fl l
+output.
+.It Fl F
+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,
+an equals sign
+.Pq Ql =
+after each socket,
+a percent sign
+.Pq Ql %
+after each whiteout,
+and a vertical bar
+.Pq Ql \&|
+after each that is a
+.Tn FIFO .
+.It Fl f
+Output is not sorted.
+This option turns on the
+.Fl a
+option.
+.It Fl G
+Enable colorized output.
+This option is equivalent to defining
+.Ev CLICOLOR
+in the environment.
+(See below.)
+.It Fl g
+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 (the owner name is suppressed).
+.It Fl H
+Symbolic links on the command line are followed.
+This option is assumed if
+none of the
+.Fl F , d ,
+or
+.Fl l
+options are specified.
+.It Fl h
+When used with the
+.Fl l
+option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte
+and Petabyte in order to reduce the number of digits to three or less
+using base 2 for sizes.
+.It Fl i
+For each file, print the file's file serial number (inode number).
+.It Fl k
+If the
+.Fl s
+option is specified, print the file size allocation in kilobytes,
+not blocks.
+This option overrides the environment variable
+.Ev BLOCKSIZE .
+.It Fl L
+Follow all symbolic links to final target and list the file or directory the link references
+rather than the link itself.
+This option cancels the
+.Fl P
+option.
+.It Fl l
+(The lowercase letter
+.Dq ell . )
+List in long format.
+(See below.)
+A total sum for all the file
+sizes is output on a line before the long listing.
+.It Fl m
+Stream output format; list files across the page, separated by commas.
+.It Fl n
+Display user and group IDs numerically,
+rather than converting to a user or group name in a long
+.Pq Fl l
+output.
+This option turns on the
+.Fl l
+option.
+.It Fl O
+Include the file flags in a long
+.Pq Fl l
+output.
+.It Fl o
+List in long format, but omit the group id.
+.It Fl P
+If argument is a symbolic link, list the link itself rather than the
+object the link references.
+This option cancels the
+.Fl H
+and
+.Fl L
+options.
+.It Fl p
+Write a slash
+.Pq Ql /
+after each filename if that file is a directory.
+.It Fl q
+Force printing of non-graphic characters in file names as
+the character
+.Ql \&? ;
+this is the default when output is to a terminal.
+.It Fl R
+Recursively list subdirectories encountered.
+.It Fl r
+Reverse the order of the sort to get reverse
+lexicographical order or the oldest entries first (or largest files
+last, if combined with sort by size
+.It Fl S
+Sort files by size
+.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.
+If the output is to a terminal, a total sum for all the file
+sizes is output on a line before the listing.
+The environment variable
+.Ev BLOCKSIZE
+overrides the unit size of 512 bytes.
+.It Fl T
+When used with the
+.Fl l
+(lowercase letter
+.Dq ell )
+option, display complete time information for the file, including
+month, day, hour, minute, second, and year.
+.It Fl t
+Sort by time modified (most recently modified
+first) before sorting the operands by lexicographical
+order.
+.It Fl u
+Use time of last access,
+instead of last modification
+of the file for sorting
+.Pq Fl t
+or long printing
+.Pq Fl l .
+.It Fl U
+Use time of file creation, instead of last modification for sorting
+.Pq Fl t
+or long output
+.Pq Fl l .
+.It Fl v
+Force unedited printing of non-graphic characters; this is the default when
+output is not to a terminal.
+.It Fl W
+Display whiteouts when scanning directories.
+.Pq Fl S
+flag).
+.It Fl w
+Force raw printing of non-printable characters.
+This is the default
+when output is not to a terminal.
+.It Fl x
+The same as
+.Fl C ,
+except that the multi-column output is produced with entries sorted
+across, rather than down, the columns.
+.It Fl %
+Distinguish dataless files and directories with a '%' character in long
+.Pq Fl l
+output, and don't materialize dataless directories when listing them.
+.El
+.Pp
+The
+.Fl 1 , C , x ,
+and
+.Fl l
+options all override each other;
+the last one specified determines the format used.
+.Pp
+The
+.Fl c
+and
+.Fl u
+options override each other; the last one specified determines
+the file time used.
+.Pp
+The
+.Fl B , b , w ,
+and
+.Fl q
+options all override each other;
+the last one specified determines the format used
+for non-printable characters.
+.Pp
+The
+.Fl H , L
+and
+.Fl P
+options all override each other (either partially or fully); they
+are applied in the order specified.
+.Pp
+By default,
+.Nm ls
+lists one entry per line to standard
+output; the exceptions are to terminals or when the
+.Fl C
+or
+.Fl x
+options are specified.
+.Pp
+File information is displayed with one or more
+.Ao blank Ac Ns s
+separating the information associated with the
+.Fl i , s ,
+and
+.Fl l
+options.
+.Ss The Long Format
+If the
+.Fl l
+option is given, the following information
+is displayed for each file:
+file mode,
+number of links, owner name, group name,
+number of bytes in the file, abbreviated
+month, day-of-month file was last modified,
+hour file last modified, minute file last
+modified, and the pathname.
+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 attributes,
+the permissions field printed by the
+.Fl l
+option is followed by a '@' character.
+Otherwise, if the file or directory has extended security information
+(such as an access control list),
+the permissions field printed by the
+.Fl l
+option is followed by a '+' character.
+If the
+.Fl %
+option is given, a '%' character follows the permissions field
+for dataless files and directories,
+possibly replacing the '@' or '+' 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
+is displayed in place of the hour and minute fields.
+.Pp
+If the owner or group names are not a known user or group name,
+or the
+.Fl n
+option is given,
+the numeric ID's are displayed.
+.Pp
+If the file is a character special or block special file,
+the major and minor device numbers for the file are displayed
+in the size field.
+If the file is a symbolic link,
+the pathname of the linked-to file is preceded by
+.Dq Li -> .
+.Pp
+The file mode printed under the
+.Fl l
+option consists of the
+entry type, owner permissions, and group permissions.
+The entry type character describes the type of file,
+as follows:
+.Pp
+.Bl -tag -width 4n -offset indent -compact
+.It Sy b
+Block special file.
+.It Sy c
+Character special file.
+.It Sy d
+Directory.
+.It Sy l
+Symbolic link.
+.It Sy s
+Socket link.
+.It Sy p
+.Tn FIFO .
+.It Sy \-
+Regular file.
+.El
+.Pp
+The next three fields
+are three characters each:
+owner permissions,
+group permissions, and
+other permissions.
+Each field has three character positions:
+.Bl -enum -offset indent
+.It
+If
+.Sy r ,
+the file is readable; if
+.Sy \- ,
+it is not readable.
+.It
+If
+.Sy w ,
+the file is writable; if
+.Sy \- ,
+it is not writable.
+.It
+The first of the following that applies:
+.Bl -tag -width 4n -offset indent
+.It Sy S
+If in the owner permissions, the file is not executable and
+set-user-ID mode is set.
+If in the group permissions, the file is not executable
+and set-group-ID mode is set.
+.It Sy s
+If in the owner permissions, the file is executable
+and set-user-ID mode is set.
+If in the group permissions, the file is executable
+and setgroup-ID mode is set.
+.It Sy x
+The file is executable or the directory is
+searchable.
+.It Sy \-
+The file is neither readable, writable, executable,
+nor set-user-ID nor set-group-ID mode, nor sticky.
+(See below.)
+.El
+.Pp
+These next two apply only to the third character in the last group
+(other permissions).
+.Bl -tag -width 4n -offset indent
+.It Sy T
+The sticky bit is set
+(mode
+.Li 1000 ) ,
+but not execute or search permission.
+(See
+.Xr chmod 1
+or
+.Xr sticky 8 . )
+.It Sy t
+The sticky bit is set (mode
+.Li 1000 ) ,
+and is searchable or executable.
+(See
+.Xr chmod 1
+or
+.Xr sticky 8 . )
+.El
+.El
+.Sh EXAMPLES
+The following is how to do an
+.Nm ls
+listing sorted by increasing size
+.Pp
+.Dl "ls -lrS"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm ls :
+.Bl -tag -width ".Ev CLICOLOR_FORCE"
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, the block counts
+(see
+.Fl s )
+will be displayed in units of that size block.
+.It Ev CLICOLOR
+Use
+\*[Ai]
+color sequences to distinguish file types.
+See
+.Ev LSCOLORS
+below.
+In addition to the file types mentioned in the
+.Fl F
+option some extra attributes (setuid bit set, etc.) are also displayed.
+The colorization is dependent on a terminal type with the proper
+.Xr termcap 5
+capabilities.
+The default
+.Dq Li cons25
+console has the proper capabilities,
+but to display the colors in an
+.Xr xterm 1 ,
+for example,
+the
+.Ev TERM
+variable must be set to
+.Dq Li xterm-color .
+Other terminal types may require similar adjustments.
+Colorization
+is silently disabled if the output isn't directed to a terminal
+unless the
+.Ev CLICOLOR_FORCE
+variable is defined.
+.It Ev CLICOLOR_FORCE
+Color sequences are normally disabled if the output isn't directed to
+a terminal.
+This can be overridden by setting this flag.
+The
+.Ev TERM
+variable still needs to reference a color capable terminal however
+otherwise it is not possible to determine which color sequences to
+use.
+.It Ev COLUMNS
+If this variable contains a string representing a
+decimal integer, it is used as the
+column position width for displaying
+multiple-text-column output.
+The
+.Nm ls
+utility calculates how
+many pathname text columns to display
+based on the width provided.
+(See
+.Fl C
+and
+.Fl x . )
+.It Ev LANG
+The locale to use when determining the order of day and month in the long
+.Fl l
+format output.
+See
+.Xr environ 7
+for more information.
+.It Ev LSCOLORS
+The value of this variable describes what color to use for which
+attribute when colors are enabled with
+.Ev CLICOLOR .
+This string is a concatenation of pairs of the format
+.Ar f Ns Ar b ,
+where
+.Ar f
+is the foreground color and
+.Ar b
+is the background color.
+.Pp
+The color designators are as follows:
+.Pp
+.Bl -tag -width 4n -offset indent -compact
+.It Sy a
+black
+.It Sy b
+red
+.It Sy c
+green
+.It Sy d
+brown
+.It Sy e
+blue
+.It Sy f
+magenta
+.It Sy g
+cyan
+.It Sy h
+light grey
+.It Sy A
+bold black, usually shows up as dark grey
+.It Sy B
+bold red
+.It Sy C
+bold green
+.It Sy D
+bold brown, usually shows up as yellow
+.It Sy E
+bold blue
+.It Sy F
+bold magenta
+.It Sy G
+bold cyan
+.It Sy H
+bold light grey; looks like bright white
+.It Sy x
+default foreground or background
+.El
+.Pp
+Note that the above are standard
+\*[Ai]
+colors.
+The actual display may differ
+depending on the color capabilities of the terminal in use.
+.Pp
+The order of the attributes are as follows:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+directory
+.It
+symbolic link
+.It
+socket
+.It
+pipe
+.It
+executable
+.It
+block special
+.It
+character special
+.It
+executable with setuid bit set
+.It
+executable with setgid bit set
+.It
+directory writable to others, with sticky bit
+.It
+directory writable to others, without sticky bit
+.El
+.Pp
+The default is
+.Qq "exfxcxdxbxegedabagacad" ,
+i.e. blue foreground and
+default background for regular directories, black foreground and red
+background for setuid executables, etc.
+.It Ev LS_COLWIDTHS
+If this variable is set, it is considered to be a
+colon-delimited list of minimum column widths.
+Unreasonable
+and insufficient widths are ignored (thus zero signifies
+a dynamically sized column).
+Not all columns have changeable widths.
+The fields are,
+in order: inode, block count, number of links, user name,
+group name, flags, file size, file name.
+.It Ev TERM
+The
+.Ev CLICOLOR
+functionality depends on a terminal type with color capabilities.
+.It Ev TZ
+The timezone to use when displaying dates.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh COMPATIBILITY
+The group field is now automatically included in the long listing for
+files in order to be compatible with the
+.St -p1003.2
+specification.
+.Sh LEGACY DESCRIPTION
+In legacy mode, the
+.Fl f
+option does not turn on the
+.Fl a
+option and the
+.Fl g ,
+.Fl n ,
+and
+.Fl o
+options do not turn on the
+.Fl l
+option.
+.Pp
+Also, the
+.Fl o
+option causes the file flags to be included in a long (-l) output;
+there is no
+.Fl O
+option.
+.Pp
+When
+.Fl H
+is specified (and not overridden by
+.Fl L
+or
+.Fl P )
+and a file argument is a symlink
+that resolves to a non-directory file,
+the output will reflect the nature of the link,
+rather than that of the file.
+In legacy operation, the output will describe the file.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chmod 1 ,
+.Xr sort 1 ,
+.Xr xterm 1 ,
+.Xr compat 5 ,
+.Xr termcap 5 ,
+.Xr symlink 7 ,
+.Xr sticky 8
+.Sh STANDARDS
+The
+.Nm ls
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+An
+.Nm ls
+command appeared in
+.At v1 .
+.Sh BUGS
+To maintain backward compatibility, the relationships between the many
+options are quite complex.
diff --git a/file_cmds/ls/ls.c b/file_cmds/ls/ls.c
new file mode 100644
index 0000000..e079333
--- /dev/null
+++ b/file_cmds/ls/ls.c
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <limits.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef COLORLS
+#include <termcap.h>
+#include <signal.h>
+#endif
+#ifdef __APPLE__
+#include <sys/acl.h>
+#include <sys/xattr.h>
+#include <sys/param.h>
+#include <get_compat.h>
+#include <sys/sysctl.h>
+#include <System/sys/fsctl.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+#include "ls.h"
+#include "extern.h"
+
+/*
+ * Upward approximation of the maximum number of characters needed to
+ * represent a value of integral type t as a string, excluding the
+ * NUL terminator, with provision for a sign.
+ */
+#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1)
+
+#define IS_DATALESS(sp) (f_dataless && (sp) && ((sp)->st_flags & SF_DATALESS))
+
+static void display(FTSENT *, FTSENT *);
+static u_quad_t makenines(u_quad_t);
+static int mastercmp(const FTSENT **, const FTSENT **);
+static void traverse(int, char **, int);
+
+static void (*printfcn)(DISPLAY *);
+static int (*sortfcn)(const FTSENT *, const FTSENT *);
+
+long blocksize; /* block size units */
+int termwidth = 80; /* default terminal width */
+
+/* flags */
+ int f_accesstime; /* use time of last access */
+ int f_birthtime; /* use time of file birth */
+ int f_flags; /* show flags associated with a file */
+ int f_humanval; /* show human-readable file sizes */
+ int f_inode; /* print inode */
+static int f_kblocks; /* print size in kilobytes */
+static int f_listdir; /* list actual directory, not contents */
+static int f_listdot; /* list files beginning with . */
+ int f_longform; /* long listing format */
+ int f_nonprint; /* show unprintables as ? */
+static int f_nosort; /* don't sort output */
+ int f_notabs; /* don't use tab-separated multi-col output */
+ int f_numericonly; /* don't convert uid/gid to name */
+ int f_octal; /* show unprintables as \xxx */
+ int f_octal_escape; /* like f_octal but use C escapes if possible */
+static int f_recursive; /* ls subdirectories also */
+static int f_reversesort; /* reverse whatever sort is used */
+ int f_sectime; /* print the real time for all files */
+static int f_singlecol; /* use single column output */
+ int f_size; /* list size in short listing */
+ int f_slash; /* similar to f_type, but only for dirs */
+ int f_sortacross; /* sort across rows, not down columns */
+ int f_statustime; /* use time of last mode change */
+ int f_stream; /* stream the output, separate with commas */
+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_acl; /* show ACLs in long listing */
+ int f_xattr; /* show extended attributes in long listing */
+ int f_group; /* show group */
+ int f_owner; /* show owner */
+ int f_dataless; /* distinguish dataless files in long listing,
+ and don't materialize dataless directories. */
+#ifdef COLORLS
+ int f_color; /* add type in color for non-regular files */
+
+char *ansi_bgcol; /* ANSI sequence to set background colour */
+char *ansi_fgcol; /* ANSI sequence to set foreground colour */
+char *ansi_coloff; /* ANSI sequence to reset colours */
+char *attrs_off; /* ANSI sequence to turn off attributes */
+char *enter_bold; /* ANSI sequence to set color to bold mode */
+#endif
+
+static int rval;
+
+int
+main(int argc, char *argv[])
+{
+ static char dot[] = ".", *dotav[] = {dot, NULL};
+ struct winsize win;
+ int ch, fts_options, notused;
+ char *p;
+#ifdef COLORLS
+ char termcapbuf[1024]; /* termcap definition buffer */
+ char tcapbuf[512]; /* capability buffer */
+ char *bp = tcapbuf;
+#endif
+
+ if (argc < 1)
+ usage();
+ (void)setlocale(LC_ALL, "");
+
+ /* Terminal defaults to -Cq, non-terminal defaults to -1. */
+ if (isatty(STDOUT_FILENO)) {
+ termwidth = 80;
+ if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
+ termwidth = atoi(p);
+ else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
+ win.ws_col > 0)
+ termwidth = win.ws_col;
+ f_nonprint = 1;
+ } else {
+ f_singlecol = 1;
+ /* retrieve environment variable, in case of explicit -C */
+ p = getenv("COLUMNS");
+ if (p)
+ termwidth = atoi(p);
+ }
+
+ /* Root is -A automatically. */
+ if (!getuid())
+ f_listdot = 1;
+
+ fts_options = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "1@ABCFGHLOPRSTUWabcdefghiklmnopqrstuvwx%"))
+ != -1) {
+ switch (ch) {
+ /*
+ * The -1, -C, -x and -l options all override each other so
+ * shell aliasing works right.
+ */
+ case '1':
+ f_singlecol = 1;
+ f_longform = 0;
+ f_stream = 0;
+ break;
+ case 'B':
+ f_nonprint = 0;
+ f_octal = 1;
+ f_octal_escape = 0;
+ break;
+ case 'C':
+ f_sortacross = f_longform = f_singlecol = 0;
+ break;
+ case 'l':
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ break;
+ case 'x':
+ f_sortacross = 1;
+ f_longform = 0;
+ f_singlecol = 0;
+ break;
+ /* The -c and -u options override each other. */
+ case 'c':
+ f_statustime = 1;
+ f_accesstime = f_birthtime = 0;
+ break;
+ case 'u':
+ f_accesstime = 1;
+ f_statustime = f_birthtime = 0;
+ break;
+ case 'U':
+ f_birthtime = 1;
+ f_statustime = f_accesstime = 0;
+ break;
+ case 'F':
+ f_type = 1;
+ f_slash = 0;
+ break;
+ case 'H':
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options &= ~FTS_LOGICAL;
+ fts_options |= FTS_PHYSICAL;
+ fts_options |= FTS_COMFOLLOWDIR;
+ } else
+ fts_options |= FTS_COMFOLLOW;
+ break;
+ case 'G':
+ setenv("CLICOLOR", "", 1);
+ break;
+ case 'L':
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR);
+ }
+ break;
+ case 'P':
+ fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR);
+ fts_options &= ~FTS_LOGICAL;
+ fts_options |= FTS_PHYSICAL;
+ break;
+ case 'R':
+ f_recursive = 1;
+ break;
+ case 'a':
+ fts_options |= FTS_SEEDOT;
+ /* FALLTHROUGH */
+ case 'A':
+ f_listdot = 1;
+ break;
+ /* The -d option turns off the -R option. */
+ case 'd':
+ f_listdir = 1;
+ f_recursive = 0;
+ break;
+ case 'f':
+ f_nosort = 1;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options |= FTS_SEEDOT;
+ f_listdot = 1;
+ }
+ break;
+ 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;
+ break;
+ case 'i':
+ f_inode = 1;
+ break;
+ case 'k':
+ f_kblocks = 1;
+ break;
+ case 'm':
+ f_stream = 1;
+ f_singlecol = 0;
+ f_longform = 0;
+ break;
+ case 'n':
+ f_numericonly = 1;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ }
+ break;
+ case 'o':
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ f_owner = 1;
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ } else {
+ f_flags = 1;
+ }
+ break;
+ case 'p':
+ f_slash = 1;
+ f_type = 1;
+ break;
+ case 'q':
+ f_nonprint = 1;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'r':
+ f_reversesort = 1;
+ break;
+ case 'S':
+ /* Darwin 1.4.1 compatibility */
+ f_sizesort = 1;
+ break;
+ case 's':
+ f_size = 1;
+ break;
+ case 'T':
+ f_sectime = 1;
+ break;
+ case 't':
+ f_timesort = 1;
+ break;
+ case 'W':
+ f_whiteout = 1;
+ break;
+ case 'v':
+ /* Darwin 1.4.1 compatibility */
+ f_nonprint = 0;
+ break;
+ case 'b':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 1;
+ break;
+ case 'w':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'e':
+ f_acl = 1;
+ break;
+ case '@':
+ f_xattr = 1;
+ break;
+ case 'O':
+ f_flags = 1;
+ break;
+ case '%':
+ f_dataless = 1;
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Enabling of colours is conditional on the environment. */
+ if (getenv("CLICOLOR") &&
+ (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
+#ifdef COLORLS
+ if (tgetent(termcapbuf, getenv("TERM")) == 1) {
+ ansi_fgcol = tgetstr("AF", &bp);
+ ansi_bgcol = tgetstr("AB", &bp);
+ attrs_off = tgetstr("me", &bp);
+ enter_bold = tgetstr("md", &bp);
+
+ /* To switch colours off use 'op' if
+ * available, otherwise use 'oc', or
+ * don't do colours at all. */
+ ansi_coloff = tgetstr("op", &bp);
+ if (!ansi_coloff)
+ ansi_coloff = tgetstr("oc", &bp);
+ if (ansi_fgcol && ansi_bgcol && ansi_coloff)
+ f_color = 1;
+ }
+#else
+ (void)fprintf(stderr, "Color support not compiled in.\n");
+#endif /*COLORLS*/
+
+#ifdef COLORLS
+ if (f_color) {
+ /*
+ * We can't put tabs and color sequences together:
+ * column number will be incremented incorrectly
+ * for "stty oxtabs" mode.
+ */
+ f_notabs = 1;
+ (void)signal(SIGINT, colorquit);
+ (void)signal(SIGQUIT, colorquit);
+ parsecolors(getenv("LSCOLORS"));
+ }
+#endif
+
+ /*
+ * If not -F, -i, -l, -s, -t or -% options, don't require stat
+ * information, unless in color mode in which case we do
+ * need this to determine which colors to display.
+ */
+ if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort && !f_dataless
+#ifdef COLORLS
+ && !f_color
+#endif
+ )
+ fts_options |= FTS_NOSTAT;
+
+ /*
+ * If not -F, -d or -l options, follow any symbolic links listed on
+ * the command line.
+ */
+ if (!f_longform && !f_listdir && !f_type && !f_inode)
+ fts_options |= FTS_COMFOLLOW;
+
+ /*
+ * If -W, show whiteout entries
+ */
+#ifdef FTS_WHITEOUT
+ if (f_whiteout)
+ fts_options |= FTS_WHITEOUT;
+#endif
+
+ /* If -l or -s, figure out block size. */
+ if (f_longform || f_size) {
+ if (f_kblocks)
+ blocksize = 2;
+ else {
+ (void)getbsize(&notused, &blocksize);
+ blocksize /= 512;
+ }
+ }
+ /* Select a sort function. */
+ if (f_reversesort) {
+ if (f_sizesort)
+ sortfcn = revsizecmp;
+ else if (!f_timesort)
+ sortfcn = revnamecmp;
+ else if (f_accesstime)
+ sortfcn = revacccmp;
+ else if (f_statustime)
+ sortfcn = revstatcmp;
+ else if (f_birthtime)
+ sortfcn = revbirthcmp;
+ else /* Use modification time. */
+ sortfcn = revmodcmp;
+ } else {
+ if (f_sizesort)
+ sortfcn = sizecmp;
+ else if (!f_timesort)
+ sortfcn = namecmp;
+ else if (f_accesstime)
+ sortfcn = acccmp;
+ else if (f_statustime)
+ sortfcn = statcmp;
+ else if (f_birthtime)
+ sortfcn = birthcmp;
+ else /* Use modification time. */
+ sortfcn = modcmp;
+ }
+
+ /* Select a print function. */
+ if (f_singlecol)
+ printfcn = printscol;
+ else if (f_longform)
+ printfcn = printlong;
+ else if (f_stream)
+ printfcn = printstream;
+ else
+ printfcn = printcol;
+
+#ifdef __APPLE__
+ if (f_dataless) {
+ // don't materialize dataless directories from the cloud
+ // (particularly usefull when listing recursively)
+ int state = 1;
+ if (sysctlbyname("vfs.nspace.prevent_materialization", NULL, NULL, &state, sizeof(state)) < 0) {
+ err(1, "prevent_materialization");
+ }
+ }
+#endif /* __APPLE__ */
+
+ if (argc)
+ traverse(argc, argv, fts_options);
+ else
+ traverse(1, dotav, fts_options);
+ exit(rval);
+}
+
+static int output; /* If anything output. */
+
+/*
+ * Traverse() walks the logical directory structure specified by the argv list
+ * in the order specified by the mastercmp() comparison function. During the
+ * traversal it passes linked lists of structures to display() which represent
+ * a superset (may be exact set) of the files to be displayed.
+ */
+static void
+traverse(int argc, char *argv[], int options)
+{
+ FTS *ftsp;
+ FTSENT *p, *chp;
+ int ch_options, error;
+
+ if ((ftsp =
+ fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
+ err(1, "fts_open");
+
+ display(NULL, fts_children(ftsp, 0));
+ if (f_listdir) {
+ fts_close(ftsp);
+ return;
+ }
+
+ /*
+ * If not recursing down this tree and don't need stat info, just get
+ * the names.
+ */
+ ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
+
+ while ((p = fts_read(ftsp)) != NULL)
+ switch (p->fts_info) {
+ case FTS_DC:
+ warnx("%s: directory causes a cycle", p->fts_name);
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ rval = 1;
+ }
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_D:
+ if (p->fts_level != FTS_ROOTLEVEL &&
+ p->fts_name[0] == '.' && !f_listdot) {
+ fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+
+ if (IS_DATALESS(p->fts_statp)) {
+ fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+
+ /*
+ * If already output something, put out a newline as
+ * a separator. If multiple arguments, precede each
+ * directory with its name.
+ */
+ if (output)
+ (void)printf("\n%s:\n", p->fts_path);
+ else if (argc > 1) {
+ (void)printf("%s:\n", p->fts_path);
+ output = 1;
+ }
+ chp = fts_children(ftsp, ch_options);
+ if (COMPAT_MODE("bin/ls", "Unix2003") && ((options & FTS_LOGICAL)!=0)) {
+ FTSENT *curr;
+ for (curr = chp; curr; curr = curr->fts_link) {
+ if (curr->fts_info == FTS_SLNONE)
+ curr->fts_number = NO_PRINT;
+ }
+ }
+ display(p, chp);
+
+ if (!f_recursive && chp != NULL)
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ case FTS_SLNONE: /* Same as default unless Unix conformance */
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ if ((options & FTS_LOGICAL)!=0) { /* -L was specified */
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno ?: ENOENT));
+ rval = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ error = errno;
+ fts_close(ftsp);
+ errno = error;
+
+ if (errno)
+ err(1, "fts_read");
+}
+
+/*
+ * Display() takes a linked list of FTSENT structures and passes the list
+ * along with any other necessary information to the print function. P
+ * points to the parent directory of the display list.
+ */
+static void
+display(FTSENT *p, FTSENT *list)
+{
+ struct stat *sp;
+ DISPLAY d;
+ FTSENT *cur;
+ NAMES *np;
+ off_t maxsize;
+ u_int64_t btotal, maxblock;
+ u_long lattrlen, maxlen, maxnlink, maxlattr;
+ ino_t maxinode;
+ int bcfile, maxflags;
+ gid_t maxgroup;
+ uid_t maxuser;
+ size_t flen, ulen, glen;
+ char *initmax;
+ int entries, needstats;
+ const char *user, *group;
+ char *flags, *lattr = NULL;
+ char buf[STRBUF_SIZEOF(u_quad_t) + 1];
+ char ngroup[STRBUF_SIZEOF(uid_t) + 1];
+ char nuser[STRBUF_SIZEOF(gid_t) + 1];
+#ifdef __APPLE__
+ acl_entry_t dummy;
+ ssize_t xattr_size;
+ char *filename;
+ char path[MAXPATHLEN+1];
+#endif // __APPLE__
+ /*
+ * If list is NULL there are two possibilities: that the parent
+ * directory p has no children, or that fts_children() returned an
+ * error. We ignore the error case since it will be replicated
+ * on the next call to fts_read() on the post-order visit to the
+ * directory p, and will be signaled in traverse().
+ */
+ if (list == NULL)
+ return;
+
+ needstats = f_inode || f_longform || f_size;
+ btotal = 0;
+ initmax = getenv("LS_COLWIDTHS");
+ /* Fields match -lios order. New ones should be added at the end. */
+ maxlattr = maxblock = maxinode = maxlen = maxnlink =
+ maxuser = maxgroup = maxflags = maxsize = 0;
+ if (initmax != NULL && *initmax != '\0') {
+ char *initmax2, *jinitmax;
+ int ninitmax;
+
+ /* Fill-in "::" as "0:0:0" for the sake of scanf. */
+ jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2);
+ if (jinitmax == NULL)
+ err(1, "malloc");
+ if (*initmax == ':')
+ strcpy(initmax2, "0:"), initmax2 += 2;
+ else
+ *initmax2++ = *initmax, *initmax2 = '\0';
+ for (initmax++; *initmax != '\0'; initmax++) {
+ if (initmax[-1] == ':' && initmax[0] == ':') {
+ *initmax2++ = '0';
+ *initmax2++ = initmax[0];
+ initmax2[1] = '\0';
+ } else {
+ *initmax2++ = initmax[0];
+ initmax2[1] = '\0';
+ }
+ }
+ if (initmax2[-1] == ':')
+ strcpy(initmax2, "0");
+
+ ninitmax = sscanf(jinitmax,
+#if _DARWIN_FEATURE_64_BIT_INODE
+ " %llu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ",
+#else
+ " %lu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ",
+#endif
+ &maxinode, &maxblock, &maxnlink, &maxuser,
+ &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr);
+ f_notabs = 1;
+ switch (ninitmax) {
+ case 0:
+ maxinode = 0;
+ /* FALLTHROUGH */
+ case 1:
+ maxblock = 0;
+ /* FALLTHROUGH */
+ case 2:
+ maxnlink = 0;
+ /* FALLTHROUGH */
+ case 3:
+ maxuser = 0;
+ /* FALLTHROUGH */
+ case 4:
+ maxgroup = 0;
+ /* FALLTHROUGH */
+ case 5:
+ maxflags = 0;
+ /* FALLTHROUGH */
+ case 6:
+ maxsize = 0;
+ /* FALLTHROUGH */
+ case 7:
+ maxlen = 0;
+ /* FALLTHROUGH */
+ case 8:
+ maxlattr = 0;
+ /* FALLTHROUGH */
+#ifdef COLORLS
+ if (!f_color)
+#endif
+ f_notabs = 0;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ maxinode = makenines(maxinode);
+ maxblock = makenines(maxblock);
+ maxnlink = makenines(maxnlink);
+ maxsize = makenines(maxsize);
+ }
+ bcfile = 0;
+ flags = NULL;
+ for (cur = list, entries = 0; cur; cur = cur->fts_link) {
+ if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
+ warnx("%s: %s",
+ cur->fts_name, strerror(cur->fts_errno));
+ cur->fts_number = NO_PRINT;
+ rval = 1;
+ continue;
+ }
+ /*
+ * P is NULL if list is the argv list, to which different rules
+ * apply.
+ */
+ if (p == NULL) {
+ /* Directories will be displayed later. */
+ if (cur->fts_info == FTS_D && !f_listdir) {
+ cur->fts_number = NO_PRINT;
+ continue;
+ }
+ } else {
+ /* Only display dot file if -a/-A set. */
+ if (cur->fts_name[0] == '.' && !f_listdot) {
+ cur->fts_number = NO_PRINT;
+ continue;
+ }
+ }
+ if (cur->fts_namelen > maxlen)
+ maxlen = cur->fts_namelen;
+ if (f_octal || f_octal_escape) {
+ u_long t = len_octal(cur->fts_name, cur->fts_namelen);
+
+ if (t > maxlen)
+ maxlen = t;
+ }
+ if (needstats) {
+ sp = cur->fts_statp;
+ if (sp->st_blocks > maxblock)
+ maxblock = sp->st_blocks;
+ if (sp->st_ino > maxinode)
+ maxinode = sp->st_ino;
+ if (sp->st_nlink > maxnlink)
+ maxnlink = sp->st_nlink;
+ if (sp->st_size > maxsize)
+ maxsize = sp->st_size;
+
+ btotal += sp->st_blocks;
+ if (f_longform) {
+ if (f_numericonly) {
+ (void)snprintf(nuser, sizeof(nuser),
+ "%u", sp->st_uid);
+ (void)snprintf(ngroup, sizeof(ngroup),
+ "%u", sp->st_gid);
+ user = nuser;
+ group = ngroup;
+ } else {
+ user = user_from_uid(sp->st_uid, 0);
+ group = group_from_gid(sp->st_gid, 0);
+ }
+ if ((ulen = strlen(user)) > maxuser)
+ maxuser = ulen;
+ if ((glen = strlen(group)) > maxgroup)
+ maxgroup = glen;
+ if (f_flags) {
+ flags = fflagstostr(sp->st_flags);
+ if (flags != NULL && *flags == '\0') {
+ free(flags);
+ flags = strdup("-");
+ }
+ if (flags == NULL)
+ err(1, "fflagstostr");
+ flen = strlen(flags);
+ if (flen > (size_t)maxflags)
+ maxflags = flen;
+ } else
+ flen = 0;
+ lattr = NULL;
+ lattrlen = 0;
+
+ if ((np = calloc(1, sizeof(NAMES) + lattrlen +
+ ulen + glen + flen + 4)) == NULL)
+ err(1, "malloc");
+
+ np->user = &np->data[0];
+ (void)strcpy(np->user, user);
+ np->group = &np->data[ulen + 1];
+ (void)strcpy(np->group, group);
+#ifdef __APPLE__
+ if (cur->fts_level == FTS_ROOTLEVEL) {
+ filename = cur->fts_name;
+ } else {
+ snprintf(path, sizeof(path), "%s/%s", cur->fts_parent->fts_accpath, cur->fts_name);
+ filename = path;
+ }
+ xattr_size = listxattr(filename, NULL, 0, XATTR_NOFOLLOW);
+ if (xattr_size < 0) {
+ xattr_size = 0;
+ }
+ if ((xattr_size > 0) && f_xattr) {
+ /* collect sizes */
+ np->xattr_names = malloc(xattr_size);
+ listxattr(filename, np->xattr_names, xattr_size, XATTR_NOFOLLOW);
+ for (char *name = np->xattr_names; name < np->xattr_names + xattr_size;
+ name += strlen(name)+1) {
+ np->xattr_sizes = reallocf(np->xattr_sizes, (np->xattr_count+1) * sizeof(np->xattr_sizes[0]));
+ np->xattr_sizes[np->xattr_count] = getxattr(filename, name, 0, 0, 0, XATTR_NOFOLLOW);
+ np->xattr_count++;
+ }
+ }
+ /* symlinks can not have ACLs */
+ np->acl = acl_get_link_np(filename, ACL_TYPE_EXTENDED);
+ if (np->acl) {
+ if (acl_get_entry(np->acl, ACL_FIRST_ENTRY, &dummy) == -1) {
+ acl_free(np->acl);
+ np->acl = NULL;
+ }
+ }
+ if (xattr_size > 0) {
+ np->mode_suffix = '@';
+ } else if (np->acl) {
+ np->mode_suffix = '+';
+ } else {
+ np->mode_suffix = ' ';
+ }
+ if (IS_DATALESS(sp)) {
+ np->mode_suffix = '%';
+ }
+ if (!f_acl) {
+ acl_free(np->acl);
+ np->acl = NULL;
+ }
+#endif // __APPLE__
+ if (S_ISCHR(sp->st_mode) ||
+ S_ISBLK(sp->st_mode))
+ bcfile = 1;
+
+ if (f_flags) {
+ np->flags = &np->data[ulen + glen + 2];
+ (void)strcpy(np->flags, flags);
+ free(flags);
+ }
+ cur->fts_pointer = np;
+ }
+ }
+ ++entries;
+ }
+
+ if (!entries)
+ return;
+
+ d.list = list;
+ d.entries = entries;
+ d.maxlen = maxlen;
+ if (needstats) {
+ d.bcfile = bcfile;
+ d.btotal = btotal;
+ (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxblock);
+ d.s_block = strlen(buf);
+ d.s_flags = maxflags;
+ d.s_lattr = maxlattr;
+ d.s_group = maxgroup;
+#if _DARWIN_FEATURE_64_BIT_INODE
+ (void)snprintf(buf, sizeof(buf), "%llu", maxinode);
+#else
+ (void)snprintf(buf, sizeof(buf), "%lu", maxinode);
+#endif
+ d.s_inode = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%lu", maxnlink);
+ d.s_nlink = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxsize);
+ d.s_size = strlen(buf);
+ d.s_user = maxuser;
+ }
+ printfcn(&d);
+ output = 1;
+
+ if (f_longform) {
+ for (cur = list; cur; cur = cur->fts_link) {
+ np = cur->fts_pointer;
+ if (np) {
+ if (np->acl) {
+ acl_free(np->acl);
+ }
+ free(np->xattr_names);
+ free(np->xattr_sizes);
+ free(np);
+ cur->fts_pointer = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * Ordering for mastercmp:
+ * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
+ * as larger than directories. Within either group, use the sort function.
+ * All other levels use the sort function. Error entries remain unsorted.
+ */
+static int
+mastercmp(const FTSENT **a, const FTSENT **b)
+{
+ int a_info, b_info;
+
+ a_info = (*a)->fts_info;
+ if (a_info == FTS_ERR)
+ return (0);
+ b_info = (*b)->fts_info;
+ if (b_info == FTS_ERR)
+ return (0);
+
+ if (a_info == FTS_NS || b_info == FTS_NS)
+ return (namecmp(*a, *b));
+
+ if (a_info != b_info &&
+ (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
+ if (a_info == FTS_D)
+ return (1);
+ if (b_info == FTS_D)
+ return (-1);
+ }
+ return (sortfcn(*a, *b));
+}
+
+/*
+ * Makenines() returns (10**n)-1. This is useful for converting a width
+ * into a number that wide in decimal.
+ */
+static u_quad_t
+makenines(u_quad_t n)
+{
+ u_long i;
+ u_quad_t reg;
+
+ reg = 1;
+ /* Use a loop instead of pow(), since all values of n are small. */
+ for (i = 0; i < n; i++)
+ reg *= 10;
+ reg--;
+
+ return reg;
+}
diff --git a/file_cmds/ls/ls.h b/file_cmds/ls/ls.h
new file mode 100644
index 0000000..d1ef037
--- /dev/null
+++ b/file_cmds/ls/ls.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * 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.
+ *
+ * from: @(#)ls.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/bin/ls/ls.h,v 1.18 2002/05/19 02:51:36 tjr Exp $
+ */
+
+#ifndef _LS_H_
+#define _LS_H_
+
+#define NO_PRINT 1
+
+extern long blocksize; /* block size units */
+
+extern int f_accesstime; /* use time of last access */
+extern int f_birthtime; /* use time of file birth */
+extern int f_flags; /* show flags associated with a file */
+extern int f_humanval; /* show human-readable file sizes */
+extern int f_inode; /* print inode */
+extern int f_longform; /* long listing format */
+extern int f_octal; /* print unprintables in octal */
+extern int f_octal_escape; /* like f_octal but use C escapes if possible */
+extern int f_nonprint; /* show unprintables as ? */
+extern int f_sectime; /* print the real time for all files */
+extern int f_size; /* list size in short listing */
+extern int f_slash; /* append a '/' if the file is a directory */
+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_xattr; /* print extended attributes in long format */
+extern int f_group; /* list group without owner */
+extern int f_owner; /* list owner without group */
+#ifdef COLORLS
+extern int f_color; /* add type in color for non-regular files */
+#endif
+extern int f_numericonly; /* don't convert uid/gid to name */
+
+#ifdef __APPLE__
+#include <sys/acl.h>
+#endif // __APPLE__
+
+typedef struct {
+ FTSENT *list;
+ u_int64_t btotal;
+ int bcfile;
+ int entries;
+ int maxlen;
+ u_int s_block;
+ u_int s_flags;
+ u_int s_lattr;
+ u_int s_group;
+ u_int s_inode;
+ u_int s_nlink;
+ u_int s_size;
+ u_int s_user;
+} DISPLAY;
+
+typedef struct {
+ char *user;
+ char *group;
+ char *flags;
+#ifndef __APPLE__
+ char *lattr;
+#else
+ char *xattr_names; /* f_xattr */
+ int *xattr_sizes;
+ acl_t acl; /* f_acl */
+ int xattr_count;
+ char mode_suffix; /* @ | + | % | <space> */
+#endif /* __APPLE__ */
+ char data[1];
+} NAMES;
+
+#endif /* _LS_H_ */
diff --git a/file_cmds/ls/print.c b/file_cmds/ls/print.c
new file mode 100644
index 0000000..de04bf3
--- /dev/null
+++ b/file_cmds/ls/print.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__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/xattr.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <TargetConditionals.h>
+#include <membership.h>
+#include <membershipPriv.h>
+#include <uuid/uuid.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <math.h>
+#include <langinfo.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef COLORLS
+#include <ctype.h>
+#include <termcap.h>
+#include <signal.h>
+#endif
+#include <stdint.h> /* intmax_t */
+#include <assert.h>
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "ls.h"
+#include "extern.h"
+
+static int printaname(FTSENT *, u_long, u_long);
+static void printlink(FTSENT *);
+static void printtime(time_t);
+static int printtype(u_int);
+static void printsize(size_t, off_t);
+#ifdef COLORLS
+static void endcolor(int);
+static int colortype(mode_t);
+#endif
+
+#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
+
+#ifdef COLORLS
+/* Most of these are taken from <sys/stat.h> */
+typedef enum Colors {
+ C_DIR, /* directory */
+ C_LNK, /* symbolic link */
+ C_SOCK, /* socket */
+ C_FIFO, /* pipe */
+ C_EXEC, /* executable */
+ C_BLK, /* block special */
+ C_CHR, /* character special */
+ C_SUID, /* setuid executable */
+ C_SGID, /* setgid executable */
+ C_WSDIR, /* directory writeble to others, with sticky
+ * bit */
+ C_WDIR, /* directory writeble to others, without
+ * sticky bit */
+ C_NUMCOLORS /* just a place-holder */
+} Colors;
+
+static const char *defcolors = "exfxcxdxbxegedabagacad";
+
+/* colors for file types */
+static struct {
+ int num[2];
+ int bold;
+} colors[C_NUMCOLORS];
+#endif
+
+void
+printscol(DISPLAY *dp)
+{
+ FTSENT *p;
+
+ assert(dp);
+ if (COMPAT_MODE("bin/ls", "Unix2003") && (dp->list != NULL)) {
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+ }
+
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ (void)printaname(p, dp->s_inode, dp->s_block);
+ (void)putchar('\n');
+ }
+}
+
+/*
+ * print name in current style
+ */
+static int
+printname(const char *name)
+{
+ if (f_octal || f_octal_escape)
+ return prn_octal(name);
+ else if (f_nonprint)
+ return prn_printable(name);
+ else
+ return prn_normal(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 type;
+ char *name = NULL;
+ char *recname = NULL;
+
+#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
+ name = (char *) malloc(MAXNAMETAG);
+
+ if (NULL == name) {
+ err(1, "malloc");
+ }
+
+ if (f_numericonly) {
+ goto errout;
+ }
+
+ if (mbr_identifier_translate(ID_TYPE_UUID, *uu, sizeof(*uu), ID_TYPE_NAME, (void **) &recname, &type)) {
+ goto errout;
+ }
+
+ snprintf(name, MAXNAMETAG, "%s:%s", (type == MBR_REC_TYPE_USER ? "user" : "group"), recname);
+ free(recname);
+
+ return name;
+errout:
+ uuid_unparse_upper(*uu, name);
+
+ return name;
+}
+
+static void
+printxattr(DISPLAY *dp, int count, char *buf, int sizes[])
+{
+ for (int i = 0; i < count; i++) {
+ putchar('\t');
+ printname(buf);
+ putchar('\t');
+ printsize(dp->s_size, sizes[i]);
+ putchar('\n');
+ buf += strlen(buf) + 1;
+ }
+}
+
+static void
+printacl(acl_t acl, int isdir)
+{
+ acl_entry_t entry = NULL;
+ 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 (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;
+ if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
+ 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)
+{
+ struct stat *sp;
+ FTSENT *p;
+ NAMES *np;
+ char buf[20];
+#ifdef COLORLS
+ int color_printed = 0;
+#endif
+
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ sp = p->fts_statp;
+ if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ (void)printf("%*llu ", dp->s_inode, (u_quad_t)sp->st_ino);
+#else
+ (void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
+#endif
+ if (f_size)
+ (void)printf("%*qu ",
+ dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize));
+ strmode(sp->st_mode, buf);
+ np = p->fts_pointer;
+#ifdef __APPLE__
+ buf[10] = '\0'; /* make +/@ abut the mode */
+ char str[2] = { np->mode_suffix, '\0' };
+#endif /* __APPLE__ */
+ if (f_group && f_owner) { /* means print neither */
+#ifdef __APPLE__
+ (void)printf("%s%s %*u ", buf, str, dp->s_nlink,
+ sp->st_nlink);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u ", buf, dp->s_nlink,
+ sp->st_nlink);
+#endif /* __APPLE__ */
+ }
+ else if (f_group) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, 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 if (f_owner) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#endif /* __APPLE__ */
+ }
+ else {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s %-*s ", buf, str, 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);
+ 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 ",
+ major(sp->st_rdev),
+ (u_int)minor(sp->st_rdev));
+ else
+ (void)printf("%3d, %3d ",
+ major(sp->st_rdev), minor(sp->st_rdev));
+ else if (dp->bcfile)
+ (void)printf("%*s%*qu ",
+ 8 - dp->s_size, "", dp->s_size, (u_int64_t)sp->st_size);
+ else
+ printsize(dp->s_size, sp->st_size);
+ if (f_accesstime)
+ printtime(sp->st_atime);
+ else if (f_statustime)
+ printtime(sp->st_ctime);
+ else if (f_birthtime)
+ printtime(sp->st_birthtime);
+ else
+ printtime(sp->st_mtime);
+#ifdef COLORLS
+ if (f_color)
+ color_printed = colortype(sp->st_mode);
+#endif
+ (void)printname(p->fts_name);
+#ifdef COLORLS
+ if (f_color && color_printed)
+ endcolor(0);
+#endif
+ if (f_type)
+ (void)printtype(sp->st_mode);
+ if (S_ISLNK(sp->st_mode))
+ printlink(p);
+ (void)putchar('\n');
+#ifdef __APPLE__
+ if (np->xattr_count && f_xattr) {
+ printxattr(dp, np->xattr_count, np->xattr_names, np->xattr_sizes);
+ }
+ if (np->acl != NULL && f_acl) {
+ printacl(np->acl, S_ISDIR(sp->st_mode));
+ }
+#endif /* __APPLE__ */
+ }
+}
+
+void
+printstream(DISPLAY *dp)
+{
+ FTSENT *p;
+ extern int termwidth;
+ int chcnt;
+
+ for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
+ if (p->fts_number == NO_PRINT)
+ continue;
+ if (strlen(p->fts_name) + chcnt +
+ (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
+ putchar('\n');
+ chcnt = 0;
+ }
+ chcnt += printaname(p, dp->s_inode, dp->s_block);
+ if (p->fts_link) {
+ printf(", ");
+ chcnt += 2;
+ }
+ }
+ if (chcnt)
+ putchar('\n');
+}
+
+void
+printcol(DISPLAY *dp)
+{
+ extern int termwidth;
+ static FTSENT **array;
+ static int lastentries = -1;
+ FTSENT *p;
+ int base;
+ int chcnt;
+ int cnt;
+ int col;
+ int colwidth;
+ int endcol;
+ int num;
+ int numcols;
+ int numrows;
+ int row;
+ int tabwidth;
+
+ if (f_notabs)
+ tabwidth = 1;
+ else
+ tabwidth = 8;
+
+ /*
+ * Have to do random access in the linked list -- build a table
+ * of pointers.
+ */
+ if ((lastentries == -1) || (dp->entries > lastentries)) {
+ lastentries = dp->entries;
+ if ((array = realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
+ warn(NULL);
+ printscol(dp);
+ return;
+ }
+ }
+ memset(array, 0, dp->entries * sizeof(FTSENT *));
+ for (p = dp->list, num = 0; p; p = p->fts_link)
+ if (p->fts_number != NO_PRINT)
+ array[num++] = p;
+
+ colwidth = dp->maxlen;
+ if (f_inode)
+ colwidth += dp->s_inode + 1;
+ if (f_size)
+ colwidth += dp->s_block + 1;
+ if (f_type)
+ colwidth += 1;
+
+ colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
+ if (termwidth < 2 * colwidth) {
+ printscol(dp);
+ return;
+ }
+ numcols = termwidth / colwidth;
+ numrows = num / numcols;
+ if (num % numcols)
+ ++numrows;
+
+ assert(dp->list);
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+
+ base = 0;
+ for (row = 0; row < numrows; ++row) {
+ endcol = colwidth;
+ if (!f_sortacross)
+ base = row;
+ for (col = 0, chcnt = 0; col < numcols; ++col) {
+ assert(base < dp->entries);
+ chcnt += printaname(array[base], dp->s_inode, dp->s_block);
+ if (f_sortacross)
+ base++;
+ else
+ base += numrows;
+ if (base >= num)
+ break;
+ while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
+ <= endcol) {
+ if (f_sortacross && col + 1 >= numcols)
+ break;
+ (void)putchar(f_notabs ? ' ' : '\t');
+ chcnt = cnt;
+ }
+ endcol += colwidth;
+ }
+ (void)putchar('\n');
+ }
+}
+
+/*
+ * print [inode] [size] name
+ * return # of characters printed, no trailing characters.
+ */
+static int
+printaname(FTSENT *p, u_long inodefield, u_long sizefield)
+{
+ struct stat *sp;
+ int chcnt;
+#ifdef COLORLS
+ int color_printed = 0;
+#endif
+
+ sp = p->fts_statp;
+ chcnt = 0;
+ if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ chcnt += printf("%*llu ", (int)inodefield, (u_quad_t)sp->st_ino);
+#else
+ chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
+#endif
+ if (f_size)
+ chcnt += printf("%*qu ",
+ (int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize));
+#ifdef COLORLS
+ if (f_color)
+ color_printed = colortype(sp->st_mode);
+#endif
+ chcnt += printname(p->fts_name);
+#ifdef COLORLS
+ if (f_color && color_printed)
+ endcolor(0);
+#endif
+ if (f_type)
+ chcnt += printtype(sp->st_mode);
+ return (chcnt);
+}
+
+static void
+printtime(time_t ftime)
+{
+ char longstring[80];
+ static time_t now;
+ const char *format;
+ static int d_first = -1;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ if (now == 0)
+ now = time(NULL);
+
+#define SIXMONTHS ((365 / 2) * 86400)
+ if (f_sectime)
+ /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
+ format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
+ else if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ if (ftime + SIXMONTHS > now && ftime <= now)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ }
+ else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ strftime(longstring, sizeof(longstring), format, localtime(&ftime));
+ fputs(longstring, stdout);
+}
+
+static int
+printtype(u_int mode)
+{
+
+ if (f_slash) {
+ if ((mode & S_IFMT) == S_IFDIR) {
+ (void)putchar('/');
+ return (1);
+ }
+ return (0);
+ }
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ (void)putchar('/');
+ return (1);
+ case S_IFIFO:
+ (void)putchar('|');
+ return (1);
+ case S_IFLNK:
+ (void)putchar('@');
+ return (1);
+ case S_IFSOCK:
+ (void)putchar('=');
+ return (1);
+ case S_IFWHT:
+ (void)putchar('%');
+ return (1);
+ default:
+ break;
+ }
+ if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ (void)putchar('*');
+ return (1);
+ }
+ return (0);
+}
+
+#ifdef COLORLS
+static int
+putch(int c)
+{
+ (void)putchar(c);
+ return 0;
+}
+
+static int
+writech(int c)
+{
+ char tmp = c;
+
+ (void)write(STDOUT_FILENO, &tmp, 1);
+ return 0;
+}
+
+static void
+printcolor(Colors c)
+{
+ char *ansiseq;
+
+ if (colors[c].bold)
+ tputs(enter_bold, 1, putch);
+
+ if (colors[c].num[0] != -1) {
+ ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
+ if (ansiseq)
+ tputs(ansiseq, 1, putch);
+ }
+ if (colors[c].num[1] != -1) {
+ ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
+ if (ansiseq)
+ tputs(ansiseq, 1, putch);
+ }
+}
+
+static void
+endcolor(int sig)
+{
+ tputs(ansi_coloff, 1, sig ? writech : putch);
+ tputs(attrs_off, 1, sig ? writech : putch);
+}
+
+static int
+colortype(mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ if (mode & S_IWOTH)
+ if (mode & S_ISTXT)
+ printcolor(C_WSDIR);
+ else
+ printcolor(C_WDIR);
+ else
+ printcolor(C_DIR);
+ return (1);
+ case S_IFLNK:
+ printcolor(C_LNK);
+ return (1);
+ case S_IFSOCK:
+ printcolor(C_SOCK);
+ return (1);
+ case S_IFIFO:
+ printcolor(C_FIFO);
+ return (1);
+ case S_IFBLK:
+ printcolor(C_BLK);
+ return (1);
+ case S_IFCHR:
+ printcolor(C_CHR);
+ return (1);
+ }
+ if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ if (mode & S_ISUID)
+ printcolor(C_SUID);
+ else if (mode & S_ISGID)
+ printcolor(C_SGID);
+ else
+ printcolor(C_EXEC);
+ return (1);
+ }
+ return (0);
+}
+
+void
+parsecolors(const char *cs)
+{
+ int i;
+ int j;
+ int len;
+ char c[2];
+ short legacy_warn = 0;
+
+ if (cs == NULL)
+ cs = ""; /* LSCOLORS not set */
+ len = strlen(cs);
+ for (i = 0; i < C_NUMCOLORS; i++) {
+ colors[i].bold = 0;
+
+ if (len <= 2 * i) {
+ c[0] = defcolors[2 * i];
+ c[1] = defcolors[2 * i + 1];
+ } else {
+ c[0] = cs[2 * i];
+ c[1] = cs[2 * i + 1];
+ }
+ for (j = 0; j < 2; j++) {
+ /* Legacy colours used 0-7 */
+ if (c[j] >= '0' && c[j] <= '7') {
+ colors[i].num[j] = c[j] - '0';
+ if (!legacy_warn) {
+ fprintf(stderr,
+ "warn: LSCOLORS should use "
+ "characters a-h instead of 0-9 ("
+ "see the manual page)\n");
+ }
+ legacy_warn = 1;
+ } else if (c[j] >= 'a' && c[j] <= 'h')
+ colors[i].num[j] = c[j] - 'a';
+ else if (c[j] >= 'A' && c[j] <= 'H') {
+ colors[i].num[j] = c[j] - 'A';
+ colors[i].bold = 1;
+ } else if (tolower((unsigned char)c[j] == 'x'))
+ colors[i].num[j] = -1;
+ else {
+ fprintf(stderr,
+ "error: invalid character '%c' in LSCOLORS"
+ " env var\n", c[j]);
+ colors[i].num[j] = -1;
+ }
+ }
+ }
+}
+
+void
+colorquit(int sig)
+{
+ endcolor(sig);
+
+ (void)signal(sig, SIG_DFL);
+ (void)kill(getpid(), sig);
+}
+
+#endif /* COLORLS */
+
+static void
+printlink(FTSENT *p)
+{
+ int lnklen;
+ char name[MAXPATHLEN + 1];
+ char path[MAXPATHLEN + 1];
+
+ if (p->fts_level == FTS_ROOTLEVEL)
+ (void)snprintf(name, sizeof(name), "%s", p->fts_name);
+ else
+ (void)snprintf(name, sizeof(name),
+ "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
+ if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
+ (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
+ return;
+ }
+ path[lnklen] = '\0';
+ (void)printf(" -> ");
+ (void)printname(path);
+}
+
+static void
+printsize(size_t width, off_t bytes)
+{
+
+ if (f_humanval) {
+ char buf[5];
+
+ humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ (void)printf("%5s ", buf);
+ } else
+ (void)printf("%*jd ", (u_int)width, (intmax_t)bytes);
+}
diff --git a/file_cmds/ls/util.c b/file_cmds/ls/util.c
new file mode 100644
index 0000000..1aae3f1
--- /dev/null
+++ b/file_cmds/ls/util.c
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * 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.
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ls/util.c,v 1.38 2005/06/03 11:05:58 dd Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int
+prn_normal(const char *s)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ int i, n;
+ size_t clen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ n = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ if (clen == (size_t)-2) {
+ n += printf("%s", s);
+ break;
+ }
+ if (clen == (size_t)-1) {
+ memset(&mbs, 0, sizeof(mbs));
+ putchar((unsigned char)*s);
+ s++;
+ n++;
+ continue;
+ }
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ s += clen;
+ if (iswprint(wc))
+ n += wcwidth(wc);
+ }
+ return (n);
+}
+
+int
+prn_printable(const char *s)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ int i, n;
+ size_t clen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ n = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ if (clen == (size_t)-1) {
+ putchar('?');
+ s++;
+ n++;
+ memset(&mbs, 0, sizeof(mbs));
+ continue;
+ }
+ if (clen == (size_t)-2) {
+ putchar('?');
+ n++;
+ break;
+ }
+ if (!iswprint(wc)) {
+ putchar('?');
+ s += clen;
+ n++;
+ continue;
+ }
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ s += clen;
+ n += wcwidth(wc);
+ }
+ return (n);
+}
+
+/*
+ * The fts system makes it difficult to replace fts_name with a different-
+ * sized string, so we just calculate the real length here and do the
+ * conversion in prn_octal()
+ *
+ * XXX when using f_octal_escape (-b) rather than f_octal (-B), the
+ * length computed by len_octal may be too big. I just can't be buggered
+ * to fix this as an efficient fix would involve a lookup table. Same goes
+ * for the rather inelegant code in prn_octal.
+ *
+ * DES 1998/04/23
+ */
+
+size_t
+len_octal(const char *s, int len)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ size_t clen, r;
+
+ memset(&mbs, 0, sizeof(mbs));
+ r = 0;
+ while (len != 0 && (clen = mbrtowc(&wc, s, len, &mbs)) != 0) {
+ if (clen == (size_t)-1) {
+ r += 4;
+ s++;
+ len--;
+ memset(&mbs, 0, sizeof(mbs));
+ continue;
+ }
+ if (clen == (size_t)-2) {
+ r += 4 * len;
+ break;
+ }
+ if (iswprint(wc))
+ r++;
+ else
+ r += 4 * clen;
+ s += clen;
+ }
+ return (r);
+}
+
+int
+prn_octal(const char *s)
+{
+ static const char esc[] = "\\\\\"\"\aa\bb\ff\nn\rr\tt\vv";
+ const char *p;
+ mbstate_t mbs;
+ wchar_t wc;
+ size_t clen;
+ unsigned char ch;
+ int goodchar, i, len, prtlen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ len = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ goodchar = clen != (size_t)-1 && clen != (size_t)-2;
+ if (goodchar && iswprint(wc) && wc != L'\"' && wc != L'\\') {
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ len += wcwidth(wc);
+ } else if (goodchar && f_octal_escape && wc >= 0 &&
+ wc <= (wchar_t)UCHAR_MAX &&
+ (p = strchr(esc, (char)wc)) != NULL) {
+ putchar('\\');
+ putchar(p[1]);
+ len += 2;
+ } else {
+ if (goodchar)
+ prtlen = clen;
+ else if (clen == (size_t)-1)
+ prtlen = 1;
+ else
+ prtlen = strlen(s);
+ for (i = 0; i < prtlen; i++) {
+ ch = (unsigned char)s[i];
+ putchar('\\');
+ putchar('0' + (ch >> 6));
+ putchar('0' + ((ch >> 3) & 7));
+ putchar('0' + (ch & 7));
+ len += 4;
+ }
+ }
+ if (clen == (size_t)-2)
+ break;
+ if (clen == (size_t)-1) {
+ memset(&mbs, 0, sizeof(mbs));
+ s++;
+ } else
+ s += clen;
+ }
+ return (len);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+#ifdef COLORLS
+ "usage: ls [-@ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1%%]"
+#else
+ "usage: ls [-@ABCFHLOPRSTUWabcdefghiklmnopqrstuwx1%%]"
+#endif
+ " [file ...]\n");
+ exit(1);
+}
diff --git a/file_cmds/mkdir/mkdir.1 b/file_cmds/mkdir/mkdir.1
new file mode 100644
index 0000000..2903a00
--- /dev/null
+++ b/file_cmds/mkdir/mkdir.1
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94
+.\" $FreeBSD: src/bin/mkdir/mkdir.1,v 1.17 2002/06/30 06:50:16 tjr Exp $
+.\"
+.Dd January 25, 1994
+.Dt MKDIR 1
+.Os
+.Sh NAME
+.Nm mkdir
+.Nd make directories
+.Sh SYNOPSIS
+.Nm
+.Op Fl pv
+.Op Fl m Ar mode
+.Ar directory_name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility creates the directories named as operands, in the order specified,
+using mode
+.Li rwxrwxrwx (\&0777)
+as modified by the current
+.Xr umask 2 .
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width indent
+.It Fl m Ar mode
+Set the file permission bits of the final created directory to
+the specified mode.
+The
+.Ar mode
+argument can be in any of the formats specified to the
+.Xr chmod 1
+command.
+If a symbolic mode is specified, the operation characters
+.Dq +
+and
+.Dq -
+are interpreted relative to an initial mode of
+.Dq a=rwx .
+.It Fl p
+Create intermediate directories as required.
+If this option is not specified, the full path prefix of each
+operand must already exist.
+On the other hand, with this option specified, no error will
+be reported if a directory given as an operand already exists.
+Intermediate directories are created with permission bits of
+.Li rwxrwxrwx (\&0777)
+as modified by the current umask, plus write and search
+permission for the owner.
+.It Fl v
+Be verbose when creating directories, listing them as they are created.
+.El
+.Pp
+The user must have write permission in the parent directory.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr rmdir 1
+.Sh COMPATIBILITY
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/file_cmds/mkdir/mkdir.c b/file_cmds/mkdir/mkdir.c
new file mode 100644
index 0000000..8aede86
--- /dev/null
+++ b/file_cmds/mkdir/mkdir.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1983, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/mkdir/mkdir.c,v 1.26 2002/06/30 05:13:54 obrien Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int vflag;
+
+int
+main(int argc, char *argv[])
+{
+ int ch, exitval, success, pflag;
+ mode_t omode, *set = (mode_t *)NULL;
+ char *mode;
+
+ pflag = 0;
+ mode = NULL;
+ while ((ch = getopt(argc, argv, "m:pv")) != -1)
+ switch(ch) {
+ case 'm':
+ mode = optarg;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+// argc -= optind;
+ argv += optind;
+ if (argv[0] == NULL)
+ usage();
+
+ if (mode == NULL) {
+ omode = S_IRWXU | S_IRWXG | S_IRWXO;
+ } else {
+ if ((set = setmode(mode)) == NULL)
+ errx(1, "invalid file mode: %s", mode);
+ omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
+ free(set);
+ }
+
+ for (exitval = 0; *argv != NULL; ++argv) {
+ success = 1;
+ if (pflag) {
+ int status = mkpath_np(*argv, omode);
+ if (status && status != EEXIST) {
+ warnc(status, "%s", *argv);
+ success = 0;
+ }
+ } else if (mkdir(*argv, omode) < 0) {
+ if (errno == ENOTDIR || errno == ENOENT)
+ warn("%s", dirname(*argv));
+ else
+ warn("%s", *argv);
+ success = 0;
+ } else if (vflag)
+ (void)printf("mkdir: created directory '%s'\n", *argv);
+
+ if (!success)
+ exitval = 1;
+ /*
+ * The mkdir() and umask() calls both honor only the low
+ * nine bits, so if you try to set a mode including the
+ * sticky, setuid, setgid bits you lose them. Don't do
+ * this unless the user has specifically requested a mode,
+ * as chmod will (obviously) ignore the umask.
+ */
+ if (success && mode != NULL && chmod(*argv, omode) == -1) {
+ warn("%s", *argv);
+ exitval = 1;
+ }
+ }
+ exit(exitval);
+}
+
+void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: mkdir [-pv] [-m mode] directory ...\n");
+ exit (EX_USAGE);
+}
diff --git a/file_cmds/mkfifo/mkfifo.1 b/file_cmds/mkfifo/mkfifo.1
new file mode 100644
index 0000000..39ca982
--- /dev/null
+++ b/file_cmds/mkfifo/mkfifo.1
@@ -0,0 +1,103 @@
+.\" $NetBSD: mkfifo.1,v 1.6 1997/10/19 05:11:52 lukem Exp $
+.\"
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mkfifo.1 8.2 (Berkeley) 1/5/94
+.\"
+.Dd January 5, 1994
+.Dt MKFIFO 1
+.Os BSD 4.4
+.Sh NAME
+.Nm mkfifo
+.Nd make fifos
+.Sh SYNOPSIS
+.Nm mkfifo
+.Op Fl m Ar mode
+.Ar fifo_name ...
+.Sh DESCRIPTION
+.Nm mkfifo
+creates the fifos requested, in the order specified.
+By default,
+the resulting fifos have mode
+.Li \&0666
+(rw-rw-rw-), limited by the current
+.Xr umask 2 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl m
+Set the file permission bits of newly-created fifos to
+.Ar mode ,
+without respect to the current umask.
+.Pp
+The mode is specified as in
+.Xr chmod 1 .
+In symbolic mode strings, the
+.Dq +
+and
+.Dq -
+operators are interpreted relative to an assumed initial mode of
+.Dq a=rw
+.El
+.Pp
+.Nm mkfifo
+requires write permission in the parent directory.
+.Pp
+.Nm mkfifo
+exits with 0 if successful, and with >0 if an error occurred.
+.Sh LEGACY DESCRIPTION
+In legacy mode, the fifo's file permission bits
+are always limited by the current umask.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr mkdir 1 ,
+.Xr rm 1 ,
+.Xr umask 1 ,
+.Xr mkfifo 2 ,
+.Xr umask 2 ,
+.Xr compat 5 ,
+.Xr mknod 8
+.Sh STANDARDS
+The
+.Nm mkfifo
+utility is expected to be
+.St -p1003.2-92
+compliant.
+.Sh HISTORY
+.Nm mkfifo
+command appeared in
+.Bx 4.4 .
diff --git a/file_cmds/mkfifo/mkfifo.c b/file_cmds/mkfifo/mkfifo.c
new file mode 100644
index 0000000..c06fc5a
--- /dev/null
+++ b/file_cmds/mkfifo/mkfifo.c
@@ -0,0 +1,128 @@
+/* $NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $ */
+
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkfifo.c 8.2 (Berkeley) 1/5/94";
+#endif
+__RCSID("$NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $");
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <err.h>
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+int main __P((int, char **));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, exitval;
+ void * set;
+ mode_t mode = 0;
+ int m_used = 0;
+
+ setlocale (LC_ALL, "");
+
+ /* The default mode is the value of the bitwise inclusive or of
+ S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH
+ modified by the file creation mask */
+ if (!COMPAT_MODE("bin/mkfifo", "Unix2003")) {
+ mode = 0666 & ~umask(0);
+ }
+
+ while ((ch = getopt(argc, argv, "m:")) != -1)
+ switch(ch) {
+ case 'm':
+ m_used = 1;
+ if (!(set = setmode(optarg))) {
+ errx(1, "invalid file mode.");
+ /* NOTREACHED */
+ }
+ /* In symbolic mode strings, the + and - operators are
+ interpreted relative to an assumed initial mode of
+ a=rw. */
+ mode = getmode (set, 0666);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+// argc -= optind;
+ argv += optind;
+ if (argv[0] == NULL)
+ usage();
+
+ if (COMPAT_MODE("bin/mkfifo", "Unix2003")) {
+ mode_t maskbits = umask(0); /* now must disable umask so -m mode won't be masked again */
+ if (!m_used)
+ mode = 0666 & ~maskbits;
+ }
+
+ for (exitval = 0; *argv; ++argv) {
+ if (mkfifo(*argv, mode) < 0) {
+ warn("%s", *argv);
+ exitval = 1;
+ }
+ }
+ exit(exitval);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: mkfifo [-m mode] fifoname ...\n");
+ exit(1);
+}
diff --git a/file_cmds/mknod/mknod.8 b/file_cmds/mknod/mknod.8
new file mode 100644
index 0000000..eb4f292
--- /dev/null
+++ b/file_cmds/mknod/mknod.8
@@ -0,0 +1,137 @@
+.\" $NetBSD: mknod.8,v 1.15 1998/09/11 07:20:48 mycroft Exp $
+.\"
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd September 11, 1998
+.Dt MKNOD 8
+.Os NetBSD 1.4
+.Sh NAME
+.Nm mknod
+.Nd make device special file
+.Sh SYNOPSIS
+.Nm
+.Op Fl F Ar format
+.Ar name
+.Op Cm c | Cm b
+.Ar major minor
+.Nm
+.Op Fl F Ar format
+.Ar name
+.Op Cm c | Cm b
+.Ar major unit subunit
+.Nm
+.Ar name
+.Op Cm c | Cm b
+.Ar number
+.Nm
+.Ar name
+.Ar w
+.Sh DESCRIPTION
+The
+.Nm
+command creates device special files.
+.Pp
+To make nodes manually, the required arguments are:
+.Pp
+.Bl -tag -width majorx
+.It Ar name
+Device name, for example
+.Dq sd
+for a SCSI disk on an HP300 or a
+.Dq pty
+for pseudo-devices.
+.It Cm b | Cm c | Cm w
+Type of device. If the
+device is a block type device such as a tape or disk drive which needs
+both cooked and raw special files,
+the type is
+.Cm b .
+Whiteout nodes are type
+.Cm w .
+All other devices are character type devices, such as terminal
+and pseudo devices, and are type
+.Cm c .
+.It Ar major
+The major device number is an integer number which tells the kernel
+which device driver entry point to use.
+.It Ar minor
+The minor device number tells the kernel which one of several similar
+devices the node corresponds to; for example, it may be a specific serial
+port or pty.
+.It Ar unit and subunit
+The unit and subunit numbers select a subset of a device; for example, the
+unit may specify a particular SCSI disk, and the subunit a partition on
+that disk. (Currently this form of specification is only supported by the
+.Ar bsdos
+format, for compatibility with the
+.Bsx
+.Xr mknod 8 . )
+.El
+.Pp
+Device numbers for different operating systems may be packed in a different
+format. To create device nodes that may be used by such an operating system
+(e.g. in an exported file system used for netbooting), the
+.Fl F
+option is used. The following formats are recognized:
+native,
+386bsd,
+4bsd,
+bsdos,
+freebsd,
+hpux,
+isc,
+linux,
+netbsd,
+osf1,
+sco,
+solaris,
+sunos,
+svr3,
+svr4 and
+ultrix.
+.Pp
+Alternatively, a single opaque device number may be specified.
+.Sh SEE ALSO
+.Xr mkfifo 1 ,
+.Xr mkfifo 2 ,
+.Xr mknod 2
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
+The
+.Fl F
+option appeared in
+.Nx 1.4 .
diff --git a/file_cmds/mknod/mknod.c b/file_cmds/mknod/mknod.c
new file mode 100644
index 0000000..c4848af
--- /dev/null
+++ b/file_cmds/mknod/mknod.c
@@ -0,0 +1,405 @@
+/* $NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.\n");
+__RCSID("$NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+int main __P((int, char *[]));
+static void usage __P((void));
+typedef dev_t pack_t __P((int, u_long []));
+
+
+pack_t pack_native;
+
+dev_t
+pack_native(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev(numbers[0], numbers[1]);
+ if (major(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8)))
+#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \
+ (((x) & 0x000000ff) >> 0)))
+#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \
+ (((y) << 12) & 0xfff00000) | \
+ (((y) << 0) & 0x000000ff)))
+
+pack_t pack_netbsd;
+
+dev_t
+pack_netbsd(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_netbsd(numbers[0], numbers[1]);
+ if (major_netbsd(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_netbsd(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0))
+#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0xffff00ff)))
+
+pack_t pack_freebsd;
+
+dev_t
+pack_freebsd(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_freebsd(numbers[0], numbers[1]);
+ if (major_freebsd(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_freebsd(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0x000000ff)))
+
+pack_t pack_8_8;
+
+dev_t
+pack_8_8(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_8_8(numbers[0], numbers[1]);
+ if (major_8_8(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_8_8(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0))
+#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 0) & 0x000fffff)))
+
+pack_t pack_12_20;
+
+dev_t
+pack_12_20(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if (major_12_20(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_12_20(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18))
+#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0))
+#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \
+ (((y) << 0) & 0x0003ffff)))
+
+pack_t pack_14_18;
+
+dev_t
+pack_14_18(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_14_18(numbers[0], numbers[1]);
+ if (major_14_18(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_14_18(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24))
+#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0))
+#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \
+ (((y) << 0) & 0x00ffffff)))
+
+pack_t pack_8_24;
+
+dev_t
+pack_8_24(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_8_24(numbers[0], numbers[1]);
+ if (major_8_24(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_8_24(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8))
+#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 8) & 0x000fff00) | \
+ (((z) << 0) & 0x000000ff)))
+
+pack_t pack_bsdos;
+
+dev_t
+pack_bsdos(n, numbers)
+ int n;
+ u_long numbers[];
+{
+ dev_t dev=0; /* Quiet -Wall */
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if (major_12_20(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (minor_12_20(dev) != numbers[1])
+ errx(1, "invalid minor number");
+ } else if (n == 3) {
+ dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
+ if (major_12_12_8(dev) != numbers[0])
+ errx(1, "invalid major number");
+ if (unit_12_12_8(dev) != numbers[1])
+ errx(1, "invalid unit number");
+ if (subunit_12_12_8(dev) != numbers[2])
+ errx(1, "invalid subunit number");
+ } else
+ errx(1, "too many fields for format");
+ return (dev);
+}
+
+
+struct format {
+ char *name;
+ pack_t *pack;
+} formats[] = {
+ {"386bsd", pack_8_8},
+ {"4bsd", pack_8_8},
+ {"bsdos", pack_bsdos},
+ {"freebsd", pack_freebsd},
+ {"hpux", pack_8_24},
+ {"isc", pack_8_8},
+ {"linux", pack_8_8},
+ {"native", pack_native},
+ {"netbsd", pack_netbsd},
+ {"osf1", pack_12_20},
+ {"sco", pack_8_8},
+ {"solaris", pack_14_18},
+ {"sunos", pack_8_8},
+ {"svr3", pack_8_8},
+ {"svr4", pack_14_18},
+ {"ultrix", pack_8_8},
+};
+
+int compare_format __P((const void *, const void *));
+
+int
+compare_format(key, element)
+ const void *key;
+ const void *element;
+{
+ const char *name;
+ const struct format *format;
+
+ name = key;
+ format = element;
+
+ return (strcmp(name, format->name));
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *name;
+ mode_t mode;
+ dev_t dev;
+ pack_t *pack;
+ u_long numbers[8];
+ struct format *format;
+ char *p;
+ int n;
+ int ch;
+
+ pack = pack_native;
+
+ while ((ch = getopt(argc, argv, "F:")) != -1) {
+ switch (ch) {
+ case 'F':
+ format = bsearch(optarg, formats,
+ sizeof(formats)/sizeof(formats[0]),
+ sizeof(formats[0]), compare_format);
+ if (format == 0)
+ errx(1, "invalid format: %s", optarg);
+ pack = format->pack;
+ break;
+
+ default:
+ case '?':
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2 || argc > 10)
+ usage();
+
+ name = *argv;
+ argc--;
+ argv++;
+
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ if (*argv[0] == 'c')
+ mode |= S_IFCHR;
+ else if (*argv[0] == 'b')
+ mode |= S_IFBLK;
+ else if (*argv[0] == 'w')
+ mode |= S_IFWHT;
+ else
+ errx(1, "node type must be 'b' or 'c' or 'w'.");
+ argc--;
+ argv++;
+
+ for (n = 0; n < argc; n++) {
+ if (S_ISWHT(mode)) {
+ errx(1, "whiteout nodes have no device numbers.");
+ }
+ numbers[n] = strtoul(argv[n], &p, 0);
+ if ((p && *p != '\0') || (numbers[n] == ULONG_MAX && errno == ERANGE))
+ errx(1, "invalid number: %s", argv[n]);
+ }
+
+ if (S_ISWHT(mode))
+ dev = 0;
+ else if (argc == 1)
+ dev = (dev_t)numbers[0];
+ else
+ dev = (*pack)(argc, numbers);
+
+#if 0
+ printf("name: %s\nmode: %05o\ndev: %08x\n", name, mode, dev);
+#else
+ if (mknod(name, mode, dev) < 0)
+ err(1, "%s", name);
+#endif
+
+ exit(0);
+}
+
+void
+usage()
+{
+
+ fprintf(stderr, "usage: mknod [-F format] name [b | c] major minor\n");
+ fprintf(stderr, " mknod [-F format] name [b | c] major unit subunit\n");
+ fprintf(stderr, " mknod name [b | c] number\n");
+ fprintf(stderr, " mknod name w\n");
+ exit(1);
+}
diff --git a/file_cmds/mtree/commoncrypto.c b/file_cmds/mtree/commoncrypto.c
new file mode 100644
index 0000000..11e97ce
--- /dev/null
+++ b/file_cmds/mtree/commoncrypto.c
@@ -0,0 +1,378 @@
+#include <dispatch/dispatch.h>
+#include <os/assumes.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <stdbool.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/attr.h>
+#include <unistd.h>
+#include <sys/xattr.h>
+#include <sys/mount.h>
+#include <apfs/apfs_fsctl.h>
+
+#include "commoncrypto.h"
+#include "extern.h"
+#include "metrics.h"
+
+const int kSHA256NullTerminatedBuffLen = 65;
+static const char hex[] = "0123456789abcdef";
+
+/* Functions for SHA256_File_XATTRs */
+#define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b)
+char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf);
+void Quicksort(char **array, int num);
+
+/* Generic version of libmd's *_File() functions. */
+char *
+Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
+{
+ int fd;
+ __block CCDigestCtx ctx;
+ dispatch_queue_t queue;
+ dispatch_semaphore_t sema;
+ dispatch_io_t io;
+ __block int s_error = 0;
+ uint8_t digest[32]; // SHA256 is the biggest
+ size_t i, length;
+
+ /* dispatch_io_create_with_path requires an absolute path */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ (void)fcntl(fd, F_NOCACHE, 1);
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+
+ queue = dispatch_queue_create("com.apple.mtree.io", NULL);
+ os_assert(queue);
+ sema = dispatch_semaphore_create(0);
+ os_assert(sema);
+
+ io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
+ if (error != 0) {
+ s_error = error;
+ RECORD_FAILURE(27440, s_error);
+ }
+ (void)close(fd);
+ (void)dispatch_semaphore_signal(sema);
+ });
+ os_assert(io);
+ dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
+ if (data != NULL) {
+ (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
+ return (bool)true;
+ });
+ }
+
+ if (error != 0) {
+ s_error = error;
+ RECORD_FAILURE(27441, s_error);
+ }
+ });
+ dispatch_release(io); // it will close on its own
+
+ (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ dispatch_release(queue);
+ dispatch_release(sema);
+
+ if (s_error != 0) {
+ errno = s_error;
+ return NULL;
+ }
+
+ /* Finalize and convert to hex. */
+ (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
+ length = CCDigestOutputSize(&ctx);
+ os_assert(length <= sizeof(digest));
+ for (i = 0; i < length; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+
+ return buf;
+}
+
+xattr_info *
+SHA256_Path_XATTRs(char *path, char *buf) {
+ xattr_info *ai = NULL;
+
+ if (mflag) {
+ ai = get_xdstream_privateid(path, buf);
+ } else {
+ ai = calculate_SHA256_XATTRs(path, buf);
+ }
+
+ return ai;
+}
+
+
+xattr_info *
+calculate_SHA256_XATTRs(char *path, char *buf)
+{
+ errno_t error = 0;
+ char *xattrsSummary = NULL;
+ int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
+ ssize_t nameBufSize = listxattr(path, NULL, 0, options);
+ uint64_t xd_obj_id = 0;
+ if (nameBufSize > 0) {
+ char *nameBuf = malloc(nameBufSize);
+
+ listxattr(path, nameBuf, nameBufSize, options);
+
+ size_t xattrsLen = 1;
+ size_t xattrIndex = 0;
+ char **xattrs = malloc(xattrsLen * sizeof(char *));
+ char *nextName = nameBuf;
+ while (nextName < nameBuf + nameBufSize)
+ {
+ char *name = nextName;
+ if (xattrIndex == xattrsLen) {
+ xattrsLen *= 2;
+ xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
+ }
+ xattrs[xattrIndex++] = name;
+ nextName += strlen(name) + 1;
+ }
+
+ // sort the xattr array as they're not guaranteed to come in the same order
+ qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) {
+ char *left = *(char **)l;
+ char *right = *(char **)r;
+ return strcmp(left, right);
+ });
+
+ // gather the data for the xattrs
+ bool didAddXATTR = false;
+ int xattrBufLen = kSHA256NullTerminatedBuffLen;
+ void *xattrBuf = malloc(xattrBufLen); // resized if necessary
+ char *digest;
+ ssize_t result = 0;
+ char *oldSummary = NULL;
+ //XXX Make xattr_info an array of structs if necessary
+ xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
+ for (int i = 0; i < xattrIndex; i++) {
+ char *name = xattrs[i];
+ ssize_t xlen = getxattr(path, name, NULL, 0, 0, options);
+ if (xlen > xattrBufLen) {
+ xattrBufLen = xlen;
+ xattrBuf = realloc(xattrBuf, xattrBufLen);
+ }
+ bzero(xattrBuf, xattrBufLen);
+ result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options);
+ if (result < 0) {
+ error = errno;
+ RECORD_FAILURE(27442, error);
+ errc(1, error, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path);
+ }
+
+ digest = SHA256_Data(xattrBuf, xattrBufLen, buf);
+ if (!digest)
+ err(1, "%s", xattrsSummary);
+ if (!didAddXATTR)
+ {
+ didAddXATTR = true;
+ asprintf(&xattrsSummary, "%s:%s", name, digest);
+ } else {
+ oldSummary = xattrsSummary;
+ asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest);
+ free(oldSummary);
+ }
+#ifdef APFSIOC_XDSTREAM_OBJ_ID
+ // System volume has stream based xattrs only in form of resource forks
+ if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
+ struct xdstream_obj_id x_obj;
+ x_obj.xdi_name = name;
+ x_obj.xdi_xdtream_obj_id = 0;
+
+ result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
+ if (!result) {
+ xd_obj_id = x_obj.xdi_xdtream_obj_id;
+ } else if (errno == ENOTTY) {
+ // Not an apfs filesystem, return zero.
+ xd_obj_id = 0;
+ } else {
+ error = errno;
+ RECORD_FAILURE(27444, error);
+ errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
+ }
+ }
+#endif
+ ai->xdstream_priv_id = xd_obj_id;
+ }
+
+ free(xattrBuf);
+ free(nameBuf);
+ free(xattrs);
+
+ digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf);
+ if (!digest)
+ err(1, "%s", xattrsSummary);
+
+ ai->digest = digest;
+
+ free(xattrsSummary);
+ return ai;
+ }
+ return NULL;
+}
+
+xattr_info *
+get_xdstream_privateid(char *path, char *buf) {
+ errno_t error = 0;
+ int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW;
+ ssize_t nameBufSize = listxattr(path, NULL, 0, options);
+ uint64_t xd_obj_id = 0;
+
+ if (nameBufSize > 0) {
+ //XXX Make xattr_info an array of structs if necessary
+ xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info));
+ char *nameBuf = malloc(nameBufSize);
+ int result = 0;
+
+ listxattr(path, nameBuf, nameBufSize, options);
+
+ size_t xattrsLen = 1;
+ size_t xattrIndex = 0;
+ char **xattrs = malloc(xattrsLen * sizeof(char *));
+ char *nextName = nameBuf;
+ while (nextName < nameBuf + nameBufSize)
+ {
+ char *name = nextName;
+ if (xattrIndex == xattrsLen) {
+ xattrsLen *= 2;
+ xattrs = realloc(xattrs, xattrsLen * sizeof(char *));
+ }
+ xattrs[xattrIndex++] = name;
+ nextName += strlen(name) + 1;
+ }
+
+ for (int i = 0; i < xattrIndex; i++) {
+ char *name = xattrs[i];
+ // System volume has stream based xattrs only in form of resource forks
+ if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) {
+ struct xdstream_obj_id x_obj;
+ x_obj.xdi_name = name;
+ x_obj.xdi_xdtream_obj_id = 0;
+
+ result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0);
+ if (!result && x_obj.xdi_xdtream_obj_id != 0) {
+ xd_obj_id = x_obj.xdi_xdtream_obj_id;
+ } else if (errno == ENOTTY) {
+ // Not an apfs filesystem, return zero.
+ xd_obj_id = 0;
+ } else {
+ error = errno;
+ RECORD_FAILURE(29983, error);
+ errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error);
+ }
+ }
+ }
+
+ ai->xdstream_priv_id = xd_obj_id;
+ // insert a dummy value as digest is not used in presence of mflag
+ ai->digest = "authapfs";
+
+ free(nameBuf);
+ free(xattrs);
+ return ai;
+ }
+
+ return NULL;
+}
+
+char *SHA256_Path_ACL(char *path, char *buf)
+{
+ errno_t error = 0;
+ int result = 0;
+ char *data = NULL;
+ char *digest = NULL;
+
+ struct attrlist list = {
+ .bitmapcount = ATTR_BIT_MAP_COUNT,
+ .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY,
+ };
+
+ struct ACLBuf {
+ uint32_t len;
+ attribute_set_t returned_attrs;
+ attrreference_t acl;
+ char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future
+ } __attribute__((aligned(4), packed));
+
+ struct ACLBuf aclBuf;
+
+ result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW);
+
+ if (result) {
+ error = errno;
+ RECORD_FAILURE(27445, error);
+ errc(1, error, "SHA256_Path_ACL: getattrlist");
+ }
+
+ // if the path does not have an acl, return none
+ if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) )
+ || ( aclBuf.acl.attr_length == 0 ) ) {
+ return kNone;
+ }
+
+ data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset;
+
+ digest = SHA256_Data(data, aclBuf.acl.attr_length, buf);
+ if (!digest)
+ err(1, "SHA256_Path_ACL: SHA256_Data");
+
+ return digest;
+}
+
+/* Functions for Digest_Path_* */
+char *
+Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) {
+
+ uint8_t digest[32]; // SHA256 is the biggest
+ CCDigestCtx ctx;
+ size_t i, length;
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size));
+
+ /* Finalize and convert to hex. */
+ (void)os_assumes_zero(CCDigestFinal(&ctx, digest));
+ length = CCDigestOutputSize(&ctx);
+ os_assert(length <= sizeof(digest));
+ for (i = 0; i < length; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+
+ return buf;
+}
+
+uint64_t
+get_sibling_id(const char *path)
+{
+ struct attrlist attr_list = {0};
+ struct attrbuf attr_buf = {0};
+ errno_t error = 0;
+
+ attr_list.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attr_list.forkattr = ATTR_CMNEXT_LINKID;
+
+ error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW);
+ if (error) {
+ error = errno;
+ RECORD_FAILURE(27447, error);
+ errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path);
+ }
+
+ return attr_buf.sibling_id;
+}
diff --git a/file_cmds/mtree/commoncrypto.h b/file_cmds/mtree/commoncrypto.h
new file mode 100644
index 0000000..c547035
--- /dev/null
+++ b/file_cmds/mtree/commoncrypto.h
@@ -0,0 +1,36 @@
+
+#ifndef _COMMON_CRYPTO_H_
+#define _COMMON_CRYPTO_H_
+
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#define kNone "none"
+
+extern const int kSHA256NullTerminatedBuffLen;
+
+#define MD5File(f, b) Digest_File(kCCDigestMD5, f, b)
+#define SHA1_File(f, b) Digest_File(kCCDigestSHA1, f, b)
+#define RIPEMD160_File(f, b) Digest_File(kCCDigestRMD160, f, b)
+#define SHA256_File(f, b) Digest_File(kCCDigestSHA256, f, b)
+
+typedef struct {
+ char *digest;
+ uint64_t xdstream_priv_id;
+} xattr_info;
+
+struct attrbuf {
+ uint32_t info_length;
+ uint64_t sibling_id;
+} __attribute__((aligned, packed));
+
+typedef struct attrbuf attrbuf_t;
+
+char *Digest_File(CCDigestAlg algorithm, const char *filename, char *buf);
+
+xattr_info *calculate_SHA256_XATTRs(char *path, char *buf);
+xattr_info *SHA256_Path_XATTRs(char *path, char *buf);
+xattr_info *get_xdstream_privateid(char *path, char *buf);
+char *SHA256_Path_ACL(char *path, char *buf);
+uint64_t get_sibling_id(const char *path);
+
+#endif /* _COMMON_CRYPTO_H_ */
diff --git a/file_cmds/mtree/compare.c b/file_cmds/mtree/compare.c
new file mode 100644
index 0000000..e585928
--- /dev/null
+++ b/file_cmds/mtree/compare.c
@@ -0,0 +1,688 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobez Exp $");
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#ifndef __APPLE__
+#ifdef ENABLE_MD5
+#include <md5.h>
+#endif
+#ifdef ENABLE_RMD160
+#include <ripemd.h>
+#endif
+#ifdef ENABLE_SHA1
+#include <sha.h>
+#endif
+#ifdef ENABLE_SHA256
+#include <sha256.h>
+#endif
+#endif /* !__APPLE__ */
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+#ifdef __APPLE__
+#include "commoncrypto.h"
+#endif /* __APPLE__ */
+
+#define INDENTNAMELEN 8
+#define LABEL \
+ if (!label++) { \
+ len = printf("%s changed\n", RP(p)); \
+ tab = "\t"; \
+ }
+
+extern CFMutableDictionaryRef dict;
+
+// max/min times apfs can store on disk
+#define APFS_MAX_TIME 0x7fffffffffffffffLL
+#define APFS_MIN_TIME (-0x7fffffffffffffffLL-1)
+
+static uint64_t
+timespec_to_apfs_timestamp(struct timespec *ts)
+{
+ int64_t total;
+ int64_t seconds;
+
+ // `tv_nsec' can be > one billion, so we split it into two components:
+ // seconds and actual nanoseconds
+ // this allows us to detect overflow on the *total* number of nanoseconds
+ // e.g. if (MAX_SECONDS+2, -2billion) is passed in, we return MAX_SECONDS
+ seconds = ((int64_t)ts->tv_nsec / (int64_t)NSEC_PER_SEC);
+
+ // compute total nanoseconds, checking for overflow:
+ // seconds = sec + (ns/10e9)
+ // total = seconds*10e9 + ns%10e9
+ if (__builtin_saddll_overflow(ts->tv_sec, seconds, &seconds) ||
+ __builtin_smulll_overflow(seconds, NSEC_PER_SEC, &total) ||
+ __builtin_saddll_overflow(((int64_t)ts->tv_nsec % (int64_t)NSEC_PER_SEC), total, &total)) {
+ // checking the sign of "seconds" tells us whether to cap the value at
+ // the max or min time
+ total = (ts->tv_sec > 0) ? APFS_MAX_TIME : APFS_MIN_TIME;
+ }
+
+ return (uint64_t)total;
+}
+
+static void
+set_key_value_pair(void *in_key, uint64_t *in_val, bool is_string)
+{
+ CFStringRef key;
+ CFNumberRef val;
+
+ if (is_string) {
+ key = CFStringCreateWithCString(NULL, (const char*)in_key, kCFStringEncodingUTF8);
+
+ } else {
+ key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%llu"), *(uint64_t*)in_key);
+ }
+
+ val = CFNumberCreate(NULL, kCFNumberSInt64Type, in_val);
+
+ // we always expect the key to be not present
+ if (key && val) {
+ CFDictionaryAddValue(dict, key, val);
+ } else {
+ if (key) {
+ CFRelease(key);
+ }
+ if (val) {
+ CFRelease(val);
+ }
+ RECORD_FAILURE(1, EINVAL);
+ errx(1, "set_key_value_pair: key/value is null");
+ }
+
+ if (key) {
+ CFRelease(key);
+ }
+ if (val) {
+ CFRelease(val);
+ }
+}
+
+int
+compare(char *name __unused, NODE *s, FTSENT *p)
+{
+ int error = 0;
+ struct timeval tv[2];
+ uint32_t val;
+ int fd, label;
+ off_t len;
+ char *cp;
+ const char *tab = "";
+ char *fflags, *badflags;
+ u_long flags;
+
+ label = 0;
+ switch(s->type) {
+ case F_BLOCK:
+ if (!S_ISBLK(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(2, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_CHAR:
+ if (!S_ISCHR(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(3, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_DIR:
+ if (!S_ISDIR(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(4, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_FIFO:
+ if (!S_ISFIFO(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(5, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_FILE:
+ if (!S_ISREG(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(6, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_LINK:
+ if (!S_ISLNK(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(7, EINVAL);
+ goto typeerr;
+ }
+ break;
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode)) {
+ RECORD_FAILURE(8, EINVAL);
+typeerr: LABEL;
+ (void)printf("\ttype expected %s found %s\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ return (label);
+ }
+ break;
+ }
+ /* Set the uid/gid first, then set the mode. */
+ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+ LABEL;
+ (void)printf("%suser expected %lu found %lu",
+ tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
+ if (uflag) {
+ if (chown(p->fts_accpath, s->st_uid, -1)) {
+ error = errno;
+ RECORD_FAILURE(9, error);
+ (void)printf(" not modified: %s\n",
+ strerror(error));
+ } else {
+ (void)printf(" modified\n");
+ }
+ } else {
+ (void)printf("\n");
+ }
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ (void)printf("%sgid expected %lu found %lu",
+ tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
+ if (uflag) {
+ if (chown(p->fts_accpath, -1, s->st_gid)) {
+ error = errno;
+ RECORD_FAILURE(10, error);
+ (void)printf(" not modified: %s\n",
+ strerror(error));
+ } else {
+ (void)printf(" modified\n");
+ }
+ } else {
+ (void)printf("\n");
+ }
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ !S_ISLNK(p->fts_statp->st_mode) &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions expected %#o found %#o",
+ tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+ if (uflag) {
+ if (chmod(p->fts_accpath, s->st_mode)) {
+ error = errno;
+ RECORD_FAILURE(11, error);
+ (void)printf(" not modified: %s\n",
+ strerror(error));
+ } else {
+ (void)printf(" modified\n");
+ }
+ } else {
+ (void)printf("\n");
+ }
+ tab = "\t";
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ (void)printf("%slink_count expected %u found %u\n",
+ tab, s->st_nlink, p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
+ !S_ISDIR(p->fts_statp->st_mode)) {
+ LABEL;
+ (void)printf("%ssize expected %jd found %jd\n", tab,
+ (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
+ tab = "\t";
+ }
+ if ((s->flags & F_TIME) &&
+ ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
+ (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%smodification time expected %.24s.%09ld ",
+ tab, ctime(&s->st_mtimespec.tv_sec), s->st_mtimespec.tv_nsec);
+ (void)printf("found %.24s.%09ld",
+ ctime(&p->fts_statp->st_mtimespec.tv_sec), p->fts_statp->st_mtimespec.tv_nsec);
+ if (uflag) {
+ tv[0].tv_sec = s->st_mtimespec.tv_sec;
+ tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000;
+ tv[1] = tv[0];
+ if (utimes(p->fts_accpath, tv)) {
+ error = errno;
+ RECORD_FAILURE(12, error);
+ (void)printf(" not modified: %s\n",
+ strerror(error));
+ } else {
+ (void)printf(" modified\n");
+ }
+ } else {
+ (void)printf("\n");
+ }
+ tab = "\t";
+ }
+ if (!insert_mod && mflag) {
+ uint64_t s_mod_time = timespec_to_apfs_timestamp(&s->st_mtimespec);
+ char *mod_string = "MODIFICATION";
+ set_key_value_pair(mod_string, &s_mod_time, true);
+ insert_mod = 1;
+ }
+ }
+ if (s->flags & F_CKSUM) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(13, error);
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(error));
+ tab = "\t";
+ } else if (crc(fd, &val, &len)) {
+ (void)close(fd);
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(14, error);
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(error));
+ tab = "\t";
+ } else {
+ (void)close(fd);
+ if (s->cksum != val) {
+ LABEL;
+ (void)printf("%scksum expected %lu found %lu\n",
+ tab, s->cksum, (unsigned long)val);
+ tab = "\t";
+ }
+ }
+ }
+ if (s->flags & F_FLAGS) {
+ // There are unpublished flags that should not fail comparison
+ // we convert to string and back to filter them out
+ fflags = badflags = flags_to_string(p->fts_statp->st_flags);
+ if (strcmp("none", fflags) == 0) {
+ flags = 0;
+ } else if (strtofflags(&badflags, &flags, NULL) != 0)
+ errx(1, "invalid flag %s", badflags);
+ free(fflags);
+ if (s->st_flags != flags) {
+ LABEL;
+ fflags = flags_to_string(s->st_flags);
+ (void)printf("%sflags expected \"%s\"", tab, fflags);
+ free(fflags);
+
+ fflags = flags_to_string(flags);
+ (void)printf(" found \"%s\"", fflags);
+ free(fflags);
+
+ if (uflag) {
+ if (chflags(p->fts_accpath, (u_int)s->st_flags)) {
+ error = errno;
+ RECORD_FAILURE(15, error);
+ (void)printf(" not modified: %s\n",
+ strerror(error));
+ } else {
+ (void)printf(" modified\n");
+ }
+ } else {
+ (void)printf("\n");
+ }
+ tab = "\t";
+ }
+ }
+#ifdef ENABLE_MD5
+ if (s->flags & F_MD5) {
+ char *new_digest, buf[33];
+#ifdef __clang__
+/* clang doesn't like MD5 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ new_digest = MD5File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!new_digest) {
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(16, error);
+ printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
+ strerror(error));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->md5digest)) {
+ LABEL;
+ printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
+ new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* ENABLE_MD5 */
+#ifdef ENABLE_SHA1
+ if (s->flags & F_SHA1) {
+ char *new_digest, buf[41];
+#ifdef __clang__
+/* clang doesn't like SHA1 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ new_digest = SHA1_File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!new_digest) {
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(17, error);
+ printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
+ strerror(error));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha1digest)) {
+ LABEL;
+ printf("%sSHA-1 expected %s found %s\n",
+ tab, s->sha1digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* ENABLE_SHA1 */
+#ifdef ENABLE_RMD160
+ if (s->flags & F_RMD160) {
+ char *new_digest, buf[41];
+#ifdef __clang__
+/* clang doesn't like RIPEMD160 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ new_digest = RIPEMD160_File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!new_digest) {
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(18, error);
+ printf("%sRIPEMD160: %s: %s\n", tab,
+ p->fts_accpath, strerror(error));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->rmd160digest)) {
+ LABEL;
+ printf("%sRIPEMD160 expected %s found %s\n",
+ tab, s->rmd160digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* ENABLE_RMD160 */
+#ifdef ENABLE_SHA256
+ if (s->flags & F_SHA256) {
+ char *new_digest, buf[kSHA256NullTerminatedBuffLen];
+
+ new_digest = SHA256_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ error = errno;
+ RECORD_FAILURE(19, error);
+ printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath,
+ strerror(error));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha256digest)) {
+ LABEL;
+ printf("%sSHA-256 expected %s found %s\n",
+ tab, s->sha256digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* ENABLE_SHA256 */
+
+ if (s->flags & F_SLINK &&
+ strcmp(cp = rlink(p->fts_accpath), s->slink)) {
+ LABEL;
+ (void)printf("%slink_ref expected %s found %s\n",
+ tab, s->slink, cp);
+ }
+ if ((s->flags & F_BTIME) &&
+ ((s->st_birthtimespec.tv_sec != p->fts_statp->st_birthtimespec.tv_sec) ||
+ (s->st_birthtimespec.tv_nsec != p->fts_statp->st_birthtimespec.tv_nsec))) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%sbirth time expected %.24s.%09ld ",
+ tab, ctime(&s->st_birthtimespec.tv_sec), s->st_birthtimespec.tv_nsec);
+ (void)printf("found %.24s.%09ld\n",
+ ctime(&p->fts_statp->st_birthtimespec.tv_sec), p->fts_statp->st_birthtimespec.tv_nsec);
+ tab = "\t";
+ }
+ if (!insert_birth && mflag) {
+ uint64_t s_create_time = timespec_to_apfs_timestamp(&s->st_birthtimespec);
+ char *birth_string = "BIRTH";
+ set_key_value_pair(birth_string, &s_create_time, true);
+ insert_birth = 1;
+ }
+ }
+ if ((s->flags & F_ATIME) &&
+ ((s->st_atimespec.tv_sec != p->fts_statp->st_atimespec.tv_sec) ||
+ (s->st_atimespec.tv_nsec != p->fts_statp->st_atimespec.tv_nsec))) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%saccess time expected %.24s.%09ld ",
+ tab, ctime(&s->st_atimespec.tv_sec), s->st_atimespec.tv_nsec);
+ (void)printf("found %.24s.%09ld\n",
+ ctime(&p->fts_statp->st_atimespec.tv_sec), p->fts_statp->st_atimespec.tv_nsec);
+ tab = "\t";
+ }
+ if (!insert_access && mflag) {
+ uint64_t s_access_time = timespec_to_apfs_timestamp(&s->st_atimespec);
+ char *access_string = "ACCESS";
+ set_key_value_pair(access_string, &s_access_time, true);
+ insert_access = 1;
+
+ }
+ }
+ if ((s->flags & F_CTIME) &&
+ ((s->st_ctimespec.tv_sec != p->fts_statp->st_ctimespec.tv_sec) ||
+ (s->st_ctimespec.tv_nsec != p->fts_statp->st_ctimespec.tv_nsec))) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%smetadata modification time expected %.24s.%09ld ",
+ tab, ctime(&s->st_ctimespec.tv_sec), s->st_ctimespec.tv_nsec);
+ (void)printf("found %.24s.%09ld\n",
+ ctime(&p->fts_statp->st_ctimespec.tv_sec), p->fts_statp->st_ctimespec.tv_nsec);
+ tab = "\t";
+ }
+ if (!insert_change && mflag) {
+ uint64_t s_mod_time = timespec_to_apfs_timestamp(&s->st_ctimespec);
+ char *change_string = "CHANGE";
+ set_key_value_pair(change_string, &s_mod_time, true);
+ insert_change = 1;
+ }
+ }
+ if (s->flags & F_PTIME) {
+ int supported;
+ struct timespec ptimespec = ptime(p->fts_accpath, &supported);
+ if (!supported) {
+ LABEL;
+ (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
+ tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
+ tab = "\t";
+ } else if (supported && ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) ||
+ (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec))) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%stime added to parent folder expected %.24s.%09ld ",
+ tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
+ (void)printf("found %.24s.%09ld\n",
+ ctime(&ptimespec.tv_sec), ptimespec.tv_nsec);
+ tab = "\t";
+ } else if (!insert_parent && mflag) {
+ uint64_t s_added_time = timespec_to_apfs_timestamp(&s->st_ptimespec);
+ char *added_string = "DATEADDED";
+ set_key_value_pair(added_string, &s_added_time, true);
+ insert_parent = 1;
+ }
+ }
+ }
+ if (s->flags & F_XATTRS) {
+ char buf[kSHA256NullTerminatedBuffLen];
+ xattr_info *ai;
+ ai = SHA256_Path_XATTRs(p->fts_accpath, buf);
+ if (!mflag) {
+ if (ai && !ai->digest) {
+ LABEL;
+ printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest);
+ tab = "\t";
+ } else if (ai && strcmp(ai->digest, s->xattrsdigest)) {
+ LABEL;
+ printf("%sxattrsdigest expected %s found %s\n",
+ tab, s->xattrsdigest, ai->digest);
+ tab = "\t";
+ }
+ }
+ if (mflag) {
+ if (ai && ai->xdstream_priv_id != s->xdstream_priv_id) {
+ set_key_value_pair((void*)&ai->xdstream_priv_id, &s->xdstream_priv_id, false);
+ }
+ }
+ free(ai);
+ }
+ if ((s->flags & F_INODE) &&
+ (p->fts_statp->st_ino != s->st_ino)) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%sinode expected %llu found %llu\n",
+ tab, s->st_ino, p->fts_statp->st_ino);
+ tab = "\t";
+ }
+ if (mflag) {
+ set_key_value_pair((void*)&p->fts_statp->st_ino, &s->st_ino, false);
+ }
+ }
+ if (s->flags & F_ACL) {
+ char *new_digest, buf[kSHA256NullTerminatedBuffLen];
+ new_digest = SHA256_Path_ACL(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sacldigest missing, expected: %s\n", tab, s->acldigest);
+ tab = "\t";
+ } else if (strcmp(new_digest, s->acldigest)) {
+ LABEL;
+ printf("%sacldigest expected %s found %s\n",
+ tab, s->acldigest, new_digest);
+ tab = "\t";
+ }
+ }
+ if (s->flags & F_SIBLINGID) {
+ uint64_t new_sibling_id = get_sibling_id(p->fts_accpath);
+ new_sibling_id = (new_sibling_id != p->fts_statp->st_ino) ? new_sibling_id : 0;
+ if (new_sibling_id != s->sibling_id) {
+ if (!mflag) {
+ LABEL;
+ (void)printf("%ssibling id expected %llu found %llu\n",
+ tab, s->sibling_id, new_sibling_id);
+ tab = "\t";
+ }
+ if (mflag) {
+ set_key_value_pair((void*)&new_sibling_id, &s->sibling_id, false);
+ }
+ }
+ }
+
+ return (label);
+}
+
+const char *
+inotype(u_int type)
+{
+ switch(type & S_IFMT) {
+ case S_IFBLK:
+ return ("block");
+ case S_IFCHR:
+ return ("char");
+ case S_IFDIR:
+ return ("dir");
+ case S_IFIFO:
+ return ("fifo");
+ case S_IFREG:
+ return ("file");
+ case S_IFLNK:
+ return ("link");
+ case S_IFSOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+const char *
+ftype(u_int type)
+{
+ switch(type) {
+ case F_BLOCK:
+ return ("block");
+ case F_CHAR:
+ return ("char");
+ case F_DIR:
+ return ("dir");
+ case F_FIFO:
+ return ("fifo");
+ case F_FILE:
+ return ("file");
+ case F_LINK:
+ return ("link");
+ case F_SOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+char *
+rlink(char *name)
+{
+ int error = 0;
+ static char lbuf[MAXPATHLEN];
+ ssize_t len;
+
+ if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) {
+ error = errno;
+ RECORD_FAILURE(20, error);
+ errc(1, error, "line %d: %s", lineno, name);
+ }
+ lbuf[len] = '\0';
+ return (lbuf);
+}
diff --git a/file_cmds/mtree/create.c b/file_cmds/mtree/create.c
new file mode 100644
index 0000000..9300eaa
--- /dev/null
+++ b/file_cmds/mtree/create.c
@@ -0,0 +1,611 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/create.c,v 1.37 2005/03/29 11:44:17 tobez Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <grp.h>
+#ifndef __APPLE__
+#ifdef ENABLE_MD5
+#include <md5.h>
+#endif
+#ifdef ENABLE_SHA1
+#include <sha.h>
+#endif
+#ifdef ENABLE_RMD160
+#include <ripemd.h>
+#endif
+#ifdef ENABLE_SHA256
+#include <sha256.h>
+#endif
+#endif /* !__APPLE__ */
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+#ifdef __APPLE__
+#include "commoncrypto.h"
+#endif /* __APPLE__ */
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+static u_long flags = 0xffffffff;
+static char *xattrs = kNone;
+static char *acl = kNone;
+static u_quad_t xdstream_id;
+
+static int dsort(const FTSENT **, const FTSENT **);
+static void output(int, int *, const char *, ...) __printflike(3, 4);
+static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *, char **, char **, u_quad_t *);
+static void statf(int, FTSENT *);
+
+void
+cwalk(void)
+{
+ int error = 0;
+ FTS *t;
+ FTSENT *p;
+ time_t cl;
+ char *argv[2], host[MAXHOSTNAMELEN];
+ char dot[] = ".";
+ int indent = 0;
+ char *path;
+
+ if (!nflag) {
+ (void)time(&cl);
+ (void)gethostname(host, sizeof(host));
+ (void)printf(
+ "#\t user: %s\n#\tmachine: %s\n",
+ getlogin(), host);
+ (void)printf(
+ "#\t tree: %s\n#\t date: %s",
+ fullpath, ctime(&cl));
+ }
+
+ argv[0] = dot;
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) {
+ error = errno;
+ RECORD_FAILURE(76, error);
+ errc(1, error, "fts_open()");
+ }
+ while ((p = fts_read(t))) {
+ if (iflag)
+ indent = p->fts_level * 4;
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ if (!dflag)
+ (void)printf("\n");
+ if (!nflag) {
+ path = escape_path(p->fts_path);
+ (void)printf("# %s\n", path);
+ free(path);
+ }
+ statd(t, p, &uid, &gid, &mode, &flags, &xattrs, &acl, &xdstream_id);
+ statf(indent, p);
+ break;
+ case FTS_DP:
+ if (!nflag && (p->fts_level > 0)) {
+ path = escape_path(p->fts_path);
+ (void)printf("%*s# %s\n", indent, "", path);
+ free(path);
+ }
+ (void)printf("%*s..\n", indent, "");
+ if (!dflag)
+ (void)printf("\n");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ default:
+ if (!dflag)
+ statf(indent, p);
+ break;
+
+ }
+ }
+ (void)fts_close(t);
+ if (sflag && keys & F_CKSUM) {
+ RECORD_FAILURE(77, WARN_CHECKSUM);
+ warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total);
+ }
+}
+
+static void
+statf(int indent, FTSENT *p)
+{
+ int error = 0;
+ struct group *gr;
+ struct passwd *pw;
+ uint32_t val;
+ off_t len;
+ int fd, offset;
+ char *fflags;
+ char *escaped_name;
+
+ escaped_name = calloc(1, p->fts_namelen * 4 + 1);
+ if (escaped_name == NULL) {
+ RECORD_FAILURE(78, ENOMEM);
+ errx(1, "statf(): calloc() failed");
+ }
+ strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB);
+
+ if (iflag || S_ISDIR(p->fts_statp->st_mode))
+ offset = printf("%*s%s", indent, "", escaped_name);
+ else
+ offset = printf("%*s %s", indent, "", escaped_name);
+
+ free(escaped_name);
+
+ if (offset > (INDENTNAMELEN + indent))
+ offset = MAXLINELEN;
+ else
+ offset += printf("%*s", (INDENTNAMELEN + indent) - offset, "");
+
+ if (!S_ISREG(p->fts_statp->st_mode) && !dflag)
+ output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode));
+ if (p->fts_statp->st_uid != uid) {
+ if (keys & F_UNAME) {
+ pw = getpwuid(p->fts_statp->st_uid);
+ if (pw != NULL) {
+ output(indent, &offset, "uname=%s", pw->pw_name);
+ } else if (wflag) {
+ RECORD_FAILURE(27448, WARN_UNAME);
+ warnx("Could not get uname for uid=%u",
+ p->fts_statp->st_uid);
+ } else {
+ RECORD_FAILURE(79, EINVAL);
+ errx(1,
+ "Could not get uname for uid=%u",
+ p->fts_statp->st_uid);
+ }
+ }
+ if (keys & F_UID)
+ output(indent, &offset, "uid=%u", p->fts_statp->st_uid);
+ }
+ if (p->fts_statp->st_gid != gid) {
+ if (keys & F_GNAME) {
+ gr = getgrgid(p->fts_statp->st_gid);
+ if (gr != NULL) {
+ output(indent, &offset, "gname=%s", gr->gr_name);
+ } else if (wflag) {
+ RECORD_FAILURE(27449, WARN_UNAME);
+ warnx("Could not get gname for gid=%u",
+ p->fts_statp->st_gid);
+ } else {
+ RECORD_FAILURE(80, EINVAL);
+ errx(1,
+ "Could not get gname for gid=%u",
+ p->fts_statp->st_gid);
+ }
+ }
+ if (keys & F_GID)
+ output(indent, &offset, "gid=%u", p->fts_statp->st_gid);
+ }
+ if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
+ output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS);
+ if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
+ output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink);
+ if (keys & F_SIZE)
+ output(indent, &offset, "size=%jd",
+ (intmax_t)p->fts_statp->st_size);
+ if (keys & F_TIME) {
+ if (tflag && !insert_mod) {
+ output(indent, &offset, "time=%ld.%09ld",
+ (long)ts.tv_sec, ts.tv_nsec);
+ insert_mod = 1;
+ }
+ if (!tflag) {
+ output(indent, &offset, "time=%ld.%09ld",
+ (long)p->fts_statp->st_mtimespec.tv_sec,
+ p->fts_statp->st_mtimespec.tv_nsec);
+ }
+ }
+ if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
+ crc(fd, &val, &len)) {
+ error = errno;
+ RECORD_FAILURE(27450, error);
+ errc(1, error, "%s", p->fts_accpath);
+ }
+ (void)close(fd);
+ output(indent, &offset, "cksum=%lu", (unsigned long)val);
+ }
+#ifdef ENABLE_MD5
+ if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[33];
+#ifdef __clang__
+/* clang doesn't like MD5 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ digest = MD5File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!digest) {
+ error = errno;
+ RECORD_FAILURE(81, error);
+ errc(1, error, "%s", p->fts_accpath);
+ }
+ output(indent, &offset, "md5digest=%s", digest);
+ }
+#endif /* ENABLE_MD5 */
+#ifdef ENABLE_SHA1
+ if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[41];
+#ifdef __clang__
+/* clang doesn't like SHA1 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ digest = SHA1_File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!digest) {
+ error = errno;
+ RECORD_FAILURE(82, error);
+ errc(1, error, "%s", p->fts_accpath);
+ }
+ output(indent, &offset, "sha1digest=%s", digest);
+ }
+#endif /* ENABLE_SHA1 */
+#ifdef ENABLE_RMD160
+ if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[41];
+#ifdef __clang__
+/* clang doesn't like RIPEMD160 due to security concerns, but it's used for file data/metadata integrity.. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ digest = RIPEMD160_File(p->fts_accpath, buf);
+#pragma clang diagnostic pop
+#endif
+ if (!digest) {
+ error = errno;
+ RECORD_FAILURE(83, error);
+ errc(1, error, "%s", p->fts_accpath);
+ }
+ output(indent, &offset, "ripemd160digest=%s", digest);
+ }
+#endif /* ENABLE_RMD160 */
+#ifdef ENABLE_SHA256
+ if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[kSHA256NullTerminatedBuffLen];
+
+ digest = SHA256_File(p->fts_accpath, buf);
+ if (!digest) {
+ error = errno;
+ RECORD_FAILURE(84, error);
+ errc(1, error, "%s", p->fts_accpath);
+ }
+ output(indent, &offset, "sha256digest=%s", digest);
+ }
+#endif /* ENABLE_SHA256 */
+ if (keys & F_SLINK &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+ char visbuf[MAXPATHLEN * 4];
+ char *s = rlink(p->fts_accpath);
+ strvis(visbuf, s, VIS_WHITE | VIS_OCTAL);
+ output(indent, &offset, "link=%s", visbuf);
+ }
+ if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
+ fflags = flags_to_string(p->fts_statp->st_flags);
+ output(indent, &offset, "flags=%s", fflags);
+ free(fflags);
+ }
+ if (keys & F_BTIME) {
+ if (tflag && !insert_birth) {
+ output(indent, &offset, "btime=%ld.%09ld",
+ ts.tv_sec, ts.tv_nsec);
+ insert_birth = 1;
+ }
+ if (!tflag) {
+ output(indent, &offset, "btime=%ld.%09ld",
+ p->fts_statp->st_birthtimespec.tv_sec,
+ p->fts_statp->st_birthtimespec.tv_nsec);
+ }
+ }
+ // only check access time on regular files, as traversing a folder will update its access time
+ if (keys & F_ATIME && S_ISREG(p->fts_statp->st_mode)) {
+ if (tflag && !insert_access) {
+ output(indent, &offset, "atime=%ld.%09ld",
+ ts.tv_sec, ts.tv_nsec);
+ insert_access = 1;
+ }
+ if (!tflag) {
+ output(indent, &offset, "atime=%ld.%09ld",
+ p->fts_statp->st_atimespec.tv_sec,
+ p->fts_statp->st_atimespec.tv_nsec);
+ }
+ }
+ if (keys & F_CTIME) {
+ if (tflag && !insert_change) {
+ output(indent, &offset, "ctime=%ld.%09ld",
+ ts.tv_sec, ts.tv_nsec);
+ insert_change = 1;
+ }
+ if (!tflag) {
+ output(indent, &offset, "ctime=%ld.%09ld",
+ p->fts_statp->st_ctimespec.tv_sec,
+ p->fts_statp->st_ctimespec.tv_nsec);
+ }
+ }
+ // date added to parent folder is only supported for files and directories
+ if (keys & F_PTIME && (S_ISREG(p->fts_statp->st_mode) ||
+ S_ISDIR(p->fts_statp->st_mode))) {
+ int supported;
+ struct timespec ptimespec = ptime(p->fts_accpath, &supported);
+ if (tflag && !insert_parent) {
+ output(indent, &offset, "ptime=%ld.%09ld",
+ ts.tv_sec, ts.tv_nsec);
+ insert_parent = 1;
+ }
+ if (!tflag && supported) {
+ output(indent, &offset, "ptime=%ld.%09ld",
+ ptimespec.tv_sec,
+ ptimespec.tv_nsec);
+ }
+ }
+ if (keys & F_XATTRS) {
+ char buf[kSHA256NullTerminatedBuffLen];
+ xattr_info *ai;
+
+ ai = SHA256_Path_XATTRs(p->fts_accpath, buf);
+ if (ai && ai->digest) {
+ if ((strcmp(ai->digest, xattrs) != 0) || (ai->xdstream_priv_id != xdstream_id)) {
+ output(indent, &offset, "xattrsdigest=%s.%llu", ai->digest, ai->xdstream_priv_id);
+ }
+ free(ai);
+ ai = NULL;
+ }
+ }
+ if (keys & F_INODE) {
+ output(indent, &offset, "inode=%llu", p->fts_statp->st_ino);
+ }
+ if (keys & F_ACL) {
+ char *digest, buf[kSHA256NullTerminatedBuffLen];
+
+ digest = SHA256_Path_ACL(p->fts_accpath, buf);
+ if (digest && (strcmp(digest, acl) != 0)) {
+ output(indent, &offset, "acldigest=%s", digest);
+ }
+ }
+ if (keys & F_SIBLINGID) {
+ uint64_t sibling_id = get_sibling_id(p->fts_accpath);
+ sibling_id = (sibling_id != p->fts_statp->st_ino) ? sibling_id : 0;
+ output(indent, &offset, "siblingid=%llu", sibling_id);
+ }
+
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+#define MAXFLAGS 256
+#define MAXS 16
+
+static int
+statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags, char **pxattrs, char **pacl, u_quad_t *xdstream_id)
+{
+ int error = 0;
+ FTSENT *p;
+ gid_t sgid;
+ uid_t suid;
+ mode_t smode;
+ u_long sflags;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid = *pgid;
+ uid_t saveuid = *puid;
+ mode_t savemode = *pmode;
+ u_long saveflags = *pflags;
+ char *savexattrs = *pxattrs;
+ char *saveacl = *pacl;
+ u_quad_t savexdstream_id = *xdstream_id;
+ u_short maxgid, maxuid, maxmode, maxflags;
+ u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS];
+ char *fflags;
+ static int first = 1;
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ error = errno;
+ if (error) {
+ RECORD_FAILURE(85, error);
+ errc(1, error, "%s", RP(parent));
+ }
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+ bzero(f, sizeof(f));
+
+ maxuid = maxgid = maxmode = maxflags = 0;
+ for (; p; p = p->fts_link) {
+ if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+
+ /*
+ * XXX
+ * note that we don't count the most common xattr/acl digest
+ * so set will always the default value (none)
+ */
+
+ /*
+ * XXX
+ * note that the below will break when file flags
+ * are extended beyond the first 4 bytes of each
+ * half word of the flags
+ */
+#define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
+ sflags = p->fts_statp->st_flags;
+ if (FLAGS2IDX(sflags) < MAXFLAGS &&
+ ++f[FLAGS2IDX(sflags)] > maxflags) {
+ saveflags = sflags;
+ maxflags = f[FLAGS2IDX(sflags)];
+ }
+ }
+ }
+ /*
+ * If the /set record is the same as the last one we do not need to output
+ * a new one. So first we check to see if anything changed. Note that we
+ * always output a /set record for the first directory.
+ */
+ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) ||
+ (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) ||
+ ((keys & F_MODE) && (*pmode != savemode)) ||
+ ((keys & F_FLAGS) && (*pflags != saveflags)) ||
+ (first)) {
+ first = 0;
+ if (dflag)
+ (void)printf("/set type=dir");
+ else
+ (void)printf("/set type=file");
+ if (keys & F_UNAME) {
+ pw = getpwuid(saveuid);
+ if (pw != NULL) {
+ (void)printf(" uname=%s", pw->pw_name);
+ } else if (wflag) {
+ RECORD_FAILURE(27451, WARN_UNAME);
+ warnx( "Could not get uname for uid=%u", saveuid);
+ } else {
+ RECORD_FAILURE(86, EINVAL);
+ errx(1, "Could not get uname for uid=%u", saveuid);
+ }
+ }
+ if (keys & F_UID)
+ (void)printf(" uid=%lu", (u_long)saveuid);
+ if (keys & F_GNAME) {
+ gr = getgrgid(savegid);
+ if (gr != NULL) {
+ (void)printf(" gname=%s", gr->gr_name);
+ } else if (wflag) {
+ RECORD_FAILURE(27452, WARN_UNAME);
+ warnx("Could not get gname for gid=%u", savegid);
+ } else {
+ RECORD_FAILURE(87, EINVAL);
+ errx(1, "Could not get gname for gid=%u", savegid);
+ }
+ }
+ if (keys & F_GID)
+ (void)printf(" gid=%lu", (u_long)savegid);
+ if (keys & F_MODE)
+ (void)printf(" mode=%#o", savemode);
+ if (keys & F_NLINK)
+ (void)printf(" nlink=1");
+ if (keys & F_FLAGS) {
+ fflags = flags_to_string(saveflags);
+ (void)printf(" flags=%s", fflags);
+ free(fflags);
+ }
+ if (keys & F_XATTRS)
+ (void)printf(" xattrsdigest=%s.%llu", savexattrs, savexdstream_id);
+ if (keys & F_ACL)
+ (void)printf(" acldigest=%s", saveacl);
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ *pflags = saveflags;
+ *pxattrs = savexattrs;
+ *pacl = saveacl;
+ *xdstream_id = savexdstream_id;
+ }
+ return (0);
+}
+
+static int
+dsort(const FTSENT **a, const FTSENT **b)
+{
+ if (S_ISDIR((*a)->fts_statp->st_mode)) {
+ if (!S_ISDIR((*b)->fts_statp->st_mode))
+ return (1);
+ } else if (S_ISDIR((*b)->fts_statp->st_mode))
+ return (-1);
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+#include <stdarg.h>
+
+void
+output(int indent, int *offset, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ (void)printf(" \\\n%*s", INDENTNAMELEN + indent, "");
+ *offset = INDENTNAMELEN + indent;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
diff --git a/file_cmds/mtree/excludes.c b/file_cmds/mtree/excludes.c
new file mode 100644
index 0000000..cb432bf
--- /dev/null
+++ b/file_cmds/mtree/excludes.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. 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>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/excludes.c,v 1.8 2003/10/21 08:27:05 phk Exp $");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "metrics.h"
+#include "extern.h"
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+ LIST_ENTRY(exclude) link;
+ const char *glob;
+ int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+void
+init_excludes(void)
+{
+ LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+ FILE *fp;
+ char *line, *str;
+ struct exclude *e;
+ size_t len;
+
+ fp = fopen(name, "r");
+ if (fp == 0)
+ err(1, "%s", name);
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ if (line[len - 1] == '\n')
+ len--;
+ if (len == 0)
+ continue;
+
+ str = malloc(len + 1);
+ e = malloc(sizeof *e);
+ if (str == 0 || e == 0) {
+ RECORD_FAILURE(59, ENOMEM);
+ errx(1, "memory allocation error");
+ }
+ e->glob = str;
+ memcpy(str, line, len);
+ str[len] = '\0';
+ if (strchr(str, '/'))
+ e->pathname = 1;
+ else
+ e->pathname = 0;
+ LIST_INSERT_HEAD(&excludes, e, link);
+ }
+ fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+ struct exclude *e;
+
+ /* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+ LIST_FOREACH(e, &excludes, link) {
+ if ((e->pathname && MATCH(e->glob, path))
+ || MATCH(e->glob, fname))
+ return 1;
+ }
+ return 0;
+}
diff --git a/file_cmds/mtree/extern.h b/file_cmds/mtree/extern.h
new file mode 100644
index 0000000..47533c2
--- /dev/null
+++ b/file_cmds/mtree/extern.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.sbin/mtree/extern.h,v 1.13 2004/01/11 19:38:48 phk Exp $
+ */
+
+#ifndef _EXTERN_H_
+#define _EXTERN_H_
+
+#include "mtree.h"
+
+extern uint32_t crc_total;
+
+#ifdef _FTS_H_
+int compare(char *, NODE *, FTSENT *);
+#endif
+int crc(int, uint32_t *, off_t *);
+void cwalk(void);
+char *flags_to_string(u_long);
+char *escape_path(char *string);
+struct timespec ptime(char *path, int *supported);
+
+const char *inotype(u_int);
+u_int parsekey(char *, int *);
+char *rlink(char *);
+NODE *mtree_readspec(FILE *fi);
+int mtree_verifyspec(FILE *fi);
+int mtree_specspec(FILE *fi, FILE *fj);
+
+int check_excludes(const char *, const char *);
+void init_excludes(void);
+void read_excludes_file(const char *);
+const char * ftype(u_int type);
+
+extern int ftsoptions;
+extern u_int keys;
+extern int lineno;
+extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag, mflag, tflag;
+extern int insert_mod, insert_birth, insert_access, insert_change, insert_parent;
+extern struct timespec ts;
+#ifdef MAXPATHLEN
+extern char fullpath[MAXPATHLEN];
+#endif
+
+#endif /* _EXTERN_H_ */
diff --git a/file_cmds/mtree/fix_failure_locations.py b/file_cmds/mtree/fix_failure_locations.py
new file mode 100755
index 0000000..a49906e
--- /dev/null
+++ b/file_cmds/mtree/fix_failure_locations.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+#
+# This script is used to automatically fix up the location numbers in
+# calls to RECORD_FAILURE(). When adding a new call to RECORD_FAILURE,
+# write it like:
+# RECORD_FAILURE(0, ...);
+# Don't put any white space between the open parenthesis, zero and comma.
+# Once you have added the new calls to RECORD_FAILURE, then run this script,
+# passing it the path to the directory, like this:
+# python3 mtree/fix_failure_locations.py mtree/
+#
+# This script will edit the files, changing the "0" to the next available
+# location number. It will also detect and complain if you have duplicate
+# location numbers.
+#
+# DO NOT reuse location numbers! It is best if locations are consistent across
+# all versions that have that RECORD_FAILURE call.
+#
+
+import sys
+import os
+import re
+from collections import defaultdict
+from datetime import datetime,timezone
+
+class LocationUpdater(object):
+ epoch = datetime(2020, 6, 17, 23, 22, 46, 562458, tzinfo=timezone.utc)
+ location_base = int((datetime.now(timezone.utc) - epoch).total_seconds() / 60)
+ # Match the number in "RECORD_FAILURE(<number>,"
+ fail_re = (re.compile('(?<=\\bRECORD_FAILURE\\()\\d+(?=,)'),re.compile('(?<=\\bRECORD_FAILURE_MSG\\()\\d+(?=,)'))
+
+ def __init__(self, path):
+ self.location = self.location_base
+ self.path = path
+ # Counters for how often each location number was found
+ self.counts = defaultdict(int)
+ self.locations_changed = 0
+
+ # Replace the "0" in "RECORD_FAILURE(0," with next location number, in *.c
+ def fixLocations(self):
+ def replace_loc(match):
+ location = int(match.group(0))
+ if location == 0:
+ # Replace location 0 with the next available location
+ self.location += 1
+ self.locations_changed += 1
+ location = self.location
+ # Count the number of times this location number was used
+ self.counts[location] += 1
+ # Return the (possibly updated) location number
+ return str(location)
+ rootpath = self.path
+ for dirpath, dirnames, filenames in os.walk(rootpath):
+ for filename in filenames:
+ if filename.endswith(".c") or filename.endswith(".cpp"):
+ path = os.path.join(dirpath, filename)
+ content = open(path, "r").read()
+ for fail_re in self.fail_re:
+ if fail_re.search(content):
+ locations_changed_before = self.locations_changed
+ content = fail_re.sub(replace_loc, content)
+ if self.locations_changed != locations_changed_before:
+ # We updated a location number, so write the changed file
+ print("Updating file {}".format(path))
+ open(path,"w").write(content)
+
+ def duplicates(self):
+ # Return the list of keys whose count is greater than 1
+ return [k for (k,v) in iter(self.counts.items()) if v > 1]
+
+updater = LocationUpdater(sys.argv[1])
+updater.fixLocations()
+dups = updater.duplicates()
+if len(dups):
+ print("WARNING! Duplicate location numbers: {}".format(dups))
+ sys.exit(1)
diff --git a/file_cmds/mtree/metrics.c b/file_cmds/mtree/metrics.c
new file mode 100644
index 0000000..01a9fe2
--- /dev/null
+++ b/file_cmds/mtree/metrics.c
@@ -0,0 +1,155 @@
+/*
+* Copyright (c) 2020 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "metrics.h"
+
+#define MAX_WARNINGS_LOGGED 5
+#define MAX_ERRORS_LOGGED 5
+#define WARN_FIRST -1
+
+#ifndef ROUNDUP
+#define ROUNDUP(COUNT, MULTIPLE) ((((COUNT) + (MULTIPLE) - 1) / (MULTIPLE)) * (MULTIPLE))
+#endif
+
+typedef struct failure_info {
+ int location;
+ int code;
+} failure_info_t;
+
+typedef struct metrics {
+ FILE *file;
+ time_t start_time;
+ int warning_count;
+ failure_info_t warnings[MAX_WARNINGS_LOGGED];
+ int error_count;
+ failure_info_t errors[MAX_ERRORS_LOGGED];
+ int last_error_location;
+ char *path;
+ int result;
+} metrics_t;
+metrics_t metrics = {};
+
+void
+set_metrics_file(FILE *file)
+{
+ metrics.file = file;
+}
+
+void
+set_metric_start_time(time_t time)
+{
+ metrics.start_time = time;
+}
+
+void
+set_metric_path(char *path)
+{
+ metrics.path = strdup(path);
+}
+
+void
+mtree_record_failure(int location, int code)
+{
+ if (code <= WARN_FIRST) {
+ if (metrics.warning_count < MAX_WARNINGS_LOGGED) {
+ metrics.warning_count++;
+ } else {
+ // Shift up the warnings to make space for the latest one.
+ for (int index = 0; index < MAX_ERRORS_LOGGED - 1; index++) {
+ metrics.warnings[index] = metrics.warnings[index + 1];
+ }
+ }
+ metrics.warnings[metrics.warning_count - 1].location = location;
+ metrics.warnings[metrics.warning_count - 1].code = code;
+ } else {
+ int error_index = -1;
+ if (metrics.error_count <= MAX_ERRORS_LOGGED) {
+ if (metrics.error_count > 0) {
+ // Log all but the last error which occured in the location and
+ // code arrays. The last (location, error) is logged in
+ // (metrics.last_error_location, metrics.error)
+ error_index = metrics.error_count - 1;
+ }
+ metrics.error_count++;
+ } else {
+ // Shift up the errors to make space for the latest one.
+ for (int index = 0; index < MAX_ERRORS_LOGGED - 1; index++) {
+ metrics.errors[index] = metrics.errors[index + 1];
+ }
+ error_index = MAX_ERRORS_LOGGED - 1;
+ }
+ if (error_index >= 0) {
+ metrics.errors[error_index].location = metrics.last_error_location;
+ metrics.errors[error_index].code = metrics.result;
+ }
+ metrics.last_error_location = location;
+ metrics.result = code;
+ }
+}
+/*
+ * Note on format of metric string
+ * 1) dev points to the path
+ * 2) result is the overall result code from mtree
+ * 3) warnings and errors (upto 5 each) are printed in the format :
+ * w:(location1:code1),(location2:code2).... and
+ * e:(location1:code1),(location2:code2).... respectively.
+ * 4) fl is the last failure location of the run which is 0 if there is no failure
+ * 5) time is the total time taken for the run
+ */
+void
+print_metrics_to_file(void)
+{
+ if (metrics.file == NULL) {
+ return;
+ }
+
+ fprintf(metrics.file, "dev=%s result=%d ",
+ metrics.path ? metrics.path : "", metrics.result);
+ if (metrics.warning_count) {
+ fprintf(metrics.file, "w:");
+ for (int index = 0; index < metrics.warning_count; index++) {
+ fprintf(metrics.file, "(%d:%d)",
+ metrics.warnings[index].location, metrics.warnings[index].code);
+ }
+ fprintf(metrics.file, " ");
+ }
+ if (metrics.error_count > 1) {
+ fprintf(metrics.file, "e:");
+ for (int index = 0; index < metrics.error_count - 1; index++) {
+ fprintf(metrics.file, "(%d:%d)",
+ metrics.errors[index].location, metrics.errors[index].code);
+ }
+ fprintf(metrics.file, " ");
+ }
+ fprintf(metrics.file, "fl=%d time=%ld\n",
+ metrics.last_error_location, ROUNDUP((time(NULL) - metrics.start_time), 60) / 60);
+
+ fclose(metrics.file);
+ if (metrics.path) {
+ free(metrics.path);
+ metrics.path = NULL;
+ }
+}
diff --git a/file_cmds/mtree/metrics.h b/file_cmds/mtree/metrics.h
new file mode 100644
index 0000000..35dca43
--- /dev/null
+++ b/file_cmds/mtree/metrics.h
@@ -0,0 +1,48 @@
+/*
+* Copyright (c) 2020 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#ifndef _METRICS_H_
+#define _METRICS_H_
+
+#include <sys/time.h>
+#include <stdio.h>
+
+// mtree error logging
+enum mtree_result {
+ SUCCESS = 0,
+ WARN_TIME = -1,
+ WARN_USAGE = -2,
+ WARN_CHECKSUM = -3,
+ WARN_MISMATCH = -4,
+ WARN_UNAME = -5,
+ /* Could also be a POSIX errno value */
+};
+
+void set_metrics_file(FILE *file);
+void set_metric_start_time(time_t time);
+void set_metric_path(char *path);
+#define RECORD_FAILURE(location, error) mtree_record_failure(location, error)
+void mtree_record_failure(int location, int code);
+void print_metrics_to_file(void);
+
+#endif /* _METRICS_H_ */
diff --git a/file_cmds/mtree/misc.c b/file_cmds/mtree/misc.c
new file mode 100644
index 0000000..66e566e
--- /dev/null
+++ b/file_cmds/mtree/misc.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /*not lint */
+#endif
+#include <sys/cdefs.h>
+#include <errno.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fts.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+#import <sys/attr.h>
+#include <vis.h>
+
+typedef struct _key {
+ const char *name; /* key name */
+ u_int val; /* value */
+
+#define NEEDVALUE 0x01
+ u_int flags;
+} KEY;
+
+/* NB: the following table must be sorted lexically. */
+static KEY keylist[] = {
+ {"acldigest", F_ACL, NEEDVALUE},
+ {"atime", F_ATIME, NEEDVALUE},
+ {"btime", F_BTIME, NEEDVALUE},
+ {"cksum", F_CKSUM, NEEDVALUE},
+ {"ctime", F_CTIME, NEEDVALUE},
+ {"flags", F_FLAGS, NEEDVALUE},
+ {"gid", F_GID, NEEDVALUE},
+ {"gname", F_GNAME, NEEDVALUE},
+ {"ignore", F_IGN, 0},
+ {"inode", F_INODE, NEEDVALUE},
+ {"link", F_SLINK, NEEDVALUE},
+#ifdef ENABLE_MD5
+ {"md5digest", F_MD5, NEEDVALUE},
+#endif
+ {"mode", F_MODE, NEEDVALUE},
+ {"nlink", F_NLINK, NEEDVALUE},
+ {"nochange", F_NOCHANGE, 0},
+ {"ptime", F_PTIME, NEEDVALUE},
+#ifdef ENABLE_RMD160
+ {"ripemd160digest", F_RMD160, NEEDVALUE},
+#endif
+#ifdef ENABLE_SHA1
+ {"sha1digest", F_SHA1, NEEDVALUE},
+#endif
+#ifdef ENABLE_SHA256
+ {"sha256digest", F_SHA256, NEEDVALUE},
+#endif
+ {"siblingid", F_SIBLINGID, NEEDVALUE},
+ {"size", F_SIZE, NEEDVALUE},
+ {"time", F_TIME, NEEDVALUE},
+ {"type", F_TYPE, NEEDVALUE},
+ {"uid", F_UID, NEEDVALUE},
+ {"uname", F_UNAME, NEEDVALUE},
+ {"xattrsdigest", F_XATTRS, NEEDVALUE},
+};
+
+int keycompare(const void *, const void *);
+
+u_int
+parsekey(char *name, int *needvaluep)
+{
+ KEY *k, tmp;
+
+ tmp.name = name;
+ k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL) {
+ RECORD_FAILURE(107, EINVAL);
+ errx(1, "line %d: unknown keyword %s", lineno, name);
+ }
+
+ if (needvaluep)
+ *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+ return (k->val);
+}
+
+int
+keycompare(const void *a, const void *b)
+{
+ return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name));
+}
+
+char *
+flags_to_string(u_long fflags)
+{
+ int error = 0;
+ char *string;
+
+ string = fflagstostr(fflags);
+ if (string != NULL && *string == '\0') {
+ free(string);
+ string = strdup("none");
+ }
+ if (string == NULL) {
+ error = errno;
+ RECORD_FAILURE(108, error);
+ errc(1, error, NULL);
+ }
+
+ return string;
+}
+
+// escape path and always return a new string so it can be freed
+char *
+escape_path(char *string)
+{
+ char *escapedPath = calloc(1, strlen(string) * 4 + 1);
+ if (escapedPath == NULL) {
+ RECORD_FAILURE(109, ENOMEM);
+ errx(1, "escape_path(): calloc() failed");
+ }
+ strvis(escapedPath, string, VIS_NL | VIS_CSTYLE | VIS_OCTAL);
+
+ return escapedPath;
+}
+
+struct ptimebuf {
+ uint32_t length;
+ attribute_set_t returned_attrs;
+ struct timespec st_ptimespec;
+} __attribute__((aligned(4), packed));
+
+// ptime is not supported on root filesystems or HFS filesystems older than the feature being introduced
+struct timespec
+ptime(char *path, int *supported) {
+
+ int error = 0;
+ int ret = 0;
+ struct ptimebuf buf;
+ struct attrlist list = {
+ .bitmapcount = ATTR_BIT_MAP_COUNT,
+ .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_ADDEDTIME,
+ };
+ ret = getattrlist(path, &list, &buf, sizeof(buf), FSOPT_NOFOLLOW);
+ if (ret) {
+ error = errno;
+ RECORD_FAILURE(110, error);
+ errc(1, error, "ptime: getattrlist");
+ }
+
+ *supported = 0;
+ if (buf.returned_attrs.commonattr & ATTR_CMN_ADDEDTIME) {
+ *supported = 1;
+ }
+
+ return buf.st_ptimespec;
+
+}
diff --git a/file_cmds/mtree/mtree.8 b/file_cmds/mtree/mtree.8
new file mode 100644
index 0000000..1aa529a
--- /dev/null
+++ b/file_cmds/mtree/mtree.8
@@ -0,0 +1,393 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD: src/usr.sbin/mtree/mtree.8,v 1.53 2005/07/31 03:30:47 keramida Exp $
+.\"
+.Dd March 29, 2005
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm mtree
+.Op Fl LPUcdeinqruxw
+.Bk -words
+.Op Fl f Ar spec
+.Ek
+.Bk -words
+.Op Fl f Ar spec
+.Ek
+.Bk -words
+.Op Fl K Ar keywords
+.Ek
+.Bk -words
+.Op Fl k Ar keywords
+.Ek
+.Bk -words
+.Op Fl p Ar path
+.Ek
+.Bk -words
+.Op Fl s Ar seed
+.Ek
+.Bk -words
+.Op Fl X Ar exclude-list
+.Ek
+.Sh DESCRIPTION
+The
+.Nm mtree
+utility compares the file hierarchy rooted in the current directory against a
+specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specifications, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.\" ==========
+.It Fl c
+Print a specification for the file hierarchy to the standard output.
+.\" ==========
+.It Fl d
+Ignore everything except directory type files.
+.\" ==========
+.It Fl e
+Do not complain about files that are in the file hierarchy, but not in the
+specification.
+.\" ==========
+.It Fl f Ar file
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.Pp
+If this option is specified twice,
+the two specifications are compared with each other,
+rather than to the file hierarchy.
+The specifications be sorted like output generated using
+.Fl c .
+The output format in this case is somewhat remniscent of
+.Xr comm 1 ,
+having "in first spec only", "in second spec only", and "different"
+columns, prefixed by zero, one and two TAB characters respectively.
+Each entry in the "different" column occupies two lines,
+one from each specification.
+.\" ==========
+.It Fl i
+Indent the output 4 spaces each time a directory level is descended when
+create a specification with the
+.Fl c
+option.
+This does not affect either the /set statements or the comment before each
+directory.
+It does however affect the comment before the close of each directory.
+.\" ==========
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated)
+.Ar keywords
+to the current set of keywords.
+.\" ==========
+.It Fl k Ar keywords
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+.Ar keywords
+instead of the current set of keywords.
+.\" ==========
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.\" ==========
+.It Fl n
+Do not emit pathname comments when creating a specification.
+Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.\" ==========
+.It Fl P
+Do not follow symbolic links in the file hierarchy, instead consider
+the symbolic link itself in any comparisons.
+This is the default.
+.\" ==========
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.\" ==========
+.It Fl q
+Quiet mode.
+Do not complain when a
+.Dq missing
+directory cannot be created because it already exists.
+This occurs when the directory is a symbolic link.
+.\" ==========
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.\" ==========
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Cm cksum
+was specified.
+The checksum is seeded with the specified value.
+.\" ==========
+.It Fl U
+Modify the owner, group, permissions, and modification time of existing
+files to match the specification and create any missing directories or
+symbolic links.
+User, group and permissions must all be specified for missing directories
+to be created.
+Corrected mismatches are not considered errors.
+.\" ==========
+.It Fl u
+Same as
+.Fl U
+except a status of 2 is returned if the file hierarchy did not match
+the specification.
+.\" ==========
+.It Fl w
+Make some error conditions non-fatal warnings.
+.\" ==========
+.It Fl X Ar exclude-list
+The specified file contains
+.Xr fnmatch 3
+patterns matching files to be excluded from
+the specification, one to a line.
+If the pattern contains a
+.Ql \&/
+character, it will be matched against entire pathnames (relative to
+the starting directory); otherwise,
+it will be matched against basenames only.
+No comments are allowed in
+the
+.Ar exclude-list
+file.
+.\" ==========
+.It Fl x
+Do not descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e., strings
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm flags
+The file flags as a symbolic name.
+See
+.Xr chflags 1
+for information on these names.
+If no flags are to be set the string
+.Dq none
+may be used to override the current default.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm md5digest
+The MD5 message digest of the file.
+.It Cm sha1digest
+The
+.Tn FIPS
+160-1
+.Pq Dq Tn SHA-1
+message digest of the file.
+.It Cm ripemd160digest
+The
+.Tn RIPEMD160
+message digest of the file.
+.It Cm mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Cm nlink
+The number of hard links the file is expected to have.
+.It Cm nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
+.It Cm uid
+The file owner as a numeric value.
+.It Cm uname
+The file owner as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file.
+.It Cm btime
+The creation (birth) time of the file.
+.It Cm atime
+The last access time of the file.
+.It Cm ctime
+The last metadata modification time of the file.
+.It Cm ptime
+The time the file was added to its parent folder.
+.It Cm inode
+The inode number of the file.
+.It Cm xattrsdigest
+Digest of the extended attributes of the file.
+.It Cm acldigest
+Digest of the access control list of the file.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+The default set of keywords are
+.Cm flags ,
+.Cm gid ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.Cm link ,
+.Cm time ,
+and
+.Cm uid .
+.Pp
+There are four types of lines in a specification.
+.Pp
+The first type of line sets a global value for a keyword, and consists of
+the string ``/set'' followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+(``=''), followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.Pp
+The second type of line unsets keywords and consists of the string
+``/unset'', followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+.Pp
+The third type of line is a file specification and consists of a file
+name, followed by whitespace, followed by zero or more whitespace
+separated keyword/value pairs.
+The file name may be preceded by whitespace characters.
+The file name may contain any of the standard file name matching
+characters (``['', ``]'', ``?'' or ``*''), in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign (``=''), followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+All paths are relative.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+Which brings us to the last type of line in a specification: a line
+containing only the string
+.Dq Pa ..\&
+causes the current directory
+path to ascend one level.
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Pp
+The
+.Nm mtree
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+A status of 2 is converted to a status of 0 if the
+.Fl U
+option is used.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things; the files in
+.Pa /etc/mtree
+were used to create almost all directories in this
+.Fx
+distribution.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr md5 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr md5 3 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm mtree
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Tn MD5
+digest capability was added in
+.Fx 2.1 ,
+in response to the widespread use of programs which can spoof
+.Xr cksum 1 .
+The
+.Tn SHA-1
+and
+.Tn RIPEMD160
+digests were added in
+.Fx 4.0 ,
+as new attacks have demonstrated weaknesses in
+.Tn MD5 .
+Support for file flags was added in
+.Fx 4.0 ,
+and mostly comes from
+.Nx .
diff --git a/file_cmds/mtree/mtree.c b/file_cmds/mtree/mtree.c
new file mode 100644
index 0000000..edf0cce
--- /dev/null
+++ b/file_cmds/mtree/mtree.c
@@ -0,0 +1,358 @@
+/*-
+ * Copyright (c) 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp $");
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+#define SECONDS_IN_A_DAY (60 * 60 * 24)
+
+int ftsoptions = FTS_PHYSICAL;
+int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag, mflag, tflag;
+int insert_mod, insert_birth, insert_access, insert_change, insert_parent;
+struct timespec ts;
+u_int keys;
+char fullpath[MAXPATHLEN];
+CFMutableDictionaryRef dict;
+char *filepath;
+
+static void usage(void);
+static bool write_plist_to_file(void);
+
+static void
+do_cleanup(void) {
+
+ if (mflag) {
+ if (dict)
+ CFRelease(dict);
+ if (filepath)
+ free(filepath);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0;
+ int ch;
+ char *dir, *p;
+ int status;
+ FILE *spec1, *spec2;
+ char *timestamp = NULL;
+ char *timeformat = "%FT%T";
+ FILE *file = NULL;
+
+ dir = NULL;
+ keys = KEYDEFAULT;
+ init_excludes();
+ spec1 = stdin;
+ spec2 = NULL;
+ set_metric_start_time(time(NULL));
+
+ atexit(do_cleanup);
+ atexit(print_metrics_to_file);
+
+ while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:m:F:t:E:")) != -1)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (spec1 == stdin) {
+ spec1 = fopen(optarg, "r");
+ if (spec1 == NULL) {
+ error = errno;
+ RECORD_FAILURE(88, error);
+ errc(1, error, "%s", optarg);
+ }
+ } else if (spec2 == NULL) {
+ spec2 = fopen(optarg, "r");
+ if (spec2 == NULL) {
+ error = errno;
+ RECORD_FAILURE(89, error);
+ errc(1, error, "%s", optarg);
+ }
+ } else {
+ RECORD_FAILURE(90, WARN_USAGE);
+ usage();
+ }
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'K':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'k':
+ keys = F_TYPE;
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'L':
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'P':
+ ftsoptions &= ~FTS_LOGICAL;
+ ftsoptions |= FTS_PHYSICAL;
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = (uint32_t)~strtoul(optarg, &p, 0);
+ if (*p) {
+ RECORD_FAILURE(91, WARN_USAGE);
+ errx(1, "illegal seed value -- %s", optarg);
+ }
+ break;
+ case 'U':
+ Uflag = 1;
+ uflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'w':
+ wflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case 'X':
+ read_excludes_file(optarg);
+ break;
+ case 'm':
+ mflag = 1;
+ filepath = strdup(optarg);
+ dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ insert_access = insert_mod = insert_birth = insert_change = 0;
+ break;
+ case 'F':
+ timeformat = optarg;
+ break;
+ case 't':
+ timestamp = optarg;
+ tflag = 1;
+ break;
+ case 'E':
+ if (!strcmp(optarg, "-")) {
+ file = stdout;
+ } else {
+ file = fopen(optarg, "w");
+ }
+ if (file == NULL) {
+ warnx("Could not open metrics log file %s", optarg);
+ } else {
+ set_metrics_file(file);
+ }
+ break;
+
+ case '?':
+ default:
+ RECORD_FAILURE(92, WARN_USAGE);
+ usage();
+ }
+ argc -= optind;
+// argv += optind;
+
+ if (argc) {
+ RECORD_FAILURE(93, WARN_USAGE);
+ usage();
+ }
+
+ if (timestamp) {
+ struct tm t = {};
+ char *r = strptime(timestamp, timeformat, &t);
+ if (r && r[0] == '\0') {
+ ts.tv_sec = mktime(&t);
+ if ((ts.tv_sec - time(NULL)) > 30 * SECONDS_IN_A_DAY) {
+ RECORD_FAILURE(94, WARN_TIME);
+ errx(1, "Time is more then 30 days in the future");
+ } else if (ts.tv_sec < 0) {
+ RECORD_FAILURE(95, WARN_TIME);
+ errx(1, "Time is too far in the past");
+ }
+ } else {
+ RECORD_FAILURE(96, WARN_TIME);
+ errx(1,"Cannot parse timestamp '%s' using format \"%s\"\n", timestamp, timeformat);
+ }
+ }
+
+ if (dir && chdir(dir)) {
+ error = errno;
+ RECORD_FAILURE(97, error);
+ errc(1, error, "%s", dir);
+ }
+
+ if ((cflag || sflag) && !getwd(fullpath)) {
+ RECORD_FAILURE(98, errno);
+ errx(1, "%s", fullpath);
+ }
+
+ if (dir) {
+ set_metric_path(dir);
+ }
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ if (spec2 != NULL) {
+ status = mtree_specspec(spec1, spec2);
+ if (Uflag & (status == MISMATCHEXIT)) {
+ status = 0;
+ } else {
+ RECORD_FAILURE(99, status);
+ }
+ } else {
+ status = mtree_verifyspec(spec1);
+ if (Uflag & (status == MISMATCHEXIT)) {
+ status = 0;
+ } else {
+ RECORD_FAILURE(100, status);
+ }
+ if (mflag && CFDictionaryGetCount(dict)) {
+ if (!write_plist_to_file()) {
+ RECORD_FAILURE(101, EIO);
+ errx(1, "could not write manifest to the file\n");
+ }
+ }
+ }
+ exit(status);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+"usage: mtree [-LPUcdeinqruxw] [-f spec] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"
+"\t[-X excludes]\n");
+ exit(1);
+}
+
+static bool
+write_plist_to_file(void)
+{
+ if (!dict || !filepath) {
+ RECORD_FAILURE(102, EINVAL);
+ return false;
+ }
+
+ CFIndex bytes_written = 0;
+ bool status = true;
+
+ CFStringRef file_path_str = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)filepath, kCFStringEncodingUTF8);
+
+ // Create a URL specifying the file to hold the XML data.
+ CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+ file_path_str, // file path name
+ kCFURLPOSIXPathStyle, // interpret as POSIX path
+ false); // is it a directory?
+
+ if (!fileURL) {
+ CFRelease(file_path_str);
+ RECORD_FAILURE(103, EINVAL);
+ return false;
+ }
+
+ CFWriteStreamRef output_stream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL);
+
+ if (!output_stream) {
+ CFRelease(file_path_str);
+ CFRelease(fileURL);
+ RECORD_FAILURE(104, EIO);
+ return false;
+ }
+
+ if ((status = CFWriteStreamOpen(output_stream))) {
+ bytes_written = CFPropertyListWrite((CFPropertyListRef)dict, output_stream, kCFPropertyListXMLFormat_v1_0, 0, NULL);
+ CFWriteStreamClose(output_stream);
+ } else {
+ status = false;
+ RECORD_FAILURE(105, EIO);
+ goto out;
+ }
+
+ if (!bytes_written) {
+ status = false;
+ RECORD_FAILURE(106, EIO);
+ }
+
+out:
+ CFRelease(output_stream);
+ CFRelease(fileURL);
+ CFRelease(file_path_str);
+
+ return status;
+}
diff --git a/file_cmds/mtree/mtree.h b/file_cmds/mtree/mtree.h
new file mode 100644
index 0000000..16c927b
--- /dev/null
+++ b/file_cmds/mtree/mtree.h
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)mtree.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD: src/usr.sbin/mtree/mtree.h,v 1.7 2005/03/29 11:44:17 tobez Exp $
+ */
+
+#ifndef _MTREE_H_
+#define _MTREE_H_
+
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID | F_FLAGS)
+
+#define MISMATCHEXIT 2
+
+typedef struct _node {
+ struct _node *parent, *child; /* up, down */
+ struct _node *prev, *next; /* left, right */
+ off_t st_size; /* size */
+ struct timespec st_mtimespec; /* last modification time */
+ u_long cksum; /* check sum */
+ char *md5digest; /* MD5 digest */
+ char *sha1digest; /* SHA-1 digest */
+ char *sha256digest; /* SHA-256 digest */
+ char *rmd160digest; /* RIPEMD160 digest */
+ char *slink; /* symbolic link reference */
+ uid_t st_uid; /* uid */
+ gid_t st_gid; /* gid */
+#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ mode_t st_mode; /* mode */
+ u_long st_flags; /* flags */
+ nlink_t st_nlink; /* link count */
+ struct timespec st_birthtimespec; /* birth time (creation time) */
+ struct timespec st_atimespec; /* access time */
+ struct timespec st_ctimespec; /* metadata modification time */
+ struct timespec st_ptimespec; /* time added to parent folder */
+ char *xattrsdigest; /* digest of extended attributes */
+ u_quad_t xdstream_priv_id; /* private id of the xattr data stream */
+ ino_t st_ino; /* inode */
+ char *acldigest; /* digest of access control list */
+ u_quad_t sibling_id; /* sibling id */
+
+#define F_CKSUM 0x00000001 /* check sum */
+#define F_DONE 0x00000002 /* directory done */
+#define F_GID 0x00000004 /* gid */
+#define F_GNAME 0x00000008 /* group name */
+#define F_IGN 0x00000010 /* ignore */
+#define F_MAGIC 0x00000020 /* name has magic chars */
+#define F_MODE 0x00000040 /* mode */
+#define F_NLINK 0x00000080 /* number of links */
+#define F_SIZE 0x00000100 /* size */
+#define F_SLINK 0x00000200 /* The file the symbolic link is expected to reference */
+#define F_TIME 0x00000400 /* modification time (mtime) */
+#define F_TYPE 0x00000800 /* file type */
+#define F_UID 0x00001000 /* uid */
+#define F_UNAME 0x00002000 /* user name */
+#define F_VISIT 0x00004000 /* file visited */
+#define F_MD5 0x00008000 /* MD5 digest */
+#define F_NOCHANGE 0x00010000 /* If owner/mode "wrong", do */
+ /* not change */
+#define F_SHA1 0x00020000 /* SHA-1 digest */
+#define F_RMD160 0x00040000 /* RIPEMD160 digest */
+#define F_FLAGS 0x00080000 /* file flags */
+#define F_SHA256 0x00100000 /* SHA-256 digest */
+#define F_BTIME 0x00200000 /* creation time */
+#define F_ATIME 0x00400000 /* access time */
+#define F_CTIME 0x00800000 /* metadata modification time (ctime) */
+#define F_PTIME 0x01000000 /* time added to parent folder */
+#define F_XATTRS 0x02000000 /* digest of extended attributes */
+#define F_INODE 0x04000000 /* inode */
+#define F_ACL 0x08000000 /* digest of access control list */
+#define F_SIBLINGID 0x10000000 /* sibling id */
+ u_int flags; /* items set */
+
+#define F_BLOCK 0x001 /* block special */
+#define F_CHAR 0x002 /* char special */
+#define F_DIR 0x004 /* directory */
+#define F_FIFO 0x008 /* fifo */
+#define F_FILE 0x010 /* regular file */
+#define F_LINK 0x020 /* symbolic link */
+#define F_SOCK 0x040 /* socket */
+ u_char type; /* file type */
+
+ char name[1]; /* file name (must be last) */
+} NODE;
+
+#define RP(p) \
+ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+ (p)->fts_path + 2 : (p)->fts_path)
+
+#endif /* _MTREE_H_ */
diff --git a/file_cmds/mtree/spec.c b/file_cmds/mtree/spec.c
new file mode 100644
index 0000000..f15d857
--- /dev/null
+++ b/file_cmds/mtree/spec.c
@@ -0,0 +1,470 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/spec.c,v 1.22 2005/03/29 11:44:17 tobez Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <vis.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+int lineno; /* Current spec line number. */
+
+static void set(char *, NODE *);
+static void unset(char *, NODE *);
+
+NODE *
+mtree_readspec(FILE *fi)
+{
+ NODE *centry, *last;
+ char *p;
+ NODE ginfo, *root;
+ int c_cur, c_next;
+ char buf[2048];
+
+ centry = last = root = NULL;
+ bzero(&ginfo, sizeof(ginfo));
+ c_cur = c_next = 0;
+ for (lineno = 1; fgets(buf, sizeof(buf), fi);
+ ++lineno, c_cur = c_next, c_next = 0) {
+ /* Skip empty lines. */
+ if (buf[0] == '\n')
+ continue;
+
+ /* Find end of line. */
+ if ((p = index(buf, '\n')) == NULL) {
+ RECORD_FAILURE(21, ERANGE);
+ errx(1, "line %d too long", lineno);
+ }
+
+ /* See if next line is continuation line. */
+ if (p[-1] == '\\') {
+ --p;
+ c_next = 1;
+ }
+
+ /* Null-terminate the line. */
+ *p = '\0';
+
+ /* Skip leading whitespace. */
+ for (p = buf; *p && isspace(*p); ++p);
+
+ /* If nothing but whitespace or comment char, continue. */
+ if (!*p || *p == '#')
+ continue;
+
+#ifdef DEBUG
+ (void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
+#endif
+ if (c_cur) {
+ set(p, centry);
+ continue;
+ }
+
+ /* Grab file name, "$", "set", or "unset". */
+ if ((p = strtok(p, "\n\t ")) == NULL) {
+ RECORD_FAILURE(22, EINVAL);
+ errx(1, "line %d: missing field", lineno);
+ }
+
+ if (p[0] == '/')
+ switch(p[1]) {
+ case 's':
+ if (strcmp(p + 1, "set"))
+ break;
+ set(NULL, &ginfo);
+ continue;
+ case 'u':
+ if (strcmp(p + 1, "unset"))
+ break;
+ unset(NULL, &ginfo);
+ continue;
+ }
+
+ if (index(p, '/')) {
+ RECORD_FAILURE(23, EINVAL);
+ errx(1, "line %d: slash character in file name",
+ lineno);
+ }
+
+ if (!strcmp(p, "..")) {
+ /* Don't go up, if haven't gone down. */
+ if (!root)
+ goto noparent;
+ if (last->type != F_DIR || last->flags & F_DONE) {
+ if (last == root)
+ goto noparent;
+ last = last->parent;
+ }
+ last->flags |= F_DONE;
+ continue;
+
+noparent: RECORD_FAILURE(24, EINVAL);
+ errx(1, "line %d: no parent node", lineno);
+ }
+
+ if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) {
+ RECORD_FAILURE(25, ENOMEM);
+ errx(1, "calloc");
+ }
+ *centry = ginfo;
+#define MAGIC "?*["
+ if (strpbrk(p, MAGIC))
+ centry->flags |= F_MAGIC;
+ if (strunvis(centry->name, p) == -1) {
+ RECORD_FAILURE(26, EILSEQ);
+ errx(1, "filename %s is ill-encoded", p);
+ }
+ set(NULL, centry);
+
+ if (!root) {
+ last = root = centry;
+ root->parent = root;
+ } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+ centry->parent = last;
+ last = last->child = centry;
+ } else {
+ centry->parent = last->parent;
+ centry->prev = last;
+ last = last->next = centry;
+ }
+ }
+ return (root);
+}
+
+static void
+set(char *t, NODE *ip)
+{
+ int error = 0;
+ int type;
+ char *kw, *val = NULL;
+ struct group *gr;
+ struct passwd *pw;
+ mode_t *m;
+ int value;
+ char *ep;
+
+ for (; (kw = strtok(t, "= \t\n")); t = NULL) {
+ ip->flags |= type = parsekey(kw, &value);
+ if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) {
+ RECORD_FAILURE(27, EINVAL);
+ errx(1, "line %d: missing value", lineno);
+ }
+ switch(type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(28, EINVAL);
+ errx(1, "line %d: invalid checksum %s",
+ lineno, val);
+ }
+ break;
+ case F_MD5:
+ ip->md5digest = strdup(val);
+ if (!ip->md5digest) {
+ RECORD_FAILURE(29, ENOMEM);
+ errx(1, "strdup");
+ }
+ break;
+ case F_SHA1:
+ ip->sha1digest = strdup(val);
+ if (!ip->sha1digest) {
+ RECORD_FAILURE(30, ENOMEM);
+ errx(1, "strdup");
+ }
+ break;
+ case F_SHA256:
+ ip->sha256digest = strdup(val);
+ if (!ip->sha256digest) {
+ RECORD_FAILURE(31, ENOMEM);
+ errx(1, "strdup");
+ }
+ break;
+ case F_RMD160:
+ ip->rmd160digest = strdup(val);
+ if (!ip->rmd160digest) {
+ RECORD_FAILURE(32, ENOMEM);
+ errx(1, "strdup");
+ }
+ break;
+ case F_FLAGS:
+ if (strcmp("none", val) == 0) {
+ ip->st_flags = 0;
+ } else if (strtofflags(&val, &ip->st_flags, NULL) != 0) {
+ RECORD_FAILURE(33, EINVAL);
+ errx(1, "line %d: invalid flag %s",lineno, val);
+ }
+ break;
+ case F_GID:
+ ip->st_gid = (gid_t)strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(34, EINVAL);
+ errx(1, "line %d: invalid gid %s", lineno, val);
+ }
+ break;
+ case F_GNAME:
+ if ((gr = getgrnam(val)) == NULL) {
+ RECORD_FAILURE(35, EINVAL);
+ errx(1, "line %d: unknown group %s", lineno, val);
+ }
+ ip->st_gid = gr->gr_gid;
+ break;
+ case F_IGN:
+ /* just set flag bit */
+ break;
+ case F_MODE:
+ if ((m = setmode(val)) == NULL) {
+ RECORD_FAILURE(36, EINVAL);
+ errx(1, "line %d: invalid file mode %s",
+ lineno, val);
+ }
+ ip->st_mode = getmode(m, 0);
+ free(m);
+ break;
+ case F_NLINK:
+ ip->st_nlink = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(37, EINVAL);
+ errx(1, "line %d: invalid link count %s",
+ lineno, val);
+ }
+ break;
+ case F_SIZE:
+ ip->st_size = strtoq(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(38, EINVAL);
+ errx(1, "line %d: invalid size %s",
+ lineno, val);
+ }
+ break;
+ case F_SLINK:
+ ip->slink = malloc(strlen(val) + 1);
+ if (ip->slink == NULL) {
+ RECORD_FAILURE(39, ENOMEM);
+ errx(1, "malloc");
+ }
+ if (strunvis(ip->slink, val) == -1) {
+ RECORD_FAILURE(40, EILSEQ);
+ errx(1, "symlink %s is ill-encoded", val);
+ }
+ break;
+ case F_TIME:
+ ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep != '.') {
+ RECORD_FAILURE(41, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ val = ep + 1;
+ ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(42, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ break;
+ case F_TYPE:
+ switch(*val) {
+ case 'b':
+ if (!strcmp(val, "block"))
+ ip->type = F_BLOCK;
+ break;
+ case 'c':
+ if (!strcmp(val, "char"))
+ ip->type = F_CHAR;
+ break;
+ case 'd':
+ if (!strcmp(val, "dir"))
+ ip->type = F_DIR;
+ break;
+ case 'f':
+ if (!strcmp(val, "file"))
+ ip->type = F_FILE;
+ if (!strcmp(val, "fifo"))
+ ip->type = F_FIFO;
+ break;
+ case 'l':
+ if (!strcmp(val, "link"))
+ ip->type = F_LINK;
+ break;
+ case 's':
+ if (!strcmp(val, "socket"))
+ ip->type = F_SOCK;
+ break;
+ default:
+ RECORD_FAILURE(43, EINVAL);
+ errx(1, "line %d: unknown file type %s",
+ lineno, val);
+ }
+ break;
+ case F_UID:
+ ip->st_uid = (uid_t)strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(44, EINVAL);
+ errx(1, "line %d: invalid uid %s", lineno, val);
+ }
+ break;
+ case F_UNAME:
+ if ((pw = getpwnam(val)) == NULL) {
+ RECORD_FAILURE(45, EINVAL);
+ errx(1, "line %d: unknown user %s", lineno, val);
+ }
+ ip->st_uid = pw->pw_uid;
+ break;
+ case F_BTIME:
+ ip->st_birthtimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep != '.') {
+ RECORD_FAILURE(46, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ val = ep + 1;
+ ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(47, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ break;
+ case F_ATIME:
+ ip->st_atimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep != '.') {
+ RECORD_FAILURE(48, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ val = ep + 1;
+ ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(49, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ break;
+ case F_CTIME:
+ ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep != '.') {
+ RECORD_FAILURE(50, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ val = ep + 1;
+ ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(51, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ break;
+ case F_PTIME:
+ ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep != '.') {
+ RECORD_FAILURE(52, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ val = ep + 1;
+ ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(53, EINVAL);
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ }
+ break;
+ case F_XATTRS:
+ ep = strtok(val,".");
+ ip->xattrsdigest = strdup(ep);
+ if (!ip->xattrsdigest) {
+ error = errno;
+ RECORD_FAILURE(54, error);
+ errc(1, error, "strdup");
+ }
+ val = strtok(NULL,".");
+ if (val) {
+ ip->xdstream_priv_id = strtoull(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(55, EINVAL);
+ errx(1, "line %d: invalid private id %s",
+ lineno, val);
+ }
+ } else {
+ ip->xdstream_priv_id = 0;
+ }
+ break;
+ case F_INODE:
+ ip->st_ino = (ino_t)strtoull(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(56, EINVAL);
+ errx(1, "line %d: invalid inode %s",
+ lineno, val);
+ }
+ break;
+ case F_ACL:
+ ip->acldigest = strdup(val);
+ if (!ip->acldigest) {
+ error = errno;
+ RECORD_FAILURE(57, error);
+ errc(1, error, "strdup");
+ }
+ break;
+ case F_SIBLINGID:
+ ip->sibling_id = (quad_t)strtoull(val, &ep, 10);
+ if (*ep) {
+ RECORD_FAILURE(58, EINVAL);
+ errx(1, "line %d: invalid sibling id %s", lineno, val);
+ }
+ }
+ }
+}
+
+static void
+unset(char *t, NODE *ip)
+{
+ char *p;
+
+ while ((p = strtok(t, "\n\t ")))
+ ip->flags &= ~parsekey(p, NULL);
+}
diff --git a/file_cmds/mtree/specspec.c b/file_cmds/mtree/specspec.c
new file mode 100644
index 0000000..cb08384
--- /dev/null
+++ b/file_cmds/mtree/specspec.c
@@ -0,0 +1,327 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/specspec.c,v 1.6 2005/03/29 11:44:17 tobez Exp $");
+
+#include <sys/param.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+#define FF(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
+#define FS(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
+#define FM(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
+
+static void
+shownode(NODE *n, int f, char const *path)
+{
+ struct group *gr;
+ struct passwd *pw;
+
+ printf("%s%s %s", path, n->name, ftype(n->type));
+ if (f & F_CKSUM)
+ printf(" cksum=%lu", n->cksum);
+ if (f & F_GID)
+ printf(" gid=%d", n->st_gid);
+ if (f & F_GNAME) {
+ gr = getgrgid(n->st_gid);
+ if (gr == NULL)
+ printf(" gid=%d", n->st_gid);
+ else
+ printf(" gname=%s", gr->gr_name);
+ }
+ if (f & F_MODE)
+ printf(" mode=%o", n->st_mode);
+ if (f & F_NLINK)
+ printf(" nlink=%d", n->st_nlink);
+ if (f & F_SIZE)
+ printf(" size=%jd", (intmax_t)n->st_size);
+ if (f & F_TIME)
+ printf(" time=%ld.%09ld", n->st_mtimespec.tv_sec, n->st_mtimespec.tv_nsec);
+ if (f & F_UID)
+ printf(" uid=%d", n->st_uid);
+ if (f & F_UNAME) {
+ pw = getpwuid(n->st_uid);
+ if (pw == NULL)
+ printf(" uid=%d", n->st_uid);
+ else
+ printf(" uname=%s", pw->pw_name);
+ }
+ if (f & F_MD5)
+ printf(" md5digest=%s", n->md5digest);
+ if (f & F_SHA1)
+ printf(" sha1digest=%s", n->sha1digest);
+ if (f & F_RMD160)
+ printf(" rmd160digest=%s", n->rmd160digest);
+ if (f & F_SHA256)
+ printf(" sha256digest=%s", n->sha256digest);
+ if (f & F_FLAGS)
+ printf(" flags=%s", flags_to_string(n->st_flags));
+ if (f & F_BTIME)
+ printf(" btime=%ld.%09ld", n->st_birthtimespec.tv_sec, n->st_birthtimespec.tv_nsec);
+ if (f & F_ATIME)
+ printf(" atime=%ld.%09ld", n->st_atimespec.tv_sec, n->st_atimespec.tv_nsec);
+ if (f & F_CTIME)
+ printf(" ctime=%ld.%09ld", n->st_ctimespec.tv_sec, n->st_ctimespec.tv_nsec);
+ if (f & F_PTIME)
+ printf(" ptime=%ld.%09ld", n->st_ptimespec.tv_sec, n->st_ptimespec.tv_nsec);
+ if (f & F_XATTRS)
+ printf(" xattrsdigest=%s.%llu", n->xattrsdigest, n->xdstream_priv_id);
+ if (f & F_INODE)
+ printf(" inode=%llu", n->st_ino);
+ if (f & F_ACL)
+ printf(" acldigest=%s", n->acldigest);
+ if (f & F_SIBLINGID)
+ printf(" siblingid=%llu", n->sibling_id);
+
+ printf("\n");
+}
+
+static int
+mismatch(NODE *n1, NODE *n2, int differ, char const *path)
+{
+
+ if (n2 == NULL) {
+ shownode(n1, differ, path);
+ return (1);
+ }
+ if (n1 == NULL) {
+ printf("\t");
+ shownode(n2, differ, path);
+ return (1);
+ }
+ if (!(differ & keys))
+ return(0);
+ printf("\t\t");
+ shownode(n1, differ, path);
+ printf("\t\t");
+ shownode(n2, differ, path);
+ return (1);
+}
+
+static int
+compare_nodes(NODE *n1, NODE *n2, char const *path)
+{
+ int differs;
+
+ if (n1 != NULL && n1->type == F_LINK)
+ n1->flags &= ~F_MODE;
+ if (n2 != NULL && n2->type == F_LINK)
+ n2->flags &= ~F_MODE;
+ differs = 0;
+ if ((n1 == NULL) && (n2 == NULL)) {
+ return 0;
+ } else if (n1 == NULL) {
+ differs = n2->flags;
+ RECORD_FAILURE(111, WARN_MISMATCH);
+ mismatch(n1, n2, differs, path);
+ return (1);
+ } else if (n2 == NULL) {
+ differs = n1->flags;
+ RECORD_FAILURE(112, WARN_MISMATCH);
+ mismatch(n1, n2, differs, path);
+ return (1);
+ } else if (n1->type != n2->type) {
+ differs = 0;
+ RECORD_FAILURE(113, WARN_MISMATCH);
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ if (FF(n1, n2, F_CKSUM, cksum))
+ differs |= F_CKSUM;
+ if (FF(n1, n2, F_GID, st_gid))
+ differs |= F_GID;
+ if (FF(n1, n2, F_GNAME, st_gid))
+ differs |= F_GNAME;
+ if (FF(n1, n2, F_MODE, st_mode))
+ differs |= F_MODE;
+ if (FF(n1, n2, F_NLINK, st_nlink))
+ differs |= F_NLINK;
+ if (FF(n1, n2, F_SIZE, st_size))
+ differs |= F_SIZE;
+ if (FS(n1, n2, F_SLINK, slink))
+ differs |= F_SLINK;
+ if (FM(n1, n2, F_TIME, st_mtimespec))
+ differs |= F_TIME;
+ if (FF(n1, n2, F_UID, st_uid))
+ differs |= F_UID;
+ if (FF(n1, n2, F_UNAME, st_uid))
+ differs |= F_UNAME;
+ if (FS(n1, n2, F_MD5, md5digest))
+ differs |= F_MD5;
+ if (FS(n1, n2, F_SHA1, sha1digest))
+ differs |= F_SHA1;
+ if (FS(n1, n2, F_RMD160, rmd160digest))
+ differs |= F_RMD160;
+ if (FS(n1, n2, F_SHA256, sha256digest))
+ differs |= F_SHA256;
+ if (FF(n1, n2, F_FLAGS, st_flags))
+ differs |= F_FLAGS;
+ if (FM(n1, n2, F_BTIME, st_birthtimespec))
+ differs |= F_BTIME;
+ if (FM(n1, n2, F_ATIME, st_atimespec))
+ differs |= F_ATIME;
+ if (FM(n1, n2, F_CTIME, st_ctimespec))
+ differs |= F_CTIME;
+ if (FM(n1, n2, F_PTIME, st_ptimespec))
+ differs |= F_PTIME;
+ if (FS(n1, n2, F_XATTRS, xattrsdigest))
+ differs |= F_XATTRS;
+ if (FF(n1, n2, F_INODE, st_ino))
+ differs |= F_INODE;
+ if (FS(n1, n2, F_ACL, acldigest))
+ differs |= F_ACL;
+ if (FF(n1, n2, F_SIBLINGID, sibling_id))
+ differs |= F_SIBLINGID;
+
+ if (differs) {
+ RECORD_FAILURE(114, WARN_MISMATCH);
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ return (0);
+}
+static int
+walk_in_the_forest(NODE *t1, NODE *t2, char const *path)
+{
+ int r, i, c;
+ NODE *c1, *c2, *n1, *n2;
+ char *np;
+
+ r = 0;
+
+ if (t1 != NULL)
+ c1 = t1->child;
+ else
+ c1 = NULL;
+ if (t2 != NULL)
+ c2 = t2->child;
+ else
+ c2 = NULL;
+ while (c1 != NULL || c2 != NULL) {
+ n1 = n2 = NULL;
+ if (c1 != NULL)
+ n1 = c1->next;
+ if (c2 != NULL)
+ n2 = c2->next;
+ if (c1 != NULL && c2 != NULL) {
+ if (c1->type != F_DIR && c2->type == F_DIR) {
+ n2 = c2;
+ c2 = NULL;
+ } else if (c1->type == F_DIR && c2->type != F_DIR) {
+ n1 = c1;
+ c1 = NULL;
+ } else {
+ i = strcmp(c1->name, c2->name);
+ if (i > 0) {
+ n1 = c1;
+ c1 = NULL;
+ } else if (i < 0) {
+ n2 = c2;
+ c2 = NULL;
+ }
+ }
+ }
+ if (c1 == NULL && c2->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c2->name);
+ i = walk_in_the_forest(c1, c2, np);
+ if (i) {
+ RECORD_FAILURE(115, WARN_MISMATCH);
+ }
+ free(np);
+ c = compare_nodes(c1, c2, path);
+ if (c) {
+ RECORD_FAILURE(116, WARN_MISMATCH);
+ }
+ i += c;
+ } else if (c2 == NULL && c1->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c1->name);
+ i = walk_in_the_forest(c1, c2, np);
+ if (i) {
+ RECORD_FAILURE(117, WARN_MISMATCH);
+ }
+ free(np);
+ c = compare_nodes(c1, c2, path);
+ if (c) {
+ RECORD_FAILURE(118, WARN_MISMATCH);
+ }
+ i += c;
+ } else if (c1 == NULL || c2 == NULL) {
+ i = compare_nodes(c1, c2, path);
+ if (i) {
+ RECORD_FAILURE(119, WARN_MISMATCH);
+ }
+ } else if (c1->type == F_DIR && c2->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c1->name);
+ i = walk_in_the_forest(c1, c2, np);
+ if (i) {
+ RECORD_FAILURE(120, WARN_MISMATCH);
+ }
+ free(np);
+ c = compare_nodes(c1, c2, path);
+ if (c) {
+ RECORD_FAILURE(121, WARN_MISMATCH);
+ }
+ i += c;
+ } else {
+ i = compare_nodes(c1, c2, path);
+ if (i) {
+ RECORD_FAILURE(122, WARN_MISMATCH);
+ }
+ }
+ r += i;
+ c1 = n1;
+ c2 = n2;
+ }
+ return (r);
+}
+
+int
+mtree_specspec(FILE *fi, FILE *fj)
+{
+ int rval;
+ NODE *root1, *root2;
+
+ root1 = mtree_readspec(fi);
+ root2 = mtree_readspec(fj);
+ rval = walk_in_the_forest(root1, root2, "");
+ rval += compare_nodes(root1, root2, "");
+ if (rval > 0) {
+ RECORD_FAILURE(123, WARN_MISMATCH);
+ return (MISMATCHEXIT);
+ }
+ return (0);
+}
diff --git a/file_cmds/mtree/test/test00.sh b/file_cmds/mtree/test/test00.sh
new file mode 100644
index 0000000..a2e4b28
--- /dev/null
+++ b/file_cmds/mtree/test/test00.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD: src/usr.sbin/mtree/test/test00.sh,v 1.3 2003/11/05 22:26:08 phk Exp $
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+
+mkdir ${TMP}/mt/foo
+mkdir ${TMP}/mr/\*
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '*' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\*
+
+mkdir -p ${TMP}/mt/foo
+mkdir ${TMP}/mr/\[f\]oo
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '[' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\[f\]oo
+
+mkdir -p ${TMP}/mt/foo
+mkdir ${TMP}/mr/\?oo
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '?' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\?oo
+
+mkdir ${TMP}/mr/\#
+mtree -c -p ${TMP}/mr > ${TMP}/_
+if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then
+ true
+else
+ echo "ERROR Mtree create fell for filename with '#' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+if [ ! -d ${TMP}/mt/\# ] ; then
+ echo "ERROR Mtree update failed to create name with '#' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\#
+
+rm -rf ${TMP}
+exit 0
diff --git a/file_cmds/mtree/test/test01.sh b/file_cmds/mtree/test/test01.sh
new file mode 100644
index 0000000..d056b91
--- /dev/null
+++ b/file_cmds/mtree/test/test01.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD: src/usr.sbin/mtree/test/test01.sh,v 1.1 2003/10/30 12:01:32 phk Exp $
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+
+ln -s "xx this=is=wrong" ${TMP}/mr/foo
+mtree -c -p ${TMP}/mr > ${TMP}/_
+
+if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then
+ true
+else
+ echo "ERROR Mtree failed on symlink with space char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+x=x`(cd ${TMP}/mr ; ls -l foo 2>&1) || true`
+y=x`(cd ${TMP}/mt ; ls -l foo 2>&1) || true`
+
+if [ "$x" != "$y" ] ; then
+ echo "ERROR Recreation of spaced symlink failed" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
diff --git a/file_cmds/mtree/test/test02.sh b/file_cmds/mtree/test/test02.sh
new file mode 100644
index 0000000..450ebb3
--- /dev/null
+++ b/file_cmds/mtree/test/test02.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Dan Nelson
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD: src/usr.sbin/mtree/test/test02.sh,v 1.1 2003/10/31 13:39:19 phk Exp $
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+touch -t 199901020304 ${TMP}/mr/oldfile
+touch ${TMP}/mt/oldfile
+
+mtree -c -p ${TMP}/mr > ${TMP}/_
+
+mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null
+
+x=x`(cd ${TMP}/mr ; ls -l 2>&1) || true`
+y=x`(cd ${TMP}/mt ; ls -l 2>&1) || true`
+
+if [ "$x" != "$y" ] ; then
+ echo "ERROR Update of mtime failed" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
+
diff --git a/file_cmds/mtree/test/test03.sh b/file_cmds/mtree/test/test03.sh
new file mode 100644
index 0000000..52e08c3
--- /dev/null
+++ b/file_cmds/mtree/test/test03.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD: src/usr.sbin/mtree/test/test03.sh,v 1.2 2005/03/29 11:44:17 tobez Exp $
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP}
+
+K=uid,uname,gid,gname,flags,md5digest,size,ripemd160digest,sha1digest,sha256digest,cksum
+
+rm -rf _FOO
+mkdir _FOO
+touch _FOO/_uid
+touch _FOO/_size
+touch _FOO/zztype
+
+touch _FOO/_bar
+mtree -c -K $K -p .. > ${TMP}/_r
+mtree -c -K $K -p .. > ${TMP}/_r2
+rm -rf _FOO/_bar
+
+rm -rf _FOO/zztype
+mkdir _FOO/zztype
+
+date > _FOO/_size
+
+chown nobody _FOO/_uid
+
+touch _FOO/_foo
+mtree -c -K $K -p .. > ${TMP}/_t
+
+rm -fr _FOO
+
+if mtree -f ${TMP}/_r -f ${TMP}/_r2 ; then
+ true
+else
+ echo "ERROR Compare identical failed" 1>&2
+ exit 1
+fi
+
+if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then
+ echo "ERROR Compare different succeeded" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 10 ] ; then
+ echo "ERROR wrong number of lines: `wc -l ${TMP}/_`" 1>&2
+ exit 1
+fi
+
+exit 0
diff --git a/file_cmds/mtree/test/test04.sh b/file_cmds/mtree/test/test04.sh
new file mode 100644
index 0000000..453d146
--- /dev/null
+++ b/file_cmds/mtree/test/test04.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Dan Nelson
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD: src/usr.sbin/mtree/test/test04.sh,v 1.1 2003/11/13 11:02:57 phk Exp $
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+mkdir ${TMP}/mr/a
+mkdir ${TMP}/mr/b
+mkdir ${TMP}/mt/a
+mkdir ${TMP}/mt/b
+touch ${TMP}/mt/z
+
+mtree -c -p ${TMP}/mr > ${TMP}/_r
+mtree -c -p ${TMP}/mt > ${TMP}/_t
+
+if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then
+ echo "ERROR wrong exit on difference" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 1 ] ; then
+ echo "ERROR spec/spec compare generated wrong output" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+if mtree -f ${TMP}/_t -f ${TMP}/_r > ${TMP}/_ ; then
+ echo "ERROR wrong exit on difference" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 1 ] ; then
+ echo "ERROR spec/spec compare generated wrong output" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
+
diff --git a/file_cmds/mtree/verify.c b/file_cmds/mtree/verify.c
new file mode 100644
index 0000000..7471652
--- /dev/null
+++ b/file_cmds/mtree/verify.c
@@ -0,0 +1,341 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mtree/verify.c,v 1.24 2005/08/11 15:43:55 brian Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <removefile.h>
+#include "metrics.h"
+#include "mtree.h"
+#include "extern.h"
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static int miss(NODE *, char *, size_t path_length);
+static int vwalk(void);
+
+int
+mtree_verifyspec(FILE *fi)
+{
+ int rval, mval;
+ size_t path_length = 0;
+
+ root = mtree_readspec(fi);
+ rval = vwalk();
+ mval = miss(root, path, path_length);
+
+ if (rval != 0) {
+ RECORD_FAILURE(60, WARN_MISMATCH);
+ return rval;
+ } else {
+ RECORD_FAILURE(61, WARN_MISMATCH);
+ return mval;
+ }
+}
+
+static int
+vwalk(void)
+{
+ int error = 0;
+ FTS *t;
+ FTSENT *p;
+ NODE *ep, *level;
+ int specdepth, rval;
+ char *argv[2];
+ char dot[] = ".";
+
+ argv[0] = dot;
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) {
+ error = errno;
+ RECORD_FAILURE(62, error);
+ errc(1, error, "line %d: fts_open", lineno);
+ }
+ level = root;
+ specdepth = rval = 0;
+ while ((p = fts_read(t))) {
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ case FTS_SL:
+ break;
+ case FTS_DP:
+ if (level == NULL) {
+ RECORD_FAILURE(63, EINVAL);
+ errx(1 , "invalid root in vwalk");
+ }
+ if (specdepth > p->fts_level) {
+ for (level = level->parent; level->prev;
+ level = level->prev);
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", RP(p), strerror(p->fts_errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ if (specdepth != p->fts_level)
+ goto extra;
+ for (ep = level; ep; ep = ep->next)
+ if ((ep->flags & F_MAGIC &&
+ !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+ !strcmp(ep->name, p->fts_name)) {
+ ep->flags |= F_VISIT;
+ if ((ep->flags & F_NOCHANGE) == 0 &&
+ compare(ep->name, ep, p)) {
+ RECORD_FAILURE(64, WARN_MISMATCH);
+ rval = MISMATCHEXIT;
+ }
+ if (ep->flags & F_IGN)
+ (void)fts_set(t, p, FTS_SKIP);
+ else if (ep->child && ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ level = ep->child;
+ ++specdepth;
+ }
+ break;
+ }
+
+ if (ep)
+ continue;
+extra:
+ if (!eflag) {
+ (void)printf("%s extra", RP(p));
+
+ if (rflag) {
+ /* rflag implies: delete stuff if "extra" is observed" */
+ if (mflag) {
+ /* -mflag is used for sealing & verification -- use removefile for recursive behavior */
+ removefile_state_t rmstate;
+ rmstate = removefile_state_alloc();
+ if (removefile(p->fts_accpath, rmstate, (REMOVEFILE_RECURSIVE))) {
+ error = errno;
+ RECORD_FAILURE(65, error);
+ errx (1, "\n error deleting item (or descendant) at path %s (%s)", RP(p), strerror(error));
+ }
+ else {
+ /* removefile success */
+ (void) printf(", removed");
+ }
+ removefile_state_free(rmstate);
+
+ }
+ else {
+ /* legacy: use rmdir/unlink if "-m" not specified */
+ int syserr = 0;
+
+ if (S_ISDIR(p->fts_statp->st_mode)){
+ syserr = rmdir(p->fts_accpath);
+ }
+ else {
+ syserr = unlink(p->fts_accpath);
+ }
+
+ /* log failures */
+ if (syserr) {
+ error = errno;
+ RECORD_FAILURE(66, error);
+ (void) printf(", not removed :%s", strerror(error));
+ }
+ }
+ } else if (mflag) {
+ RECORD_FAILURE(68956, WARN_MISMATCH);
+ errx(1, "cannot generate the XML dictionary");
+ }
+ (void)putchar('\n');
+ }
+ (void)fts_set(t, p, FTS_SKIP);
+ }
+ (void)fts_close(t);
+ if (sflag) {
+ RECORD_FAILURE(67, WARN_CHECKSUM);
+ warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total);
+ }
+ return (rval);
+}
+
+static int
+miss(NODE *p, char *tail, size_t path_length)
+{
+ int create;
+ char *tp;
+ const char *type, *what;
+ int serr;
+ int rval = 0;
+ int rrval = 0;
+ size_t file_name_length = 0;
+
+ for (; p; p = p->next) {
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ file_name_length = strnlen(p->name, MAXPATHLEN);
+ path_length += file_name_length;
+ if (path_length >= MAXPATHLEN) {
+ RECORD_FAILURE(61971, ENAMETOOLONG);
+ continue;
+ }
+ (void)strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT)) {
+ /* Don't print missing message if file exists as a
+ symbolic link and the -q flag is set. */
+ struct stat statbuf;
+
+ if (qflag && stat(path, &statbuf) == 0) {
+ p->flags |= F_VISIT;
+ } else {
+ (void)printf("%s missing", path);
+ RECORD_FAILURE(68, WARN_MISMATCH);
+ rval = MISMATCHEXIT;
+ }
+ }
+ if (p->type != F_DIR && p->type != F_LINK) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (p->type == F_LINK)
+ type = "symlink";
+ else
+ type = "directory";
+ if (!(p->flags & F_VISIT) && uflag) {
+ if (!(p->flags & (F_UID | F_UNAME))) {
+ (void)printf(" (%s not created: user not specified)", type);
+ } else if (!(p->flags & (F_GID | F_GNAME))) {
+ (void)printf(" (%s not created: group not specified)", type);
+ } else if (p->type == F_LINK) {
+ if (symlink(p->slink, path)) {
+ serr = errno;
+ RECORD_FAILURE(69, serr);
+ (void)printf(" (symlink not created: %s)\n",
+ strerror(serr));
+ } else {
+ (void)printf(" (created)\n");
+ }
+ if (lchown(path, p->st_uid, p->st_gid) == -1) {
+ serr = errno;
+ if (p->st_uid == (uid_t)-1)
+ what = "group";
+ else if (lchown(path, (uid_t)-1,
+ p->st_gid) == -1)
+ what = "user & group";
+ else {
+ what = "user";
+ errno = serr;
+ }
+ serr = errno;
+ RECORD_FAILURE(70, serr);
+ (void)printf("%s: %s not modified: %s"
+ "\n", path, what, strerror(serr));
+ }
+ continue;
+ } else if (!(p->flags & F_MODE)) {
+ (void)printf(" (directory not created: mode not specified)");
+ } else if (mkdir(path, S_IRWXU)) {
+ serr = errno;
+ RECORD_FAILURE(71, serr);
+ (void)printf(" (directory not created: %s)",
+ strerror(serr));
+ } else {
+ create = 1;
+ (void)printf(" (created)");
+ }
+ }
+ if (!(p->flags & F_VISIT))
+ (void)putchar('\n');
+
+ for (tp = tail; *tp; ++tp);
+ *tp = '/';
+ ++path_length;
+ rrval = miss(p->child, tp + 1, path_length);
+ if (rrval != 0) {
+ RECORD_FAILURE(72, WARN_MISMATCH);
+ rval = rrval;
+ }
+ path_length -= (file_name_length + 1);
+ *tp = '\0';
+
+ if (!create)
+ continue;
+ if (chown(path, p->st_uid, p->st_gid) == -1) {
+ serr = errno;
+ if (p->st_uid == (uid_t)-1)
+ what = "group";
+ else if (chown(path, (uid_t)-1, p->st_gid) == -1)
+ what = "user & group";
+ else {
+ what = "user";
+ errno = serr;
+ }
+ serr = errno;
+ RECORD_FAILURE(73, serr);
+ (void)printf("%s: %s not modified: %s\n",
+ path, what, strerror(serr));
+ }
+ if (chmod(path, p->st_mode)) {
+ serr = errno;
+ RECORD_FAILURE(74, serr);
+ (void)printf("%s: permissions not set: %s\n",
+ path, strerror(serr));
+ }
+ if ((p->flags & F_FLAGS) && p->st_flags &&
+ chflags(path, (u_int)p->st_flags)) {
+ serr = errno;
+ RECORD_FAILURE(75, serr);
+ (void)printf("%s: file flags not set: %s\n",
+ path, strerror(serr));
+ }
+ }
+ return rval;
+}
diff --git a/file_cmds/mv/mv.1 b/file_cmds/mv/mv.1
new file mode 100644
index 0000000..569e5c5
--- /dev/null
+++ b/file_cmds/mv/mv.1
@@ -0,0 +1,184 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mv.1 8.1 (Berkeley) 5/31/93
+.\" $FreeBSD: src/bin/mv/mv.1,v 1.25 2002/08/26 06:16:51 keramida Exp $
+.\"
+.Dd July 9, 2002
+.Dt MV 1
+.Os
+.Sh NAME
+.Nm mv
+.Nd move files
+.Sh SYNOPSIS
+.Nm mv
+.Op Fl f | i | n
+.Op Fl v
+.Ar source target
+.Nm mv
+.Op Fl f | i | n
+.Op Fl v
+.Ar source ... directory
+.Sh DESCRIPTION
+In its first form, the
+.Nm mv
+utility renames the file named by the
+.Ar source
+operand to the destination path named by the
+.Ar target
+operand.
+This form is assumed when the last operand does not name an already
+existing directory.
+.Pp
+In its second form,
+.Nm mv
+moves each file named by a
+.Ar source
+operand to a destination file in the existing directory named by the
+.Ar directory
+operand.
+The destination path for each operand is the pathname produced by the
+concatenation of the last operand, a slash, and the final pathname
+component of the named file.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl f
+Do not prompt for confirmation before overwriting the destination
+path.
+(The
+.Fl f
+option overrides any previous
+.Fl i
+or
+.Fl n
+options.)
+.It Fl i
+Cause
+.Nm mv
+to write a prompt to standard error before moving a file that would
+overwrite an existing file.
+If the response from the standard input begins with the character
+.Ql y
+or
+.Ql Y ,
+the move is attempted.
+(The
+.Fl i
+option overrides any previous
+.Fl f
+or
+.Fl n
+options.)
+.It Fl n
+Do not overwrite an existing file.
+(The
+.Fl n
+option overrides any previous
+.Fl f
+or
+.Fl i
+options.)
+.It Fl v
+Cause
+.Nm mv
+to be verbose, showing files after they are moved.
+.El
+.Pp
+It is an error for either the
+.Ar source
+operand or the destination path to specify a directory unless both do.
+.Pp
+If the destination path does not have a mode which permits writing,
+.Nm mv
+prompts the user for confirmation as specified for the
+.Fl i
+option.
+.Pp
+As the
+.Xr rename 2
+call does not work across file systems,
+.Nm mv
+uses
+.Xr cp 1
+and
+.Xr rm 1
+to accomplish the move.
+The effect is equivalent to:
+.Bd -literal -offset indent
+rm -f destination_path && \e
+cp -pRP source_file destination && \e
+rm -rf source_file
+.Ed
+.Sh DIAGNOSTICS
+.Ex -std
+.Pp
+The command "mv dir/afile dir" will abort with an error message.
+.Sh LEGACY DIAGNOSTICS
+In legacy mode, the command "mv dir/afile dir" will fail silently,
+returning an exit code of 0.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr cp 1 ,
+.Xr rm 1 ,
+.Xr symlink 7
+.Sh COMPATIBILITY
+The
+.Fl n
+and
+.Fl v
+options are non-standard and their use in scripts is not recommended.
+.Pp
+The
+.Nm mv
+utility now supports HFS+ Finder and Extended Attributes and resource forks.
+The
+.Nm mv
+utility will no longer strip resource forks off of HFS files.
+For an alternative method,
+refer to
+.Xr cp 1 .
+.Sh STANDARDS
+The
+.Nm mv
+utility is expected to be
+.St -p1003.2
+compatible.
+.Sh HISTORY
+A
+.Nm mv
+command appeared in
+.At v1 .
diff --git a/file_cmds/mv/mv.c b/file_cmds/mv/mv.c
new file mode 100644
index 0000000..52a2938
--- /dev/null
+++ b/file_cmds/mv/mv.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ken Smith of The State University of New York at Buffalo.
+ *
+ * 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 <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/mv/mv.c,v 1.39 2002/07/09 17:45:13 johan Exp $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <locale.h>
+
+#ifdef __APPLE__
+#include <copyfile.h>
+#include <sys/mount.h>
+#endif
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "pathnames.h"
+
+int fflg, iflg, nflg, vflg;
+
+int copy(char *, char *);
+int do_move(char *, char *);
+int fastcopy(char *, char *, struct stat *);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ size_t baselen, len;
+ int rval;
+ char *p, *endp;
+ struct stat sb;
+#ifdef __APPLE__
+ struct stat fsb, tsb;
+#endif /* __APPLE__ */
+ int ch;
+ char path[PATH_MAX];
+
+ while ((ch = getopt(argc, argv, "finv")) != -1)
+ switch (ch) {
+ case 'i':
+ iflg = 1;
+ fflg = nflg = 0;
+ break;
+ case 'f':
+ fflg = 1;
+ iflg = nflg = 0;
+ break;
+ case 'n':
+ nflg = 1;
+ fflg = iflg = 0;
+ break;
+ case 'v':
+ vflg = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage();
+
+ /*
+ * If the stat on the target fails or the target isn't a directory,
+ * try the move. More than 2 arguments is an error in this case.
+ */
+ if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
+ if (argc > 2)
+ usage();
+ exit(do_move(argv[0], argv[1]));
+ }
+
+#ifdef __APPLE__
+ if (argc == 2 && !lstat(argv[0], &fsb) && !lstat(argv[1], &tsb) &&
+ fsb.st_ino == tsb.st_ino && fsb.st_dev == tsb.st_dev &&
+ fsb.st_gen == tsb.st_gen) {
+ /*
+ * We appear to be trying to move a directory into itself,
+ * but it may be that the filesystem is case insensitive and
+ * we are trying to rename the directory to a case-variant.
+ * Ignoring trailing slashes, we look for any difference in
+ * the directory names. If there is a difference we do
+ * the rename, otherwise we fall-thru to the traditional
+ * error. Note the lstat calls above (rather than stat)
+ * permit the renaming of symlinks to case-variants.
+ */
+ char *q;
+
+ for (p = argv[0] + strlen(argv[0]); p != argv[0]; ) {
+ p--;
+ if (*p != '/')
+ break;
+ }
+ for (q = argv[1] + strlen(argv[1]); q != argv[1]; ) {
+ q--;
+ if (*q != '/')
+ break;
+ }
+ for ( ; ; p--, q--) {
+ if (*p != *q)
+ exit(do_move(argv[0], argv[1]));
+ if (*p == '/')
+ break;
+ if (p == argv[0]) {
+ if (q == argv[1] || *(q-1) == '/')
+ break;
+ exit(do_move(argv[0], argv[1]));
+ }
+ if (q == argv[1]) {
+ if (p == argv[0] || *(p-1) == '/')
+ break;
+ exit(do_move(argv[0], argv[1]));
+ }
+ }
+ }
+#endif /* __APPLE__ */
+
+ /* It's a directory, move each file into it. */
+ if (strlen(argv[argc - 1]) > sizeof(path) - 1)
+ errx(1, "%s: destination pathname too long", *argv);
+ (void)strcpy(path, argv[argc - 1]);
+ baselen = strlen(path);
+ endp = &path[baselen];
+ if (!baselen || *(endp - 1) != '/') {
+ *endp++ = '/';
+ ++baselen;
+ }
+ for (rval = 0; --argc; ++argv) {
+ /*
+ * Find the last component of the source pathname. It
+ * may have trailing slashes.
+ */
+ p = *argv + strlen(*argv);
+ while (p != *argv && p[-1] == '/')
+ --p;
+ while (p != *argv && p[-1] != '/')
+ --p;
+
+ if ((baselen + (len = strlen(p))) >= PATH_MAX) {
+ warnx("%s: destination pathname too long", *argv);
+ rval = 1;
+ } else {
+ memmove(endp, p, (size_t)len + 1);
+ if (COMPAT_MODE("bin/mv", "unix2003")) {
+ /*
+ * For Unix 2003 compatibility, check if old and new are
+ * same file, and produce an error * (like on Sun) that
+ * conformance test 66 in mv.ex expects.
+ */
+ if (!stat(*argv, &fsb) && !stat(path, &tsb) &&
+ fsb.st_ino == tsb.st_ino &&
+ fsb.st_dev == tsb.st_dev &&
+ fsb.st_gen == tsb.st_gen) {
+ (void)fprintf(stderr, "mv: %s and %s are identical\n",
+ *argv, path);
+ rval = 2; /* Like the Sun */
+ } else {
+ if (do_move(*argv, path))
+ rval = 1;
+ }
+ } else {
+ if (do_move(*argv, path))
+ rval = 1;
+ }
+ }
+ }
+ exit(rval);
+}
+
+int
+do_move(char *from, char *to)
+{
+ struct stat sb;
+ int ask, ch, first;
+ char modep[15];
+ char resp[] = {'\0', '\0'};
+
+ /*
+ * Check access. If interactive and file exists, ask user if it
+ * should be replaced. Otherwise if file exists but isn't writable
+ * make sure the user wants to clobber it.
+ */
+ if (!fflg && !access(to, F_OK)) {
+
+ /* prompt only if source exist */
+ if (lstat(from, &sb) == -1) {
+ warn("%s", from);
+ return (1);
+ }
+
+#define YESNO "(y/n [n]) "
+ ask = 0;
+ if (nflg) {
+ if (vflg)
+ printf("%s not overwritten\n", to);
+ return (0);
+ } else if (iflg) {
+ (void)fprintf(stderr, "overwrite %s? %s", to, YESNO);
+ ask = 1;
+ } else if (access(to, W_OK) && !stat(to, &sb)) {
+ strmode(sb.st_mode, modep);
+ (void)fprintf(stderr, "override %s%s%s/%s for %s? %s",
+ modep + 1, modep[9] == ' ' ? "" : " ",
+ user_from_uid(sb.st_uid, 0),
+ group_from_gid(sb.st_gid, 0), to, YESNO);
+ ask = 1;
+ }
+ if (ask) {
+ /* Load user specified locale */
+ setlocale(LC_MESSAGES, "");
+
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+
+ /* only care about the first character */
+ resp[0] = first;
+
+ if (rpmatch(resp) != 1) {
+ (void)fprintf(stderr, "not overwritten\n");
+ return (0);
+ }
+ }
+ }
+ if (!rename(from, to)) {
+ if (vflg)
+ printf("%s -> %s\n", from, to);
+ return (0);
+ }
+
+ if (errno == EXDEV) {
+ struct statfs sfs;
+ char path[PATH_MAX];
+
+ /* Can't mv(1) a mount point. */
+ if (realpath(from, path) == NULL) {
+ warnx("cannot resolve %s: %s", from, path);
+ return (1);
+ }
+ if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) {
+ warnx("cannot rename a mount point");
+ return (1);
+ }
+ } else {
+ warn("rename %s to %s", from, to);
+ return (1);
+ }
+
+ /*
+ * If rename fails because we're trying to cross devices, and
+ * it's a regular file, do the copy internally; otherwise, use
+ * cp and rm.
+ */
+ if (lstat(from, &sb)) {
+ warn("%s", from);
+ return (1);
+ }
+ return (S_ISREG(sb.st_mode) ?
+ fastcopy(from, to, &sb) : copy(from, to));
+}
+
+int
+fastcopy(char *from, char *to, struct stat *sbp)
+{
+ struct timeval tval[2];
+ static u_int blen;
+ static char *bp;
+ mode_t oldmode;
+ ssize_t nread;
+ int from_fd, to_fd;
+
+ if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
+ warn("%s", from);
+ return (1);
+ }
+ if (blen < sbp->st_blksize) {
+ if (bp != NULL)
+ free(bp);
+ if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) {
+ blen = 0;
+ warnx("malloc failed");
+ return (1);
+ }
+ blen = sbp->st_blksize;
+ }
+ while ((to_fd =
+ open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
+ if (errno == EEXIST && unlink(to) == 0)
+ continue;
+ warn("%s", to);
+ (void)close(from_fd);
+ return (1);
+ }
+#ifdef __APPLE__
+ {
+ struct statfs sfs;
+
+ /*
+ * Pre-allocate blocks for the destination file if it
+ * resides on Xsan.
+ */
+ if (fstatfs(to_fd, &sfs) == 0 &&
+ strcmp(sfs.f_fstypename, "acfs") == 0) {
+ fstore_t fst;
+
+ fst.fst_flags = 0;
+ fst.fst_posmode = F_PEOFPOSMODE;
+ fst.fst_offset = 0;
+ fst.fst_length = sbp->st_size;
+
+ (void) fcntl(to_fd, F_PREALLOCATE, &fst);
+ }
+ }
+#endif /* __APPLE__ */
+ while ((nread = read(from_fd, bp, (size_t)blen)) > 0)
+ if (write(to_fd, bp, (size_t)nread) != nread) {
+ warn("%s", to);
+ goto err;
+ }
+ if (nread < 0) {
+ warn("%s", from);
+err: if (unlink(to))
+ warn("%s: remove", to);
+ (void)close(from_fd);
+ (void)close(to_fd);
+ return (1);
+ }
+#ifdef __APPLE__
+ /* XATTR can fail if to_fd has mode 000 */
+ if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ warn("%s: unable to move extended attributes and ACL from %s",
+ to, from);
+ }
+#endif
+ (void)close(from_fd);
+
+ oldmode = sbp->st_mode & ALLPERMS;
+ if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) {
+ warn("%s: set owner/group (was: %lu/%lu)", to,
+ (u_long)sbp->st_uid, (u_long)sbp->st_gid);
+ if (oldmode & (S_ISUID | S_ISGID)) {
+ warnx(
+"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)",
+ to, oldmode);
+ sbp->st_mode &= ~(S_ISUID | S_ISGID);
+ }
+ }
+ if (fchmod(to_fd, sbp->st_mode))
+ warn("%s: set mode (was: 0%03o)", to, oldmode);
+ /*
+ * XXX
+ * NFS doesn't support chflags; ignore errors unless there's reason
+ * to believe we're losing bits. (Note, this still won't be right
+ * if the server supports flags and we were trying to *remove* flags
+ * on a file that we copied, i.e., that we didn't create.)
+ */
+ errno = 0;
+ if (fchflags(to_fd, (u_int)sbp->st_flags))
+ if (errno != ENOTSUP || sbp->st_flags != 0)
+ warn("%s: set flags (was: 0%07o)", to, sbp->st_flags);
+
+ tval[0].tv_sec = sbp->st_atime;
+ tval[1].tv_sec = sbp->st_mtime;
+ tval[0].tv_usec = tval[1].tv_usec = 0;
+ if (utimes(to, tval))
+ warn("%s: set times", to);
+
+ if (close(to_fd)) {
+ warn("%s", to);
+ return (1);
+ }
+
+ if (unlink(from)) {
+ warn("%s: remove", from);
+ return (1);
+ }
+ if (vflg)
+ printf("%s -> %s\n", from, to);
+ return (0);
+}
+
+int
+copy(char *from, char *to)
+{
+ int pid, status;
+
+ /* posix_spawn cp from to && rm from */
+
+ if ((pid = fork()) == 0) {
+ execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to,
+ (char *)NULL);
+ warn("%s", _PATH_CP);
+ _exit(1);
+ }
+ if (waitpid(pid, &status, 0) == -1) {
+ warn("%s: waitpid", _PATH_CP);
+ return (1);
+ }
+ if (!WIFEXITED(status)) {
+ warnx("%s: did not terminate normally", _PATH_CP);
+ return (1);
+ }
+ if (WEXITSTATUS(status)) {
+ warnx("%s: terminated with %d (non-zero) status",
+ _PATH_CP, WEXITSTATUS(status));
+ return (1);
+ }
+ if (!(pid = vfork())) {
+ execl(_PATH_RM, "mv", "-rf", "--", from, (char *)NULL);
+ warn("%s", _PATH_RM);
+ _exit(1);
+ }
+ if (waitpid(pid, &status, 0) == -1) {
+ warn("%s: waitpid", _PATH_RM);
+ return (1);
+ }
+ if (!WIFEXITED(status)) {
+ warnx("%s: did not terminate normally", _PATH_RM);
+ return (1);
+ }
+ if (WEXITSTATUS(status)) {
+ warnx("%s: terminated with %d (non-zero) status",
+ _PATH_RM, WEXITSTATUS(status));
+ return (1);
+ }
+ return (0);
+}
+
+void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: mv [-f | -i | -n] [-v] source target",
+ " mv [-f | -i | -n] [-v] source ... directory");
+ exit(EX_USAGE);
+}
diff --git a/file_cmds/mv/pathnames.h b/file_cmds/mv/pathnames.h
new file mode 100644
index 0000000..fe1d6cd
--- /dev/null
+++ b/file_cmds/mv/pathnames.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/bin/mv/pathnames.h,v 1.6 2002/05/17 11:38:48 jmallett Exp $
+ */
+
+#ifndef _MV_PATHNAMES_H_
+#define _MV_PATHNAMES_H_
+
+#define _PATH_RM "/bin/rm"
+#ifdef __APPLE__
+#define _PATH_CP "/bin/cp"
+#endif /* __APPLE__ */
+
+#endif /* _MV_PATHNAMES_H_ */
diff --git a/file_cmds/pathchk/pathchk.1 b/file_cmds/pathchk/pathchk.1
new file mode 100644
index 0000000..efe2a05
--- /dev/null
+++ b/file_cmds/pathchk/pathchk.1
@@ -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/file_cmds/pathchk/pathchk.c b/file_cmds/pathchk/pathchk.c
new file mode 100644
index 0000000..233a6d0
--- /dev/null
+++ b/file_cmds/pathchk/pathchk.c
@@ -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);
+}
diff --git a/file_cmds/pax/ar_io.c b/file_cmds/pax/ar_io.c
new file mode 100644
index 0000000..1c4b943
--- /dev/null
+++ b/file_cmds/pax/ar_io.c
@@ -0,0 +1,1318 @@
+/* $OpenBSD: ar_io.c,v 1.38 2008/06/11 00:49:08 pvalchev Exp $ */
+/* $NetBSD: ar_io.c,v 1.5 1996/03/26 23:54:13 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: ar_io.c,v 1.38 2008/06/11 00:49:08 pvalchev Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#ifndef __APPLE__
+#include <sys/mtio.h>
+#endif /* !__APPLE__ */
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <err.h>
+#include <stdint.h>
+#include "pax.h"
+#include "options.h"
+#include "extern.h"
+
+/*
+ * Routines which deal directly with the archive I/O device/file.
+ */
+
+#define DMOD 0666 /* default mode of created archives */
+#define EXT_MODE O_RDONLY /* open mode for list/extract */
+#define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */
+#define APP_MODE O_RDWR /* mode for append */
+#define STDO "<STDOUT>" /* pseudo name for stdout */
+#define STDN "<STDIN>" /* pseudo name for stdin */
+#define _NONE "<NONE>" /* pseudo name for no files */
+static int arfd = -1; /* archive file descriptor */
+static int artyp = ISREG; /* archive type: file/FIFO/tape */
+static int arvol = 1; /* archive volume number */
+static int lstrval = -1; /* return value from last i/o */
+static int io_ok; /* i/o worked on volume after resync */
+static int did_io; /* did i/o ever occur on volume? */
+static int done; /* set via tty termination */
+static struct stat arsb; /* stat of archive device at open */
+static int invld_rec; /* tape has out of spec record size */
+static int wr_trail = 1; /* trailer was rewritten in append */
+static int can_unlnk = 0; /* do we unlink null archives? */
+const char *arcname; /* printable name of archive */
+const char *gzip_program; /* name of gzip program */
+static pid_t zpid = -1; /* pid of child process */
+int force_one_volume; /* 1 if we ignore volume changes */
+
+#ifndef __APPLE__
+static int get_phys(void);
+#endif /* __APPLE__ */
+extern sigset_t s_mask;
+static void ar_start_gzip(int, const char *, int);
+
+/*
+ * ar_open()
+ * Opens the next archive volume. Determines the type of the device and
+ * sets up block sizes as required by the archive device and the format.
+ * Note: we may be called with name == NULL on the first open only.
+ * Return:
+ * -1 on failure, 0 otherwise
+ */
+
+int
+ar_open(const char *name)
+{
+#ifndef __APPLE__
+ struct mtget mb;
+#endif /* __APPLE__ */
+ if (arfd != -1)
+ (void)close(arfd);
+ arfd = -1;
+ can_unlnk = did_io = io_ok = invld_rec = 0;
+ artyp = ISREG;
+ flcnt = 0;
+
+ /*
+ * open based on overall operation mode
+ */
+ switch (act) {
+ case LIST:
+ case EXTRACT:
+ if (name == NULL) {
+ arfd = STDIN_FILENO;
+ arcname = STDN;
+ } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0)
+ syswarn(1, errno, "Failed open to read on %s", name);
+ if (arfd != -1 && gzip_program != NULL)
+ ar_start_gzip(arfd, gzip_program, 0);
+ break;
+ case ARCHIVE:
+ if (name == NULL) {
+ arfd = STDOUT_FILENO;
+ arcname = STDO;
+ } else if ((arfd = open(name, AR_MODE, DMOD)) < 0)
+ syswarn(1, errno, "Failed open to write on %s", name);
+ else
+ can_unlnk = 1;
+ if (arfd != -1 && gzip_program != NULL)
+ ar_start_gzip(arfd, gzip_program, 1);
+ break;
+ case APPND:
+ if (name == NULL) {
+ arfd = STDOUT_FILENO;
+ arcname = STDO;
+ } else if ((arfd = open(name, APP_MODE, DMOD)) < 0)
+ syswarn(1, errno, "Failed open to read/write on %s",
+ name);
+ break;
+ case COPY:
+ /*
+ * arfd not used in COPY mode
+ */
+ arcname = _NONE;
+ lstrval = 1;
+ return(0);
+ }
+ if (arfd < 0)
+ return(-1);
+
+ if (chdname != NULL)
+ if (dochdir(chdname) == -1) {
+ return(-1);
+ }
+ /*
+ * set up is based on device type
+ */
+ if (fstat(arfd, &arsb) < 0) {
+ syswarn(0, errno, "Failed stat on %s", arcname);
+ (void)close(arfd);
+ arfd = -1;
+ can_unlnk = 0;
+ return(-1);
+ }
+ if (S_ISDIR(arsb.st_mode)) {
+ paxwarn(0, "Cannot write an archive on top of a directory %s",
+ arcname);
+ (void)close(arfd);
+ arfd = -1;
+ can_unlnk = 0;
+ return(-1);
+ }
+
+#ifndef __APPLE__
+ if (S_ISCHR(arsb.st_mode))
+ artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE;
+ else
+#endif /* !__APPLE__ */
+ if (S_ISBLK(arsb.st_mode))
+ artyp = ISBLK;
+ else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE))
+ artyp = ISPIPE;
+ else
+ artyp = ISREG;
+
+ /*
+ * make sure we beyond any doubt that we only can unlink regular files
+ * we created
+ */
+ if (artyp != ISREG)
+ can_unlnk = 0;
+ /*
+ * if we are writing, we are done
+ */
+ if (act == ARCHIVE) {
+ blksz = rdblksz = wrblksz;
+ lstrval = 1;
+ return(0);
+ }
+
+ /*
+ * set default blksz on read. APPNDs writes rdblksz on the last volume
+ * On all new archive volumes, we shift to wrblksz (if the user
+ * specified one, otherwise we will continue to use rdblksz). We
+ * must set blocksize based on what kind of device the archive is
+ * stored.
+ */
+ switch (artyp) {
+ case ISTAPE:
+ /*
+ * Tape drives come in at least two flavors. Those that support
+ * variable sized records and those that have fixed sized
+ * records. They must be treated differently. For tape drives
+ * that support variable sized records, we must make large
+ * reads to make sure we get the entire record, otherwise we
+ * will just get the first part of the record (up to size we
+ * asked). Tapes with fixed sized records may or may not return
+ * multiple records in a single read. We really do not care
+ * what the physical record size is UNLESS we are going to
+ * append. (We will need the physical block size to rewrite
+ * the trailer). Only when we are appending do we go to the
+ * effort to figure out the true PHYSICAL record size.
+ */
+ blksz = rdblksz = MAXBLK;
+ break;
+ case ISPIPE:
+ case ISBLK:
+ case ISCHR:
+ /*
+ * Blocksize is not a major issue with these devices (but must
+ * be kept a multiple of 512). If the user specified a write
+ * block size, we use that to read. Under append, we must
+ * always keep blksz == rdblksz. Otherwise we go ahead and use
+ * the device optimal blocksize as (and if) returned by stat
+ * and if it is within pax specs.
+ */
+ if ((act == APPND) && wrblksz) {
+ blksz = rdblksz = wrblksz;
+ break;
+ }
+
+ if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) &&
+ ((arsb.st_blksize % BLKMULT) == 0))
+ rdblksz = arsb.st_blksize;
+ else
+ rdblksz = DEVBLK;
+ /*
+ * For performance go for large reads when we can without harm
+ */
+ if ((act == APPND) || (artyp == ISCHR))
+ blksz = rdblksz;
+ else
+ blksz = MAXBLK;
+ break;
+ case ISREG:
+ /*
+ * if the user specified wrblksz works, use it. Under appends
+ * we must always keep blksz == rdblksz
+ */
+ if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){
+ blksz = rdblksz = wrblksz;
+ break;
+ }
+ /*
+ * See if we can find the blocking factor from the file size
+ */
+ for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT)
+ if ((arsb.st_size % rdblksz) == 0)
+ break;
+ /*
+ * When we cannot find a match, we may have a flawed archive.
+ */
+ if (rdblksz <= 0)
+ rdblksz = FILEBLK;
+ /*
+ * for performance go for large reads when we can
+ */
+ if (act == APPND)
+ blksz = rdblksz;
+ else
+ blksz = MAXBLK;
+ break;
+ default:
+ /*
+ * should never happen, worst case, slow...
+ */
+ blksz = rdblksz = BLKMULT;
+ break;
+ }
+ lstrval = 1;
+ return(0);
+}
+
+/*
+ * ar_close()
+ * closes archive device, increments volume number, and prints i/o summary
+ */
+void
+ar_close(void)
+{
+ int status;
+
+ if (arfd < 0) {
+ did_io = io_ok = flcnt = 0;
+ return;
+ }
+
+ /*
+ * Close archive file. This may take a LONG while on tapes (we may be
+ * forced to wait for the rewind to complete) so tell the user what is
+ * going on (this avoids the user hitting control-c thinking pax is
+ * broken).
+ */
+ if (vflag && (artyp == ISTAPE)) {
+ if (vfpart)
+ (void)putc('\n', listf);
+ (void)fprintf(listf,
+ "%s: Waiting for tape drive close to complete...",
+ argv0);
+ (void)fflush(listf);
+ }
+
+ /*
+ * if nothing was written to the archive (and we created it), we remove
+ * it
+ */
+ if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) &&
+ (arsb.st_size == 0)) {
+ (void)unlink(arcname);
+ can_unlnk = 0;
+ }
+
+ /*
+ * for a quick extract/list, pax frequently exits before the child
+ * process is done
+ */
+ if ((act == LIST || act == EXTRACT) && nflag && zpid > 0)
+ kill(zpid, SIGINT);
+
+ (void)close(arfd);
+
+ /* Do not exit before child to ensure data integrity */
+ if (zpid > 0)
+ waitpid(zpid, &status, 0);
+
+ if (vflag && (artyp == ISTAPE)) {
+ (void)fputs("done.\n", listf);
+ vfpart = 0;
+ (void)fflush(listf);
+ }
+ arfd = -1;
+
+ if (!io_ok && !did_io) {
+ flcnt = 0;
+ return;
+ }
+ did_io = io_ok = 0;
+
+ /*
+ * The volume number is only increased when the last device has data
+ * and we have already determined the archive format.
+ */
+ if (frmt != NULL)
+ ++arvol;
+
+ if (!vflag) {
+ flcnt = 0;
+ return;
+ }
+
+ /*
+ * Print out a summary of I/O for this archive volume.
+ */
+ if (vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+
+ /*
+ * If we have not determined the format yet, we just say how many bytes
+ * we have skipped over looking for a header to id. there is no way we
+ * could have written anything yet.
+ */
+ if (frmt == NULL) {
+# ifdef LONG_OFF_T
+ (void)fprintf(listf, "%s: unknown format, %lu bytes skipped.\n",
+# else
+ (void)fprintf(listf, "%s: unknown format, %qu bytes skipped.\n",
+# endif
+ argv0, rdcnt);
+ (void)fflush(listf);
+ flcnt = 0;
+ return;
+ }
+
+ if (strcmp(NM_CPIO, argv0) == 0)
+ (void)fprintf(listf, "%qu blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120);
+ else if (strcmp(NM_TAR, argv0) != 0 && strcmp(NM_PAX, argv0) != 0)
+ (void)fprintf(listf,
+# ifdef LONG_OFF_T
+ "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n",
+ argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt);
+# else
+ "%s: %s vol %d, %lu files, %ju bytes read, %ju bytes written.\n",
+ argv0, frmt->name, arvol-1, flcnt, (uintmax_t)rdcnt, (uintmax_t)wrcnt);
+# endif
+ (void)fflush(listf);
+ flcnt = 0;
+}
+
+/*
+ * ar_drain()
+ * drain any archive format independent padding from an archive read
+ * from a socket or a pipe. This is to prevent the process on the
+ * other side of the pipe from getting a SIGPIPE (pax will stop
+ * reading an archive once a format dependent trailer is detected).
+ */
+void
+ar_drain(void)
+{
+ int res;
+ char drbuf[MAXBLK];
+
+ /*
+ * we only drain from a pipe/socket. Other devices can be closed
+ * without reading up to end of file. We sure hope that pipe is closed
+ * on the other side so we will get an EOF.
+ */
+ if ((artyp != ISPIPE) || (lstrval <= 0))
+ return;
+
+ /*
+ * keep reading until pipe is drained
+ */
+ while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0)
+ ;
+ lstrval = res;
+}
+
+/*
+ * ar_set_wr()
+ * Set up device right before switching from read to write in an append.
+ * device dependent code (if required) to do this should be added here.
+ * For all archive devices we are already positioned at the place we want
+ * to start writing when this routine is called.
+ * Return:
+ * 0 if all ready to write, -1 otherwise
+ */
+
+int
+ar_set_wr(void)
+{
+ off_t cpos;
+
+ /*
+ * we must make sure the trailer is rewritten on append, ar_next()
+ * will stop us if the archive containing the trailer was not written
+ */
+ wr_trail = 0;
+
+ /*
+ * Add any device dependent code as required here
+ */
+ if (artyp != ISREG)
+ return(0);
+ /*
+ * Ok we have an archive in a regular file. If we were rewriting a
+ * file, we must get rid of all the stuff after the current offset
+ * (it was not written by pax).
+ */
+ if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) ||
+ (ftruncate(arfd, cpos) < 0)) {
+ syswarn(1, errno, "Unable to truncate archive file");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * ar_app_ok()
+ * check if the last volume in the archive allows appends. We cannot check
+ * this until we are ready to write since there is no spec that says all
+ * volumes in a single archive have to be of the same type...
+ * Return:
+ * 0 if we can append, -1 otherwise.
+ */
+
+int
+ar_app_ok(void)
+{
+ if (artyp == ISPIPE) {
+ paxwarn(1, "Cannot append to an archive obtained from a pipe.");
+ return(-1);
+ }
+
+ if (!invld_rec)
+ return(0);
+ paxwarn(1,"Cannot append, device record size %d does not support %s spec",
+ rdblksz, argv0);
+ return(-1);
+}
+
+/*
+ * ar_read()
+ * read up to a specified number of bytes from the archive into the
+ * supplied buffer. When dealing with tapes we may not always be able to
+ * read what we want.
+ * Return:
+ * Number of bytes in buffer. 0 for end of file, -1 for a read error.
+ */
+
+int
+ar_read(char *buf, int cnt)
+{
+ int res = 0;
+
+ /*
+ * if last i/o was in error, no more reads until reset or new volume
+ */
+ if (lstrval <= 0)
+ return(lstrval);
+
+ /*
+ * how we read must be based on device type
+ */
+ switch (artyp) {
+ case ISTAPE:
+ if ((res = read(arfd, buf, cnt)) > 0) {
+ /*
+ * CAUTION: tape systems may not always return the same
+ * sized records so we leave blksz == MAXBLK. The
+ * physical record size that a tape drive supports is
+ * very hard to determine in a uniform and portable
+ * manner.
+ */
+ io_ok = 1;
+ if (res != rdblksz) {
+ /*
+ * Record size changed. If this happens on
+ * any record after the first, we probably have
+ * a tape drive which has a fixed record size
+ * (we are getting multiple records in a single
+ * read). Watch out for record blocking that
+ * violates pax spec (must be a multiple of
+ * BLKMULT).
+ */
+ rdblksz = res;
+ if (rdblksz % BLKMULT)
+ invld_rec = 1;
+ }
+ return(res);
+ }
+ break;
+ case ISREG:
+ case ISBLK:
+ case ISCHR:
+ case ISPIPE:
+ default:
+ /*
+ * Files are so easy to deal with. These other things cannot
+ * be trusted at all. So when we are dealing with character
+ * devices and pipes we just take what they have ready for us
+ * and return. Trying to do anything else with them runs the
+ * risk of failure.
+ */
+ if ((res = read(arfd, buf, cnt)) > 0) {
+ io_ok = 1;
+ return(res);
+ }
+ break;
+ }
+
+ /*
+ * We are in trouble at this point, something is broken...
+ */
+ lstrval = res;
+ if (res < 0)
+ syswarn(1, errno, "Failed read on archive volume %d", arvol);
+ else
+ paxwarn(0, "End of archive volume %d reached", arvol);
+ return(res);
+}
+
+/*
+ * ar_write()
+ * Write a specified number of bytes in supplied buffer to the archive
+ * device so it appears as a single "block". Deals with errors and tries
+ * to recover when faced with short writes.
+ * Return:
+ * Number of bytes written. 0 indicates end of volume reached and with no
+ * flaws (as best that can be detected). A -1 indicates an unrecoverable
+ * error in the archive occurred.
+ */
+
+int
+ar_write(char *buf, int bsz)
+{
+ int res;
+ off_t cpos;
+
+ /*
+ * do not allow pax to create a "bad" archive. Once a write fails on
+ * an archive volume prevent further writes to it.
+ */
+ if (lstrval <= 0)
+ return(lstrval);
+
+ if ((res = write(arfd, buf, bsz)) == 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.
+ */
+ if (res < 0)
+ lstrval = res;
+ else
+ lstrval = 0;
+
+ switch (artyp) {
+ case ISREG:
+ if ((res > 0) && (res % BLKMULT)) {
+ /*
+ * try to fix up partial writes which are not BLKMULT
+ * in size by forcing the runt record to next archive
+ * volume
+ */
+ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0)
+ break;
+ cpos -= (off_t)res;
+ if (ftruncate(arfd, cpos) < 0)
+ break;
+ res = lstrval = 0;
+ break;
+ }
+ if (res >= 0)
+ break;
+ /*
+ * if file is out of space, handle it like a return of 0
+ */
+ if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT))
+ res = lstrval = 0;
+ break;
+ case ISTAPE:
+ case ISCHR:
+ case ISBLK:
+ if (res >= 0)
+ break;
+ if (errno == EACCES) {
+ paxwarn(0, "Write failed, archive is write protected.");
+ lstrval = 0;
+ return(0);
+ }
+ /*
+ * see if we reached the end of media, if so force a change to
+ * the next volume
+ */
+ if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO))
+ res = lstrval = 0;
+ break;
+ case ISPIPE:
+ default:
+ /*
+ * we cannot fix errors to these devices
+ */
+ break;
+ }
+
+ /*
+ * Better tell the user the bad news...
+ * if this is a block aligned archive format, we may have a bad archive
+ * if the format wants the header to start at a BLKMULT boundary.. While
+ * we can deal with the mis-aligned data, it violates spec and other
+ * archive readers will likely fail. if the format is not block
+ * aligned, the user may be lucky (and the archive is ok).
+ */
+ if (res >= 0) {
+ if (res > 0)
+ wr_trail = 1;
+ io_ok = 1;
+ }
+
+ /*
+ * If we were trying to rewrite the trailer and it didn't work, we
+ * must quit right away.
+ */
+ if (!wr_trail && (res <= 0)) {
+ paxwarn(1,"Unable to append, trailer re-write failed. Quitting.");
+ return(res);
+ }
+
+ if (res == 0)
+ paxwarn(0, "End of archive volume %d reached", arvol);
+ else if (res < 0)
+ syswarn(1, errno, "Failed write to archive volume: %d", arvol);
+ else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0))
+ paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED");
+ else
+ paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED");
+ return(res);
+}
+
+/*
+ * ar_rdsync()
+ * Try to move past a bad spot on a flawed archive as needed to continue
+ * I/O. Clears error flags to allow I/O to continue.
+ * Return:
+ * 0 when ok to try i/o again, -1 otherwise.
+ */
+
+int
+ar_rdsync(void)
+{
+ long fsbz;
+ off_t cpos;
+ off_t mpos;
+#ifndef __APPLE__
+ struct mtop mb;
+#endif /* !__APPLE__ */
+
+ /*
+ * Fail resync attempts at user request (done) or if this is going to be
+ * an update/append to a existing archive. if last i/o hit media end,
+ * we need to go to the next volume not try a resync
+ */
+ if ((done > 0) || (lstrval == 0))
+ return(-1);
+
+ if ((act == APPND) || (act == ARCHIVE)) {
+ paxwarn(1, "Cannot allow updates to an archive with flaws.");
+ return(-1);
+ }
+ if (io_ok)
+ did_io = 1;
+
+ switch (artyp) {
+#ifndef __APPLE__
+ case ISTAPE:
+ /*
+ * if the last i/o was a successful data transfer, we assume
+ * the fault is just a bad record on the tape that we are now
+ * past. If we did not get any data since the last resync try
+ * to move the tape forward one PHYSICAL record past any
+ * damaged tape section. Some tape drives are stubborn and need
+ * to be pushed.
+ */
+ if (io_ok) {
+ io_ok = 0;
+ lstrval = 1;
+ break;
+ }
+ mb.mt_op = MTFSR;
+ mb.mt_count = 1;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0)
+ break;
+ lstrval = 1;
+ break;
+#endif /* !__APPLE__ */
+ case ISREG:
+ case ISCHR:
+ case ISBLK:
+ /*
+ * try to step over the bad part of the device.
+ */
+ io_ok = 0;
+ if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG))
+ fsbz = BLKMULT;
+ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0)
+ break;
+ mpos = fsbz - (cpos % (off_t)fsbz);
+ if (lseek(arfd, mpos, SEEK_CUR) < 0)
+ break;
+ lstrval = 1;
+ break;
+ case ISPIPE:
+ default:
+ /*
+ * cannot recover on these archive device types
+ */
+ io_ok = 0;
+ break;
+ }
+ if (lstrval <= 0) {
+ paxwarn(1, "Unable to recover from an archive read failure.");
+ return(-1);
+ }
+ paxwarn(0, "Attempting to recover from an archive read failure.");
+ return(0);
+}
+
+/*
+ * ar_fow()
+ * Move the I/O position within the archive forward the specified number of
+ * bytes as supported by the device. If we cannot move the requested
+ * number of bytes, return the actual number of bytes moved in skipped.
+ * Return:
+ * 0 if moved the requested distance, -1 on complete failure, 1 on
+ * partial move (the amount moved is in skipped)
+ */
+
+int
+ar_fow(off_t sksz, off_t *skipped)
+{
+ off_t cpos;
+ off_t mpos;
+
+ *skipped = 0;
+ if (sksz <= 0)
+ return(0);
+
+ /*
+ * we cannot move forward at EOF or error
+ */
+ if (lstrval <= 0)
+ return(lstrval);
+
+ /*
+ * Safer to read forward on devices where it is hard to find the end of
+ * the media without reading to it. With tapes we cannot be sure of the
+ * number of physical blocks to skip (we do not know physical block
+ * size at this point), so we must only read forward on tapes!
+ */
+ if (artyp != ISREG)
+ return(0);
+
+ /*
+ * figure out where we are in the archive
+ */
+ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) {
+ /*
+ * we can be asked to move farther than there are bytes in this
+ * volume, if so, just go to file end and let normal buf_fill()
+ * deal with the end of file (it will go to next volume by
+ * itself)
+ */
+ if ((mpos = cpos + sksz) > arsb.st_size) {
+ *skipped = arsb.st_size - cpos;
+ mpos = arsb.st_size;
+ } else
+ *skipped = sksz;
+ if (lseek(arfd, mpos, SEEK_SET) >= 0)
+ return(0);
+ }
+ syswarn(1, errno, "Forward positioning operation on archive failed");
+ lstrval = -1;
+ return(-1);
+}
+
+/*
+ * ar_rev()
+ * move the i/o position within the archive backwards the specified byte
+ * count as supported by the device. With tapes drives we RESET rdblksz to
+ * the PHYSICAL blocksize.
+ * NOTE: We should only be called to move backwards so we can rewrite the
+ * last records (the trailer) of an archive (APPEND).
+ * Return:
+ * 0 if moved the requested distance, -1 on complete failure
+ */
+
+int
+ar_rev(off_t sksz)
+{
+ off_t cpos;
+#ifndef __APPLE__
+ struct mtop mb;
+ int phyblk;
+#endif /* __APPLE__ */
+
+ /*
+ * make sure we do not have try to reverse on a flawed archive
+ */
+ if (lstrval < 0)
+ return(lstrval);
+
+ switch (artyp) {
+ case ISPIPE:
+ if (sksz <= 0)
+ break;
+ /*
+ * cannot go backwards on these critters
+ */
+ paxwarn(1, "Reverse positioning on pipes is not supported.");
+ lstrval = -1;
+ return(-1);
+ case ISREG:
+ case ISBLK:
+ case ISCHR:
+ default:
+ if (sksz <= 0)
+ break;
+
+ /*
+ * For things other than files, backwards movement has a very
+ * high probability of failure as we really do not know the
+ * true attributes of the device we are talking to (the device
+ * may not even have the ability to lseek() in any direction).
+ * First we figure out where we are in the archive.
+ */
+ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) {
+ syswarn(1, errno,
+ "Unable to obtain current archive byte offset");
+ lstrval = -1;
+ return(-1);
+ }
+
+ /*
+ * we may try to go backwards past the start when the archive
+ * is only a single record. If this happens and we are on a
+ * multi-volume archive, we need to go to the end of the
+ * previous volume and continue our movement backwards from
+ * there.
+ */
+ if ((cpos -= sksz) < (off_t)0L) {
+ if (arvol > 1) {
+ /*
+ * this should never happen
+ */
+ paxwarn(1,"Reverse position on previous volume.");
+ lstrval = -1;
+ return(-1);
+ }
+ cpos = (off_t)0L;
+ }
+ if (lseek(arfd, cpos, SEEK_SET) < 0) {
+ syswarn(1, errno, "Unable to seek archive backwards");
+ lstrval = -1;
+ return(-1);
+ }
+ break;
+#ifndef __APPLE__
+ case ISTAPE:
+ /*
+ * Calculate and move the proper number of PHYSICAL tape
+ * blocks. If the sksz is not an even multiple of the physical
+ * tape size, we cannot do the move (this should never happen).
+ * (We also cannot handle trailers spread over two vols.)
+ * get_phys() also makes sure we are in front of the filemark.
+ */
+ if ((phyblk = get_phys()) <= 0) {
+ lstrval = -1;
+ return(-1);
+ }
+
+ /*
+ * make sure future tape reads only go by physical tape block
+ * size (set rdblksz to the real size).
+ */
+ rdblksz = phyblk;
+
+ /*
+ * if no movement is required, just return (we must be after
+ * get_phys() so the physical blocksize is properly set)
+ */
+ if (sksz <= 0)
+ break;
+
+ /*
+ * ok we have to move. Make sure the tape drive can do it.
+ */
+ if (sksz % phyblk) {
+ paxwarn(1,
+ "Tape drive unable to backspace requested amount");
+ lstrval = -1;
+ return(-1);
+ }
+
+ /*
+ * move backwards the requested number of bytes
+ */
+ mb.mt_op = MTBSR;
+ mb.mt_count = sksz/phyblk;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+ syswarn(1,errno, "Unable to backspace tape %d blocks.",
+ mb.mt_count);
+ lstrval = -1;
+ return(-1);
+ }
+ break;
+#endif /* !__APPLE__ */
+ }
+ lstrval = 1;
+ return(0);
+}
+#ifndef __APPLE__
+/*
+ * get_phys()
+ * Determine the physical block size on a tape drive. We need the physical
+ * block size so we know how many bytes we skip over when we move with
+ * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when
+ * return.
+ * This is one really SLOW routine...
+ * Return:
+ * physical block size if ok (ok > 0), -1 otherwise
+ */
+
+static int
+get_phys(void)
+{
+ int padsz = 0;
+ int res;
+ int phyblk;
+ struct mtop mb;
+ char scbuf[MAXBLK];
+
+ /*
+ * move to the file mark, and then back up one record and read it.
+ * this should tell us the physical record size the tape is using.
+ */
+ if (lstrval == 1) {
+ /*
+ * we know we are at file mark when we get back a 0 from
+ * read()
+ */
+ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0)
+ padsz += res;
+ if (res < 0) {
+ syswarn(1, errno, "Unable to locate tape filemark.");
+ return(-1);
+ }
+ }
+
+ /*
+ * move backwards over the file mark so we are at the end of the
+ * last record.
+ */
+ mb.mt_op = MTBSF;
+ mb.mt_count = 1;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+ syswarn(1, errno, "Unable to backspace over tape filemark.");
+ return(-1);
+ }
+
+ /*
+ * move backwards so we are in front of the last record and read it to
+ * get physical tape blocksize.
+ */
+ mb.mt_op = MTBSR;
+ mb.mt_count = 1;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+ syswarn(1, errno, "Unable to backspace over last tape block.");
+ return(-1);
+ }
+ if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) {
+ syswarn(1, errno, "Cannot determine archive tape blocksize.");
+ return(-1);
+ }
+
+ /*
+ * read forward to the file mark, then back up in front of the filemark
+ * (this is a bit paranoid, but should be safe to do).
+ */
+ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0)
+ ;
+ if (res < 0) {
+ syswarn(1, errno, "Unable to locate tape filemark.");
+ return(-1);
+ }
+ mb.mt_op = MTBSF;
+ mb.mt_count = 1;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+ syswarn(1, errno, "Unable to backspace over tape filemark.");
+ return(-1);
+ }
+
+ /*
+ * set lstrval so we know that the filemark has not been seen
+ */
+ lstrval = 1;
+
+ /*
+ * return if there was no padding
+ */
+ if (padsz == 0)
+ return(phyblk);
+
+ /*
+ * make sure we can move backwards over the padding. (this should
+ * never fail).
+ */
+ if (padsz % phyblk) {
+ paxwarn(1, "Tape drive unable to backspace requested amount");
+ return(-1);
+ }
+
+ /*
+ * move backwards over the padding so the head is where it was when
+ * we were first called (if required).
+ */
+ mb.mt_op = MTBSR;
+ mb.mt_count = padsz/phyblk;
+ if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+ syswarn(1,errno,"Unable to backspace tape over %d pad blocks",
+ mb.mt_count);
+ return(-1);
+ }
+ return(phyblk);
+}
+#endif /* !__APPLE__ */
+/*
+ * ar_next()
+ * prompts the user for the next volume in this archive. For some devices
+ * we may allow the media to be changed. Otherwise a new archive is
+ * prompted for. By pax spec, if there is no controlling tty or an eof is
+ * read on tty input, we must quit pax.
+ * Return:
+ * 0 when ready to continue, -1 when all done
+ */
+
+int
+ar_next(void)
+{
+ char buf[PAXPATHLEN+2];
+ static int freeit = 0;
+ sigset_t o_mask;
+
+ /*
+ * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so
+ * things like writing EOF etc will be done) (Watch out ar_close() can
+ * also be called via a signal handler, so we must prevent a race.
+ */
+ if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0)
+ syswarn(0, errno, "Unable to set signal mask");
+ ar_close();
+ if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0)
+ syswarn(0, errno, "Unable to restore signal mask");
+
+ if (frmt == NULL || done || !wr_trail || force_one_volume || strcmp(NM_TAR, argv0) == 0 ||
+ strcmp(NM_PAX, argv0) == 0)
+ return(-1);
+
+ tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0);
+
+ /*
+ * if i/o is on stdin or stdout, we cannot reopen it (we do not know
+ * the name), the user will be forced to type it in.
+ */
+ if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG)
+ && (artyp != ISPIPE)) {
+ if (artyp == ISTAPE) {
+ tty_prnt("%s ready for archive tape volume: %d\n",
+ arcname, arvol);
+ tty_prnt("Load the NEXT TAPE on the tape drive");
+ } else {
+ tty_prnt("%s ready for archive volume: %d\n",
+ arcname, arvol);
+ tty_prnt("Load the NEXT STORAGE MEDIA (if required)");
+ }
+
+ if ((act == ARCHIVE) || (act == APPND))
+ tty_prnt(" and make sure it is WRITE ENABLED.\n");
+ else
+ tty_prnt("\n");
+
+ for (;;) {
+ tty_prnt("Type \"y\" to continue, \".\" to quit %s,",
+ argv0);
+ tty_prnt(" or \"s\" to switch to new device.\nIf you");
+ tty_prnt(" cannot change storage media, type \"s\"\n");
+ tty_prnt("Is the device ready and online? > ");
+
+ if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){
+ done = 1;
+ lstrval = -1;
+ tty_prnt("Quitting %s!\n", argv0);
+ vfpart = 0;
+ return(-1);
+ }
+
+ if ((buf[0] == '\0') || (buf[1] != '\0')) {
+ tty_prnt("%s unknown command, try again\n",buf);
+ continue;
+ }
+
+ switch (buf[0]) {
+ case 'y':
+ case 'Y':
+ /*
+ * we are to continue with the same device
+ */
+ if (ar_open(arcname) >= 0)
+ return(0);
+ tty_prnt("Cannot re-open %s, try again\n",
+ arcname);
+ continue;
+ case 's':
+ case 'S':
+ /*
+ * user wants to open a different device
+ */
+ tty_prnt("Switching to a different archive\n");
+ break;
+ default:
+ tty_prnt("%s unknown command, try again\n",buf);
+ continue;
+ }
+ break;
+ }
+ } else
+ tty_prnt("Ready for archive volume: %d\n", arvol);
+
+ /*
+ * have to go to a different archive
+ */
+ for (;;) {
+ tty_prnt("Input archive name or \".\" to quit %s.\n", argv0);
+ tty_prnt("Archive name > ");
+
+ if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) {
+ done = 1;
+ lstrval = -1;
+ tty_prnt("Quitting %s!\n", argv0);
+ vfpart = 0;
+ return(-1);
+ }
+ if (buf[0] == '\0') {
+ tty_prnt("Empty file name, try again\n");
+ continue;
+ }
+ if (!strcmp(buf, "..")) {
+ tty_prnt("Illegal file name: .. try again\n");
+ continue;
+ }
+ if (strlen(buf) > PAXPATHLEN) {
+ tty_prnt("File name too long, try again\n");
+ continue;
+ }
+
+ /*
+ * try to open new archive
+ */
+ if (ar_open(buf) >= 0) {
+ if (freeit) {
+ (void)free((char *)arcname);
+ freeit = 0;
+ }
+ if ((arcname = strdup(buf)) == NULL) {
+ done = 1;
+ lstrval = -1;
+ paxwarn(0, "Cannot save archive name.");
+ return(-1);
+ }
+ freeit = 1;
+ break;
+ }
+ tty_prnt("Cannot open %s, try again\n", buf);
+ continue;
+ }
+ return(0);
+}
+
+/*
+ * ar_start_gzip()
+ * starts the gzip compression/decompression process as a child, using magic
+ * to keep the fd the same in the calling function (parent).
+ */
+void
+ar_start_gzip(int fd, const char *gzip_program, int wr)
+{
+ int fds[2];
+ const char *gzip_flags = NULL;
+
+ if (pipe(fds) < 0)
+ err(1, "could not pipe");
+ zpid = fork();
+ if (zpid < 0)
+ err(1, "could not fork");
+
+ /* parent */
+ if (zpid) {
+ if (wr)
+ dup2(fds[1], fd);
+ else
+ dup2(fds[0], fd);
+ close(fds[0]);
+ close(fds[1]);
+ } else {
+ if (wr) {
+ dup2(fds[0], STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ gzip_flags = "-c";
+ } else {
+ dup2(fds[1], STDOUT_FILENO);
+ dup2(fd, STDIN_FILENO);
+ gzip_flags = "-dc";
+ }
+ close(fds[0]);
+ close(fds[1]);
+ if (execlp(gzip_program, gzip_program, gzip_flags, (char *)NULL) < 0)
+ err(1, "could not exec %s", gzip_program);
+ /* NOTREACHED */
+ }
+}
diff --git a/file_cmds/pax/ar_subs.c b/file_cmds/pax/ar_subs.c
new file mode 100644
index 0000000..b4896ea
--- /dev/null
+++ b/file_cmds/pax/ar_subs.c
@@ -0,0 +1,1517 @@
+/* $OpenBSD: ar_subs.c,v 1.32 2008/05/06 06:54:28 henning Exp $ */
+/* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: ar_subs.c,v 1.32 2008/05/06 06:54:28 henning Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifdef __APPLE__
+#include <sys/param.h>
+#include <copyfile.h>
+#include <libgen.h>
+#include <sys/queue.h>
+#endif
+#include "pax.h"
+#include "options.h"
+#include "extern.h"
+
+static int path_check(ARCHD *, int);
+static void wr_archive(ARCHD *, int is_app);
+static int get_arc(void);
+static int next_head(ARCHD *);
+extern sigset_t s_mask;
+
+/*
+ * Routines which control the overall operation modes of pax as specified by
+ * the user: list, append, read ...
+ */
+
+static char hdbuf[BLKMULT]; /* space for archive header on read */
+u_long flcnt; /* number of files processed */
+
+static char cwdpath[MAXPATHLEN]; /* current working directory path */
+static size_t cwdpathlen; /* current working directory path len */
+
+int
+updatepath(void)
+{
+ if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) {
+ syswarn(1, errno, "Cannot get working directory");
+ return -1;
+ }
+ cwdpathlen = strlen(cwdpath);
+ return 0;
+}
+
+int
+fdochdir(int fcwd)
+{
+ if (fchdir(fcwd) == -1) {
+ syswarn(1, errno, "Cannot chdir to `.'");
+ return -1;
+ }
+ return updatepath();
+}
+
+int
+dochdir(const char *name)
+{
+ if (chdir(name) == -1)
+ syswarn(1, errno, "Cannot chdir to `%s'", name);
+ return updatepath();
+}
+
+static int
+path_check(ARCHD *arcn, int level)
+{
+ char buf[MAXPATHLEN];
+ char *p;
+
+ if ((p = strrchr(arcn->name, '/')) == NULL)
+ return 0;
+ *p = '\0';
+
+ if (realpath(arcn->name, buf) == NULL) {
+ int error;
+ error = path_check(arcn, level + 1);
+ *p = '/';
+ if (error == 0)
+ return 0;
+ if (level == 0)
+ syswarn(1, 0, "Cannot resolve `%s'", arcn->name);
+ return -1;
+ }
+ if (cwdpathlen == 1) { /* We're in the root */
+ *p = '/';
+ return 0;
+ }
+ if ((strncmp(buf, cwdpath, cwdpathlen) != 0) || (buf[cwdpathlen] != '\0' && buf[cwdpathlen] != '/')) {
+ *p = '/';
+ syswarn(1, 0, "Attempt to write file `%s' that resolves into "
+ "`%s/%s' outside current working directory `%s' ignored",
+ arcn->name, buf, p + 1, cwdpath);
+ return -1;
+ }
+ *p = '/';
+ return 0;
+}
+
+/*
+ * list()
+ * list the contents of an archive which match user supplied pattern(s)
+ * (no pattern matches all).
+ */
+
+void
+list(void)
+{
+ ARCHD *arcn;
+ int res;
+ ARCHD archd;
+ time_t now;
+
+ arcn = &archd;
+ /*
+ * figure out archive type; pass any format specific options to the
+ * archive option processing routine; call the format init routine. We
+ * also save current time for ls_list() so we do not make a system
+ * call for each file we need to print. If verbose (vflag) start up
+ * the name and group caches.
+ */
+ if ((get_arc() < 0) || ((*frmt->options)() < 0) ||
+ ((*frmt->st_rd)() < 0))
+ return;
+
+ if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0)))
+ return;
+
+ now = time(NULL);
+
+ /*
+ * step through the archive until the format says it is done
+ */
+ while (next_head(arcn) == 0) {
+ if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) {
+ /*
+ * we need to read, to get the real filename
+ */
+ off_t cnt;
+ if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF
+ ? -1 : -2, &cnt))
+ (void)rd_skip(cnt + arcn->pad);
+ continue;
+ }
+
+ /*
+ * check for pattern, and user specified options match.
+ * When all patterns are matched we are done.
+ */
+ if ((res = pat_match(arcn)) < 0)
+ break;
+
+ if ((res == 0) && (sel_chk(arcn) == 0)) {
+ /*
+ * pattern resulted in a selected file
+ */
+ if (pat_sel(arcn) < 0)
+ break;
+
+ /*
+ * modify the name as requested by the user if name
+ * survives modification, do a listing of the file
+ */
+ if ((res = mod_name(arcn)) < 0)
+ break;
+ if (res == 0)
+ ls_list(arcn, now, stdout);
+ }
+
+ /*
+ * skip to next archive format header using values calculated
+ * by the format header read routine
+ */
+ if (rd_skip(arcn->skip + arcn->pad) == 1)
+ break;
+ }
+
+ /*
+ * all done, let format have a chance to cleanup, and make sure that
+ * the patterns supplied by the user were all matched
+ */
+ (void)(*frmt->end_rd)();
+ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
+ ar_close();
+ pat_chk();
+}
+
+/*
+ * extract()
+ * extract the member(s) of an archive as specified by user supplied
+ * pattern(s) (no patterns extracts all members)
+ */
+
+void
+extract(void)
+{
+ ARCHD *arcn;
+ int res;
+ off_t cnt;
+ ARCHD archd;
+ 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;
+ LIST_ENTRY(copyfile_list_entry_t) link;
+ } *cle;
+
+ LIST_INIT(&copyfile_list);
+#endif
+
+ arcn = &archd;
+ /*
+ * figure out archive type; pass any format specific options to the
+ * archive option processing routine; call the format init routine;
+ * start up the directory modification time and access mode database
+ */
+ if ((get_arc() < 0) || ((*frmt->options)() < 0) ||
+ ((*frmt->st_rd)() < 0) || (dir_start() < 0))
+ return;
+
+ /*
+ * When we are doing interactive rename, we store the mapping of names
+ * so we can fix up hard links files later in the archive.
+ */
+ if (iflag && (name_start() < 0))
+ return;
+
+ now = time(NULL);
+
+ /*
+ * step through each entry on the archive until the format read routine
+ * says it is done
+ */
+ while (next_head(arcn) == 0) {
+ if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) {
+ /*
+ * we need to read, to get the real filename
+ */
+ if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF
+ ? -1 : -2, &cnt))
+ (void)rd_skip(cnt + arcn->pad);
+ continue;
+ }
+
+ /*
+ * check for pattern, and user specified options match. When
+ * all the patterns are matched we are done
+ */
+ if ((res = pat_match(arcn)) < 0)
+ break;
+
+ if ((res > 0) || (sel_chk(arcn) != 0)) {
+ /*
+ * file is not selected. skip past any file data and
+ * padding and go back for the next archive member
+ */
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+
+ /*
+ * with -u or -D only extract when the archive member is newer
+ * than the file with the same name in the file system (no
+ * test of being the same type is required).
+ * NOTE: this test is done BEFORE name modifications as
+ * specified by pax. this operation can be confusing to the
+ * user who might expect the test to be done on an existing
+ * file AFTER the name mod. In honesty the pax spec is probably
+ * flawed in this respect.
+ */
+ if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) {
+ if (uflag && Dflag) {
+ if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+ (arcn->sb.st_ctime <= sb.st_ctime)) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ } else if (Dflag) {
+ if (arcn->sb.st_ctime <= sb.st_ctime) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ } else if (arcn->sb.st_mtime <= sb.st_mtime) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ }
+
+ /*
+ * this archive member is now been selected. modify the name.
+ */
+ if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0))
+ break;
+ if (res > 0) {
+ /*
+ * a bad name mod, skip and purge name from link table
+ */
+ purg_lnk(arcn);
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+
+ /*
+ * Non standard -Y and -Z flag. When the existing file is
+ * same age or newer skip
+ */
+ if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) {
+ if (Yflag && Zflag) {
+ if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+ (arcn->sb.st_ctime <= sb.st_ctime)) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ } else if (Yflag) {
+ if (arcn->sb.st_ctime <= sb.st_ctime) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ } else if (arcn->sb.st_mtime <= sb.st_mtime) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+ }
+
+ if (vflag) {
+ if (vflag > 1)
+ ls_list(arcn, now, listf);
+ else {
+ (void)safe_print(arcn->name, listf);
+ vfpart = 1;
+ }
+ }
+
+ /*
+ * if required, chdir around.
+ */
+ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL))
+ dochdir(arcn->pat->chdname);
+
+ if (secure && path_check(arcn, 0) != 0) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ continue;
+ }
+
+ /*
+ * all ok, extract this member based on type
+ */
+ if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) {
+ /*
+ * process archive members that are not regular files.
+ * throw out padding and any data that might follow the
+ * header (as determined by the format).
+ */
+ if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+ res = lnk_creat(arcn);
+ else
+ res = node_creat(arcn);
+
+ (void)rd_skip(arcn->skip + arcn->pad);
+ if (res < 0)
+ purg_lnk(arcn);
+
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ continue;
+ }
+ /*
+ * we have a file with data here. If we can not create it, skip
+ * over the data and purge the name from hard link table
+ */
+ if ((fd = file_creat(arcn)) < 0) {
+ (void)rd_skip(arcn->skip + arcn->pad);
+ purg_lnk(arcn);
+ continue;
+ }
+ /*
+ * extract the file from the archive and skip over padding and
+ * any unprocessed data
+ */
+ res = (*frmt->rd_data)(arcn, fd, &cnt);
+ file_close(arcn, fd);
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ 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->dst, "%s/%s",
+ dirname(arcn->name), basename(arcn->name) + 2) != -1) {
+ LIST_INSERT_HEAD(&copyfile_list, cle, link);
+ } else {
+ free(cle->src);
+ }
+ }
+#endif
+ /*
+ * if required, chdir around.
+ */
+ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL))
+ fdochdir(cwdfd);
+ }
+#ifdef __APPLE__
+ LIST_FOREACH(cle, &copyfile_list, link)
+ {
+ if(copyfile_disable || copyfile(cle->src, cle->dst, NULL,
+ COPYFILE_UNPACK | COPYFILE_XATTR | COPYFILE_ACL)) {
+ if (!copyfile_disable) {
+ syswarn(1, errno, "Unable to set metadata on %s", cle->dst);
+ }
+ } else {
+ unlink(cle->src);
+ }
+ free(cle->dst);
+ free(cle->src);
+ }
+#endif
+
+ /*
+ * all done, restore directory modes and times as required; make sure
+ * all patterns supplied by the user were matched; block off signals
+ * to avoid chance for multiple entry into the cleanup code.
+ */
+ (void)(*frmt->end_rd)();
+ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
+ ar_close();
+ proc_dir();
+ pat_chk();
+}
+
+/*
+ * wr_archive()
+ * Write an archive. used in both creating a new archive and appends on
+ * previously written archive.
+ */
+
+static void
+wr_archive(ARCHD *arcn, int is_app)
+{
+ int res;
+ int hlk;
+ int wr_one;
+ off_t cnt;
+ int (*wrf)(ARCHD *);
+ 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
+ * that detects them.
+ */
+ if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0))
+ return;
+
+ if (hlk && want_linkdata) hlk=0; /* Treat hard links as individual files */
+
+ /*
+ * start up the file traversal code and format specific write
+ */
+ if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0))
+ return;
+ wrf = frmt->wr;
+
+ /*
+ * When we are doing interactive rename, we store the mapping of names
+ * so we can fix up hard links files later in the archive.
+ */
+ if (iflag && (name_start() < 0))
+ return;
+
+ /*
+ * if this is not append, and there are no files, we do not write a
+ * trailer
+ */
+ wr_one = is_app;
+
+ now = time(NULL);
+
+ /*
+ * while there are files to archive, process them one at at time
+ */
+ while (next_file(arcn) == 0) {
+ /*
+ * check if this file meets user specified options match.
+ */
+ if (sel_chk(arcn) != 0) {
+ ftree_notsel();
+ continue;
+ }
+ fd = -1;
+ if (uflag) {
+ /*
+ * only archive if this file is newer than a file with
+ * the same name that is already stored on the archive
+ */
+ if ((res = chk_ftime(arcn)) < 0)
+ break;
+ if (res > 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)) {
+ char *tmpdir = P_tmpdir, *TMPDIR;
+ int fd_src, fd_dst;
+
+ if (!issetugid() && (TMPDIR = getenv("TMPDIR"))) {
+ tmpdir = TMPDIR;
+ }
+ asprintf(&md_fname, "%s%s", tmpdir, "/pax-md-XXXXXX");
+ if (!md_fname) {
+ syswarn(1, errno, "Unable to create temporary file name");
+ return;
+ }
+ 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;
+ fd_dst = mkstemp(md_fname);
+ if (fd_dst >= 0) {
+ fd_src = open(arcn->name, O_RDONLY, 0);
+ if (fd_src < 0) {
+ syswarn(1, errno, "Unable to open %s for reading", arcn->name);
+ close(fd_dst);
+ unlink(md_fname);
+ free(md_fname);
+ md_fname = NULL;
+ goto next;
+ }
+ if(fcopyfile(fd_src, fd_dst, NULL,
+ COPYFILE_PACK | COPYFILE_XATTR | COPYFILE_ACL) < 0) {
+ syswarn(1, errno,
+ "Unable to preserve metadata on %s", arcn->name);
+ close(fd_src);
+ close(fd_dst);
+ unlink(md_fname);
+ free(md_fname);
+ md_fname = NULL;
+ goto next;
+ }
+ close(fd_src);
+ fstat(fd_dst, &arcn->sb);
+ close(fd_dst);
+ } else {
+ syswarn(1, errno, "Unable to create temporary file %s", md_fname);
+ free(md_fname);
+ goto next;
+ }
+ arcn->skip = arcn->sb.st_size;
+
+ if (!strncmp(dirname(arcn->name), ".", 2)) {
+ snprintf(arcn->name, sizeof(arcn->name),
+ "._%s", basename(arcn->name));
+ } else {
+ snprintf(arcn->name, sizeof(arcn->name),
+ "%s/._%s",
+ dirname(arcn->name), basename(arcn->name));
+ }
+ arcn->nlen = strlen(arcn->name);
+ arcn->org_name = arcn->name;
+ metadata = 1;
+ } else if (metadata) {
+next:
+ metadata = 0;
+ memcpy(arcn, &arcn_copy, sizeof(ARCHD));
+ strncpy(arcn->name, arcn_copy_name, PAXPATHLEN+1);
+ }
+#endif /* __APPLE__ */
+
+ fd = -1;
+
+ /*
+ * this file is considered selected now. see if this is a hard
+ * link to a file already stored
+ */
+ ftree_sel(arcn);
+ if (hlk && (chk_lnk(arcn) < 0)) {
+ if (md_fname) {
+ unlink(md_fname);
+ free(md_fname);
+ md_fname = NULL;
+ }
+ break;
+ }
+
+ if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) ||
+ (arcn->type == PAX_CTG)) {
+ /*
+ * we will have to read this file. by opening it now we
+ * can avoid writing a header to the archive for a file
+ * 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);
+ md_fname = NULL;
+ } else
+ fd = open(arcn->org_name, O_RDONLY, 0);
+ if (fd < 0) {
+#else /* !__APPLE__ */
+ if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+#endif /* __APPLE__ */
+ syswarn(1,errno, "Unable to open %s to read",
+ arcn->org_name);
+ purg_lnk(arcn);
+ continue;
+ }
+ }
+
+ /*
+ * Now modify the name as requested by the user
+ */
+ if ((res = mod_name(arcn)) < 0) {
+ /*
+ * name modification says to skip this file, close the
+ * file and purge link table entry
+ */
+ rdfile_close(arcn, &fd);
+ purg_lnk(arcn);
+ break;
+ }
+
+ if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) {
+ /*
+ * unable to obtain the crc we need, close the file,
+ * purge link table entry
+ */
+ rdfile_close(arcn, &fd);
+ purg_lnk(arcn);
+ continue;
+ }
+
+ if (vflag) {
+ if (vflag > 1)
+ ls_list(arcn, now, listf);
+ else {
+ (void)safe_print(arcn->name, listf);
+ vfpart = 1;
+ }
+ }
+ ++flcnt;
+
+ /*
+ * looks safe to store the file, have the format specific
+ * routine write routine store the file header on the archive
+ */
+ if ((res = (*wrf)(arcn)) < 0) {
+ rdfile_close(arcn, &fd);
+ break;
+ }
+ wr_one = 1;
+ if (res > 0) {
+ /*
+ * format write says no file data needs to be stored
+ * so we are done messing with this file
+ */
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ rdfile_close(arcn, &fd);
+ continue;
+ }
+
+ /*
+ * Add file data to the archive, quit on write error. if we
+ * cannot write the entire file contents to the archive we
+ * must pad the archive to replace the missing file data
+ * (otherwise during an extract the file header for the file
+ * which FOLLOWS this one will not be where we expect it to
+ * be).
+ */
+ res = (*frmt->wr_data)(arcn, fd, &cnt);
+ rdfile_close(arcn, &fd);
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ if (res < 0)
+ break;
+
+ /*
+ * pad as required, cnt is number of bytes not written
+ */
+ if (((cnt > 0) && (wr_skip(cnt) < 0)) ||
+ ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0)))
+ break;
+#ifdef __APPLE__
+ if (metadata)
+ goto next;
+#endif /* __APPLE__ */
+ }
+
+ /*
+ * tell format to write trailer; pad to block boundary; reset directory
+ * mode/access times, and check if all patterns supplied by the user
+ * were matched. block off signals to avoid chance for multiple entry
+ * into the cleanup code
+ */
+ if (wr_one) {
+ (*frmt->end_wr)();
+ wr_fin();
+ }
+ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
+ ar_close();
+ if (tflag)
+ proc_dir();
+ ftree_chk();
+}
+
+/*
+ * append()
+ * Add file to previously written archive. Archive format specified by the
+ * user must agree with archive. The archive is read first to collect
+ * modification times (if -u) and locate the archive trailer. The archive
+ * is positioned in front of the record with the trailer and wr_archive()
+ * is called to add the new members.
+ * PAX IMPLEMENTATION DETAIL NOTE:
+ * -u is implemented by adding the new members to the end of the archive.
+ * Care is taken so that these do not end up as links to the older
+ * version of the same file already stored in the archive. It is expected
+ * when extraction occurs these newer versions will over-write the older
+ * ones stored "earlier" in the archive (this may be a bad assumption as
+ * it depends on the implementation of the program doing the extraction).
+ * It is really difficult to splice in members without either re-writing
+ * the entire archive (from the point were the old version was), or having
+ * assistance of the format specification in terms of a special update
+ * header that invalidates a previous archive record. The POSIX spec left
+ * the method used to implement -u unspecified. This pax is able to
+ * over write existing files that it creates.
+ */
+
+void
+append(void)
+{
+ ARCHD *arcn;
+ int res;
+ ARCHD archd;
+ const FSUB *orgfrmt;
+ int udev;
+ off_t tlen;
+
+ arcn = &archd;
+ orgfrmt = frmt;
+
+ /*
+ * Do not allow an append operation if the actual archive is of a
+ * different format than the user specified format.
+ */
+ if (get_arc() < 0)
+ return;
+ if ((orgfrmt != NULL) && (orgfrmt != frmt)) {
+ paxwarn(1, "Cannot mix current archive format %s with %s",
+ frmt->name, orgfrmt->name);
+ return;
+ }
+
+ /*
+ * pass the format any options and start up format
+ */
+ if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0))
+ return;
+
+ /*
+ * if we only are adding members that are newer, we need to save the
+ * mod times for all files we see.
+ */
+ if (uflag && (ftime_start() < 0))
+ return;
+
+ /*
+ * some archive formats encode hard links by recording the device and
+ * file serial number (inode) but copy the file anyway (multiple times)
+ * to the archive. When we append, we run the risk that newly added
+ * files may have the same device and inode numbers as those recorded
+ * on the archive but during a previous run. If this happens, when the
+ * archive is extracted we get INCORRECT hard links. We avoid this by
+ * remapping the device numbers so that newly added files will never
+ * use the same device number as one found on the archive. remapping
+ * allows new members to safely have links among themselves. remapping
+ * also avoids problems with file inode (serial number) truncations
+ * when the inode number is larger than storage space in the archive
+ * header. See the remap routines for more details.
+ */
+ if ((udev = frmt->udev) && (dev_start() < 0))
+ return;
+
+ /*
+ * reading the archive may take a long time. If verbose tell the user
+ */
+ if (vflag) {
+ (void)fprintf(listf,
+ "%s: Reading archive to position at the end...", argv0);
+ vfpart = 1;
+ }
+
+ /*
+ * step through the archive until the format says it is done
+ */
+ while (next_head(arcn) == 0) {
+ /*
+ * check if this file meets user specified options.
+ */
+ if (sel_chk(arcn) != 0) {
+ if (rd_skip(arcn->skip + arcn->pad) == 1)
+ break;
+ continue;
+ }
+
+ if (uflag) {
+ /*
+ * see if this is the newest version of this file has
+ * already been seen, if so skip.
+ */
+ if ((res = chk_ftime(arcn)) < 0)
+ break;
+ if (res > 0) {
+ if (rd_skip(arcn->skip + arcn->pad) == 1)
+ break;
+ continue;
+ }
+ }
+
+ /*
+ * Store this device number. Device numbers seen during the
+ * read phase of append will cause newly appended files with a
+ * device number seen in the old part of the archive to be
+ * remapped to an unused device number.
+ */
+ if ((udev && (add_dev(arcn) < 0)) ||
+ (rd_skip(arcn->skip + arcn->pad) == 1))
+ break;
+ }
+
+ /*
+ * done, finish up read and get the number of bytes to back up so we
+ * can add new members. The format might have used the hard link table,
+ * purge it.
+ */
+ tlen = (*frmt->end_rd)();
+ lnk_end();
+
+ /*
+ * try to position for write, if this fails quit. if any error occurs,
+ * we will refuse to write
+ */
+ if (appnd_start(tlen) < 0)
+ return;
+
+ /*
+ * tell the user we are done reading.
+ */
+ if (vflag && vfpart) {
+ (void)fputs("done.\n", listf);
+ vfpart = 0;
+ }
+
+ /*
+ * go to the writing phase to add the new members
+ */
+ wr_archive(arcn, 1);
+}
+
+/*
+ * archive()
+ * write a new archive
+ */
+
+void
+archive(void)
+{
+ ARCHD archd;
+
+ /*
+ * if we only are adding members that are newer, we need to save the
+ * mod times for all files; set up for writing; pass the format any
+ * options write the archive
+ */
+ if ((uflag && (ftime_start() < 0)) || (wr_start() < 0))
+ return;
+ if ((*frmt->options)() < 0)
+ return;
+
+ wr_archive(&archd, 0);
+}
+
+/*
+ * copy()
+ * copy files from one part of the file system to another. this does not
+ * use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an
+ * archive was written and then extracted in the destination directory
+ * (except the files are forced to be under the destination directory).
+ */
+
+void
+copy(void)
+{
+ ARCHD *arcn;
+ int res;
+ int fddest;
+ char *dest_pt;
+ int dlen;
+ int drem;
+ int fdsrc = -1;
+ struct stat sb;
+ ARCHD archd;
+ char dirbuf[PAXPATHLEN+1];
+
+ arcn = &archd;
+ if (frmt && strcmp(frmt->name, NM_PAX)==0) {
+ /* Copy using pax format: must check if any -o options */
+ if ((*frmt->options)() < 0)
+ return;
+ if (pax_invalid_action==0)
+ pax_invalid_action = PAX_INVALID_ACTION_BYPASS;
+ }
+ /*
+ * set up the destination dir path and make sure it is a directory. We
+ * make sure we have a trailing / on the destination
+ */
+ dlen = strlcpy(dirbuf, dirptr, sizeof(dirbuf));
+ if (dlen >= sizeof(dirbuf) ||
+ (dlen == sizeof(dirbuf) - 1 && dirbuf[dlen - 1] != '/')) {
+ paxwarn(1, "directory name is too long %s", dirptr);
+ return;
+ }
+ dest_pt = dirbuf + dlen;
+ if (*(dest_pt-1) != '/') {
+ *dest_pt++ = '/';
+ *dest_pt = '\0';
+ ++dlen;
+ }
+ drem = PAXPATHLEN - dlen;
+
+ if (stat(dirptr, &sb) < 0) {
+ syswarn(1, errno, "Cannot access destination directory %s",
+ dirptr);
+ return;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ paxwarn(1, "Destination is not a directory %s", dirptr);
+ return;
+ }
+
+ /*
+ * start up the hard link table; file traversal routines and the
+ * modification time and access mode database
+ */
+ if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0))
+ return;
+
+ /*
+ * When we are doing interactive rename, we store the mapping of names
+ * so we can fix up hard links files later in the archive.
+ */
+ if (iflag && (name_start() < 0))
+ return;
+
+ /*
+ * set up to cp file trees
+ */
+ cp_start();
+
+ /*
+ * while there are files to archive, process them
+ */
+ while (next_file(arcn) == 0) {
+ fdsrc = -1;
+
+ /*
+ * Fill in arcn from any pax options
+ */
+ adjust_copy_for_pax_options(arcn);
+
+ /*
+ * check if this file meets user specified options
+ */
+ if (sel_chk(arcn) != 0) {
+ ftree_notsel();
+ continue;
+ }
+
+ /*
+ * if there is already a file in the destination directory with
+ * the same name and it is newer, skip the one stored on the
+ * archive.
+ * NOTE: this test is done BEFORE name modifications as
+ * specified by pax. this can be confusing to the user who
+ * might expect the test to be done on an existing file AFTER
+ * the name mod. In honesty the pax spec is probably flawed in
+ * this respect
+ */
+ if (uflag || Dflag) {
+ /*
+ * create the destination name
+ */
+ if (strlcpy(dest_pt, arcn->name + (*arcn->name == '/'),
+ drem + 1) > drem) {
+ paxwarn(1, "Destination pathname too long %s",
+ arcn->name);
+ continue;
+ }
+
+ /*
+ * if existing file is same age or newer skip
+ */
+ res = lstat(dirbuf, &sb);
+ *dest_pt = '\0';
+
+ if (res == 0) {
+ if (uflag && Dflag) {
+ if ((arcn->sb.st_mtime<=sb.st_mtime) &&
+ (arcn->sb.st_ctime<=sb.st_ctime))
+ continue;
+ } else if (Dflag) {
+ if (arcn->sb.st_ctime <= sb.st_ctime)
+ continue;
+ } else if (arcn->sb.st_mtime <= sb.st_mtime)
+ continue;
+ }
+ }
+
+ /*
+ * this file is considered selected. See if this is a hard link
+ * to a previous file; modify the name as requested by the
+ * user; set the final destination.
+ */
+ ftree_sel(arcn);
+ if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0))
+ break;
+ if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) {
+ /*
+ * skip file, purge from link table
+ */
+ purg_lnk(arcn);
+ continue;
+ }
+
+ /*
+ * Non standard -Y and -Z flag. When the existing file is
+ * same age or newer skip
+ */
+ if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) {
+ if (Yflag && Zflag) {
+ if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+ (arcn->sb.st_ctime <= sb.st_ctime))
+ continue;
+ } else if (Yflag) {
+ if (arcn->sb.st_ctime <= sb.st_ctime)
+ continue;
+ } else if (arcn->sb.st_mtime <= sb.st_mtime)
+ continue;
+ }
+
+ if (vflag) {
+ (void)safe_print(arcn->name, listf);
+ vfpart = 1;
+ }
+ ++flcnt;
+
+ /*
+ * try to create a hard link to the src file if requested
+ * but make sure we are not trying to overwrite ourselves.
+ */
+ if (lflag)
+ res = cross_lnk(arcn);
+ else
+ res = chk_same(arcn);
+ if (res <= 0) {
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ continue;
+ }
+
+ /*
+ * have to create a new file
+ */
+ if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) {
+ /*
+ * create a link or special file
+ */
+ if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+ res = lnk_creat(arcn);
+ else
+ res = node_creat(arcn);
+ if (res < 0)
+ purg_lnk(arcn);
+#ifdef __APPLE__
+ if (res >= 0 &&
+ arcn->type == PAX_DIR &&
+ copyfile(arcn->org_name, arcn->name, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0)
+ paxwarn(1, "Directory %s had metadata that could not be copied: %s", arcn->org_name, strerror(errno));
+#endif /* __APPLE__ */
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ continue;
+ }
+
+ /*
+ * have to copy a regular file to the destination directory.
+ * first open source file and then create the destination file
+ */
+ if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+ syswarn(1, errno, "Unable to open %s to read",
+ arcn->org_name);
+ purg_lnk(arcn);
+ continue;
+ }
+ if ((fddest = file_creat(arcn)) < 0) {
+ rdfile_close(arcn, &fdsrc);
+ purg_lnk(arcn);
+ continue;
+ }
+
+ /*
+ * copy source file data to the destination file
+ */
+ cp_file(arcn, fdsrc, fddest);
+#ifdef __APPLE__
+ /* do this before file close so that mtimes are correct regardless */
+ if (getenv(COPYFILE_DISABLE_VAR) == NULL) {
+ if (fcopyfile(fdsrc, fddest, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0)
+ paxwarn(1, "File %s had metadata that could not be copied: %s", arcn->org_name,
+ strerror(errno));
+ }
+#endif
+ file_close(arcn, fddest);
+ rdfile_close(arcn, &fdsrc);
+
+ if (vflag && vfpart) {
+ (void)putc('\n', listf);
+ vfpart = 0;
+ }
+ }
+
+ /*
+ * restore directory modes and times as required; make sure all
+ * patterns were selected block off signals to avoid chance for
+ * multiple entry into the cleanup code.
+ */
+ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
+ ar_close();
+ proc_dir();
+ ftree_chk();
+}
+
+/*
+ * next_head()
+ * try to find a valid header in the archive. Uses format specific
+ * routines to extract the header and id the trailer. Trailers may be
+ * located within a valid header or in an invalid header (the location
+ * is format specific. The inhead field from the option table tells us
+ * where to look for the trailer).
+ * We keep reading (and resyncing) until we get enough contiguous data
+ * to check for a header. If we cannot find one, we shift by a byte
+ * add a new byte from the archive to the end of the buffer and try again.
+ * If we get a read error, we throw out what we have (as we must have
+ * contiguous data) and start over again.
+ * ASSUMED: headers fit within a BLKMULT header.
+ * Return:
+ * 0 if we got a header, -1 if we are unable to ever find another one
+ * (we reached the end of input, or we reached the limit on retries. see
+ * the specs for rd_wrbuf() for more details)
+ */
+
+static int
+next_head(ARCHD *arcn)
+{
+ int ret;
+ char *hdend;
+ int res;
+ int shftsz;
+ int hsz;
+ int in_resync = 0; /* set when we are in resync mode */
+ int cnt = 0; /* counter for trailer function */
+ int first = 1; /* on 1st read, EOF isn't premature. */
+
+ /*
+ * set up initial conditions, we want a whole frmt->hsz block as we
+ * have no data yet.
+ */
+ res = hsz = frmt->hsz;
+ hdend = hdbuf;
+ shftsz = hsz - 1;
+ for (;;) {
+ /*
+ * keep looping until we get a contiguous FULL buffer
+ * (frmt->hsz is the proper size)
+ */
+ for (;;) {
+ if ((ret = rd_wrbuf(hdend, res)) == res)
+ break;
+
+ /*
+ * If we read 0 bytes (EOF) from an archive when we
+ * expect to find a header, we have stepped upon
+ * an archive without the customary block of zeroes
+ * end marker. It's just stupid to error out on
+ * them, so exit gracefully.
+ */
+ if (first && ret == 0)
+ return(-1);
+ first = 0;
+
+ /*
+ * some kind of archive read problem, try to resync the
+ * storage device, better give the user the bad news.
+ */
+ if ((ret == 0) || (rd_sync() < 0)) {
+ paxwarn(1,"Premature end of file on archive read");
+ return(-1);
+ }
+ if (!in_resync) {
+ if (act == APPND) {
+ paxwarn(1,
+ "Archive I/O error, cannot continue");
+ return(-1);
+ }
+ paxwarn(1,"Archive I/O error. Trying to recover.");
+ ++in_resync;
+ }
+
+ /*
+ * oh well, throw it all out and start over
+ */
+ res = hsz;
+ hdend = hdbuf;
+ }
+
+ /*
+ * ok we have a contiguous buffer of the right size. Call the
+ * format read routine. If this was not a valid header and this
+ * format stores trailers outside of the header, call the
+ * format specific trailer routine to check for a trailer. We
+ * have to watch out that we do not mis-identify file data or
+ * block padding as a header or trailer. Format specific
+ * trailer functions must NOT check for the trailer while we
+ * are running in resync mode. Some trailer functions may tell
+ * us that this block cannot contain a valid header either, so
+ * we then throw out the entire block and start over.
+ */
+ if ((*frmt->rd)(arcn, hdbuf) == 0)
+ break;
+
+ if (!frmt->inhead) {
+ /*
+ * this format has trailers outside of valid headers
+ */
+ if ((ret = (*frmt->trail)(arcn,hdbuf,in_resync,&cnt)) == 0){
+ /*
+ * valid trailer found, drain input as required
+ */
+ ar_drain();
+ return(-1);
+ }
+
+ if (ret == 1) {
+ /*
+ * we are in resync and we were told to throw
+ * the whole block out because none of the
+ * bytes in this block can be used to form a
+ * valid header
+ */
+ res = hsz;
+ hdend = hdbuf;
+ continue;
+ }
+ }
+
+ /*
+ * Brute force section.
+ * not a valid header. We may be able to find a header yet. So
+ * we shift over by one byte, and set up to read one byte at a
+ * time from the archive and place it at the end of the buffer.
+ * We will keep moving byte at a time until we find a header or
+ * get a read error and have to start over.
+ */
+ if (!in_resync) {
+ if (act == APPND) {
+ paxwarn(1,"Unable to append, archive header flaw");
+ return(-1);
+ }
+ paxwarn(1,"Invalid header, starting valid header search.");
+ ++in_resync;
+ }
+ memmove(hdbuf, hdbuf+1, shftsz);
+ res = 1;
+ hdend = hdbuf + shftsz;
+ }
+
+ /*
+ * ok got a valid header, check for trailer if format encodes it in the
+ * the header. NOTE: the parameters are different than trailer routines
+ * which encode trailers outside of the header!
+ */
+ if (frmt->inhead && ((*frmt->trail)(arcn,NULL,0,NULL) == 0)) {
+ /*
+ * valid trailer found, drain input as required
+ */
+ ar_drain();
+ return(-1);
+ }
+
+ ++flcnt;
+ return(0);
+}
+
+/*
+ * get_arc()
+ * Figure out what format an archive is. Handles archive with flaws by
+ * brute force searches for a legal header in any supported format. The
+ * format id routines have to be careful to NOT mis-identify a format.
+ * ASSUMED: headers fit within a BLKMULT header.
+ * Return:
+ * 0 if archive found -1 otherwise
+ */
+
+static int
+get_arc(void)
+{
+ int i;
+ int hdsz = 0;
+ int res;
+ int minhd = BLKMULT;
+ char *hdend;
+ int notice = 0;
+
+ /*
+ * find the smallest header size in all archive formats and then set up
+ * to read the archive.
+ */
+ for (i = 0; ford[i] >= 0; ++i) {
+ if (fsub[ford[i]].hsz < minhd)
+ minhd = fsub[ford[i]].hsz;
+ }
+ if (rd_start() < 0)
+ return(-1);
+ res = BLKMULT;
+ hdsz = 0;
+ hdend = hdbuf;
+ for (;;) {
+ for (;;) {
+ /*
+ * fill the buffer with at least the smallest header
+ */
+ i = rd_wrbuf(hdend, res);
+ if (i > 0)
+ hdsz += i;
+ if (hdsz >= minhd)
+ break;
+
+ /*
+ * if we cannot recover from a read error quit
+ */
+ if ((i == 0) || (rd_sync() < 0))
+ goto out;
+
+ /*
+ * when we get an error none of the data we already
+ * have can be used to create a legal header (we just
+ * got an error in the middle), so we throw it all out
+ * and refill the buffer with fresh data.
+ */
+ res = BLKMULT;
+ hdsz = 0;
+ hdend = hdbuf;
+ if (!notice) {
+ if (act == APPND)
+ return(-1);
+ paxwarn(1,"Cannot identify format. Searching...");
+ ++notice;
+ }
+ }
+
+ /*
+ * we have at least the size of the smallest header in any
+ * archive format. Look to see if we have a match. The array
+ * ford[] is used to specify the header id order to reduce the
+ * chance of incorrectly id'ing a valid header (some formats
+ * may be subsets of each other and the order would then be
+ * important).
+ */
+ for (i = 0; ford[i] >= 0; ++i) {
+ if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0)
+ continue;
+ frmt = &(fsub[ford[i]]);
+ /*
+ * yuck, to avoid slow special case code in the extract
+ * routines, just push this header back as if it was
+ * not seen. We have left extra space at start of the
+ * buffer for this purpose. This is a bit ugly, but
+ * adding all the special case code is far worse.
+ */
+ pback(hdbuf, hdsz);
+ return(0);
+ }
+
+ /*
+ * We have a flawed archive, no match. we start searching, but
+ * we never allow additions to flawed archives
+ */
+ if (!notice) {
+ if (act == APPND)
+ return(-1);
+ paxwarn(1, "Cannot identify format. Searching...");
+ ++notice;
+ }
+
+ /*
+ * brute force search for a header that we can id.
+ * we shift through byte at a time. this is slow, but we cannot
+ * determine the nature of the flaw in the archive in a
+ * portable manner
+ */
+ if (--hdsz > 0) {
+ memmove(hdbuf, hdbuf+1, hdsz);
+ res = BLKMULT - hdsz;
+ hdend = hdbuf + hdsz;
+ } else {
+ res = BLKMULT;
+ hdend = hdbuf;
+ hdsz = 0;
+ }
+ }
+
+ out:
+ /*
+ * we cannot find a header, bow, apologize and quit
+ */
+ paxwarn(1, "Sorry, unable to determine archive format.");
+ return(-1);
+}
diff --git a/file_cmds/pax/buf_subs.c b/file_cmds/pax/buf_subs.c
new file mode 100644
index 0000000..e56e822
--- /dev/null
+++ b/file_cmds/pax/buf_subs.c
@@ -0,0 +1,994 @@
+/* $OpenBSD: buf_subs.c,v 1.21 2005/11/09 19:59:06 otto Exp $ */
+/* $NetBSD: buf_subs.c,v 1.5 1995/03/21 09:07:08 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: buf_subs.c,v 1.21 2005/11/09 19:59:06 otto Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pax.h"
+#include "extern.h"
+
+/*
+ * routines which implement archive and file buffering
+ */
+
+#define MINFBSZ 512 /* default block size for hole detect */
+#define MAXFLT 10 /* default media read error limit */
+
+/*
+ * Need to change bufmem to dynamic allocation when the upper
+ * limit on blocking size is removed (though that will violate pax spec)
+ * MAXBLK define and tests will also need to be updated.
+ */
+static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */
+static char *buf; /* normal start of i/o buffer */
+static char *bufend; /* end or last char in i/o buffer */
+static char *bufpt; /* read/write point in i/o buffer */
+int blksz = MAXBLK; /* block input/output size in bytes */
+int wrblksz; /* user spec output size in bytes */
+int maxflt = MAXFLT; /* MAX consecutive media errors */
+int rdblksz; /* first read blksize (tapes only) */
+off_t wrlimit; /* # of bytes written per archive vol */
+off_t wrcnt; /* # of bytes written on current vol */
+off_t rdcnt; /* # of bytes read on current vol */
+
+/*
+ * wr_start()
+ * set up the buffering system to operate in a write mode
+ * Return:
+ * 0 if ok, -1 if the user specified write block size violates pax spec
+ */
+
+int
+wr_start(void)
+{
+ buf = &(bufmem[BLKMULT]);
+ /*
+ * Check to make sure the write block size meets pax specs. If the user
+ * does not specify a blocksize, we use the format default blocksize.
+ * We must be picky on writes, so we do not allow the user to create an
+ * archive that might be hard to read elsewhere. If all ok, we then
+ * open the first archive volume
+ */
+ if (!wrblksz)
+ wrblksz = frmt->bsz;
+ if (wrblksz > MAXBLK) {
+ paxwarn(1, "Write block size of %d too large, maximium is: %d",
+ wrblksz, MAXBLK);
+ return(-1);
+ }
+ if (wrblksz % BLKMULT) {
+ paxwarn(1, "Write block size of %d is not a %d byte multiple",
+ wrblksz, BLKMULT);
+ return(-1);
+ }
+ if (wrblksz > MAXBLK_POSIX) {
+ paxwarn(0, "Write block size of %d larger than POSIX max %d, archive may not be portable",
+ wrblksz, MAXBLK_POSIX);
+ return(-1);
+ }
+
+ /*
+ * we only allow wrblksz to be used with all archive operations
+ */
+ blksz = rdblksz = wrblksz;
+ if ((ar_open(arcname) < 0) && (ar_next() < 0))
+ return(-1);
+ wrcnt = 0;
+ bufend = buf + wrblksz;
+ bufpt = buf;
+ return(0);
+}
+
+/*
+ * rd_start()
+ * set up buffering system to read an archive
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+rd_start(void)
+{
+ /*
+ * leave space for the header pushback (see get_arc()). If we are
+ * going to append and user specified a write block size, check it
+ * right away
+ */
+ buf = &(bufmem[BLKMULT]);
+ if ((act == APPND) && wrblksz) {
+ if (wrblksz > MAXBLK) {
+ paxwarn(1,"Write block size %d too large, maximium is: %d",
+ wrblksz, MAXBLK);
+ return(-1);
+ }
+ if (wrblksz % BLKMULT) {
+ paxwarn(1, "Write block size %d is not a %d byte multiple",
+ wrblksz, BLKMULT);
+ return(-1);
+ }
+ }
+
+ /*
+ * open the archive
+ */
+ if ((ar_open(arcname) < 0) && (ar_next() < 0))
+ return(-1);
+ bufend = buf + rdblksz;
+ bufpt = bufend;
+ rdcnt = 0;
+ return(0);
+}
+
+/*
+ * cp_start()
+ * set up buffer system for copying within the file system
+ */
+
+void
+cp_start(void)
+{
+ buf = &(bufmem[BLKMULT]);
+ rdblksz = blksz = MAXBLK;
+}
+
+/*
+ * appnd_start()
+ * Set up the buffering system to append new members to an archive that
+ * was just read. The last block(s) of an archive may contain a format
+ * specific trailer. To append a new member, this trailer has to be
+ * removed from the archive. The first byte of the trailer is replaced by
+ * the start of the header of the first file added to the archive. The
+ * format specific end read function tells us how many bytes to move
+ * backwards in the archive to be positioned BEFORE the trailer. Two
+ * different position have to be adjusted, the O.S. file offset (e.g. the
+ * position of the tape head) and the write point within the data we have
+ * stored in the read (soon to become write) buffer. We may have to move
+ * back several records (the number depends on the size of the archive
+ * record and the size of the format trailer) to read up the record where
+ * the first byte of the trailer is recorded. Trailers may span (and
+ * overlap) record boundaries.
+ * We first calculate which record has the first byte of the trailer. We
+ * move the OS file offset back to the start of this record and read it
+ * up. We set the buffer write pointer to be at this byte (the byte where
+ * the trailer starts). We then move the OS file pointer back to the
+ * start of this record so a flush of this buffer will replace the record
+ * in the archive.
+ * A major problem is rewriting this last record. For archives stored
+ * on disk files, this is trivial. However, many devices are really picky
+ * about the conditions under which they will allow a write to occur.
+ * Often devices restrict the conditions where writes can be made,
+ * so it may not be feasible to append archives stored on all types of
+ * devices.
+ * Return:
+ * 0 for success, -1 for failure
+ */
+
+int
+appnd_start(off_t skcnt)
+{
+ int res;
+ off_t cnt;
+
+ if (exit_val != 0) {
+ paxwarn(0, "Cannot append to an archive that may have flaws.");
+ return(-1);
+ }
+ /*
+ * if the user did not specify a write blocksize, inherit the size used
+ * in the last archive volume read. (If a is set we still use rdblksz
+ * until next volume, cannot shift sizes within a single volume).
+ */
+ if (!wrblksz)
+ wrblksz = blksz = rdblksz;
+ else
+ blksz = rdblksz;
+
+ /*
+ * make sure that this volume allows appends
+ */
+ if (ar_app_ok() < 0)
+ return(-1);
+
+ /*
+ * Calculate bytes to move back and move in front of record where we
+ * need to start writing from. Remember we have to add in any padding
+ * that might be in the buffer after the trailer in the last block. We
+ * travel skcnt + padding ROUNDED UP to blksize.
+ */
+ skcnt += bufend - bufpt;
+ if ((cnt = (skcnt/blksz) * blksz) < skcnt)
+ cnt += blksz;
+ if (ar_rev((off_t)cnt) < 0)
+ goto out;
+
+ /*
+ * We may have gone too far if there is valid data in the block we are
+ * now in front of, read up the block and position the pointer after
+ * the valid data.
+ */
+ if ((cnt -= skcnt) > 0) {
+ /*
+ * watch out for stupid tape drives. ar_rev() will set rdblksz
+ * to be real physical blocksize so we must loop until we get
+ * the old rdblksz (now in blksz). If ar_rev() fouls up the
+ * determination of the physical block size, we will fail.
+ */
+ bufpt = buf;
+ bufend = buf + blksz;
+ while (bufpt < bufend) {
+ if ((res = ar_read(bufpt, rdblksz)) <= 0)
+ goto out;
+ bufpt += res;
+ }
+ if (ar_rev((off_t)(bufpt - buf)) < 0)
+ goto out;
+ bufpt = buf + cnt;
+ bufend = buf + blksz;
+ } else {
+ /*
+ * buffer is empty
+ */
+ bufend = buf + blksz;
+ bufpt = buf;
+ }
+ rdblksz = blksz;
+ rdcnt -= skcnt;
+ wrcnt = 0;
+
+ /*
+ * At this point we are ready to write. If the device requires special
+ * handling to write at a point were previously recorded data resides,
+ * that is handled in ar_set_wr(). From now on we operate under normal
+ * ARCHIVE mode (write) conditions
+ */
+ if (ar_set_wr() < 0)
+ return(-1);
+ act = ARCHIVE;
+ return(0);
+
+ out:
+ paxwarn(1, "Unable to rewrite archive trailer, cannot append.");
+ return(-1);
+}
+
+/*
+ * rd_sync()
+ * A read error occurred on this archive volume. Resync the buffer and
+ * try to reset the device (if possible) so we can continue to read. Keep
+ * trying to do this until we get a valid read, or we reach the limit on
+ * consecutive read faults (at which point we give up). The user can
+ * adjust the read error limit through a command line option.
+ * Returns:
+ * 0 on success, and -1 on failure
+ */
+
+int
+rd_sync(void)
+{
+ int errcnt = 0;
+ int res;
+
+ /*
+ * if the user says bail out on first fault, we are out of here...
+ */
+ if (maxflt == 0)
+ return(-1);
+ if (act == APPND) {
+ paxwarn(1, "Unable to append when there are archive read errors.");
+ return(-1);
+ }
+
+ /*
+ * poke at device and try to get past media error
+ */
+ if (ar_rdsync() < 0) {
+ if (ar_next() < 0)
+ return(-1);
+ else
+ rdcnt = 0;
+ }
+
+ for (;;) {
+ if ((res = ar_read(buf, blksz)) > 0) {
+ /*
+ * All right! got some data, fill that buffer
+ */
+ bufpt = buf;
+ bufend = buf + res;
+ rdcnt += res;
+ return(0);
+ }
+
+ /*
+ * Oh well, yet another failed read...
+ * if error limit reached, ditch. o.w. poke device to move past
+ * bad media and try again. if media is badly damaged, we ask
+ * the poor (and upset user at this point) for the next archive
+ * volume. remember the goal on reads is to get the most we
+ * can extract out of the archive.
+ */
+ if ((maxflt > 0) && (++errcnt > maxflt))
+ paxwarn(0,"Archive read error limit (%d) reached",maxflt);
+ else if (ar_rdsync() == 0)
+ continue;
+ if (ar_next() < 0)
+ break;
+ rdcnt = 0;
+ errcnt = 0;
+ }
+ return(-1);
+}
+
+/*
+ * pback()
+ * push the data used during the archive id phase back into the I/O
+ * buffer. This is required as we cannot be sure that the header does NOT
+ * overlap a block boundary (as in the case we are trying to recover a
+ * flawed archived). This was not designed to be used for any other
+ * purpose. (What software engineering, HA!)
+ * WARNING: do not even THINK of pback greater than BLKMULT, unless the
+ * pback space is increased.
+ */
+
+void
+pback(char *pt, int cnt)
+{
+ bufpt -= cnt;
+ memcpy(bufpt, pt, cnt);
+ return;
+}
+
+/*
+ * rd_skip()
+ * skip forward in the archive during a archive read. Used to get quickly
+ * past file data and padding for files the user did NOT select.
+ * Return:
+ * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected.
+ */
+
+int
+rd_skip(off_t skcnt)
+{
+ off_t res;
+ off_t cnt;
+ off_t skipped = 0;
+
+ /*
+ * consume what data we have in the buffer. If we have to move forward
+ * whole records, we call the low level skip function to see if we can
+ * move within the archive without doing the expensive reads on data we
+ * do not want.
+ */
+ if (skcnt == 0)
+ return(0);
+ res = MIN((bufend - bufpt), skcnt);
+ bufpt += res;
+ skcnt -= res;
+
+ /*
+ * if skcnt is now 0, then no additional i/o is needed
+ */
+ if (skcnt == 0)
+ return(0);
+
+ /*
+ * We have to read more, calculate complete and partial record reads
+ * based on rdblksz. we skip over "cnt" complete records
+ */
+ res = skcnt%rdblksz;
+ cnt = (skcnt/rdblksz) * rdblksz;
+
+ /*
+ * if the skip fails, we will have to resync. ar_fow will tell us
+ * how much it can skip over. We will have to read the rest.
+ */
+ if (ar_fow(cnt, &skipped) < 0)
+ return(-1);
+ res += cnt - skipped;
+ rdcnt += skipped;
+
+ /*
+ * what is left we have to read (which may be the whole thing if
+ * ar_fow() told us the device can only read to skip records);
+ */
+ while (res > 0L) {
+ cnt = bufend - bufpt;
+ /*
+ * if the read fails, we will have to resync
+ */
+ if ((cnt <= 0) && ((cnt = buf_fill()) < 0))
+ return(-1);
+ if (cnt == 0)
+ return(1);
+ cnt = MIN(cnt, res);
+ bufpt += cnt;
+ res -= cnt;
+ }
+ return(0);
+}
+
+/*
+ * wr_fin()
+ * flush out any data (and pad if required) the last block. We always pad
+ * with zero (even though we do not have to). Padding with 0 makes it a
+ * lot easier to recover if the archive is damaged. zero padding SHOULD
+ * BE a requirement....
+ */
+
+void
+wr_fin(void)
+{
+ if (bufpt > buf) {
+ memset(bufpt, 0, bufend - bufpt);
+ bufpt = bufend;
+ (void)buf_flush(blksz);
+ }
+}
+
+/*
+ * wr_rdbuf()
+ * fill the write buffer from data passed to it in a buffer (usually used
+ * by format specific write routines to pass a file header). On failure we
+ * punt. We do not allow the user to continue to write flawed archives.
+ * We assume these headers are not very large (the memory copy we use is
+ * a bit expensive).
+ * Return:
+ * 0 if buffer was filled ok, -1 o.w. (buffer flush failure)
+ */
+
+int
+wr_rdbuf(char *out, int outcnt)
+{
+ int cnt;
+
+ /*
+ * while there is data to copy copy into the write buffer. when the
+ * write buffer fills, flush it to the archive and continue
+ */
+ while (outcnt > 0) {
+ cnt = bufend - bufpt;
+ if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
+ return(-1);
+ /*
+ * only move what we have space for
+ */
+ cnt = MIN(cnt, outcnt);
+ memcpy(bufpt, out, cnt);
+ bufpt += cnt;
+ out += cnt;
+ outcnt -= cnt;
+ }
+ return(0);
+}
+
+/*
+ * rd_wrbuf()
+ * copy from the read buffer into a supplied buffer a specified number of
+ * bytes. If the read buffer is empty fill it and continue to copy.
+ * usually used to obtain a file header for processing by a format
+ * specific read routine.
+ * Return
+ * number of bytes copied to the buffer, 0 indicates EOF on archive volume,
+ * -1 is a read error
+ */
+
+int
+rd_wrbuf(char *in, int cpcnt)
+{
+ int res;
+ int cnt;
+ int incnt = cpcnt;
+
+ /*
+ * loop until we fill the buffer with the requested number of bytes
+ */
+ while (incnt > 0) {
+ cnt = bufend - bufpt;
+ if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) {
+ /*
+ * read error, return what we got (or the error if
+ * no data was copied). The caller must know that an
+ * error occurred and has the best knowledge what to
+ * do with it
+ */
+ if ((res = cpcnt - incnt) > 0)
+ return(res);
+ return(cnt);
+ }
+
+ /*
+ * calculate how much data to copy based on whats left and
+ * state of buffer
+ */
+ cnt = MIN(cnt, incnt);
+ memcpy(in, bufpt, cnt);
+ bufpt += cnt;
+ incnt -= cnt;
+ in += cnt;
+ }
+ return(cpcnt);
+}
+
+/*
+ * wr_skip()
+ * skip forward during a write. In other words add padding to the file.
+ * we add zero filled padding as it makes flawed archives much easier to
+ * recover from. the caller tells us how many bytes of padding to add
+ * This routine was not designed to add HUGE amount of padding, just small
+ * amounts (a few 512 byte blocks at most)
+ * Return:
+ * 0 if ok, -1 if there was a buf_flush failure
+ */
+
+int
+wr_skip(off_t skcnt)
+{
+ int cnt;
+
+ /*
+ * loop while there is more padding to add
+ */
+ while (skcnt > 0L) {
+ cnt = bufend - bufpt;
+ if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
+ return(-1);
+ cnt = MIN(cnt, skcnt);
+ memset(bufpt, 0, cnt);
+ bufpt += cnt;
+ skcnt -= cnt;
+ }
+ return(0);
+}
+
+/*
+ * wr_rdfile()
+ * fill write buffer with the contents of a file. We are passed an open
+ * file descriptor to the file an the archive structure that describes the
+ * file we are storing. The variable "left" is modified to contain the
+ * number of bytes of the file we were NOT able to write to the archive.
+ * it is important that we always write EXACTLY the number of bytes that
+ * the format specific write routine told us to. The file can also get
+ * bigger, so reading to the end of file would create an improper archive,
+ * we just detect this case and warn the user. We never create a bad
+ * archive if we can avoid it. Of course trying to archive files that are
+ * active is asking for trouble. It we fail, we pass back how much we
+ * could NOT copy and let the caller deal with it.
+ * Return:
+ * 0 ok, -1 if archive write failure. a short read of the file returns a
+ * 0, but "left" is set to be greater than zero.
+ */
+
+int
+wr_rdfile(ARCHD *arcn, int ifd, off_t *left)
+{
+ int cnt;
+ int res = 0;
+ off_t size = arcn->sb.st_size;
+ struct stat sb;
+
+ /*
+ * while there are more bytes to write
+ */
+ while (size > 0L) {
+ cnt = bufend - bufpt;
+ if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) {
+ *left = size;
+ return(-1);
+ }
+ cnt = MIN(cnt, size);
+ if ((res = read(ifd, bufpt, cnt)) <= 0)
+ break;
+ size -= res;
+ bufpt += res;
+ }
+
+ /*
+ * better check the file did not change during this operation
+ * or the file read failed.
+ */
+ if (res < 0)
+ syswarn(1, errno, "Read fault on %s", arcn->org_name);
+ else if (size != 0L)
+ paxwarn(1, "File changed size during read %s", arcn->org_name);
+ else if (fstat(ifd, &sb) < 0)
+ syswarn(1, errno, "Failed stat on %s", arcn->org_name);
+ else if (arcn->sb.st_mtime != sb.st_mtime)
+ paxwarn(1, "File %s was modified during copy to archive",
+ arcn->org_name);
+ *left = size;
+ return(0);
+}
+
+/*
+ * rd_wrfile()
+ * extract the contents of a file from the archive. If we are unable to
+ * extract the entire file (due to failure to write the file) we return
+ * the numbers of bytes we did NOT process. This way the caller knows how
+ * many bytes to skip past to find the next archive header. If the failure
+ * was due to an archive read, we will catch that when we try to skip. If
+ * the format supplies a file data crc value, we calculate the actual crc
+ * so that it can be compared to the value stored in the header
+ * NOTE:
+ * We call a special function to write the file. This function attempts to
+ * restore file holes (blocks of zeros) into the file. When files are
+ * sparse this saves space, and is a LOT faster. For non sparse files
+ * the performance hit is small. As of this writing, no archive supports
+ * information on where the file holes are.
+ * Return:
+ * 0 ok, -1 if archive read failure. if we cannot write the entire file,
+ * we return a 0 but "left" is set to be the amount unwritten
+ */
+
+int
+rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
+{
+ int cnt = 0;
+ off_t size = arcn->sb.st_size;
+ int res = 0;
+ char *fnm = arcn->name;
+ int isem = 1;
+ int rem;
+ int sz = MINFBSZ;
+ struct stat sb;
+ u_int32_t crc = 0;
+
+ /*
+ * pass the blocksize of the file being written to the write routine,
+ * if the size is zero, use the default MINFBSZ
+ */
+ if (ofd < 0)
+ sz = PAXPATHLEN + 1; /* GNU tar long link/file */
+ else if (fstat(ofd, &sb) == 0) {
+ if (sb.st_blksize > 0)
+ sz = (int)sb.st_blksize;
+ } else
+ syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
+ rem = sz;
+ *left = 0L;
+
+ /*
+ * Copy the archive to the file the number of bytes specified. We have
+ * to assume that we want to recover file holes as none of the archive
+ * formats can record the location of file holes.
+ */
+ while (size > 0L) {
+ cnt = bufend - bufpt;
+ /*
+ * if we get a read error, we do not want to skip, as we may
+ * miss a header, so we do not set left, but if we get a write
+ * error, we do want to skip over the unprocessed data.
+ */
+ if ((cnt <= 0) && ((cnt = buf_fill()) <= 0))
+ break;
+ cnt = MIN(cnt, size);
+ if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) {
+ *left = size;
+ break;
+ }
+
+ if (docrc) {
+ /*
+ * update the actual crc value
+ */
+ cnt = res;
+ while (--cnt >= 0)
+ crc += *bufpt++ & 0xff;
+ } else
+ bufpt += res;
+ size -= res;
+ }
+
+ /*
+ * if the last block has a file hole (all zero), we must make sure this
+ * gets updated in the file. We force the last block of zeros to be
+ * written. just closing with the file offset moved forward may not put
+ * a hole at the end of the file.
+ */
+ if (isem && (arcn->sb.st_size > 0L))
+ file_flush(ofd, fnm, isem);
+
+ /*
+ * if we failed from archive read, we do not want to skip
+ */
+ if ((size > 0L) && (*left == 0L))
+ return(-1);
+
+ /*
+ * some formats record a crc on file data. If so, then we compare the
+ * calculated crc to the crc stored in the archive
+ */
+ if (docrc && (size == 0L) && (arcn->crc != crc))
+ paxwarn(1,"Actual crc does not match expected crc %s",arcn->name);
+ return(0);
+}
+
+/*
+ * cp_file()
+ * copy the contents of one file to another. used during -rw phase of pax
+ * just as in rd_wrfile() we use a special write function to write the
+ * destination file so we can properly copy files with holes.
+ */
+
+void
+cp_file(ARCHD *arcn, int fd1, int fd2)
+{
+ int cnt;
+ off_t cpcnt = 0L;
+ int res = 0;
+ char *fnm = arcn->name;
+ int no_hole = 0;
+ int isem = 1;
+ int rem;
+ int sz = MINFBSZ;
+ struct stat sb;
+
+ /*
+ * check for holes in the source file. If none, we will use regular
+ * write instead of file write.
+ */
+ if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size)
+ ++no_hole;
+
+ /*
+ * pass the blocksize of the file being written to the write routine,
+ * if the size is zero, use the default MINFBSZ
+ */
+ if (fstat(fd2, &sb) == 0) {
+ if (sb.st_blksize > 0)
+ sz = sb.st_blksize;
+ } else
+ syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
+ rem = sz;
+
+ /*
+ * read the source file and copy to destination file until EOF
+ */
+ for (;;) {
+ if ((cnt = read(fd1, buf, blksz)) <= 0)
+ break;
+ if (no_hole)
+ res = write(fd2, buf, cnt);
+ else
+ res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm);
+ if (res != cnt)
+ break;
+ cpcnt += cnt;
+ }
+
+ /*
+ * check to make sure the copy is valid.
+ */
+ if (res < 0)
+ syswarn(1, errno, "Failed write during copy of %s to %s",
+ arcn->org_name, arcn->name);
+ else if (cpcnt != arcn->sb.st_size)
+ paxwarn(1, "File %s changed size during copy to %s",
+ arcn->org_name, arcn->name);
+ else if (fstat(fd1, &sb) < 0)
+ syswarn(1, errno, "Failed stat of %s", arcn->org_name);
+ else if (arcn->sb.st_mtime != sb.st_mtime)
+ paxwarn(1, "File %s was modified during copy to %s",
+ arcn->org_name, arcn->name);
+
+ /*
+ * if the last block has a file hole (all zero), we must make sure this
+ * gets updated in the file. We force the last block of zeros to be
+ * written. just closing with the file offset moved forward may not put
+ * a hole at the end of the file.
+ */
+ if (!no_hole && isem && (arcn->sb.st_size > 0L))
+ file_flush(fd2, fnm, isem);
+ return;
+}
+
+/*
+ * buf_fill()
+ * fill the read buffer with the next record (or what we can get) from
+ * the archive volume.
+ * Return:
+ * Number of bytes of data in the read buffer, -1 for read error, and
+ * 0 when finished (user specified termination in ar_next()).
+ */
+
+int
+buf_fill(void)
+{
+ int cnt;
+ static int fini = 0;
+
+ if (fini)
+ return(0);
+
+ for (;;) {
+ /*
+ * try to fill the buffer. on error the next archive volume is
+ * opened and we try again.
+ */
+ if ((cnt = ar_read(buf, blksz)) > 0) {
+ bufpt = buf;
+ bufend = buf + cnt;
+ rdcnt += cnt;
+ return(cnt);
+ }
+
+ /*
+ * errors require resync, EOF goes to next archive
+ */
+ if (cnt < 0)
+ break;
+ if (ar_next() < 0) {
+ fini = 1;
+ return(0);
+ }
+ rdcnt = 0;
+ }
+ exit_val = 1;
+ return(-1);
+}
+
+/*
+ * buf_flush()
+ * force the write buffer to the archive. We are passed the number of
+ * bytes in the buffer at the point of the flush. When we change archives
+ * the record size might change. (either larger or smaller).
+ * Return:
+ * 0 if all is ok, -1 when a write error occurs.
+ */
+
+int
+buf_flush(int bufcnt)
+{
+ int cnt;
+ int push = 0;
+ int totcnt = 0;
+
+ /*
+ * if we have reached the user specified byte count for each archive
+ * volume, prompt for the next volume. (The non-standard -R flag).
+ * NOTE: If the wrlimit is smaller than wrcnt, we will always write
+ * at least one record. We always round limit UP to next blocksize.
+ */
+ if ((wrlimit > 0) && (wrcnt > wrlimit)) {
+ paxwarn(0, "User specified archive volume byte limit reached.");
+ if (ar_next() < 0) {
+ wrcnt = 0;
+ exit_val = 1;
+ return(-1);
+ }
+ wrcnt = 0;
+
+ /*
+ * The new archive volume might have changed the size of the
+ * write blocksize. if so we figure out if we need to write
+ * (one or more times), or if there is now free space left in
+ * the buffer (it is no longer full). bufcnt has the number of
+ * bytes in the buffer, (the blocksize, at the point we were
+ * CALLED). Push has the amount of "extra" data in the buffer
+ * if the block size has shrunk from a volume change.
+ */
+ bufend = buf + blksz;
+ if (blksz > bufcnt)
+ return(0);
+ if (blksz < bufcnt)
+ push = bufcnt - blksz;
+ }
+
+ /*
+ * We have enough data to write at least one archive block
+ */
+ for (;;) {
+ /*
+ * write a block and check if it all went out ok
+ */
+ cnt = ar_write(buf, blksz);
+ if (cnt == blksz) {
+ /*
+ * the write went ok
+ */
+ wrcnt += cnt;
+ totcnt += cnt;
+ if (push > 0) {
+ /* we have extra data to push to the front.
+ * check for more than 1 block of push, and if
+ * so we loop back to write again
+ */
+ memcpy(buf, bufend, push);
+ bufpt = buf + push;
+ if (push >= blksz) {
+ push -= blksz;
+ continue;
+ }
+ } else
+ bufpt = buf;
+ return(totcnt);
+ } else if (cnt > 0) {
+ /*
+ * Oh drat we got a partial write!
+ * if format doesnt care about alignment let it go,
+ * we warned the user in ar_write().... but this means
+ * the last record on this volume violates pax spec....
+ */
+ totcnt += cnt;
+ wrcnt += cnt;
+ bufpt = buf + cnt;
+ cnt = bufcnt - cnt;
+ memcpy(buf, bufpt, cnt);
+ bufpt = buf + cnt;
+ if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0))
+ return(totcnt);
+ break;
+ }
+
+ /*
+ * All done, go to next archive
+ */
+ wrcnt = 0;
+ if (ar_next() < 0)
+ break;
+
+ /*
+ * The new archive volume might also have changed the block
+ * size. if so, figure out if we have too much or too little
+ * data for using the new block size
+ */
+ bufend = buf + blksz;
+ if (blksz > bufcnt)
+ return(0);
+ if (blksz < bufcnt)
+ push = bufcnt - blksz;
+ }
+
+ /*
+ * write failed, stop pax. we must not create a bad archive!
+ */
+ exit_val = 1;
+ return(-1);
+}
diff --git a/file_cmds/pax/cache.c b/file_cmds/pax/cache.c
new file mode 100644
index 0000000..e5c4996
--- /dev/null
+++ b/file_cmds/pax/cache.c
@@ -0,0 +1,426 @@
+/* $OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $ */
+/* $NetBSD: cache.c,v 1.4 1995/03/21 09:07:10 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "cache.h"
+#include "extern.h"
+
+/*
+ * routines that control user, group, uid and gid caches (for the archive
+ * member print routine).
+ * IMPORTANT:
+ * these routines cache BOTH hits and misses, a major performance improvement
+ */
+
+static int pwopn = 0; /* is password file open */
+static int gropn = 0; /* is group file open */
+static UIDC **uidtb = NULL; /* uid to name cache */
+static GIDC **gidtb = NULL; /* gid to name cache */
+static UIDC **usrtb = NULL; /* user name to uid cache */
+static GIDC **grptb = NULL; /* group name to gid cache */
+
+/*
+ * uidtb_start
+ * creates an empty uidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+uidtb_start(void)
+{
+ static int fail = 0;
+
+ if (uidtb != NULL)
+ return(0);
+ if (fail)
+ return(-1);
+ if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
+ ++fail;
+ paxwarn(1, "Unable to allocate memory for user id cache table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * gidtb_start
+ * creates an empty gidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+gidtb_start(void)
+{
+ static int fail = 0;
+
+ if (gidtb != NULL)
+ return(0);
+ if (fail)
+ return(-1);
+ if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
+ ++fail;
+ paxwarn(1, "Unable to allocate memory for group id cache table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * usrtb_start
+ * creates an empty usrtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+usrtb_start(void)
+{
+ static int fail = 0;
+
+ if (usrtb != NULL)
+ return(0);
+ if (fail)
+ return(-1);
+ if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
+ ++fail;
+ paxwarn(1, "Unable to allocate memory for user name cache table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * grptb_start
+ * creates an empty grptb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+grptb_start(void)
+{
+ static int fail = 0;
+
+ if (grptb != NULL)
+ return(0);
+ if (fail)
+ return(-1);
+ if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
+ ++fail;
+ paxwarn(1,"Unable to allocate memory for group name cache table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * name_uid()
+ * caches the name (if any) for the uid. If frc set, we always return the
+ * the stored name (if valid or invalid match). We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+
+char *
+name_uid(uid_t uid, int frc)
+{
+ struct passwd *pw;
+ UIDC *ptr;
+
+ if ((uidtb == NULL) && (uidtb_start() < 0))
+ return("");
+
+ /*
+ * see if we have this uid cached
+ */
+ ptr = uidtb[uid % UID_SZ];
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
+ /*
+ * have an entry for this uid
+ */
+ if (frc || (ptr->valid == VALID))
+ return(ptr->name);
+ return("");
+ }
+
+ /*
+ * No entry for this uid, we will add it
+ */
+ if (!pwopn) {
+ setpassent(1);
+ ++pwopn;
+ }
+ if (ptr == NULL)
+ ptr = uidtb[uid % UID_SZ] = malloc(sizeof(UIDC));
+
+ if ((pw = getpwuid(uid)) == NULL) {
+ /*
+ * no match for this uid in the local password file
+ * a string that is the uid in numeric format
+ */
+ if (ptr == NULL)
+ return("");
+ ptr->uid = uid;
+ ptr->valid = INVALID;
+ (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
+ (unsigned long)uid);
+ if (frc == 0)
+ return("");
+ } else {
+ /*
+ * there is an entry for this uid in the password file
+ */
+ if (ptr == NULL)
+ return(pw->pw_name);
+ ptr->uid = uid;
+ (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name));
+ ptr->valid = VALID;
+ }
+ return(ptr->name);
+}
+
+/*
+ * name_gid()
+ * caches the name (if any) for the gid. If frc set, we always return the
+ * the stored name (if valid or invalid match). We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+
+char *
+name_gid(gid_t gid, int frc)
+{
+ struct group *gr;
+ GIDC *ptr;
+
+ if ((gidtb == NULL) && (gidtb_start() < 0))
+ return("");
+
+ /*
+ * see if we have this gid cached
+ */
+ ptr = gidtb[gid % GID_SZ];
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
+ /*
+ * have an entry for this gid
+ */
+ if (frc || (ptr->valid == VALID))
+ return(ptr->name);
+ return("");
+ }
+
+ /*
+ * No entry for this gid, we will add it
+ */
+ if (!gropn) {
+ setgroupent(1);
+ ++gropn;
+ }
+ if (ptr == NULL)
+ ptr = gidtb[gid % GID_SZ] = malloc(sizeof(GIDC));
+
+ if ((gr = getgrgid(gid)) == NULL) {
+ /*
+ * no match for this gid in the local group file, put in
+ * a string that is the gid in numberic format
+ */
+ if (ptr == NULL)
+ return("");
+ ptr->gid = gid;
+ ptr->valid = INVALID;
+ (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
+ (unsigned long)gid);
+ if (frc == 0)
+ return("");
+ } else {
+ /*
+ * there is an entry for this group in the group file
+ */
+ if (ptr == NULL)
+ return(gr->gr_name);
+ ptr->gid = gid;
+ (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name));
+ ptr->valid = VALID;
+ }
+ return(ptr->name);
+}
+
+/*
+ * uid_name()
+ * caches the uid for a given user name. We use a simple hash table.
+ * Return
+ * the uid (if any) for a user name, or a -1 if no match can be found
+ */
+
+int
+uid_name(char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ UIDC *ptr;
+ int namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
+ return(-1);
+ if ((usrtb == NULL) && (usrtb_start() < 0))
+ return(-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return(-1);
+ *uid = ptr->uid;
+ return(0);
+ }
+
+ if (!pwopn) {
+ setpassent(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
+ (UIDC *)malloc(sizeof(UIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching uid
+ */
+ if (ptr == NULL) {
+ if ((pw = getpwnam(name)) == NULL)
+ return(-1);
+ *uid = pw->pw_uid;
+ return(0);
+ }
+ (void)strlcpy(ptr->name, name, sizeof(ptr->name));
+ if ((pw = getpwnam(name)) == NULL) {
+ ptr->valid = INVALID;
+ return(-1);
+ }
+ ptr->valid = VALID;
+ *uid = ptr->uid = pw->pw_uid;
+ return(0);
+}
+
+/*
+ * gid_name()
+ * caches the gid for a given group name. We use a simple hash table.
+ * Return
+ * the gid (if any) for a group name, or a -1 if no match can be found
+ */
+
+int
+gid_name(char *name, gid_t *gid)
+{
+ struct group *gr;
+ GIDC *ptr;
+ int namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
+ return(-1);
+ if ((grptb == NULL) && (grptb_start() < 0))
+ return(-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ ptr = grptb[st_hash(name, namelen, GID_SZ)];
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return(-1);
+ *gid = ptr->gid;
+ return(0);
+ }
+
+ if (!gropn) {
+ setgroupent(1);
+ ++gropn;
+ }
+ if (ptr == NULL)
+ ptr = grptb[st_hash(name, namelen, GID_SZ)] =
+ (GIDC *)malloc(sizeof(GIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching gid
+ */
+ if (ptr == NULL) {
+ if ((gr = getgrnam(name)) == NULL)
+ return(-1);
+ *gid = gr->gr_gid;
+ return(0);
+ }
+
+ (void)strlcpy(ptr->name, name, sizeof(ptr->name));
+ if ((gr = getgrnam(name)) == NULL) {
+ ptr->valid = INVALID;
+ return(-1);
+ }
+ ptr->valid = VALID;
+ *gid = ptr->gid = gr->gr_gid;
+ return(0);
+}
diff --git a/file_cmds/pax/cache.h b/file_cmds/pax/cache.h
new file mode 100644
index 0000000..1a269d4
--- /dev/null
+++ b/file_cmds/pax/cache.h
@@ -0,0 +1,78 @@
+/* $OpenBSD: cache.h,v 1.4 2003/10/20 06:22:27 jmc Exp $ */
+/* $NetBSD: cache.h,v 1.3 1995/03/21 09:07:12 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cache.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _CACHE_H_
+#define _CACHE_H_
+
+/*
+ * Constants and data structures used to implement group and password file
+ * caches. Traditional passwd/group cache routines perform quite poorly with
+ * archives. The chances of hitting a valid lookup with an archive is quite a
+ * bit worse than with files already resident on the file system. These misses
+ * create a MAJOR performance cost. To address this problem, these routines
+ * cache both hits and misses.
+ *
+ * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and
+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
+ */
+#define UNMLEN 32 /* >= user name found in any protocol */
+#define GNMLEN 32 /* >= group name found in any protocol */
+#define UID_SZ 317 /* size of user_name/uid cache */
+#define UNM_SZ 317 /* size of user_name/uid cache */
+#define GID_SZ 251 /* size of gid cache */
+#define GNM_SZ 317 /* size of group name cache */
+#define VALID 1 /* entry and name are valid */
+#define INVALID 2 /* entry valid, name NOT valid */
+
+/*
+ * Node structures used in the user, group, uid, and gid caches.
+ */
+
+typedef struct uidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[UNMLEN]; /* uid name */
+ uid_t uid; /* cached uid */
+} UIDC;
+
+typedef struct gidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[GNMLEN]; /* gid name */
+ gid_t gid; /* cached gid */
+} GIDC;
+
+#endif /* _CACHE_H_ */
diff --git a/file_cmds/pax/cpio.1 b/file_cmds/pax/cpio.1
new file mode 100644
index 0000000..b518a02
--- /dev/null
+++ b/file_cmds/pax/cpio.1
@@ -0,0 +1,304 @@
+.\"-
+.\" Copyright (c) 1997 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.
+.\"
+.\" 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.
+.\"
+.\" $OpenBSD: cpio.1,v 1.16 2001/05/01 17:58:01 aaron Exp $
+.\" $FreeBSD: src/bin/pax/cpio.1,v 1.6 2005/02/09 18:02:29 ru Exp $
+.\"
+.Dd February 16, 1997
+.Dt CPIO 1
+.Os
+.Sh NAME
+.Nm cpio
+.Nd copy file archives in and out
+.Sh SYNOPSIS
+.Nm cpio
+.Fl o
+.Op Fl aABcLvzZ
+.Op Fl C Ar bytes
+.Op Fl F Ar archive
+.Op Fl H Ar format
+.Op Fl O Ar archive
+.Ar "< name-list"
+.Op Ar "> archive"
+.Nm cpio
+.Fl i
+.Op Fl bBcdfmrsStuvzZ6
+.Op Fl C Ar bytes
+.Op Fl E Ar file
+.Op Fl F Ar archive
+.Op Fl H Ar format
+.Op Fl I Ar archive
+.Op Ar "pattern ..."
+.Op Ar "< archive"
+.Nm cpio
+.Fl p
+.Op Fl adlLmuv
+.Ar destination-directory
+.No < Ar name-list
+.Sh DESCRIPTION
+The
+.Nm cpio
+command copies files to and from a
+.Nm cpio
+archive.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl o
+Create an archive.
+Reads the list of files to store in the
+archive from standard input, and writes the archive on standard
+output.
+.Bl -tag -width Ds
+.It Fl A
+Append to the specified archive.
+.It Fl a
+Reset the access times on files that have been copied to the
+archive.
+.It Fl B
+Set block size of output to 5120 bytes.
+.It Fl C Ar bytes
+Set the block size of output to
+.Ar bytes .
+.It Fl c
+Use ASCII format for
+.Nm cpio
+header for portability.
+.It Fl F Ar archive
+Use the specified file name as the archive to write to.
+.It Fl H Ar format
+Write the archive in the specified format.
+Recognized formats are:
+.Pp
+.Bl -tag -width sv4cpio -compact
+.It Ar bcpio
+Old binary
+.Nm cpio
+format.
+.It Ar cpio
+Old octal character
+.Nm cpio
+format.
+.It Ar sv4cpio
+SVR4 hex
+.Nm cpio
+format.
+.It Ar tar
+Old tar format.
+.It Cm ustar
+.Tn POSIX
+ustar format.
+.El
+.It Fl L
+Follow symbolic links.
+.It Fl O Ar archive
+Use the specified file name as the archive to write to.
+.It Fl v
+Be verbose about operations.
+List filenames as they are written to the archive.
+.It Fl Z
+Compress archive using
+.Xr compress 1
+format.
+.It Fl z
+Compress archive using
+.Xr gzip 1
+format.
+.El
+.It Fl i
+Restore files from an archive.
+Reads the archive file from
+standard input and extracts files matching the
+.Ar patterns
+that were specified on the command line.
+.Bl -tag -width Ds
+.It Fl 6
+Process old-style
+.Nm cpio
+format archives.
+.It Fl B
+Set the block size of the archive being read to 5120 bytes.
+.It Fl b
+Do byte and word swapping after reading in data from the
+archive, for restoring archives created on systems with
+a different byte order.
+.It Fl C Ar bytes
+Read archive written with a block size of
+.Ar bytes .
+.It Fl c
+Expect the archive headers to be in ASCII format.
+.It Fl d
+Create any intermediate directories as needed during
+restore.
+.It Fl E Ar file
+Read list of file name patterns to extract or list from
+.Ar file .
+.It Fl F Ar archive
+Use the specified file as the input for the archive.
+.It Fl f
+Restore all files except those matching the
+.Ar patterns
+given on the command line.
+.It Fl H Ar format
+Read an archive of the specified format.
+Recognized formats are:
+.Pp
+.Bl -tag -width sv4cpio -compact
+.It Ar bcpio
+Old binary
+.Nm cpio
+format.
+.It Ar cpio
+Old octal character
+.Nm cpio
+format.
+.It Ar sv4cpio
+SVR4 hex
+.Nm cpio
+format.
+.It Ar tar
+Old tar format.
+.It Cm ustar
+.Tn POSIX
+ustar format.
+.El
+.It Fl I Ar archive
+Use the specified file as the input for the archive.
+.It Fl m
+Restore modification times on files.
+.It Fl r
+Rename restored files interactively.
+.It Fl S
+Swap words after reading data from the archive.
+.It Fl s
+Swap bytes after reading data from the archive.
+.It Fl t
+Only list the contents of the archive, no files or
+directories will be created.
+.It Fl u
+Overwrite files even when the file in the archive is
+older than the one that will be overwritten.
+.It Fl v
+Be verbose about operations.
+List filenames as they are copied in from the archive.
+.It Fl Z
+Uncompress archive using
+.Xr compress 1
+format.
+.It Fl z
+Uncompress archive using
+.Xr gzip 1
+format.
+.It Fl Z
+Uncompress archive using
+.Xr compress 1
+format.
+.It Fl 6
+Process old-style
+.Nm
+format archives.
+.El
+.It Fl p
+Copy files from one location to another in a single pass.
+The list of files to copy are read from standard input and
+written out to a directory relative to the specified
+.Ar directory
+argument.
+By default, an older file will not replace a newer file with the same name.
+.Bl -tag -width Ds
+.It Fl a
+Reset the access times on files that have been copied.
+.It Fl d
+Create any intermediate directories as needed to write
+the files at the new location.
+.It Fl L
+Follow symbolic links.
+.It Fl l
+When possible, link files rather than creating an
+extra copy.
+.It Fl m
+Restore modification times on files.
+.It Fl u
+Overwrite files even when the original file being copied is
+older than the one that will be overwritten.
+.It Fl v
+Be verbose about operations.
+List filenames as they are copied.
+.El
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev TMPDIR
+Path in which to store temporary files.
+.El
+.Sh ERRORS
+.Nm cpio
+will exit with one of the following values:
+.Bl -tag -width 2n
+.It 0
+All files were processed successfully.
+.It 1
+An error occurred.
+.El
+.Pp
+Whenever
+.Nm cpio
+cannot create a file or a link when extracting an archive or cannot
+find a file while writing an archive, or cannot preserve the user
+ID, group ID, file mode, or access and modification times when the
+.Fl p
+option is specified, a diagnostic message is written to standard
+error and a non-zero exit value will be returned, but processing
+will continue.
+In the case where
+.Nm cpio
+cannot create a link to a file,
+.Nm cpio
+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 cpio
+may have only partially extracted the 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 cpio
+may have only partially created the archive, which may violate the
+specific archive format specification.
+.Sh SEE ALSO
+.Xr pax 1 ,
+.Xr tar 1
+.Sh AUTHORS
+.An Keith Muller
+at the University of California, San Diego.
+.Sh BUGS
+The
+.Fl s
+and
+.Fl S
+options are currently not implemented.
diff --git a/file_cmds/pax/cpio.c b/file_cmds/pax/cpio.c
new file mode 100644
index 0000000..cb9f969
--- /dev/null
+++ b/file_cmds/pax/cpio.c
@@ -0,0 +1,1149 @@
+/* $OpenBSD: cpio.c,v 1.18 2008/01/01 16:22:44 tobias Exp $ */
+/* $NetBSD: cpio.c,v 1.5 1995/03/21 09:07:13 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: cpio.c,v 1.18 2008/01/01 16:22:44 tobias Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "cpio.h"
+#include "extern.h"
+
+static int rd_nm(ARCHD *, int);
+static int rd_ln_nm(ARCHD *);
+static int com_rd(ARCHD *);
+
+/*
+ * Routines which support the different cpio versions
+ */
+
+static int swp_head; /* binary cpio header byte swap */
+
+/*
+ * Routines common to all versions of cpio
+ */
+
+/*
+ * cpio_strd()
+ * Fire up the hard link detection code
+ * Return:
+ * 0 if ok -1 otherwise (the return values of lnk_start())
+ */
+
+int
+cpio_strd(void)
+{
+ return(lnk_start());
+}
+
+/*
+ * cpio_trail()
+ * Called to determine if a header block is a valid trailer. We are
+ * passed the block, the in_sync flag (which tells us we are in resync
+ * mode; looking for a valid header), and cnt (which starts at zero)
+ * which is used to count the number of empty blocks we have seen so far.
+ * Return:
+ * 0 if a valid trailer, -1 if not a valid trailer,
+ */
+
+int
+cpio_trail(ARCHD *arcn, char *notused, int notused2, int *notused3)
+{
+ /*
+ * look for trailer id in file we are about to process
+ */
+ if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0))
+ return(0);
+ return(-1);
+}
+
+/*
+ * com_rd()
+ * operations common to all cpio read functions.
+ * Return:
+ * 0
+ */
+
+static int
+com_rd(ARCHD *arcn)
+{
+ arcn->skip = 0;
+ arcn->pat = NULL;
+ arcn->org_name = arcn->name;
+ switch (arcn->sb.st_mode & C_IFMT) {
+ case C_ISFIFO:
+ arcn->type = PAX_FIF;
+ break;
+ case C_ISDIR:
+ arcn->type = PAX_DIR;
+ break;
+ case C_ISBLK:
+ arcn->type = PAX_BLK;
+ break;
+ case C_ISCHR:
+ arcn->type = PAX_CHR;
+ break;
+ case C_ISLNK:
+ arcn->type = PAX_SLK;
+ break;
+ case C_ISOCK:
+ arcn->type = PAX_SCK;
+ break;
+ case C_ISCTG:
+ case C_ISREG:
+ default:
+ /*
+ * we have file data, set up skip (pad is set in the format
+ * specific sections)
+ */
+ arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG;
+ arcn->type = PAX_REG;
+ arcn->skip = arcn->sb.st_size;
+ break;
+ }
+ if (chk_lnk(arcn) < 0)
+ return(-1);
+ return(0);
+}
+
+/*
+ * cpio_endwr()
+ * write the special file with the name trailer in the proper format
+ * Return:
+ * result of the write of the trailer from the cpio specific write func
+ */
+
+int
+cpio_endwr(void)
+{
+ ARCHD last;
+
+ /*
+ * create a trailer request and call the proper format write function
+ */
+ memset(&last, 0, sizeof(last));
+ last.nlen = sizeof(TRAILER) - 1;
+ last.type = PAX_REG;
+ last.sb.st_nlink = 1;
+ (void)strlcpy(last.name, TRAILER, sizeof(last.name));
+ return((*frmt->wr)(&last));
+}
+
+/*
+ * rd_nam()
+ * read in the file name which follows the cpio header
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+static int
+rd_nm(ARCHD *arcn, int nsz)
+{
+ /*
+ * do not even try bogus values
+ */
+ if ((nsz == 0) || (nsz > sizeof(arcn->name))) {
+ paxwarn(1, "Cpio file name length %d is out of range", nsz);
+ return(-1);
+ }
+
+ /*
+ * read the name and make sure it is not empty and is \0 terminated
+ */
+ if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') ||
+ (arcn->name[0] == '\0')) {
+ paxwarn(1, "Cpio file name in header is corrupted");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * rd_ln_nm()
+ * read in the link name for a file with links. The link name is stored
+ * like file data (and is NOT \0 terminated!)
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+static int
+rd_ln_nm(ARCHD *arcn)
+{
+ /*
+ * check the length specified for bogus values
+ */
+ if ((arcn->sb.st_size == 0) ||
+ (arcn->sb.st_size >= sizeof(arcn->ln_name))) {
+# ifdef LONG_OFF_T
+ paxwarn(1, "Cpio link name length is invalid: %lu",
+ arcn->sb.st_size);
+# else
+ paxwarn(1, "Cpio link name length is invalid: %qu",
+ arcn->sb.st_size);
+# endif
+ return(-1);
+ }
+
+ /*
+ * read in the link name and \0 terminate it
+ */
+ if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) !=
+ (int)arcn->sb.st_size) {
+ paxwarn(1, "Cpio link name read error");
+ return(-1);
+ }
+ arcn->ln_nlen = (int)arcn->sb.st_size;
+ arcn->ln_name[arcn->ln_nlen] = '\0';
+
+ /*
+ * watch out for those empty link names
+ */
+ if (arcn->ln_name[0] == '\0') {
+ paxwarn(1, "Cpio link name is corrupt");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Routines common to the extended byte oriented cpio format
+ */
+
+/*
+ * cpio_id()
+ * determine if a block given to us is a valid extended byte oriented
+ * cpio header
+ * Return:
+ * 0 if a valid header, -1 otherwise
+ */
+
+int
+cpio_id(char *blk, int size)
+{
+ if ((size < sizeof(HD_CPIO)) ||
+ (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * cpio_rd()
+ * determine if a buffer is a byte oriented extended cpio archive entry.
+ * convert and store the values in the ARCHD parameter.
+ * Return:
+ * 0 if a valid header, -1 otherwise.
+ */
+
+int
+cpio_rd(ARCHD *arcn, char *buf)
+{
+ int nsz;
+ HD_CPIO *hd;
+
+ /*
+ * check that this is a valid header, if not return -1
+ */
+ if (cpio_id(buf, sizeof(HD_CPIO)) < 0)
+ return(-1);
+ hd = (HD_CPIO *)buf;
+
+ /*
+ * byte oriented cpio (posix) does not have padding! extract the octal
+ * ascii fields from the header
+ */
+ arcn->pad = 0L;
+ arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT);
+ arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT);
+ arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT);
+ arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT);
+ arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT);
+ arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink),
+ OCT);
+ arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT);
+ arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime),
+ OCT);
+ arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+# ifdef LONG_OFF_T
+ arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize),
+ OCT);
+# else
+ arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize),
+ OCT);
+# endif
+
+ /*
+ * check name size and if valid, read in the name of this entry (name
+ * follows header in the archive)
+ */
+ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2)
+ return(-1);
+ arcn->nlen = nsz - 1;
+ if (rd_nm(arcn, nsz) < 0)
+ return(-1);
+
+ if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) {
+ /*
+ * no link name to read for this file
+ */
+ arcn->ln_nlen = 0;
+ arcn->ln_name[0] = '\0';
+ return(com_rd(arcn));
+ }
+
+ /*
+ * check link name size and read in the link name. Link names are
+ * stored like file data.
+ */
+ if (rd_ln_nm(arcn) < 0)
+ return(-1);
+
+ /*
+ * we have a valid header (with a link)
+ */
+ return(com_rd(arcn));
+}
+
+/*
+ * cpio_endrd()
+ * no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ * size of trailer header in this format
+ */
+
+off_t
+cpio_endrd(void)
+{
+ return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER)));
+}
+
+/*
+ * cpio_stwr()
+ * start up the device mapping table
+ * Return:
+ * 0 if ok, -1 otherwise (what dev_start() returns)
+ */
+
+int
+cpio_stwr(void)
+{
+ return(dev_start());
+}
+
+/*
+ * cpio_wr()
+ * copy the data in the ARCHD to buffer in extended byte oriented cpio
+ * format.
+ * Return
+ * 0 if file has data to be written after the header, 1 if file has NO
+ * data to write after the header, -1 if archive write failed
+ */
+
+int
+cpio_wr(ARCHD *arcn)
+{
+ HD_CPIO *hd;
+ int nsz;
+ char hdblk[sizeof(HD_CPIO)];
+
+ /*
+ * check and repair truncated device and inode fields in the header
+ */
+ if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0)
+ return(-1);
+
+ arcn->pad = 0L;
+ nsz = arcn->nlen + 1;
+ hd = (HD_CPIO *)hdblk;
+ if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+ arcn->sb.st_rdev = 0;
+
+ switch (arcn->type) {
+ case PAX_CTG:
+ case PAX_REG:
+ case PAX_HRG:
+ /*
+ * set data size for file data
+ */
+# ifdef LONG_OFF_T
+ if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize,
+ sizeof(hd->c_filesize), OCT)) {
+# else
+ if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize,
+ sizeof(hd->c_filesize), OCT)) {
+# endif
+ paxwarn(1,"File is too large for cpio format %s",
+ arcn->org_name);
+ return(1);
+ }
+ break;
+ case PAX_SLK:
+ /*
+ * set data size to hold link name
+ */
+ if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize,
+ sizeof(hd->c_filesize), OCT))
+ goto out;
+ break;
+ default:
+ /*
+ * all other file types have no file data
+ */
+ if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize),
+ OCT))
+ goto out;
+ break;
+ }
+
+ /*
+ * copy the values to the header using octal ascii
+ */
+ if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) ||
+ ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev),
+ OCT) ||
+ ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime),
+ OCT) ||
+ ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT))
+ goto out;
+
+ /*
+ * write the file name to the archive
+ */
+ if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) ||
+ (wr_rdbuf(arcn->name, nsz) < 0)) {
+ paxwarn(1, "Unable to write cpio header for %s", arcn->org_name);
+ return(-1);
+ }
+
+ /*
+ * if this file has data, we are done. The caller will write the file
+ * data, if we are link tell caller we are done, go to next file
+ */
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+ (arcn->type == PAX_HRG))
+ return(0);
+ if (arcn->type != PAX_SLK)
+ return(1);
+
+ /*
+ * write the link name to the archive, tell the caller to go to the
+ * next file as we are done.
+ */
+ if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) {
+ paxwarn(1,"Unable to write cpio link name for %s",arcn->org_name);
+ return(-1);
+ }
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1, "Cpio header field is too small to store file %s",
+ arcn->org_name);
+ return(1);
+}
+
+/*
+ * Routines common to the system VR4 version of cpio (with/without file CRC)
+ */
+
+/*
+ * vcpio_id()
+ * determine if a block given to us is a valid system VR4 cpio header
+ * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header
+ * uses HEX
+ * Return:
+ * 0 if a valid header, -1 otherwise
+ */
+
+int
+vcpio_id(char *blk, int size)
+{
+ if ((size < sizeof(HD_VCPIO)) ||
+ (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * crc_id()
+ * determine if a block given to us is a valid system VR4 cpio header
+ * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX
+ * Return:
+ * 0 if a valid header, -1 otherwise
+ */
+
+int
+crc_id(char *blk, int size)
+{
+ if ((size < sizeof(HD_VCPIO)) ||
+ (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * crc_strd()
+ w set file data CRC calculations. Fire up the hard link detection code
+ * Return:
+ * 0 if ok -1 otherwise (the return values of lnk_start())
+ */
+
+int
+crc_strd(void)
+{
+ docrc = 1;
+ return(lnk_start());
+}
+
+/*
+ * vcpio_rd()
+ * determine if a buffer is a system VR4 archive entry. (with/without CRC)
+ * convert and store the values in the ARCHD parameter.
+ * Return:
+ * 0 if a valid header, -1 otherwise.
+ */
+
+int
+vcpio_rd(ARCHD *arcn, char *buf)
+{
+ HD_VCPIO *hd;
+ dev_t devminor;
+ dev_t devmajor;
+ int nsz;
+
+ /*
+ * during the id phase it was determined if we were using CRC, use the
+ * proper id routine.
+ */
+ if (docrc) {
+ if (crc_id(buf, sizeof(HD_VCPIO)) < 0)
+ return(-1);
+ } else {
+ if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0)
+ return(-1);
+ }
+
+ hd = (HD_VCPIO *)buf;
+ arcn->pad = 0L;
+
+ /*
+ * extract the hex ascii fields from the header
+ */
+ arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX);
+ arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX);
+ arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX);
+ arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX);
+ arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX);
+ arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+# ifdef LONG_OFF_T
+ arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,
+ sizeof(hd->c_filesize), HEX);
+# else
+ arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,
+ sizeof(hd->c_filesize), HEX);
+# endif
+ arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink),
+ HEX);
+ devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX);
+ devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX);
+ arcn->sb.st_dev = TODEV(devmajor, devminor);
+ devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX);
+ devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX);
+ arcn->sb.st_rdev = TODEV(devmajor, devminor);
+ arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX);
+
+ /*
+ * check the length of the file name, if ok read it in, return -1 if
+ * bogus
+ */
+ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2)
+ return(-1);
+ arcn->nlen = nsz - 1;
+ if (rd_nm(arcn, nsz) < 0)
+ return(-1);
+
+ /*
+ * skip padding. header + filename is aligned to 4 byte boundaries
+ */
+ if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)
+ return(-1);
+
+ /*
+ * if not a link (or a file with no data), calculate pad size (for
+ * padding which follows the file data), clear the link name and return
+ */
+ if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) {
+ /*
+ * we have a valid header (not a link)
+ */
+ arcn->ln_nlen = 0;
+ arcn->ln_name[0] = '\0';
+ arcn->pad = VCPIO_PAD(arcn->sb.st_size);
+ return(com_rd(arcn));
+ }
+
+ /*
+ * read in the link name and skip over the padding
+ */
+ if ((rd_ln_nm(arcn) < 0) ||
+ (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0))
+ return(-1);
+
+ /*
+ * we have a valid header (with a link)
+ */
+ return(com_rd(arcn));
+}
+
+/*
+ * vcpio_endrd()
+ * no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ * size of trailer header in this format
+ */
+
+off_t
+vcpio_endrd(void)
+{
+ return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) +
+ (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER)))));
+}
+
+/*
+ * crc_stwr()
+ * start up the device mapping table, enable crc file calculation
+ * Return:
+ * 0 if ok, -1 otherwise (what dev_start() returns)
+ */
+
+int
+crc_stwr(void)
+{
+ docrc = 1;
+ return(dev_start());
+}
+
+/*
+ * vcpio_wr()
+ * copy the data in the ARCHD to buffer in system VR4 cpio
+ * (with/without crc) format.
+ * Return
+ * 0 if file has data to be written after the header, 1 if file has
+ * NO data to write after the header, -1 if archive write failed
+ */
+
+int
+vcpio_wr(ARCHD *arcn)
+{
+ HD_VCPIO *hd;
+ unsigned int nsz;
+ char hdblk[sizeof(HD_VCPIO)];
+
+ /*
+ * check and repair truncated device and inode fields in the cpio
+ * header
+ */
+ if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0)
+ return(-1);
+ nsz = arcn->nlen + 1;
+ hd = (HD_VCPIO *)hdblk;
+ if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+ arcn->sb.st_rdev = 0;
+
+ /*
+ * add the proper magic value depending whether we were asked for
+ * file data crc's, and the crc if needed.
+ */
+ if (docrc) {
+ if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic),
+ OCT) ||
+ ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum),
+ HEX))
+ goto out;
+ } else {
+ if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic),
+ OCT) ||
+ ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX))
+ goto out;
+ }
+
+ switch (arcn->type) {
+ case PAX_CTG:
+ case PAX_REG:
+ case PAX_HRG:
+ /*
+ * caller will copy file data to the archive. tell him how
+ * much to pad.
+ */
+ arcn->pad = VCPIO_PAD(arcn->sb.st_size);
+# ifdef LONG_OFF_T
+ if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize,
+ sizeof(hd->c_filesize), HEX)) {
+# else
+ if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize,
+ sizeof(hd->c_filesize), HEX)) {
+# endif
+ paxwarn(1,"File is too large for sv4cpio format %s",
+ arcn->org_name);
+ return(1);
+ }
+ break;
+ case PAX_SLK:
+ /*
+ * no file data for the caller to process, the file data has
+ * the size of the link
+ */
+ arcn->pad = 0L;
+ if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize,
+ sizeof(hd->c_filesize), HEX))
+ goto out;
+ break;
+ default:
+ /*
+ * no file data for the caller to process
+ */
+ arcn->pad = 0L;
+ if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize),
+ HEX))
+ goto out;
+ break;
+ }
+
+ /*
+ * set the other fields in the header
+ */
+ if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino),
+ HEX) ||
+ ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode),
+ HEX) ||
+ ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid),
+ HEX) ||
+ ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid),
+ HEX) ||
+ ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime),
+ HEX) ||
+ ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink),
+ HEX) ||
+ ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj),
+ HEX) ||
+ ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min),
+ HEX) ||
+ ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj),
+ HEX) ||
+ ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min),
+ HEX) ||
+ ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX))
+ goto out;
+
+ /*
+ * write the header, the file name and padding as required.
+ */
+ if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) ||
+ (wr_rdbuf(arcn->name, (int)nsz) < 0) ||
+ (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) {
+ paxwarn(1,"Could not write sv4cpio header for %s",arcn->org_name);
+ return(-1);
+ }
+
+ /*
+ * if we have file data, tell the caller we are done, copy the file
+ */
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+ (arcn->type == PAX_HRG))
+ return(0);
+
+ /*
+ * if we are not a link, tell the caller we are done, go to next file
+ */
+ if (arcn->type != PAX_SLK)
+ return(1);
+
+ /*
+ * write the link name, tell the caller we are done.
+ */
+ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) ||
+ (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) {
+ paxwarn(1,"Could not write sv4cpio link name for %s",
+ arcn->org_name);
+ return(-1);
+ }
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1,"Sv4cpio header field is too small for file %s",arcn->org_name);
+ return(1);
+}
+
+/*
+ * Routines common to the old binary header cpio
+ */
+
+/*
+ * bcpio_id()
+ * determine if a block given to us is a old binary cpio header
+ * (with/without header byte swapping)
+ * Return:
+ * 0 if a valid header, -1 otherwise
+ */
+
+int
+bcpio_id(char *blk, int size)
+{
+ if (size < sizeof(HD_BCPIO))
+ return(-1);
+
+ /*
+ * check both normal and byte swapped magic cookies
+ */
+ if (((u_short)SHRT_EXT(blk)) == MAGIC)
+ return(0);
+ if (((u_short)RSHRT_EXT(blk)) == MAGIC) {
+ if (!swp_head)
+ ++swp_head;
+ return(0);
+ }
+ return(-1);
+}
+
+/*
+ * bcpio_rd()
+ * determine if a buffer is a old binary archive entry. (it may have byte
+ * swapped header) convert and store the values in the ARCHD parameter.
+ * This is a very old header format and should not really be used.
+ * Return:
+ * 0 if a valid header, -1 otherwise.
+ */
+
+int
+bcpio_rd(ARCHD *arcn, char *buf)
+{
+ HD_BCPIO *hd;
+ int nsz;
+
+ /*
+ * check the header
+ */
+ if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0)
+ return(-1);
+
+ arcn->pad = 0L;
+ hd = (HD_BCPIO *)buf;
+ if (swp_head) {
+ /*
+ * header has swapped bytes on 16 bit boundaries
+ */
+ arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev));
+ arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino));
+ arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode));
+ arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid));
+ arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid));
+ arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink));
+ arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev));
+ arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1));
+ arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) |
+ ((time_t)(RSHRT_EXT(hd->h_mtime_2)));
+ arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1));
+ arcn->sb.st_size = (arcn->sb.st_size << 16) |
+ ((off_t)(RSHRT_EXT(hd->h_filesize_2)));
+ nsz = (int)(RSHRT_EXT(hd->h_namesize));
+ } else {
+ arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev));
+ arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino));
+ arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode));
+ arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid));
+ arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid));
+ arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink));
+ arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev));
+ arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1));
+ arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) |
+ ((time_t)(SHRT_EXT(hd->h_mtime_2)));
+ arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1));
+ arcn->sb.st_size = (arcn->sb.st_size << 16) |
+ ((off_t)(SHRT_EXT(hd->h_filesize_2)));
+ nsz = (int)(SHRT_EXT(hd->h_namesize));
+ }
+ arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+
+ /*
+ * check the file name size, if bogus give up. otherwise read the file
+ * name
+ */
+ if (nsz < 2)
+ return(-1);
+ arcn->nlen = nsz - 1;
+ if (rd_nm(arcn, nsz) < 0)
+ return(-1);
+
+ /*
+ * header + file name are aligned to 2 byte boundaries, skip if needed
+ */
+ if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)
+ return(-1);
+
+ /*
+ * if not a link (or a file with no data), calculate pad size (for
+ * padding which follows the file data), clear the link name and return
+ */
+ if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){
+ /*
+ * we have a valid header (not a link)
+ */
+ arcn->ln_nlen = 0;
+ arcn->ln_name[0] = '\0';
+ arcn->pad = BCPIO_PAD(arcn->sb.st_size);
+ return(com_rd(arcn));
+ }
+
+ if ((rd_ln_nm(arcn) < 0) ||
+ (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0))
+ return(-1);
+
+ /*
+ * we have a valid header (with a link)
+ */
+ return(com_rd(arcn));
+}
+
+/*
+ * bcpio_endrd()
+ * no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ * size of trailer header in this format
+ */
+
+off_t
+bcpio_endrd(void)
+{
+ return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) +
+ (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER)))));
+}
+
+/*
+ * bcpio_wr()
+ * copy the data in the ARCHD to buffer in old binary cpio format
+ * There is a real chance of field overflow with this critter. So we
+ * always check the conversion is ok. nobody in their right mind
+ * should write an archive in this format...
+ * Return
+ * 0 if file has data to be written after the header, 1 if file has NO
+ * data to write after the header, -1 if archive write failed
+ */
+
+int
+bcpio_wr(ARCHD *arcn)
+{
+ HD_BCPIO *hd;
+ int nsz;
+ char hdblk[sizeof(HD_BCPIO)];
+ off_t t_offt;
+ int t_int;
+ time_t t_timet;
+
+ /*
+ * check and repair truncated device and inode fields in the cpio
+ * header
+ */
+ if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0)
+ return(-1);
+
+ if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+ arcn->sb.st_rdev = 0;
+ hd = (HD_BCPIO *)hdblk;
+
+ switch (arcn->type) {
+ case PAX_CTG:
+ case PAX_REG:
+ case PAX_HRG:
+ /*
+ * caller will copy file data to the archive. tell him how
+ * much to pad.
+ */
+ arcn->pad = BCPIO_PAD(arcn->sb.st_size);
+ hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size);
+ hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size);
+ hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size);
+ hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size);
+ t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1));
+ t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2)));
+ if (arcn->sb.st_size != t_offt) {
+ paxwarn(1,"File is too large for bcpio format %s",
+ arcn->org_name);
+ return(1);
+ }
+ break;
+ case PAX_SLK:
+ /*
+ * no file data for the caller to process, the file data has
+ * the size of the link
+ */
+ arcn->pad = 0L;
+ hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen);
+ hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen);
+ hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen);
+ hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen);
+ t_int = (int)(SHRT_EXT(hd->h_filesize_1));
+ t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2)));
+ if (arcn->ln_nlen != t_int)
+ goto out;
+ break;
+ default:
+ /*
+ * no file data for the caller to process
+ */
+ arcn->pad = 0L;
+ hd->h_filesize_1[0] = (char)0;
+ hd->h_filesize_1[1] = (char)0;
+ hd->h_filesize_2[0] = (char)0;
+ hd->h_filesize_2[1] = (char)0;
+ break;
+ }
+
+ /*
+ * build up the rest of the fields
+ */
+ hd->h_magic[0] = CHR_WR_2(MAGIC);
+ hd->h_magic[1] = CHR_WR_3(MAGIC);
+ hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev);
+ hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev);
+ if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev)))
+ goto out;
+ hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino);
+ hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino);
+ if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino)))
+ goto out;
+ hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode);
+ hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode);
+ if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode)))
+ goto out;
+ hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid);
+ hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid);
+ if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid)))
+ goto out;
+ hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid);
+ hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid);
+ if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid)))
+ goto out;
+ hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink);
+ hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink);
+ if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink)))
+ goto out;
+ hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev);
+ hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev);
+ if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev)))
+ goto out;
+ hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime);
+ hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime);
+ hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime);
+ hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime);
+ t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1));
+ t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2)));
+ if (arcn->sb.st_mtime != t_timet)
+ goto out;
+ nsz = arcn->nlen + 1;
+ hd->h_namesize[0] = CHR_WR_2(nsz);
+ hd->h_namesize[1] = CHR_WR_3(nsz);
+ if (nsz != (int)(SHRT_EXT(hd->h_namesize)))
+ goto out;
+
+ /*
+ * write the header, the file name and padding as required.
+ */
+ if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) ||
+ (wr_rdbuf(arcn->name, nsz) < 0) ||
+ (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) {
+ paxwarn(1, "Could not write bcpio header for %s", arcn->org_name);
+ return(-1);
+ }
+
+ /*
+ * if we have file data, tell the caller we are done
+ */
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+ (arcn->type == PAX_HRG))
+ return(0);
+
+ /*
+ * if we are not a link, tell the caller we are done, go to next file
+ */
+ if (arcn->type != PAX_SLK)
+ return(1);
+
+ /*
+ * write the link name, tell the caller we are done.
+ */
+ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) ||
+ (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) {
+ paxwarn(1,"Could not write bcpio link name for %s",arcn->org_name);
+ return(-1);
+ }
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1,"Bcpio header field is too small for file %s", arcn->org_name);
+ return(1);
+}
diff --git a/file_cmds/pax/cpio.h b/file_cmds/pax/cpio.h
new file mode 100644
index 0000000..93617b4
--- /dev/null
+++ b/file_cmds/pax/cpio.h
@@ -0,0 +1,155 @@
+/* $OpenBSD: cpio.h,v 1.4 2003/06/02 23:32:08 millert Exp $ */
+/* $NetBSD: cpio.h,v 1.3 1995/03/21 09:07:15 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cpio.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _CPIO_H_
+#define _CPIO_H_
+
+/*
+ * Defines common to all versions of cpio
+ */
+#define TRAILER "TRAILER!!!" /* name in last archive record */
+
+/*
+ * Header encoding of the different file types
+ */
+#define C_ISDIR 040000 /* Directory */
+#define C_ISFIFO 010000 /* FIFO */
+#define C_ISREG 0100000 /* Regular file */
+#define C_ISBLK 060000 /* Block special file */
+#define C_ISCHR 020000 /* Character special file */
+#define C_ISCTG 0110000 /* Reserved for contiguous files */
+#define C_ISLNK 0120000 /* Reserved for symbolic links */
+#define C_ISOCK 0140000 /* Reserved for sockets */
+#define C_IFMT 0170000 /* type of file */
+
+/*
+ * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990
+ */
+typedef struct {
+ char c_magic[6]; /* magic cookie */
+ char c_dev[6]; /* device number */
+ char c_ino[6]; /* inode number */
+ char c_mode[6]; /* file type/access */
+ char c_uid[6]; /* owners uid */
+ char c_gid[6]; /* owners gid */
+ char c_nlink[6]; /* # of links at archive creation */
+ char c_rdev[6]; /* block/char major/minor # */
+ char c_mtime[11]; /* modification time */
+ char c_namesize[6]; /* length of pathname */
+ char c_filesize[11]; /* length of file in bytes */
+} HD_CPIO;
+
+#define MAGIC 070707 /* transportable archive id */
+
+#ifdef _PAX_
+#define AMAGIC "070707" /* ascii equivalent string of MAGIC */
+#define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */
+ /* used for dev/inode remaps */
+#endif /* _PAX_ */
+
+/*
+ * Binary cpio header structure
+ *
+ * CAUTION! CAUTION! CAUTION!
+ * Each field really represents a 16 bit short (NOT ASCII). Described as
+ * an array of chars in an attempt to improve portability!!
+ */
+typedef struct {
+ u_char h_magic[2];
+ u_char h_dev[2];
+ u_char h_ino[2];
+ u_char h_mode[2];
+ u_char h_uid[2];
+ u_char h_gid[2];
+ u_char h_nlink[2];
+ u_char h_rdev[2];
+ u_char h_mtime_1[2];
+ u_char h_mtime_2[2];
+ u_char h_namesize[2];
+ u_char h_filesize_1[2];
+ u_char h_filesize_2[2];
+} HD_BCPIO;
+
+#ifdef _PAX_
+/*
+ * extraction and creation macros for binary cpio
+ */
+#define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff))
+#define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff))
+#define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff))
+#define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff))
+#define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff))
+#define CHR_WR_3(val) ((char)((val) & 0xff))
+
+/*
+ * binary cpio masks and pads
+ */
+#define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */
+#define BCPIO_MASK 0xffff /* mask for dev/ino fields */
+#endif /* _PAX_ */
+
+/*
+ * System VR4 cpio header structure (with/without file data crc)
+ */
+typedef struct {
+ char c_magic[6]; /* magic cookie */
+ char c_ino[8]; /* inode number */
+ char c_mode[8]; /* file type/access */
+ char c_uid[8]; /* owners uid */
+ char c_gid[8]; /* owners gid */
+ char c_nlink[8]; /* # of links at archive creation */
+ char c_mtime[8]; /* modification time */
+ char c_filesize[8]; /* length of file in bytes */
+ char c_maj[8]; /* block/char major # */
+ char c_min[8]; /* block/char minor # */
+ char c_rmaj[8]; /* special file major # */
+ char c_rmin[8]; /* special file minor # */
+ char c_namesize[8]; /* length of pathname */
+ char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */
+} HD_VCPIO;
+
+#define VMAGIC 070701 /* sVr4 new portable archive id */
+#define VCMAGIC 070702 /* sVr4 new portable archive id CRC */
+#ifdef _PAX_
+#define AVMAGIC "070701" /* ascii string of above */
+#define AVCMAGIC "070702" /* ascii string of above */
+#define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */
+#define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */
+#endif /* _PAX_ */
+
+#endif /* _CPIO_H_ */
diff --git a/file_cmds/pax/extern.h b/file_cmds/pax/extern.h
new file mode 100644
index 0000000..16d3195
--- /dev/null
+++ b/file_cmds/pax/extern.h
@@ -0,0 +1,347 @@
+/* $OpenBSD: extern.h,v 1.33 2008/05/06 06:54:28 henning Exp $ */
+/* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/18/94
+ */
+
+#ifndef _PAX_EXTERN_H_
+#define _PAX_EXTERN_H_
+
+/*
+ * External references from each source file
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * ar_io.c
+ */
+extern const char *arcname;
+extern const char *gzip_program;
+extern int force_one_volume;
+int ar_open(const char *);
+void ar_close(void);
+void ar_drain(void);
+int ar_set_wr(void);
+int ar_app_ok(void);
+int ar_read(char *, int);
+int ar_write(char *, int);
+int ar_rdsync(void);
+int ar_fow(off_t, off_t *);
+int ar_rev(off_t );
+int ar_next(void);
+
+/*
+ * ar_subs.c
+ */
+extern u_long flcnt;
+int updatepath(void);
+int dochdir(const char *);
+int fdochdir(int);
+void list(void);
+void extract(void);
+void append(void);
+void archive(void);
+void copy(void);
+
+/*
+ * buf_subs.c
+ */
+extern int blksz;
+extern int wrblksz;
+extern int maxflt;
+extern int rdblksz;
+extern off_t wrlimit;
+extern off_t rdcnt;
+extern off_t wrcnt;
+int wr_start(void);
+int rd_start(void);
+void cp_start(void);
+int appnd_start(off_t);
+int rd_sync(void);
+void pback(char *, int);
+int rd_skip(off_t);
+void wr_fin(void);
+int wr_rdbuf(char *, int);
+int rd_wrbuf(char *, int);
+int wr_skip(off_t);
+int wr_rdfile(ARCHD *, int, off_t *);
+int rd_wrfile(ARCHD *, int, off_t *);
+void cp_file(ARCHD *, int, int);
+int buf_fill(void);
+int buf_flush(int);
+
+/*
+ * cache.c
+ */
+int uidtb_start(void);
+int gidtb_start(void);
+int usrtb_start(void);
+int grptb_start(void);
+char * name_uid(uid_t, int);
+char * name_gid(gid_t, int);
+int uid_name(char *, uid_t *);
+int gid_name(char *, gid_t *);
+
+/*
+ * cpio.c
+ */
+int cpio_strd(void);
+int cpio_trail(ARCHD *, char *, int, int *);
+int cpio_endwr(void);
+int cpio_id(char *, int);
+int cpio_rd(ARCHD *, char *);
+off_t cpio_endrd(void);
+int cpio_stwr(void);
+int cpio_wr(ARCHD *);
+int vcpio_id(char *, int);
+int crc_id(char *, int);
+int crc_strd(void);
+int vcpio_rd(ARCHD *, char *);
+off_t vcpio_endrd(void);
+int crc_stwr(void);
+int vcpio_wr(ARCHD *);
+int bcpio_id(char *, int);
+int bcpio_rd(ARCHD *, char *);
+off_t bcpio_endrd(void);
+int bcpio_wr(ARCHD *);
+
+/*
+ * file_subs.c
+ */
+extern char *gnu_name_string, *gnu_link_string;
+int file_creat(ARCHD *);
+void file_close(ARCHD *, int);
+int lnk_creat(ARCHD *);
+int cross_lnk(ARCHD *);
+int chk_same(ARCHD *);
+int node_creat(ARCHD *);
+int unlnk_exist(char *, int);
+int chk_path(char *, uid_t, gid_t, char **);
+void set_ftime(char *fnm, time_t mtime, time_t atime, int frc);
+void fset_ftime(char *fnm, int, time_t mtime, time_t atime, int frc);
+int set_ids(char *, uid_t, gid_t);
+int fset_ids(char *, int, uid_t, gid_t);
+int set_lids(char *, uid_t, gid_t);
+void set_pmode(char *, mode_t);
+void fset_pmode(char *, int, mode_t);
+int file_write(int, char *, int, int *, int *, int, char *);
+void file_flush(int, char *, int);
+void rdfile_close(ARCHD *, int *);
+int set_crc(ARCHD *, int);
+
+/*
+ * ftree.c
+ */
+int ftree_start(void);
+int ftree_add(char *, int);
+void ftree_sel(ARCHD *);
+void ftree_notsel(void);
+void ftree_skipped_newer(ARCHD *);
+void ftree_chk(void);
+int next_file(ARCHD *);
+
+/*
+ * gen_subs.c
+ */
+void ls_list(ARCHD *, time_t, FILE *);
+void ls_tty(ARCHD *);
+void safe_print(const char *, FILE *);
+u_long asc_ul(char *, int, int);
+int ul_asc(u_long, char *, int, int);
+#ifndef LONG_OFF_T
+u_quad_t asc_uqd(char *, int, int);
+int uqd_asc(u_quad_t, char *, int, int);
+#endif
+size_t fieldcpy(char *, size_t, const char *, size_t);
+
+/*
+ * getoldopt.c
+ */
+int getoldopt(int, char **, const char *);
+
+/*
+ * options.c
+ */
+extern const FSUB fsub[];
+extern int ford[];
+void options(int, char **);
+OPLIST * opt_next(void);
+int opt_add(const char *);
+int bad_opt(void);
+int pax_format_opt_add(char *);
+int pax_opt(void);
+char *chdname;
+
+/*
+ * pat_rep.c
+ */
+int rep_add(char *);
+int pat_add(char *, char *);
+void pat_chk(void);
+int pat_sel(ARCHD *);
+int pat_match(ARCHD *);
+int mod_name(ARCHD *);
+int set_dest(ARCHD *, char *, int);
+
+/*
+ * pax.c
+ */
+extern int act;
+extern const FSUB *frmt;
+extern int cflag;
+extern int cwdfd;
+extern int dflag;
+extern int iflag;
+extern int kflag;
+extern int lflag;
+extern int nflag;
+extern int tflag;
+extern int uflag;
+extern int vflag;
+extern int Dflag;
+extern int Hflag;
+extern int Lflag;
+extern int Xflag;
+extern int Yflag;
+extern int Zflag;
+extern int zeroflag;
+extern int vfpart;
+extern int patime;
+extern int pmtime;
+extern int nodirs;
+extern int pmode;
+extern int pids;
+extern int rmleadslash;
+extern int secure;
+extern int exit_val;
+extern int docrc;
+extern char *dirptr;
+extern char *ltmfrmt;
+extern char *argv0;
+extern FILE *listf;
+extern char *tempfile;
+extern char *tempbase;
+extern int havechd;
+
+int main(int, char **);
+void sig_cleanup(int);
+
+/*
+ * sel_subs.c
+ */
+int sel_chk(ARCHD *);
+int grp_add(char *);
+int usr_add(char *);
+int trng_add(char *);
+
+/*
+ * tables.c
+ */
+int lnk_start(void);
+int chk_lnk(ARCHD *);
+void purg_lnk(ARCHD *);
+void lnk_end(void);
+int ftime_start(void);
+int chk_ftime(ARCHD *);
+int name_start(void);
+int add_name(char *, int, char *);
+void sub_name(char *, int *, size_t);
+int dev_start(void);
+int add_dev(ARCHD *);
+int map_dev(ARCHD *, u_long, u_long);
+int atdir_start(void);
+void atdir_end(void);
+void add_atdir(char *, dev_t, ino_t, time_t, time_t);
+int get_atdir(dev_t, ino_t, time_t *, time_t *);
+int dir_start(void);
+void add_dir(char *, size_t, struct stat *, int);
+void proc_dir(void);
+u_int st_hash(char *, int, int);
+
+/*
+ * tar.c
+ */
+extern char *gnu_hack_string;
+int tar_endwr(void);
+off_t tar_endrd(void);
+int tar_trail(ARCHD *, char *, int, int *);
+int tar_id(char *, int);
+int tar_opt(void);
+int tar_rd(ARCHD *, char *);
+int tar_wr(ARCHD *);
+int ustar_strd(void);
+int ustar_stwr(void);
+int ustar_id(char *, int);
+int ustar_rd(ARCHD *, char *);
+int ustar_wr(ARCHD *);
+
+/*
+ * pax_format.c
+ */
+extern char *header_name_g;
+extern int pax_read_or_list_mode;
+#define PAX_INVALID_ACTION_BYPASS 1
+#define PAX_INVALID_ACTION_RENAME 2
+#define PAX_INVALID_ACTION_UTF8 3
+#define PAX_INVALID_ACTION_WRITE 4
+extern int want_linkdata;
+extern int pax_invalid_action;
+extern char * pax_list_opt_format;
+extern char * pax_invalid_action_write_path;
+extern char * pax_invalid_action_write_cwd;
+void pax_format_list_output(ARCHD *, time_t, FILE *, int);
+void cleanup_pax_invalid_action(void);
+void record_pax_invalid_action_results(ARCHD *, char *);
+int perform_pax_invalid_action(ARCHD *, int);
+void adjust_copy_for_pax_options(ARCHD *);
+/*
+int pax_strd(void);
+int pax_stwr(void);
+*/
+int pax_id(char *, int);
+int pax_rd(ARCHD *, char *);
+int pax_wr(ARCHD *);
+
+/*
+ * tty_subs.c
+ */
+int tty_init(void);
+void tty_prnt(const char *, ...);
+int tty_read(char *, int);
+void paxwarn(int, const char *, ...);
+void syswarn(int, int, const char *, ...);
+
+#endif /* _PAX_EXTERN_H_ */
diff --git a/file_cmds/pax/file_subs.c b/file_cmds/pax/file_subs.c
new file mode 100644
index 0000000..e78ba2b
--- /dev/null
+++ b/file_cmds/pax/file_subs.c
@@ -0,0 +1,1180 @@
+/* $OpenBSD: file_subs.c,v 1.30 2005/11/09 19:59:06 otto Exp $ */
+/* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: file_subs.c,v 1.30 2005/11/09 19:59:06 otto Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pax.h"
+#include "options.h"
+#include "extern.h"
+
+static int
+mk_link(char *, struct stat *, char *, int);
+
+/*
+ * routines that deal with file operations such as: creating, removing;
+ * and setting access modes, uid/gid and times of files
+ */
+
+#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+#define SETBITS (S_ISUID | S_ISGID)
+#define ABITS (FILEBITS | SETBITS)
+
+/*
+ * file_creat()
+ * Create and open a file.
+ * Return:
+ * file descriptor or -1 for failure
+ */
+
+int
+file_creat(ARCHD *arcn)
+{
+ int fd = -1;
+ mode_t file_mode;
+ int oerrno;
+ int rc = 0;
+ char *path_to_open;
+ char *new_path;
+ char *cwd;
+ char cwd_buff[MAXPATHLEN];
+
+ /*
+ * Assume file doesn't exist, so just try to create it, most times this
+ * works. We have to take special handling when the file does exist. To
+ * detect this, we use O_EXCL. For example when trying to create a
+ * file and a character device or fifo exists with the same name, we
+ * can accidently open the device by mistake (or block waiting to open).
+ * If we find that the open has failed, then spend the effort to
+ * figure out why. This strategy was found to have better average
+ * performance in common use than checking the file (and the path)
+ * first with lstat.
+ */
+ file_mode = arcn->sb.st_mode & FILEBITS;
+ if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
+ file_mode)) >= 0)
+ return(fd);
+
+ /*
+ * the file seems to exist. First we try to get rid of it (found to be
+ * the second most common failure when traced). If this fails, only
+ * then we go to the expense to check and create the path to the file
+ */
+ if (unlnk_exist(arcn->name, arcn->type) != 0)
+ return(-1);
+
+ path_to_open = arcn->name;
+ new_path = arcn->name;
+ cwd = getcwd(cwd_buff,sizeof(cwd_buff));
+ if (cwd==NULL) return -1;
+ for (;;) {
+ /*
+ * try to open it again, if this fails, check all the nodes in
+ * the path and give it a final try. if chk_path() finds that
+ * it cannot fix anything, we will skip the last attempt
+ */
+ if ((fd = open(path_to_open, O_WRONLY | O_CREAT | O_TRUNC,
+ file_mode)) >= 0) {
+ /* clean up the invalid_action */
+ if (pax_invalid_action>0) {
+ record_pax_invalid_action_results(arcn, path_to_open);
+ }
+ break;
+ }
+ oerrno = errno;
+ if (pax_invalid_action>0) {
+ rc = perform_pax_invalid_action(arcn, oerrno);
+ if (rc == 0) continue;
+ if (rc == 1) {
+ fd = -1;
+ break;
+ }
+ }
+ /* rc == 2 reserved for -o invalid_action=write */
+ if (nodirs || chk_path(path_to_open,arcn->sb.st_uid,arcn->sb.st_gid,
+ (rc==2) ? &new_path: NULL) < 0) {
+ syswarn((pax_invalid_action==0), oerrno, "Unable to create %s", arcn->name);
+ fd = -1;
+ break;
+ }
+ if (new_path) path_to_open = new_path; /* try again */
+ }
+ if (new_path && strcmp(new_path, arcn->name)!=0) {
+ dochdir(cwd); /* go back to original directory */
+ }
+ return(fd);
+}
+
+/*
+ * file_close()
+ * Close file descriptor to a file just created by pax. Sets modes,
+ * ownership and times as required.
+ * Return:
+ * 0 for success, -1 for failure
+ */
+
+void
+file_close(ARCHD *arcn, int fd)
+{
+ int res = 0;
+
+ if (fd < 0)
+ return;
+
+ if (close(fd) < 0)
+ syswarn(0, errno, "Unable to close file descriptor on %s",
+ arcn->name);
+
+ /*
+ * set owner/groups first as this may strip off mode bits we want
+ * then set file permission modes. Then set file access and
+ * modification times.
+ */
+ if (pids)
+ res = set_ids(arcn->name, arcn->sb.st_uid,
+ arcn->sb.st_gid);
+ else
+ res = 1; /* without pids, pax should NOT set s bits */
+
+ /*
+ * IMPORTANT SECURITY NOTE:
+ * if not preserving mode or we cannot set uid/gid, then PROHIBIT
+ * set uid/gid bits
+ */
+ if (!pmode || res)
+ arcn->sb.st_mode &= ~(SETBITS);
+ if (pmode)
+ set_pmode(arcn->name, arcn->sb.st_mode);
+ if (patime || pmtime)
+ set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
+}
+
+/*
+ * lnk_creat()
+ * Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name
+ * must exist;
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+lnk_creat(ARCHD *arcn)
+{
+ struct stat sb;
+
+ /*
+ * we may be running as root, so we have to be sure that link target
+ * is not a directory, so we lstat and check
+ */
+ if (lstat(arcn->ln_name, &sb) < 0) {
+ syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name,
+ arcn->name);
+ return(-1);
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ paxwarn(1, "A hard link to the directory %s is not allowed",
+ arcn->ln_name);
+ return(-1);
+ }
+
+ if (S_ISLNK(sb.st_mode)) {
+ int res;
+ char buff[PATH_MAX+1];
+ /*
+ * Conformance: cannot make hard link to symlink - just make a
+ * symlink to the target of the symlink
+ */
+ if ((res = readlink(arcn->ln_name, buff, sizeof(buff)-1)) < 0) {
+ syswarn(1,errno,"Unable to symlink to %s from %s", arcn->ln_name,
+ arcn->name);
+ return(-1);
+ }
+ buff[res] = 0;
+ res = symlink(buff, arcn->name);
+ return res;
+ }
+
+ return(mk_link(arcn->ln_name, &sb, arcn->name, 0));
+}
+
+/*
+ * cross_lnk()
+ * Create a hard link to arcn->org_name from arcn->name. Only used in copy
+ * with the -l flag. No warning or error if this does not succeed (we will
+ * then just create the file)
+ * Return:
+ * 1 if copy() should try to create this file node
+ * 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self).
+ */
+
+int
+cross_lnk(ARCHD *arcn)
+{
+ /*
+ * try to make a link to original file (-l flag in copy mode). make
+ * sure we do not try to link to directories in case we are running as
+ * root (and it might succeed).
+ */
+ if (arcn->type == PAX_DIR)
+ return(1);
+ if (arcn->type == PAX_SLK) { /* for Unix 03 conformance tests 202,203 */
+ if (!Lflag)
+ return(1);
+ }
+ return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1));
+}
+
+/*
+ * chk_same()
+ * In copy mode if we are not trying to make hard links between the src
+ * and destinations, make sure we are not going to overwrite ourselves by
+ * accident. This slows things down a little, but we have to protect all
+ * those people who make typing errors.
+ * Return:
+ * 1 the target does not exist, go ahead and copy
+ * 0 skip it file exists (-k) or may be the same as source file
+ */
+
+int
+chk_same(ARCHD *arcn)
+{
+ struct stat sb;
+
+ /*
+ * if file does not exist, return. if file exists and -k, skip it
+ * quietly
+ */
+ if (lstat(arcn->name, &sb) < 0)
+ return(1);
+ if (kflag)
+ return(0);
+
+ /*
+ * better make sure the user does not have src == dest by mistake
+ */
+ if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) {
+ paxwarn(1, "Unable to copy %s, file would overwrite itself",
+ arcn->name);
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * mk_link()
+ * try to make a hard link between two files. if ign set, we do not
+ * complain.
+ * Return:
+ * 0 if successful (or we are done with this file but no error, such as
+ * finding the from file exists and the user has set -k).
+ * 1 when ign was set to indicates we could not make the link but we
+ * should try to copy/extract the file as that might work (and is an
+ * allowed option). -1 an error occurred.
+ */
+
+static int
+mk_link(char *to, struct stat *to_sb, char *from, int ign)
+{
+ struct stat sb;
+ int oerrno;
+
+ /*
+ * if from file exists, it has to be unlinked to make the link. If the
+ * file exists and -k is set, skip it quietly
+ */
+ if (lstat(from, &sb) == 0) {
+ if (kflag)
+ return(0);
+
+ /*
+ * make sure it is not the same file, protect the user
+ */
+ if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) {
+ paxwarn(1, "Unable to link file %s to itself", to);
+ return(-1);
+ }
+
+ /*
+ * try to get rid of the file, based on the type
+ */
+ if (S_ISDIR(sb.st_mode)) {
+ if (rmdir(from) < 0) {
+ syswarn(1, errno, "Unable to remove %s", from);
+ return(-1);
+ }
+ } else if (unlink(from) < 0) {
+ if (!ign) {
+ syswarn(1, errno, "Unable to remove %s", from);
+ return(-1);
+ }
+ return(1);
+ }
+ }
+
+ /*
+ * from file is gone (or did not exist), try to make the hard link.
+ * if it fails, check the path and try it again (if chk_path() says to
+ * try again)
+ */
+ for (;;) {
+ if (link(to, from) == 0)
+ break;
+ oerrno = errno;
+ if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid, NULL) == 0)
+ continue;
+ if (!ign) {
+ syswarn(1, oerrno, "Could not link to %s from %s", to,
+ from);
+ return(-1);
+ }
+ return(1);
+ }
+
+ /*
+ * all right the link was made
+ */
+ return(0);
+}
+
+/*
+ * node_creat()
+ * create an entry in the file system (other than a file or hard link).
+ * If successful, sets uid/gid modes and times as required.
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+node_creat(ARCHD *arcn)
+{
+ int res;
+ int ign = 0;
+ int oerrno;
+ int pass = 0;
+ mode_t file_mode;
+ struct stat sb;
+ char target[MAXPATHLEN];
+ char *nm = arcn->name;
+ int nmlen = arcn->nlen;
+ int len;
+
+ /*
+ * create node based on type, if that fails try to unlink the node and
+ * try again. finally check the path and try again. As noted in the
+ * file and link creation routines, this method seems to exhibit the
+ * best performance in general use workloads.
+ */
+ file_mode = arcn->sb.st_mode & FILEBITS;
+
+ for (;;) {
+ switch (arcn->type) {
+ case PAX_DIR:
+ /*
+ * If -h (or -L) was given in tar-mode, follow the
+ * potential symlink chain before trying to create the
+ * directory.
+ */
+ if (strcmp(NM_TAR, argv0) == 0 && Lflag) {
+ while (lstat(nm, &sb) == 0 &&
+ S_ISLNK(sb.st_mode)) {
+ len = readlink(nm, target,
+ sizeof target - 1);
+ if (len == -1) {
+ syswarn(0, errno,
+ "cannot follow symlink %s in chain for %s",
+ nm, arcn->name);
+ res = -1;
+ goto badlink;
+ }
+ target[len] = '\0';
+ nm = target;
+ nmlen = len;
+ }
+ }
+ res = mkdir(nm, file_mode);
+
+badlink:
+ if (ign)
+ res = 0;
+ break;
+ case PAX_CHR:
+ file_mode |= S_IFCHR;
+ res = mknod(nm, file_mode, arcn->sb.st_rdev);
+ break;
+ case PAX_BLK:
+ file_mode |= S_IFBLK;
+ res = mknod(nm, file_mode, arcn->sb.st_rdev);
+ break;
+ case PAX_FIF:
+ res = mkfifo(nm, file_mode);
+ break;
+ case PAX_SCK:
+ /*
+ * Skip sockets, operation has no meaning under BSD
+ */
+ paxwarn(0,
+ "%s skipped. Sockets cannot be copied or extracted",
+ nm);
+ return(-1);
+ case PAX_SLK:
+ res = symlink(arcn->ln_name, nm);
+ break;
+ case PAX_CTG:
+ case PAX_HLK:
+ case PAX_HRG:
+ case PAX_REG:
+ default:
+ /*
+ * we should never get here
+ */
+ paxwarn(0, "%s has an unknown file type, skipping",
+ nm);
+ return(-1);
+ }
+
+ /*
+ * if we were able to create the node break out of the loop,
+ * otherwise try to unlink the node and try again. if that
+ * fails check the full path and try a final time.
+ */
+ if (res == 0)
+ break;
+
+ /*
+ * we failed to make the node
+ */
+ oerrno = errno;
+ if ((ign = unlnk_exist(nm, arcn->type)) < 0)
+ return(-1);
+
+ if (++pass <= 1)
+ continue;
+
+ if (nodirs || chk_path(nm,arcn->sb.st_uid,arcn->sb.st_gid, NULL) < 0) {
+ syswarn(1, oerrno, "Could not create: %s", nm);
+ return(-1);
+ }
+ }
+
+ /*
+ * we were able to create the node. set uid/gid, modes and times
+ */
+ if (pids)
+ res = ((arcn->type == PAX_SLK) ?
+#if defined(__APPLE__)
+ /* Mac OS X doesn't have lchown, so don't bother */
+ 0 :
+#else
+ set_lids(nm, arcn->sb.st_uid, arcn->sb.st_gid) :
+#endif
+ set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid));
+ else
+ res = 1; /* without pids, pax should NOT set s bits */
+
+ /*
+ * symlinks are done now.
+ */
+ if (arcn->type == PAX_SLK)
+ return(0);
+
+ /*
+ * IMPORTANT SECURITY NOTE:
+ * if not preserving mode or we cannot set uid/gid, then PROHIBIT any
+ * set uid/gid bits
+ */
+ if (!pmode || res)
+ arcn->sb.st_mode &= ~(SETBITS);
+ if (pmode)
+ set_pmode(nm, arcn->sb.st_mode);
+
+ if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) {
+ /*
+ * Dirs must be processed again at end of extract to set times
+ * and modes to agree with those stored in the archive. However
+ * to allow extract to continue, we may have to also set owner
+ * rights. This allows nodes in the archive that are children
+ * of this directory to be extracted without failure. Both time
+ * and modes will be fixed after the entire archive is read and
+ * before pax exits.
+ */
+ if (access(nm, R_OK | W_OK | X_OK) < 0) {
+ if (lstat(nm, &sb) < 0) {
+ syswarn(0, errno,"Could not access %s (stat)",
+ arcn->name);
+ set_pmode(nm,file_mode | S_IRWXU);
+ } else {
+ /*
+ * We have to add rights to the dir, so we make
+ * sure to restore the mode. The mode must be
+ * restored AS CREATED and not as stored if
+ * pmode is not set.
+ */
+ set_pmode(nm,
+ ((sb.st_mode & FILEBITS) | S_IRWXU));
+ if (!pmode)
+ arcn->sb.st_mode = sb.st_mode;
+ }
+
+ /*
+ * we have to force the mode to what was set here,
+ * since we changed it from the default as created.
+ */
+ add_dir(nm, nmlen, &(arcn->sb), 1);
+ } else if (pmode || patime || pmtime)
+ add_dir(nm, nmlen, &(arcn->sb), 0);
+ }
+
+ if (patime || pmtime)
+ set_ftime(nm, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
+ return(0);
+}
+
+/*
+ * unlnk_exist()
+ * Remove node from file system with the specified name. We pass the type
+ * of the node that is going to replace it. When we try to create a
+ * directory and find that it already exists, we allow processing to
+ * continue as proper modes etc will always be set for it later on.
+ * Return:
+ * 0 is ok to proceed, no file with the specified name exists
+ * -1 we were unable to remove the node, or we should not remove it (-k)
+ * 1 we found a directory and we were going to create a directory.
+ */
+
+int
+unlnk_exist(char *name, int type)
+{
+ struct stat sb;
+
+ /*
+ * the file does not exist, or -k we are done
+ */
+ if (lstat(name, &sb) < 0)
+ return(0);
+ 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
+ * create a directory anyway, tell the caller (return a 1)
+ */
+ if (rmdir(name) < 0) {
+ if (type == PAX_DIR)
+ return(1);
+ syswarn(1,errno,"Unable to remove directory %s", name);
+ return(-1);
+ }
+ return(0);
+ }
+
+ /*
+ * try to get rid of all non-directory type nodes
+ */
+ if (unlink(name) < 0) {
+ syswarn(1, errno, "Could not unlink %s", name);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * chk_path()
+ * We were trying to create some kind of node in the file system and it
+ * failed. chk_path() makes sure the path up to the node exists and is
+ * writeable. When we have to create a directory that is missing along the
+ * path somewhere, the directory we create will be set to the same
+ * uid/gid as the file has (when uid and gid are being preserved).
+ * NOTE: this routine is a real performance loss. It is only used as a
+ * last resort when trying to create entries in the file system.
+ * Return:
+ * -1 when it could find nothing it is allowed to fix.
+ * 0 otherwise
+ */
+
+int
+chk_path(char *name, uid_t st_uid, gid_t st_gid, char ** new_name)
+{
+ char *spt = name;
+ int namelen = strlen(name);
+ struct stat sb;
+ int retval = -1;
+
+ /*
+ * watch out for paths with nodes stored directly in / (e.g. /bozo)
+ */
+ if (*spt == '/')
+ ++spt;
+
+ for (;;) {
+ /*
+ * work forward from the first / and check each part of the path
+ */
+ spt = strchr(spt, '/');
+ if (spt == NULL)
+ break;
+ *spt = '\0';
+
+ /*
+ * if it exists we assume it is a directory, it is not within
+ * the spec (at least it seems to read that way) to alter the
+ * file system for nodes NOT EXPLICITLY stored on the archive.
+ * If that assumption is changed, you would test the node here
+ * and figure out how to get rid of it (probably like some
+ * recursive unlink()) or fix up the directory permissions if
+ * required (do an access()).
+ */
+ if (lstat(name, &sb) == 0) {
+ *(spt++) = '/';
+ if (new_name==NULL) continue;
+ retval = 0; /* accept it one directory at a time */
+ break;
+ }
+
+ /*
+ * the path fails at this point, see if we can create the
+ * needed directory and continue on
+ */
+ if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ *spt = '/';
+ retval = -1;
+ break;
+ }
+
+ /*
+ * we were able to create the directory. We will tell the
+ * caller that we found something to fix, and it is ok to try
+ * and create the node again.
+ */
+ retval = 0;
+ if (pids)
+ (void)set_ids(name, st_uid, st_gid);
+
+ /*
+ * make sure the user doesn't have some strange umask that
+ * causes this newly created directory to be unusable. We fix
+ * the modes and restore them back to the creation default at
+ * the end of pax
+ */
+ if ((access(name, R_OK | W_OK | X_OK) < 0) &&
+ (lstat(name, &sb) == 0)) {
+ set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU));
+ add_dir(name, namelen, &sb, 1);
+ }
+ *(spt++) = '/';
+ if (new_name==NULL) continue;
+ break;
+ }
+ if ((new_name != NULL) && retval==0) {
+ /* save the new path */
+ *(--spt) = '\0';
+ /*
+ printf ("chdir to %s\n", name);
+ */
+ if(0==chdir(name)) {
+ *spt++ = '/';
+ /*
+ printf ("remaining path: %s\n",spt);
+ */
+ *new_name = spt;
+ } else
+ *spt = '/';
+ }
+ return(retval);
+}
+
+/*
+ * set_ftime()
+ * Set the access time and modification time for a named file. If frc
+ * is non-zero we force these times to be set even if the user did not
+ * request access and/or modification time preservation (this is also
+ * used by -t to reset access times).
+ * When frc is zero, only those times the user has asked for are set, the
+ * other ones are left alone. We do not assume the un-documented feature
+ * of many utimes() implementations that consider a 0 time value as a do
+ * not set request.
+ */
+
+void
+set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
+{
+ static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
+ struct stat sb;
+
+ tv[0].tv_sec = (long)atime;
+ tv[1].tv_sec = (long)mtime;
+ if (!frc && (!patime || !pmtime)) {
+ /*
+ * if we are not forcing, only set those times the user wants
+ * set. We get the current values of the times if we need them.
+ */
+ if (lstat(fnm, &sb) == 0) {
+ if (!patime)
+ tv[0].tv_sec = (long)sb.st_atime;
+ if (!pmtime)
+ tv[1].tv_sec = (long)sb.st_mtime;
+ } else
+ syswarn(0,errno,"Unable to obtain file stats %s", fnm);
+ }
+
+ /*
+ * set the times
+ */
+ if (pax_invalid_action_write_cwd) {
+ char cwd_buff[MAXPATHLEN];
+ char * cwd;
+ cwd = getcwd(&cwd_buff[0],MAXPATHLEN);
+ chdir(pax_invalid_action_write_cwd);
+ if (utimes(pax_invalid_action_write_path, tv) < 0)
+ syswarn(1, errno, "Access/modification time set failed on: %s",
+ pax_invalid_action_write_path);
+ chdir(cwd);
+ cleanup_pax_invalid_action();
+ } else {
+ if (utimes(fnm, tv) < 0)
+ syswarn(1, errno, "Access/modification time set failed on: %s",
+ fnm);
+ }
+ return;
+}
+
+void
+fset_ftime(char *fnm, int fd, time_t mtime, time_t atime, int frc)
+{
+ static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
+ struct stat sb;
+
+ tv[0].tv_sec = (long)atime;
+ tv[1].tv_sec = (long)mtime;
+ if (!frc && (!patime || !pmtime)) {
+ /*
+ * if we are not forcing, only set those times the user wants
+ * set. We get the current values of the times if we need them.
+ */
+ if (fstat(fd, &sb) == 0) {
+ if (!patime)
+ tv[0].tv_sec = (long)sb.st_atime;
+ if (!pmtime)
+ tv[1].tv_sec = (long)sb.st_mtime;
+ } else
+ syswarn(0,errno,"Unable to obtain file stats %s", fnm);
+ }
+ /*
+ * set the times
+ */
+ if (futimes(fd, tv) < 0)
+ syswarn(1, errno, "Access/modification time set failed on: %s",
+ fnm);
+ return;
+}
+
+/*
+ * set_ids()
+ * set the uid and gid of a file system node
+ * Return:
+ * 0 when set, -1 on failure
+ */
+
+int
+set_ids(char *fnm, uid_t uid, gid_t gid)
+{
+ if (chown(fnm, uid, gid) < 0) {
+ /*
+ * ignore EPERM unless in verbose mode or being run by root.
+ * if running as pax, POSIX requires a warning.
+ */
+ if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag ||
+ geteuid() == 0)
+ syswarn(1, errno, "Unable to set file uid/gid of %s",
+ fnm);
+ return(-1);
+ }
+ return(0);
+}
+
+int
+fset_ids(char *fnm, int fd, uid_t uid, gid_t gid)
+{
+ if (fchown(fd, uid, gid) < 0) {
+ /*
+ * ignore EPERM unless in verbose mode or being run by root.
+ * if running as pax, POSIX requires a warning.
+ */
+ if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag ||
+ geteuid() == 0)
+ syswarn(1, errno, "Unable to set file uid/gid of %s",
+ fnm);
+ return(-1);
+ }
+ return(0);
+}
+
+#if !defined(__APPLE__)
+/*
+ * set_lids()
+ * set the uid and gid of a file system node
+ * Return:
+ * 0 when set, -1 on failure
+ */
+
+int
+set_lids(char *fnm, uid_t uid, gid_t gid)
+{
+ if (lchown(fnm, uid, gid) < 0) {
+ /*
+ * ignore EPERM unless in verbose mode or being run by root.
+ * if running as pax, POSIX requires a warning.
+ */
+ if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag ||
+ geteuid() == 0)
+ syswarn(1, errno, "Unable to set file uid/gid of %s",
+ fnm);
+ return(-1);
+ }
+ return(0);
+}
+#endif /* !__APPLE__ */
+
+/*
+ * set_pmode()
+ * Set file access mode
+ */
+
+void
+set_pmode(char *fnm, mode_t mode)
+{
+ mode &= ABITS;
+ if (chmod(fnm, mode) < 0)
+ syswarn(1, errno, "Could not set permissions on %s", fnm);
+ return;
+}
+
+void
+fset_pmode(char *fnm, int fd, mode_t mode)
+{
+ mode &= ABITS;
+ if (fchmod(fd, mode) < 0)
+ syswarn(1, errno, "Could not set permissions on %s", fnm);
+ return;
+}
+
+/*
+ * file_write()
+ * Write/copy a file (during copy or archive extract). This routine knows
+ * how to copy files with lseek holes in it. (Which are read as file
+ * blocks containing all 0's but do not have any file blocks associated
+ * with the data). Typical examples of these are files created by dbm
+ * variants (.pag files). While the file size of these files are huge, the
+ * actual storage is quite small (the files are sparse). The problem is
+ * the holes read as all zeros so are probably stored on the archive that
+ * way (there is no way to determine if the file block is really a hole,
+ * we only know that a file block of all zero's can be a hole).
+ * At this writing, no major archive format knows how to archive files
+ * with holes. However, on extraction (or during copy, -rw) we have to
+ * deal with these files. Without detecting the holes, the files can
+ * consume a lot of file space if just written to disk. This replacement
+ * for write when passed the basic allocation size of a file system block,
+ * uses lseek whenever it detects the input data is all 0 within that
+ * file block. In more detail, the strategy is as follows:
+ * While the input is all zero keep doing an lseek. Keep track of when we
+ * pass over file block boundaries. Only write when we hit a non zero
+ * input. once we have written a file block, we continue to write it to
+ * the end (we stop looking at the input). When we reach the start of the
+ * next file block, start checking for zero blocks again. Working on file
+ * block boundaries significantly reduces the overhead when copying files
+ * that are NOT very sparse. This overhead (when compared to a write) is
+ * almost below the measurement resolution on many systems. Without it,
+ * files with holes cannot be safely copied. It does has a side effect as
+ * it can put holes into files that did not have them before, but that is
+ * not a problem since the file contents are unchanged (in fact it saves
+ * file space). (Except on paging files for diskless clients. But since we
+ * cannot determine one of those file from here, we ignore them). If this
+ * ever ends up on a system where CTG files are supported and the holes
+ * are not desired, just do a conditional test in those routines that
+ * call file_write() and have it call write() instead. BEFORE CLOSING THE
+ * FILE, make sure to call file_flush() when the last write finishes with
+ * an empty block. A lot of file systems will not create an lseek hole at
+ * the end. In this case we drop a single 0 at the end to force the
+ * trailing 0's in the file.
+ * ---Parameters---
+ * rem: how many bytes left in this file system block
+ * isempt: have we written to the file block yet (is it empty)
+ * sz: basic file block allocation size
+ * cnt: number of bytes on this write
+ * str: buffer to write
+ * Return:
+ * number of bytes written, -1 on write (or lseek) error.
+ */
+
+int
+file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz,
+ char *name)
+{
+ char *pt;
+ char *end;
+ int wcnt;
+ char *st = str;
+ char **strp;
+
+ /*
+ * while we have data to process
+ */
+ while (cnt) {
+ if (!*rem) {
+ /*
+ * We are now at the start of file system block again
+ * (or what we think one is...). start looking for
+ * empty blocks again
+ */
+ *isempt = 1;
+ *rem = sz;
+ }
+
+ /*
+ * only examine up to the end of the current file block or
+ * remaining characters to write, whatever is smaller
+ */
+ wcnt = MIN(cnt, *rem);
+ cnt -= wcnt;
+ *rem -= wcnt;
+ if (*isempt) {
+ /*
+ * have not written to this block yet, so we keep
+ * looking for zero's
+ */
+ pt = st;
+ end = st + wcnt;
+
+ /*
+ * look for a zero filled buffer
+ */
+ while ((pt < end) && (*pt == '\0'))
+ ++pt;
+
+ if (pt == end) {
+ /*
+ * skip, buf is empty so far
+ */
+ if (fd > -1 &&
+ lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
+ syswarn(1,errno,"File seek on %s",
+ name);
+ return(-1);
+ }
+ st = pt;
+ continue;
+ }
+ /*
+ * drat, the buf is not zero filled
+ */
+ *isempt = 0;
+ }
+
+ /*
+ * have non-zero data in this file system block, have to write
+ */
+ switch (fd) {
+ case -1:
+ strp = &gnu_name_string;
+ break;
+ case -2:
+ strp = &gnu_link_string;
+ break;
+ default:
+ strp = NULL;
+ break;
+ }
+ if (strp) {
+ if (*strp)
+ err(1, "WARNING! Major Internal Error! GNU hack Failing!");
+ *strp = malloc(wcnt + 1);
+ if (*strp == NULL) {
+ paxwarn(1, "Out of memory");
+ return(-1);
+ }
+ memcpy(*strp, st, wcnt);
+ (*strp)[wcnt] = '\0';
+ break;
+ } else if (write(fd, st, wcnt) != wcnt) {
+ syswarn(1, errno, "Failed write to file %s", name);
+ return(-1);
+ }
+ st += wcnt;
+ }
+ return(st - str);
+}
+
+/*
+ * file_flush()
+ * when the last file block in a file is zero, many file systems will not
+ * let us create a hole at the end. To get the last block with zeros, we
+ * write the last BYTE with a zero (back up one byte and write a zero).
+ */
+
+void
+file_flush(int fd, char *fname, int isempt)
+{
+ static char blnk[] = "\0";
+
+ /*
+ * silly test, but make sure we are only called when the last block is
+ * filled with all zeros.
+ */
+ if (!isempt)
+ return;
+
+ /*
+ * move back one byte and write a zero
+ */
+ if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
+ syswarn(1, errno, "Failed seek on file %s", fname);
+ return;
+ }
+
+ if (write(fd, blnk, 1) < 0)
+ syswarn(1, errno, "Failed write to file %s", fname);
+ return;
+}
+
+/*
+ * rdfile_close()
+ * close a file we have beed reading (to copy or archive). If we have to
+ * reset access time (tflag) do so (the times are stored in arcn).
+ */
+
+void
+rdfile_close(ARCHD *arcn, int *fd)
+{
+ /*
+ * make sure the file is open
+ */
+ if (*fd < 0)
+ return;
+
+ (void)close(*fd);
+ *fd = -1;
+ if (!tflag)
+ return;
+
+ /*
+ * user wants last access time reset
+ */
+ set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1);
+ return;
+}
+
+/*
+ * set_crc()
+ * read a file to calculate its crc. This is a real drag. Archive formats
+ * that have this, end up reading the file twice (we have to write the
+ * header WITH the crc before writing the file contents. Oh well...
+ * Return:
+ * 0 if was able to calculate the crc, -1 otherwise
+ */
+
+int
+set_crc(ARCHD *arcn, int fd)
+{
+ int i;
+ int res;
+ off_t cpcnt = 0L;
+ u_long size;
+ u_int32_t crc = 0;
+ char tbuf[FILEBLK];
+ struct stat sb;
+
+ if (fd < 0) {
+ /*
+ * hmm, no fd, should never happen. well no crc then.
+ */
+ arcn->crc = 0L;
+ return(0);
+ }
+
+ if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf))
+ size = (u_long)sizeof(tbuf);
+
+ /*
+ * read all the bytes we think that there are in the file. If the user
+ * is trying to archive an active file, forget this file.
+ */
+ for (;;) {
+ if ((res = read(fd, tbuf, size)) <= 0)
+ break;
+ cpcnt += res;
+ for (i = 0; i < res; ++i)
+ crc += (tbuf[i] & 0xff);
+ }
+
+ /*
+ * safety check. we want to avoid archiving files that are active as
+ * they can create inconsistent archive copies.
+ */
+ if (cpcnt != arcn->sb.st_size)
+ paxwarn(1, "File changed size %s", arcn->org_name);
+ else if (fstat(fd, &sb) < 0)
+ syswarn(1, errno, "Failed stat on %s", arcn->org_name);
+ else if (arcn->sb.st_mtime != sb.st_mtime)
+ paxwarn(1, "File %s was modified during read", arcn->org_name);
+ else if (lseek(fd, (off_t)0L, SEEK_SET) < 0)
+ syswarn(1, errno, "File rewind failed on: %s", arcn->org_name);
+ else {
+ arcn->crc = crc;
+ return(0);
+ }
+ return(-1);
+}
diff --git a/file_cmds/pax/ftree.c b/file_cmds/pax/ftree.c
new file mode 100644
index 0000000..f274241
--- /dev/null
+++ b/file_cmds/pax/ftree.c
@@ -0,0 +1,585 @@
+/* $OpenBSD: ftree.c,v 1.28 2008/05/06 06:54:28 henning Exp $ */
+/* $NetBSD: ftree.c,v 1.4 1995/03/21 09:07:21 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: ftree.c,v 1.28 2008/05/06 06:54:28 henning Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fts.h>
+#include "pax.h"
+#include "ftree.h"
+#include "extern.h"
+
+/*
+ * routines to interface with the fts library function.
+ *
+ * file args supplied to pax are stored on a single linked list (of type FTREE)
+ * and given to fts to be processed one at a time. pax "selects" files from
+ * the expansion of each arg into the corresponding file tree (if the arg is a
+ * directory, otherwise the node itself is just passed to pax). The selection
+ * is modified by the -n and -u flags. The user is informed when a specific
+ * file arg does not generate any selected files. -n keeps expanding the file
+ * tree arg until one of its files is selected, then skips to the next file
+ * arg. when the user does not supply the file trees as command line args to
+ * pax, they are read from stdin
+ */
+
+static FTS *ftsp = NULL; /* current FTS handle */
+static int ftsopts; /* options to be used on fts_open */
+static char *farray[2]; /* array for passing each arg to fts */
+static FTREE *fthead = NULL; /* head of linked list of file args */
+static FTREE *fttail = NULL; /* tail of linked list of file args */
+static FTREE *ftcur = NULL; /* current file arg being processed */
+static FTSENT *ftent = NULL; /* current file tree entry */
+static int ftree_skip; /* when set skip to next file arg */
+
+static int ftree_arg(void);
+static char *getpathname(char *, int);
+
+/*
+ * ftree_start()
+ * initialize the options passed to fts_open() during this run of pax
+ * options are based on the selection of pax options by the user
+ * fts_start() also calls fts_arg() to open the first valid file arg. We
+ * also attempt to reset directory access times when -t (tflag) is set.
+ * Return:
+ * 0 if there is at least one valid file arg to process, -1 otherwise
+ */
+
+int
+ftree_start(void)
+{
+ /*
+ * set up the operation mode of fts, open the first file arg. We must
+ * use FTS_NOCHDIR, as the user may have to open multiple archives and
+ * if fts did a chdir off into the boondocks, we may create an archive
+ * volume in an place where the user did not expect to.
+ */
+ ftsopts = FTS_NOCHDIR;
+
+ /*
+ * optional user flags that effect file traversal
+ * -H command line symlink follow only (half follow)
+ * -L follow sylinks (logical)
+ * -P do not follow sylinks (physical). This is the default.
+ * -X do not cross over mount points
+ * -t preserve access times on files read.
+ * -n select only the first member of a file tree when a match is found
+ * -d do not extract subtrees rooted at a directory arg.
+ */
+ if (Lflag)
+ ftsopts |= FTS_LOGICAL;
+ else
+ ftsopts |= FTS_PHYSICAL;
+ if (Hflag)
+ ftsopts |= FTS_COMFOLLOW;
+ if (Xflag)
+ ftsopts |= FTS_XDEV;
+
+ if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
+ paxwarn(1, "Unable to allocate memory for file name buffer");
+ return(-1);
+ }
+
+ if (ftree_arg() < 0)
+ return(-1);
+ if (tflag && (atdir_start() < 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * ftree_add()
+ * add the arg to the linked list of files to process. Each will be
+ * processed by fts one at a time
+ * Return:
+ * 0 if added to the linked list, -1 if failed
+ */
+
+int
+ftree_add(char *str, int chflg)
+{
+ FTREE *ft;
+ int len;
+
+ /*
+ * simple check for bad args
+ */
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(0, "Invalid file name argument");
+ return(-1);
+ }
+
+ /*
+ * allocate FTREE node and add to the end of the linked list (args are
+ * processed in the same order they were passed to pax). Get rid of any
+ * trailing / the user may pass us. (watch out for / by itself).
+ */
+ if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
+ paxwarn(0, "Unable to allocate memory for filename");
+ return(-1);
+ }
+
+ if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
+ str[len] = '\0';
+ ft->fname = str;
+ ft->refcnt = 0;
+ ft->newercnt = 0;
+ ft->chflg = chflg;
+ ft->fow = NULL;
+ if (fthead == NULL) {
+ fttail = fthead = ft;
+ return(0);
+ }
+ fttail->fow = ft;
+ fttail = ft;
+ return(0);
+}
+
+/*
+ * ftree_sel()
+ * this entry has been selected by pax. bump up reference count and handle
+ * -n and -d processing.
+ */
+
+void
+ftree_sel(ARCHD *arcn)
+{
+ /*
+ * set reference bit for this pattern. This linked list is only used
+ * when file trees are supplied pax as args. The list is not used when
+ * the trees are read from stdin.
+ */
+ if (ftcur != NULL)
+ ftcur->refcnt = 1;
+
+ /*
+ * if -n we are done with this arg, force a skip to the next arg when
+ * pax asks for the next file in next_file().
+ * if -d we tell fts only to match the directory (if the arg is a dir)
+ * and not the entire file tree rooted at that point.
+ */
+ if (nflag)
+ ftree_skip = 1;
+
+ if (!dflag || (arcn->type != PAX_DIR))
+ return;
+
+ if (ftent != NULL)
+ (void)fts_set(ftsp, ftent, FTS_SKIP);
+}
+
+/*
+ * ftree_notsel()
+ * this entry has not been selected by pax.
+ */
+
+void
+ftree_notsel()
+{
+ if (ftent != NULL)
+ (void)fts_set(ftsp, ftent, FTS_SKIP);
+}
+
+/*
+ * ftree_skipped_newer()
+ * file has been skipped because a newer file exists and -u/-D given
+ */
+
+void
+ftree_skipped_newer(ARCHD *arcn)
+{
+ /* skipped due to -u/-D, mark accordingly */
+ if (ftcur != NULL)
+ ftcur->newercnt = 1;
+}
+
+/*
+ * ftree_chk()
+ * called at end on pax execution. Prints all those file args that did not
+ * have a selected member (reference count still 0)
+ */
+
+void
+ftree_chk(void)
+{
+ FTREE *ft;
+ int wban = 0;
+
+ /*
+ * make sure all dir access times were reset.
+ */
+ if (tflag)
+ atdir_end();
+
+ /*
+ * walk down list and check reference count. Print out those members
+ * that never had a match
+ */
+ for (ft = fthead; ft != NULL; ft = ft->fow) {
+ if ((ft->refcnt > 0) || ft->newercnt > 0 || ft->chflg)
+ continue;
+ if (wban == 0) {
+ paxwarn(1,"WARNING! These file names were not selected:");
+ ++wban;
+ }
+ (void)fprintf(stderr, "%s\n", ft->fname);
+ }
+}
+
+/*
+ * ftree_arg()
+ * Get the next file arg for fts to process. Can be from either the linked
+ * list or read from stdin when the user did not them as args to pax. Each
+ * arg is processed until the first successful fts_open().
+ * Return:
+ * 0 when the next arg is ready to go, -1 if out of file args (or EOF on
+ * stdin).
+ */
+
+static int
+ftree_arg(void)
+{
+
+ /*
+ * close off the current file tree
+ */
+ if (ftsp != NULL) {
+ (void)fts_close(ftsp);
+ ftsp = NULL;
+ }
+
+ /*
+ * keep looping until we get a valid file tree to process. Stop when we
+ * reach the end of the list (or get an eof on stdin)
+ */
+ for (;;) {
+ if (fthead == NULL) {
+ /*
+ * the user didn't supply any args, get the file trees
+ * to process from stdin;
+ */
+ if (getpathname(farray[0], PAXPATHLEN+1) == NULL)
+ return(-1);
+ } else {
+ /*
+ * the user supplied the file args as arguments to pax
+ */
+ if (ftcur == NULL)
+ ftcur = fthead;
+ else if ((ftcur = ftcur->fow) == NULL)
+ return(-1);
+ if (ftcur->chflg) {
+ /* First fchdir() back... */
+ if (fdochdir(cwdfd) < 0) {
+ syswarn(1, errno,
+ "Can't fchdir to starting directory");
+ return(-1);
+ }
+ if (dochdir(ftcur->fname) < 0) {
+ syswarn(1, errno, "Can't chdir to %s",
+ ftcur->fname);
+ return(-1);
+ }
+ continue;
+ } else
+ farray[0] = ftcur->fname;
+ }
+
+ /*
+ * watch it, fts wants the file arg stored in a array of char
+ * ptrs, with the last one a null. we use a two element array
+ * and set farray[0] to point at the buffer with the file name
+ * in it. We cannot pass all the file args to fts at one shot
+ * as we need to keep a handle on which file arg generates what
+ * files (the -n and -d flags need this). If the open is
+ * successful, return a 0.
+ */
+ if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
+ break;
+ }
+ return(0);
+}
+
+/*
+ * next_file()
+ * supplies the next file to process in the supplied archd structure.
+ * Return:
+ * 0 when contents of arcn have been set with the next file, -1 when done.
+ */
+
+int
+next_file(ARCHD *arcn)
+{
+ int cnt;
+ time_t atime;
+ time_t mtime;
+
+ /*
+ * ftree_sel() might have set the ftree_skip flag if the user has the
+ * -n option and a file was selected from this file arg tree. (-n says
+ * only one member is matched for each pattern) ftree_skip being 1
+ * forces us to go to the next arg now.
+ */
+ if (ftree_skip) {
+ /*
+ * clear and go to next arg
+ */
+ ftree_skip = 0;
+ if (ftree_arg() < 0)
+ return(-1);
+ }
+
+ /*
+ * loop until we get a valid file to process
+ */
+ for (;;) {
+ if ((ftent = fts_read(ftsp)) == NULL) {
+ if (errno)
+ syswarn(1, errno, "next_file");
+ /*
+ * out of files in this tree, go to next arg, if none
+ * we are done
+ */
+ if (ftree_arg() < 0)
+ return(-1);
+ continue;
+ }
+
+ /*
+ * handle each type of fts_read() flag
+ */
+ switch (ftent->fts_info) {
+ case FTS_D:
+ case FTS_DEFAULT:
+ case FTS_F:
+ case FTS_SL:
+ /*
+ * these are all ok
+ */
+ break;
+ case FTS_SLNONE: /* was same as above cases except Unix
+ conformance requires this error check */
+ if (Hflag || Lflag) { /* -H or -L was specified */
+ if (ftent->fts_errno)
+ paxwarn(1, "%s: %s",
+ ftent->fts_name, strerror(ftent->fts_errno));
+ }
+ break;
+ case FTS_DP:
+ /*
+ * already saw this directory. If the user wants file
+ * access times reset, we use this to restore the
+ * access time for this directory since this is the
+ * last time we will see it in this file subtree
+ * remember to force the time (this is -t on a read
+ * directory, not a created directory).
+ */
+ if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
+ ftent->fts_statp->st_ino, &mtime, &atime) < 0))
+ continue;
+ set_ftime(ftent->fts_path, mtime, atime, 1);
+ continue;
+ case FTS_DC:
+ /*
+ * fts claims a file system cycle
+ */
+ paxwarn(1,"File system cycle found at %s",ftent->fts_path);
+ continue;
+ case FTS_DNR:
+ syswarn(1, ftent->fts_errno,
+ "Unable to read directory %s", ftent->fts_path);
+ continue;
+ case FTS_ERR:
+ syswarn(1, ftent->fts_errno,
+ "File system traversal error");
+ continue;
+ case FTS_NS:
+ case FTS_NSOK:
+ syswarn(1, ftent->fts_errno,
+ "Unable to access %s", ftent->fts_path);
+ continue;
+ }
+
+ /*
+ * ok got a file tree node to process. copy info into arcn
+ * structure (initialize as required)
+ */
+ arcn->skip = 0;
+ arcn->pad = 0;
+ arcn->ln_nlen = 0;
+ arcn->ln_name[0] = '\0';
+ memcpy(&arcn->sb, ftent->fts_statp, sizeof(arcn->sb));
+
+ /*
+ * file type based set up and copy into the arcn struct
+ * SIDE NOTE:
+ * we try to reset the access time on all files and directories
+ * we may read when the -t flag is specified. files are reset
+ * when we close them after copying. we reset the directories
+ * when we are done with their file tree (we also clean up at
+ * end in case we cut short a file tree traversal). However
+ * there is no way to reset access times on symlinks.
+ */
+ switch (S_IFMT & arcn->sb.st_mode) {
+ case S_IFDIR:
+ arcn->type = PAX_DIR;
+ if (!tflag)
+ break;
+ add_atdir(ftent->fts_path, arcn->sb.st_dev,
+ arcn->sb.st_ino, arcn->sb.st_mtime,
+ arcn->sb.st_atime);
+ break;
+ case S_IFCHR:
+ arcn->type = PAX_CHR;
+ break;
+ case S_IFBLK:
+ arcn->type = PAX_BLK;
+ break;
+ case S_IFREG:
+ /*
+ * only regular files with have data to store on the
+ * archive. all others will store a zero length skip.
+ * the skip field is used by pax for actual data it has
+ * to read (or skip over).
+ */
+ arcn->type = PAX_REG;
+ arcn->skip = arcn->sb.st_size;
+ break;
+ case S_IFLNK:
+ arcn->type = PAX_SLK;
+ /*
+ * have to read the symlink path from the file
+ */
+ if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
+ PAXPATHLEN)) < 0) {
+ syswarn(1, errno, "Unable to read symlink %s",
+ ftent->fts_path);
+ continue;
+ }
+ /*
+ * set link name length, watch out readlink does not
+ * always NUL terminate the link path
+ */
+ arcn->ln_name[cnt] = '\0';
+ arcn->ln_nlen = cnt;
+ break;
+ case S_IFSOCK:
+ /*
+ * under BSD storing a socket is senseless but we will
+ * let the format specific write function make the
+ * decision of what to do with it.
+ */
+ arcn->type = PAX_SCK;
+ break;
+ case S_IFIFO:
+ arcn->type = PAX_FIF;
+ break;
+ }
+ break;
+ }
+
+ /*
+ * copy file name, set file name length
+ */
+ arcn->nlen = strlcpy(arcn->name, ftent->fts_path, sizeof(arcn->name));
+ if (arcn->nlen >= sizeof(arcn->name))
+ arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */
+ arcn->org_name = ftent->fts_path;
+ return(0);
+}
+
+/*
+ * getpathname()
+ * Reads a pathname from stdin, handling NUL- or newline-termination.
+ * Return:
+ * NULL at end of file, otherwise the NUL-terminated buffer.
+ */
+
+static char *
+getpathname(char *buf, int buflen)
+{
+ char *bp, *ep;
+ int ch, term;
+
+ if (zeroflag) {
+ /*
+ * Read a NUL-terminated pathname, being especially
+ * paranoid about proper termination and pathname length.
+ */
+ for (bp = buf, ep = buf + buflen; bp < ep; bp++) {
+ if ((ch = getchar()) == EOF) {
+ if (bp != buf)
+ paxwarn(1, "Ignoring unterminated "
+ "pathname at EOF");
+ return(NULL);
+ }
+ if ((*bp = ch) == '\0')
+ return(buf);
+ }
+ /* Too long - skip this path */
+ *--bp = '\0';
+ term = '\0';
+ } else {
+ if (fgets(buf, buflen, stdin) == NULL)
+ return(NULL);
+ if ((bp = strchr(buf, '\n')) != NULL || feof(stdin)) {
+ if (bp != NULL)
+ *bp = '\0';
+ return(buf);
+ }
+ /* Too long - skip this path */
+ term = '\n';
+ }
+ while ((ch = getchar()) != term && ch != EOF)
+ ;
+ paxwarn(1, "Ignoring too-long pathname: %s", buf);
+ return(NULL);
+}
diff --git a/file_cmds/pax/ftree.h b/file_cmds/pax/ftree.h
new file mode 100644
index 0000000..0203815
--- /dev/null
+++ b/file_cmds/pax/ftree.h
@@ -0,0 +1,56 @@
+/* $OpenBSD: ftree.h,v 1.5 2008/05/06 06:54:28 henning Exp $ */
+/* $NetBSD: ftree.h,v 1.3 1995/03/21 09:07:23 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftree.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _FTREE_H_
+#define _FTREE_H_
+
+/*
+ * Data structure used by the ftree.c routines to store the file args to be
+ * handed to fts(). It keeps a reference count of which args generated a
+ * "selected" member
+ */
+
+typedef struct ftree {
+ char *fname; /* file tree name */
+ int refcnt; /* has tree had a selected file? */
+ int newercnt; /* skipped due to -u/-D */
+ int chflg; /* change directory flag */
+ struct ftree *fow; /* pointer to next entry on list */
+} FTREE;
+
+#endif /* _FTREE_H_ */
diff --git a/file_cmds/pax/gen_subs.c b/file_cmds/pax/gen_subs.c
new file mode 100644
index 0000000..cf821fb
--- /dev/null
+++ b/file_cmds/pax/gen_subs.c
@@ -0,0 +1,458 @@
+/* $OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $ */
+/* $NetBSD: gen_subs.c,v 1.5 1995/03/21 09:07:26 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vis.h>
+#include <langinfo.h>
+#include "pax.h"
+#include "extern.h"
+
+/*
+ * a collection of general purpose subroutines used by pax
+ */
+
+/*
+ * constants used by ls_list() when printing out archive members
+ */
+#define MODELEN 20
+#define DATELEN 64
+#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY)
+#define CURFRMTM "%b %e %H:%M"
+#define OLDFRMTM "%b %e %Y"
+#define CURFRMTD "%e %b %H:%M"
+#define OLDFRMTD "%e %b %Y"
+#define NAME_WIDTH 8
+
+static int d_first = -1;
+
+/*
+ * ls_list()
+ * list the members of an archive in ls format
+ */
+
+void
+ls_list(ARCHD *arcn, time_t now, FILE *fp)
+{
+ struct stat *sbp;
+ char f_mode[MODELEN];
+ char f_date[DATELEN];
+ const char *timefrmt;
+ int term;
+
+ term = zeroflag ? '\0' : '\n'; /* path termination character */
+
+ /*
+ * if not verbose, just print the file name
+ */
+ if (!vflag) {
+ if (zeroflag)
+ (void)fputs(arcn->name, fp);
+ else
+ safe_print(arcn->name, fp);
+ (void)putc(term, fp);
+ (void)fflush(fp);
+ return;
+ }
+
+ if (pax_list_opt_format) {
+ pax_format_list_output(arcn, now, fp, term);
+ return;
+ }
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ /*
+ * user wants long mode
+ */
+ sbp = &(arcn->sb);
+ strmode(sbp->st_mode, f_mode);
+
+ /*
+ * time format based on age compared to the time pax was started.
+ */
+ if ((sbp->st_mtime + SIXMONTHS) <= now ||
+ sbp->st_mtime > now)
+ timefrmt = d_first ? OLDFRMTD : OLDFRMTM;
+ else
+ timefrmt = d_first ? CURFRMTD : CURFRMTM;
+
+ /*
+ * print file mode, link count, uid, gid and time
+ */
+ if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0)
+ f_date[0] = '\0';
+#define UT_NAMESIZE 8
+ (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, sbp->st_nlink,
+ NAME_WIDTH, UT_NAMESIZE, name_uid(sbp->st_uid, 1),
+ NAME_WIDTH, UT_NAMESIZE, name_gid(sbp->st_gid, 1));
+
+ /*
+ * print device id's for devices, or sizes for other nodes
+ */
+ if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK))
+# ifdef LONG_OFF_T
+ (void)fprintf(fp, "%4u,%4u ", MAJOR(sbp->st_rdev),
+# else
+ (void)fprintf(fp, "%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev),
+# endif
+ (unsigned long)MINOR(sbp->st_rdev));
+ else {
+ /*
+ * UNIX compliance fix: printing filename length for soft links
+ * from arcn->ln_nlen instead of sbp->st_size, which is 0.
+ */
+ off_t nlen;
+ if (arcn->type == PAX_SLK) {
+ nlen = arcn->ln_nlen;
+ } else {
+ nlen = sbp->st_size;
+ }
+# ifdef LONG_OFF_T
+ (void)fprintf(fp, "%9lu ", nlen);
+# else
+ (void)fprintf(fp, "%9qu ", nlen);
+# endif
+ }
+
+ /*
+ * print name and link info for hard and soft links
+ */
+ (void)fputs(f_date, fp);
+ (void)putc(' ', fp);
+ safe_print(arcn->name, fp);
+ if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
+ fputs(" == ", fp);
+ safe_print(arcn->ln_name, fp);
+ } else if (arcn->type == PAX_SLK) {
+ fputs(" -> ", fp);
+ safe_print(arcn->ln_name, fp);
+ }
+ (void)putc(term, fp);
+ (void)fflush(fp);
+ return;
+}
+
+/*
+ * tty_ls()
+ * print a short summary of file to tty.
+ */
+
+void
+ls_tty(ARCHD *arcn)
+{
+ char f_date[DATELEN];
+ char f_mode[MODELEN];
+ const char *timefrmt;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+
+ if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL))
+ timefrmt = d_first ? OLDFRMTD : OLDFRMTM;
+ else
+ timefrmt = d_first ? CURFRMTD : CURFRMTM;
+
+ /*
+ * convert time to string, and print
+ */
+ if (strftime(f_date, DATELEN, timefrmt,
+ localtime(&(arcn->sb.st_mtime))) == 0)
+ f_date[0] = '\0';
+ strmode(arcn->sb.st_mode, f_mode);
+ tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name);
+ return;
+}
+
+void
+safe_print(const char *str, FILE *fp)
+{
+ /*
+ * if printing to a tty, use strvis(3) to print special characters.
+ */
+ if (isatty(fileno(fp))) {
+ /*
+ * The size of visbuf must be four times the number
+ * of bytes encoded from str (plus one for the NUL).
+ */
+ char *visbuf = (char *) malloc(sizeof(char) * (4 * strlen(str) + 1));
+ if (visbuf == NULL) {
+ paxwarn(1, "Out of memory");
+ return;
+ }
+ /*
+ * using strvis(3) instead of vis(3) to account for multibyte
+ * characters
+ */
+ (void)strvis(visbuf, str, VIS_CSTYLE);
+ (void)fputs(visbuf, fp);
+ free(visbuf);
+ } else {
+ (void)fputs(str, fp);
+ }
+}
+
+/*
+ * asc_ul()
+ * convert hex/octal character string into a u_long. We do not have to
+ * check for overflow! (the headers in all supported formats are not large
+ * enough to create an overflow).
+ * NOTE: strings passed to us are NOT TERMINATED.
+ * Return:
+ * unsigned long value
+ */
+
+u_long
+asc_ul(char *str, int len, int base)
+{
+ char *stop;
+ u_long tval = 0;
+
+ stop = str + len;
+
+ /*
+ * skip over leading blanks and zeros
+ */
+ while ((str < stop) && ((*str == ' ') || (*str == '0')))
+ ++str;
+
+ /*
+ * for each valid digit, shift running value (tval) over to next digit
+ * and add next digit
+ */
+ if (base == HEX) {
+ while (str < stop) {
+ if ((*str >= '0') && (*str <= '9'))
+ tval = (tval << 4) + (*str++ - '0');
+ else if ((*str >= 'A') && (*str <= 'F'))
+ tval = (tval << 4) + 10 + (*str++ - 'A');
+ else if ((*str >= 'a') && (*str <= 'f'))
+ tval = (tval << 4) + 10 + (*str++ - 'a');
+ else
+ break;
+ }
+ } else {
+ while ((str < stop) && (*str >= '0') && (*str <= '7'))
+ tval = (tval << 3) + (*str++ - '0');
+ }
+ return(tval);
+}
+
+/*
+ * ul_asc()
+ * convert an unsigned long into an hex/oct ascii string. pads with LEADING
+ * ascii 0's to fill string completely
+ * NOTE: the string created is NOT TERMINATED.
+ */
+
+int
+ul_asc(u_long val, char *str, int len, int base)
+{
+ char *pt;
+ u_long digit;
+
+ /*
+ * WARNING str is not '\0' terminated by this routine
+ */
+ pt = str + len - 1;
+
+ /*
+ * do a tailwise conversion (start at right most end of string to place
+ * least significant digit). Keep shifting until conversion value goes
+ * to zero (all digits were converted)
+ */
+ if (base == HEX) {
+ while (pt >= str) {
+ if ((digit = (val & 0xf)) < 10)
+ *pt-- = '0' + (char)digit;
+ else
+ *pt-- = 'a' + (char)(digit - 10);
+ if ((val = (val >> 4)) == (u_long)0)
+ break;
+ }
+ } else {
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = (val >> 3)) == (u_long)0)
+ break;
+ }
+ }
+
+ /*
+ * pad with leading ascii ZEROS. We return -1 if we ran out of space.
+ */
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_long)0)
+ return(-1);
+ return(0);
+}
+
+#ifndef LONG_OFF_T
+/*
+ * asc_uqd()
+ * convert hex/octal character string into a u_quad_t. We do not have to
+ * check for overflow! (the headers in all supported formats are not large
+ * enough to create an overflow).
+ * NOTE: strings passed to us are NOT TERMINATED.
+ * Return:
+ * u_quad_t value
+ */
+
+u_quad_t
+asc_uqd(char *str, int len, int base)
+{
+ char *stop;
+ u_quad_t tval = 0;
+
+ stop = str + len;
+
+ /*
+ * skip over leading blanks and zeros
+ */
+ while ((str < stop) && ((*str == ' ') || (*str == '0')))
+ ++str;
+
+ /*
+ * for each valid digit, shift running value (tval) over to next digit
+ * and add next digit
+ */
+ if (base == HEX) {
+ while (str < stop) {
+ if ((*str >= '0') && (*str <= '9'))
+ tval = (tval << 4) + (*str++ - '0');
+ else if ((*str >= 'A') && (*str <= 'F'))
+ tval = (tval << 4) + 10 + (*str++ - 'A');
+ else if ((*str >= 'a') && (*str <= 'f'))
+ tval = (tval << 4) + 10 + (*str++ - 'a');
+ else
+ break;
+ }
+ } else {
+ while ((str < stop) && (*str >= '0') && (*str <= '7'))
+ tval = (tval << 3) + (*str++ - '0');
+ }
+ return(tval);
+}
+
+/*
+ * uqd_asc()
+ * convert an u_quad_t into a hex/oct ascii string. pads with LEADING
+ * ascii 0's to fill string completely
+ * NOTE: the string created is NOT TERMINATED.
+ */
+
+int
+uqd_asc(u_quad_t val, char *str, int len, int base)
+{
+ char *pt;
+ u_quad_t digit;
+
+ /*
+ * WARNING str is not '\0' terminated by this routine
+ */
+ pt = str + len - 1;
+
+ /*
+ * do a tailwise conversion (start at right most end of string to place
+ * least significant digit). Keep shifting until conversion value goes
+ * to zero (all digits were converted)
+ */
+ if (base == HEX) {
+ while (pt >= str) {
+ if ((digit = (val & 0xf)) < 10)
+ *pt-- = '0' + (char)digit;
+ else
+ *pt-- = 'a' + (char)(digit - 10);
+ if ((val = (val >> 4)) == (u_quad_t)0)
+ break;
+ }
+ } else {
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = (val >> 3)) == (u_quad_t)0)
+ break;
+ }
+ }
+
+ /*
+ * pad with leading ascii ZEROS. We return -1 if we ran out of space.
+ */
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_quad_t)0)
+ return(-1);
+ return(0);
+}
+#endif
+
+/*
+ * Copy at max min(bufz, fieldsz) chars from field to buf, stopping
+ * at the first NUL char. NUL terminate buf if there is room left.
+ */
+size_t
+fieldcpy(char *buf, size_t bufsz, const char *field, size_t fieldsz)
+{
+ char *p = buf;
+ const char *q = field;
+ size_t i = 0;
+
+ if (fieldsz > bufsz)
+ fieldsz = bufsz;
+ while (i < fieldsz && *q != '\0') {
+ *p++ = *q++;
+ i++;
+ }
+ if (i < bufsz)
+ *p = '\0';
+ return(i);
+}
diff --git a/file_cmds/pax/getoldopt.c b/file_cmds/pax/getoldopt.c
new file mode 100644
index 0000000..3133f62
--- /dev/null
+++ b/file_cmds/pax/getoldopt.c
@@ -0,0 +1,74 @@
+/* $OpenBSD: getoldopt.c,v 1.8 2003/07/02 21:19:33 deraadt Exp $ */
+/* $NetBSD: getoldopt.c,v 1.3 1995/03/21 09:07:28 cgd Exp $ */
+
+/*
+ * Plug-compatible replacement for getopt() for parsing tar-like
+ * arguments. If the first argument begins with "-", it uses getopt;
+ * otherwise, it uses the old rules used by tar, dump, and ps.
+ *
+ * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed
+ * in the Public Domain for your edification and enjoyment.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char rcsid[] = "$OpenBSD: getoldopt.c,v 1.8 2003/07/02 21:19:33 deraadt Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "pax.h"
+#include "extern.h"
+
+int
+getoldopt(int argc, char **argv, const char *optstring)
+{
+ static char *key; /* Points to next keyletter */
+ static char use_getopt; /* !=0 if argv[1][0] was '-' */
+ char c;
+ char *place;
+
+ optarg = NULL;
+
+ if (key == NULL) { /* First time */
+ if (argc < 2)
+ return (-1);
+ key = argv[1];
+ if (*key == '-')
+ use_getopt++;
+ else
+ optind = 2;
+ }
+
+ if (use_getopt)
+ return (getopt(argc, argv, optstring));
+
+ c = *key++;
+ if (c == '\0') {
+ key--;
+ return (-1);
+ }
+ place = strchr(optstring, c);
+
+ if (place == NULL || c == ':') {
+ fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
+ return ('?');
+ }
+
+ place++;
+ if (*place == ':') {
+ if (optind < argc) {
+ optarg = argv[optind];
+ optind++;
+ } else {
+ fprintf(stderr, "%s: %c argument missing\n",
+ argv[0], c);
+ return ('?');
+ }
+ }
+
+ return (c);
+}
diff --git a/file_cmds/pax/options.c b/file_cmds/pax/options.c
new file mode 100644
index 0000000..0544e5e
--- /dev/null
+++ b/file_cmds/pax/options.c
@@ -0,0 +1,1747 @@
+/* $OpenBSD: options.c,v 1.70 2008/06/11 00:49:08 pvalchev Exp $ */
+/* $NetBSD: options.c,v 1.6 1996/03/26 23:54:18 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: options.c,v 1.70 2008/06/11 00:49:08 pvalchev Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <search.h>
+#ifndef __APPLE__
+#include <sys/mtio.h>
+#endif /* __APPLE__ */
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <paths.h>
+#include <getopt.h>
+#include "pax.h"
+#include "options.h"
+#include "cpio.h"
+#include "tar.h"
+#include "extern.h"
+
+/*
+ * Routines which handle command line options
+ */
+
+static char flgch[] = FLGCH; /* list of all possible flags */
+static OPLIST *ophead = NULL; /* head for format specific options -x */
+static OPLIST *optail = NULL; /* option tail */
+
+static int no_op(void);
+static void printflg(unsigned int);
+static int c_frmt(const void *, const void *);
+static off_t str_offt(char *);
+static char *pax_getline(FILE *fp);
+static void pax_options(int, char **);
+void pax_usage(void);
+static void tar_options(int, char **);
+static void tar_usage(void);
+static void cpio_options(int, char **);
+static void cpio_usage(void);
+
+/* errors from getline */
+#define GETLINE_FILE_CORRUPT 1
+#define GETLINE_OUT_OF_MEM 2
+static int getline_error;
+
+
+#define GZIP_CMD "gzip" /* command to run as gzip */
+#define COMPRESS_CMD "compress" /* command to run as compress */
+#define BZIP2_CMD "bzip2" /* command to run as bzip2 */
+
+/*
+ * Format specific routine table - MUST BE IN SORTED ORDER BY NAME
+ * (see pax.h for description of each function)
+ *
+ * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read,
+ * read, end_read, st_write, write, end_write, trail,
+ * rd_data, wr_data, options
+ */
+
+const FSUB fsub[] = {
+/* OLD BINARY CPIO */
+ {"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd,
+ bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail,
+ rd_wrfile, wr_rdfile, bad_opt},
+
+/* OLD OCTAL CHARACTER CPIO */
+ {"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd,
+ cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail,
+ rd_wrfile, wr_rdfile, bad_opt},
+
+/* POSIX 3 PAX */
+ {"pax", 5120, BLKMULT, 0, 1, BLKMULT, 0, pax_id, ustar_strd,
+ pax_rd, tar_endrd, ustar_stwr, pax_wr, tar_endwr, tar_trail,
+ rd_wrfile, wr_rdfile, pax_opt},
+
+/* SVR4 HEX CPIO */
+ {"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd,
+ vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail,
+ rd_wrfile, wr_rdfile, bad_opt},
+
+/* SVR4 HEX CPIO WITH CRC */
+ {"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd,
+ vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail,
+ rd_wrfile, wr_rdfile, bad_opt},
+
+/* OLD TAR */
+ {"tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op,
+ tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail,
+ rd_wrfile, wr_rdfile, tar_opt},
+
+/* POSIX USTAR */
+ {"ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd,
+ ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail,
+ rd_wrfile, wr_rdfile, bad_opt},
+};
+#define F_OCPIO 0 /* format when called as cpio -6 */
+#define F_ACPIO 1 /* format when called as cpio -c */
+#define F_PAX 2 /* -x pax */
+#define F_SCPIO 3 /* -x sv4cpio */
+#define F_CPIO 4 /* format when called as cpio */
+#define F_OTAR 5 /* format when called as tar -o */
+#define F_TAR 6 /* format when called as tar */
+#define DEFLT F_TAR /* default write format from list above */
+
+/*
+ * ford is the archive search order used by get_arc() to determine what kind
+ * of archive we are dealing with. This helps to properly id archive formats
+ * some formats may be subsets of others....
+ */
+int ford[] = {F_PAX, F_TAR, F_OTAR, F_CPIO, F_SCPIO, F_ACPIO, F_OCPIO, -1 };
+
+/*
+ * Do we have -C anywhere?
+ */
+int havechd = 0;
+
+/*
+ * options()
+ * figure out if we are pax, tar or cpio. Call the appropriate options
+ * parser
+ */
+
+void
+options(int argc, char **argv)
+{
+
+ /*
+ * Are we acting like pax, tar or cpio (based on argv[0])
+ */
+ if ((argv0 = strrchr(argv[0], '/')) != NULL)
+ argv0++;
+ else
+ argv0 = argv[0];
+
+ if (strcmp(NM_TAR, argv0) == 0) {
+ tar_options(argc, argv);
+ return;
+ } else if (strcmp(NM_CPIO, argv0) == 0) {
+ cpio_options(argc, argv);
+ return;
+ }
+ /*
+ * assume pax as the default
+ */
+ argv0 = NM_PAX;
+ pax_options(argc, argv);
+}
+
+#define OPT_INSECURE 1
+struct option pax_longopts[] = {
+ { "insecure", no_argument, 0, OPT_INSECURE },
+ { 0, 0, 0, 0 },
+};
+
+/*
+ * pax_options()
+ * look at the user specified flags. set globals as required and check if
+ * the user specified a legal set of flags. If not, complain and exit
+ */
+
+static void
+pax_options(int argc, char **argv)
+{
+ int c;
+ size_t i;
+ unsigned int flg = 0;
+ unsigned int bflg = 0;
+ char *pt;
+ FSUB tmp;
+ size_t n_fsub;
+ char * tmp_name;
+
+ listf = stderr;
+ /*
+ * process option flags
+ */
+ while ((c=getopt_long(argc,argv,"0ab:cdf:ijklno:p:rs:tuvwx:zB:DE:G:HLOPT:U:XYZ", pax_longopts, NULL)) != -1) {
+ switch (c) {
+ case '0':
+ /*
+ * Use \0 as pathname terminator.
+ * (For use with the -print0 option of find(1).)
+ */
+ zeroflag = 1;
+ flg |= C0F;
+ break;
+ case 'a':
+ /*
+ * append
+ */
+ flg |= AF;
+ break;
+ case 'b':
+ /*
+ * specify blocksize
+ */
+ flg |= BF;
+ if ((wrblksz = (int)str_offt(optarg)) <= 0) {
+ paxwarn(1, "Invalid block size %s", optarg);
+ pax_usage();
+ }
+ break;
+ case 'c':
+ /*
+ * inverse match on patterns
+ */
+ cflag = 1;
+ flg |= CF;
+ break;
+ case 'd':
+ /*
+ * match only dir on extract, not the subtree at dir
+ */
+ dflag = 1;
+ flg |= DF;
+ break;
+ case 'f':
+ /*
+ * filename where the archive is stored
+ */
+ if ((optarg[0] == '-') && (optarg[1]== '\0')) {
+ /*
+ * treat a - as stdin (like tar)
+ */
+ arcname = NULL;
+ break;
+ }
+ arcname = optarg;
+ flg |= FF;
+ break;
+ case 'i':
+ /*
+ * interactive file rename
+ */
+ iflag = 1;
+ flg |= IF;
+ break;
+ case 'j':
+ /*
+ * use bzip2. Non standard option.
+ */
+ gzip_program = BZIP2_CMD;
+ break;
+ case 'k':
+ /*
+ * do not clobber files that exist
+ */
+ kflag = 1;
+ flg |= KF;
+ break;
+ case 'l':
+ /*
+ * try to link src to dest with copy (-rw)
+ */
+ lflag = 1;
+ flg |= LF;
+ break;
+ case 'n':
+ /*
+ * select first match for a pattern only
+ */
+ nflag = 1;
+ flg |= NF;
+ break;
+ case 'o':
+ /*
+ * pass format specific options
+ */
+ flg |= OF;
+ if (pax_format_opt_add(optarg) < 0)
+ pax_usage();
+ break;
+ case 'p':
+ /*
+ * specify file characteristic options
+ */
+ for (pt = optarg; *pt != '\0'; ++pt) {
+ switch (*pt) {
+ case 'a':
+ /*
+ * do not preserve access time
+ */
+ patime = 0;
+ break;
+ case 'e':
+ /*
+ * preserve user id, group id, file
+ * mode, access/modification times
+ */
+ pids = 1;
+ pmode = 1;
+ patime = 1;
+ pmtime = 1;
+ break;
+ case 'm':
+ /*
+ * do not preserve modification time
+ */
+ pmtime = 0;
+ break;
+ case 'o':
+ /*
+ * preserve uid/gid
+ */
+ pids = 1;
+ break;
+ case 'p':
+ /*
+ * preserve file mode bits
+ */
+ pmode = 1;
+ break;
+ default:
+ paxwarn(1, "Invalid -p string: %c", *pt);
+ pax_usage();
+ break;
+ }
+ }
+ flg |= PF;
+ break;
+ case 'r':
+ /*
+ * read the archive
+ */
+ pax_read_or_list_mode=1;
+ flg |= RF;
+ break;
+ case 's':
+ /*
+ * file name substitution name pattern
+ */
+ if (rep_add(optarg) < 0) {
+ pax_usage();
+ break;
+ }
+ flg |= SF;
+ break;
+ case 't':
+ /*
+ * preserve access time on filesystem nodes we read
+ */
+ tflag = 1;
+ flg |= TF;
+ break;
+ case 'u':
+ /*
+ * ignore those older files
+ */
+ uflag = 1;
+ flg |= UF;
+ break;
+ case 'v':
+ /*
+ * verbose operation mode
+ */
+ vflag = 1;
+ flg |= VF;
+ break;
+ case 'w':
+ /*
+ * write an archive
+ */
+ flg |= WF;
+ break;
+ case 'x':
+ /*
+ * specify an archive format on write
+ */
+ tmp.name = optarg;
+ n_fsub = sizeof(fsub)/sizeof(FSUB);
+ if ((frmt = (FSUB *)bsearch(&tmp, fsub, n_fsub, sizeof(FSUB),
+ c_frmt)) != NULL) {
+ flg |= XF;
+ break;
+ }
+ paxwarn(1, "Unknown -x format: %s", optarg);
+ (void)fputs("pax: Known -x formats are:", stderr);
+ for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i)
+ (void)fprintf(stderr, " %s", fsub[i].name);
+ (void)fputs("\n\n", stderr);
+ pax_usage();
+ break;
+ case 'z':
+ /*
+ * use gzip. Non standard option.
+ */
+ gzip_program = GZIP_CMD;
+ break;
+ case 'B':
+ /*
+ * non-standard option on number of bytes written on a
+ * single archive volume.
+ */
+ if ((wrlimit = str_offt(optarg)) <= 0) {
+ paxwarn(1, "Invalid write limit %s", optarg);
+ pax_usage();
+ }
+ if (wrlimit % BLKMULT) {
+ paxwarn(1, "Write limit is not a %d byte multiple",
+ BLKMULT);
+ pax_usage();
+ }
+ flg |= CBF;
+ break;
+ case 'D':
+ /*
+ * On extraction check file inode change time before the
+ * modification of the file name. Non standard option.
+ */
+ Dflag = 1;
+ flg |= CDF;
+ break;
+ case 'E':
+ /*
+ * non-standard limit on read faults
+ * 0 indicates stop after first error, values
+ * indicate a limit, "NONE" try forever
+ */
+ flg |= CEF;
+ if (strcmp(NONE, optarg) == 0)
+ maxflt = -1;
+ else if ((maxflt = atoi(optarg)) < 0) {
+ paxwarn(1, "Error count value must be positive");
+ pax_usage();
+ }
+ break;
+ case 'G':
+ /*
+ * non-standard option for selecting files within an
+ * archive by group (gid or name)
+ */
+ if (grp_add(optarg) < 0) {
+ pax_usage();
+ break;
+ }
+ flg |= CGF;
+ break;
+ case 'H':
+ /*
+ * follow command line symlinks only
+ */
+ Hflag = 1;
+ flg |= CHF;
+ Lflag = 0; /* -H and -L are mutually exclusive */
+ flg &= ~CLF; /* only use the last one seen */
+ break;
+ case 'L':
+ /*
+ * follow symlinks
+ */
+ Lflag = 1;
+ flg |= CLF;
+ Hflag = 0; /* -H and -L are mutually exclusive */
+ flg &= ~CHF; /* only use the last one seen */
+ break;
+ case 'O':
+ /*
+ * Force one volume. Non standard option.
+ */
+ force_one_volume = 1;
+ break;
+ case 'P':
+ /*
+ * do NOT follow symlinks (default)
+ */
+ Lflag = 0;
+ flg |= CPF;
+ break;
+ case 'T':
+ /*
+ * non-standard option for selecting files within an
+ * archive by modification time range (lower,upper)
+ */
+ if (trng_add(optarg) < 0) {
+ pax_usage();
+ break;
+ }
+ flg |= CTF;
+ break;
+ case 'U':
+ /*
+ * non-standard option for selecting files within an
+ * archive by user (uid or name)
+ */
+ if (usr_add(optarg) < 0) {
+ pax_usage();
+ break;
+ }
+ flg |= CUF;
+ break;
+ case 'X':
+ /*
+ * do not pass over mount points in the file system
+ */
+ Xflag = 1;
+ flg |= CXF;
+ break;
+ case 'Y':
+ /*
+ * On extraction check file inode change time after the
+ * modification of the file name. Non standard option.
+ */
+ Yflag = 1;
+ flg |= CYF;
+ break;
+ case 'Z':
+ /*
+ * On extraction check modification time after the
+ * modification of the file name. Non standard option.
+ */
+ Zflag = 1;
+ flg |= CZF;
+ break;
+ case OPT_INSECURE:
+ secure = 0;
+ break;
+ default:
+ pax_usage();
+ break;
+ }
+ }
+
+ /*
+ * Fix for POSIX.cmd/pax/pax.ex test 132: force -wu options to look
+ * like -wua options were specified.
+ */
+ if (uflag && (flg & WF) && !(flg & RF)) { /* -w but not -r -w */
+ flg |= AF;
+ }
+
+ /*
+ * figure out the operation mode of pax read,write,extract,copy,append
+ * or list. check that we have not been given a bogus set of flags
+ * for the operation mode.
+ */
+ if (ISLIST(flg)) {
+ act = LIST;
+ pax_read_or_list_mode=1;
+ listf = stdout;
+ bflg = flg & BDLIST;
+ } else if (ISEXTRACT(flg)) {
+ act = EXTRACT;
+ bflg = flg & BDEXTR;
+ } else if (ISARCHIVE(flg)) {
+ act = ARCHIVE;
+ bflg = flg & BDARCH;
+ } else if (ISAPPND(flg)) {
+ act = APPND;
+ bflg = flg & BDARCH;
+ } else if (ISCOPY(flg)) {
+ act = COPY;
+ bflg = flg & BDCOPY;
+ } else
+ pax_usage();
+ if (bflg) {
+ printflg(flg);
+ pax_usage();
+ }
+
+ /*
+ * if we are writing (ARCHIVE) we use the default format if the user
+ * did not specify a format. when we write during an APPEND, we will
+ * adopt the format of the existing archive if none was supplied.
+ */
+ if (!(flg & XF) && (act == ARCHIVE))
+ frmt = &(fsub[DEFLT]);
+
+ /*
+ * if copying (-r and -w) and there is no -x specified, we act as
+ * if -x pax was specified.
+ */
+ if (!(flg & XF) && (act == COPY))
+ frmt = &(fsub[F_PAX]);
+
+ /*
+ * Initialize the global extended header template.
+ */
+ tmp_name = getenv("TMPDIR");
+ if (tmp_name) {
+ asprintf(&header_name_g, "%s%s", tmp_name, "/GlobalHead.%p.%n");
+ } else {
+ header_name_g = "/tmp/GlobalHead.%p.%n";
+ }
+
+ /*
+ * process the args as they are interpreted by the operation mode
+ */
+ switch (act) {
+ case LIST:
+ case EXTRACT:
+ for (; optind < argc; optind++)
+ if (pat_add(argv[optind], NULL) < 0)
+ pax_usage();
+ break;
+ case COPY:
+ if (optind >= argc) {
+ paxwarn(0, "Destination directory was not supplied");
+ pax_usage();
+ }
+ --argc;
+ dirptr = argv[argc];
+ /* FALL THROUGH */
+ case ARCHIVE:
+ case APPND:
+ for (; optind < argc; optind++)
+ if (ftree_add(argv[optind], 0) < 0)
+ pax_usage();
+ /*
+ * no read errors allowed on updates/append operation!
+ */
+ maxflt = 0;
+ break;
+ }
+}
+
+
+/*
+ * tar_options()
+ * look at the user specified flags. set globals as required and check if
+ * the user specified a legal set of flags. If not, complain and exit
+ */
+
+static void
+tar_options(int argc, char **argv)
+{
+ int c;
+ int fstdin = 0;
+ int Oflag = 0;
+ int nincfiles = 0;
+ int incfiles_max = 0;
+ struct incfile {
+ char *file;
+ char *dir;
+ };
+ struct incfile *incfiles = NULL;
+
+ /*
+ * Set default values.
+ */
+ rmleadslash = 1;
+
+ /*
+ * process option flags
+ */
+ while ((c = getoldopt(argc, argv,
+ "b:cef:hjmopqruts:vwxzBC:HI:LOPXZ014578")) != -1) {
+ switch (c) {
+ case 'b':
+ /*
+ * specify blocksize in 512-byte blocks
+ */
+ if ((wrblksz = (int)str_offt(optarg)) <= 0) {
+ paxwarn(1, "Invalid block size %s", optarg);
+ tar_usage();
+ }
+ wrblksz *= 512; /* XXX - check for int oflow */
+ break;
+ case 'c':
+ /*
+ * create an archive
+ */
+ act = ARCHIVE;
+ break;
+ case 'e':
+ /*
+ * stop after first error
+ */
+ maxflt = 0;
+ break;
+ case 'f':
+ /*
+ * filename where the archive is stored
+ */
+ if ((optarg[0] == '-') && (optarg[1]== '\0')) {
+ /*
+ * treat a - as stdin
+ */
+ fstdin = 1;
+ arcname = NULL;
+ break;
+ }
+ fstdin = 0;
+ arcname = optarg;
+ break;
+ case 'h':
+ /*
+ * follow symlinks
+ */
+ Lflag = 1;
+ break;
+ case 'j':
+ /*
+ * use bzip2. Non standard option.
+ */
+ gzip_program = BZIP2_CMD;
+ break;
+ case 'm':
+ /*
+ * do not preserve modification time
+ */
+ pmtime = 0;
+ break;
+ case 'O':
+ Oflag = 1;
+ break;
+ case 'o':
+ Oflag = 2;
+ break;
+ case 'p':
+ /*
+ * preserve uid/gid and file mode, regardless of umask
+ */
+ pmode = 1;
+ pids = 1;
+ break;
+ case 'q':
+ /*
+ * select first match for a pattern only
+ */
+ nflag = 1;
+ break;
+ case 'r':
+ case 'u':
+ /*
+ * append to the archive
+ */
+ act = APPND;
+ break;
+ case 's':
+ /*
+ * file name substitution name pattern
+ */
+ if (rep_add(optarg) < 0) {
+ tar_usage();
+ break;
+ }
+ break;
+ case 't':
+ /*
+ * list contents of the tape
+ */
+ act = LIST;
+ break;
+ case 'v':
+ /*
+ * verbose operation mode
+ */
+ vflag++;
+ break;
+ case 'w':
+ /*
+ * interactive file rename
+ */
+ iflag = 1;
+ break;
+ case 'x':
+ /*
+ * extract an archive, preserving mode,
+ * and mtime if possible.
+ */
+ act = EXTRACT;
+ pmtime = 1;
+ break;
+ case 'z':
+ /*
+ * use gzip. Non standard option.
+ */
+ gzip_program = GZIP_CMD;
+ break;
+ case 'B':
+ /*
+ * Nothing to do here, this is pax default
+ */
+ break;
+ case 'C':
+ havechd++;
+ chdname = optarg;
+ break;
+ case 'H':
+ /*
+ * follow command line symlinks only
+ */
+ Hflag = 1;
+ break;
+ case 'I':
+ if (++nincfiles > incfiles_max) {
+ incfiles_max = nincfiles + 3;
+ incfiles = realloc(incfiles,
+ sizeof(*incfiles) * incfiles_max);
+ if (incfiles == NULL) {
+ paxwarn(0, "Unable to allocate space "
+ "for option list");
+ exit(1);
+ }
+ }
+ incfiles[nincfiles - 1].file = optarg;
+ incfiles[nincfiles - 1].dir = chdname;
+ break;
+ case 'L':
+ /*
+ * follow symlinks
+ */
+ Lflag = 1;
+ break;
+ case 'P':
+ /*
+ * do not remove leading '/' from pathnames
+ */
+ rmleadslash = 0;
+ break;
+ case 'X':
+ /*
+ * do not pass over mount points in the file system
+ */
+ Xflag = 1;
+ break;
+ case 'Z':
+ /*
+ * use compress.
+ */
+ gzip_program = COMPRESS_CMD;
+ break;
+ case '0':
+ arcname = DEV_0;
+ break;
+ case '1':
+ arcname = DEV_1;
+ break;
+ case '4':
+ arcname = DEV_4;
+ break;
+ case '5':
+ arcname = DEV_5;
+ break;
+ case '7':
+ arcname = DEV_7;
+ break;
+ case '8':
+ arcname = DEV_8;
+ break;
+ default:
+ tar_usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Traditional tar behaviour (pax uses stderr unless in list mode) */
+ if (fstdin == 1 && act == ARCHIVE)
+ listf = stderr;
+ else
+ listf = stdout;
+
+ /* Traditional tar behaviour (pax wants to read file list from stdin) */
+ if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0)
+ exit(0);
+
+ /*
+ * process the args as they are interpreted by the operation mode
+ */
+ switch (act) {
+ case LIST:
+ case EXTRACT:
+ default:
+ {
+ int sawpat = 0;
+ char *file, *dir = NULL;
+
+ while (nincfiles || *argv != NULL) {
+ /*
+ * If we queued up any include files,
+ * pull them in now. Otherwise, check
+ * for -I and -C positional flags.
+ * Anything else must be a file to
+ * extract.
+ */
+ if (nincfiles) {
+ file = incfiles->file;
+ dir = incfiles->dir;
+ incfiles++;
+ nincfiles--;
+ } else if (strcmp(*argv, "-I") == 0) {
+ if (*++argv == NULL)
+ break;
+ file = *argv++;
+ dir = chdname;
+ } else
+ file = NULL;
+ if (file != NULL) {
+ FILE *fp;
+ char *str;
+
+ if (strcmp(file, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen(file, "r")) == NULL) {
+ paxwarn(1, "Unable to open file '%s' for read", file);
+ tar_usage();
+ }
+ while ((str = pax_getline(fp)) != NULL) {
+ if (pat_add(str, dir) < 0)
+ tar_usage();
+ sawpat = 1;
+ }
+ if (strcmp(file, "-") != 0)
+ fclose(fp);
+ if (getline_error) {
+ paxwarn(1, "Problem with file '%s'", file);
+ tar_usage();
+ }
+ } else if (strcmp(*argv, "-C") == 0) {
+ if (*++argv == NULL)
+ break;
+ chdname = *argv++;
+ havechd++;
+ } else if (pat_add(*argv++, chdname) < 0)
+ tar_usage();
+ else
+ sawpat = 1;
+ }
+ /*
+ * if patterns were added, we are doing chdir()
+ * on a file-by-file basis, else, just one
+ * global chdir (if any) after opening input.
+ */
+ if (sawpat > 0)
+ chdname = NULL;
+ }
+ break;
+ case ARCHIVE:
+ case APPND:
+ frmt = &(fsub[Oflag ? F_OTAR : F_TAR]);
+
+ if (Oflag == 2 && opt_add("write_opt=nodir") < 0)
+ tar_usage();
+
+ if (chdname != NULL) { /* initial chdir() */
+ if (ftree_add(chdname, 1) < 0)
+ tar_usage();
+ }
+
+ while (nincfiles || *argv != NULL) {
+ char *file, *dir = NULL;
+
+ /*
+ * If we queued up any include files, pull them in
+ * now. Otherwise, check for -I and -C positional
+ * flags. Anything else must be a file to include
+ * in the archive.
+ */
+ if (nincfiles) {
+ file = incfiles->file;
+ dir = incfiles->dir;
+ incfiles++;
+ nincfiles--;
+ } else if (strcmp(*argv, "-I") == 0) {
+ if (*++argv == NULL)
+ break;
+ file = *argv++;
+ dir = NULL;
+ } else
+ file = NULL;
+ if (file != NULL) {
+ FILE *fp;
+ char *str;
+
+ /* Set directory if needed */
+ if (dir) {
+ if (ftree_add(dir, 1) < 0)
+ tar_usage();
+ }
+
+ if (strcmp(file, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen(file, "r")) == NULL) {
+ paxwarn(1, "Unable to open file '%s' for read", file);
+ tar_usage();
+ }
+ while ((str = pax_getline(fp)) != NULL) {
+ if (ftree_add(str, 0) < 0)
+ tar_usage();
+ }
+ if (strcmp(file, "-") != 0)
+ fclose(fp);
+ if (getline_error) {
+ paxwarn(1, "Problem with file '%s'",
+ file);
+ tar_usage();
+ }
+ } else if (strcmp(*argv, "-C") == 0) {
+ if (*++argv == NULL)
+ break;
+ if (ftree_add(*argv++, 1) < 0)
+ tar_usage();
+ havechd++;
+ } else if (ftree_add(*argv++, 0) < 0)
+ tar_usage();
+ }
+ /*
+ * no read errors allowed on updates/append operation!
+ */
+ maxflt = 0;
+ break;
+ }
+ if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) {
+ arcname = getenv("TAPE");
+ if ((arcname == NULL) || (*arcname == '\0'))
+ arcname = _PATH_DEFTAPE;
+ }
+}
+
+int mkpath(char *);
+
+int
+mkpath(path)
+ char *path;
+{
+ struct stat sb;
+ char *slash;
+ int done = 0;
+
+ slash = path;
+
+ while (!done) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ done = (*slash == '\0');
+ *slash = '\0';
+
+ if (stat(path, &sb)) {
+ if (errno != ENOENT || mkdir(path, 0777)) {
+ paxwarn(1, "%s", path);
+ return (-1);
+ }
+ } else if (!S_ISDIR(sb.st_mode)) {
+ syswarn(1, ENOTDIR, "%s", path);
+ return (-1);
+ }
+
+ if (!done)
+ *slash = '/';
+ }
+
+ return (0);
+}
+/*
+ * cpio_options()
+ * look at the user specified flags. set globals as required and check if
+ * the user specified a legal set of flags. If not, complain and exit
+ */
+
+static void
+cpio_options(int argc, char **argv)
+{
+ int c, i;
+ char *str;
+ FSUB tmp;
+ FILE *fp;
+ size_t n_fsub;
+
+ kflag = 1;
+ pids = 1;
+ pmode = 1;
+ pmtime = 0;
+ arcname = NULL;
+ dflag = 1;
+ act = -1;
+ nodirs = 1;
+ while ((c=getopt(argc,argv,"abcdfijklmoprstuvzABC:E:F:H:I:LO:SZ6")) != -1)
+ switch (c) {
+ case 'a':
+ /*
+ * preserve access time on files read
+ */
+ tflag = 1;
+ break;
+ case 'b':
+ /*
+ * swap bytes and half-words when reading data
+ */
+ break;
+ case 'c':
+ /*
+ * ASCII cpio header
+ */
+ frmt = &(fsub[F_ACPIO]);
+ break;
+ case 'd':
+ /*
+ * create directories as needed
+ */
+ nodirs = 0;
+ break;
+ case 'f':
+ /*
+ * invert meaning of pattern list
+ */
+ cflag = 1;
+ break;
+ case 'i':
+ /*
+ * restore an archive
+ */
+ act = EXTRACT;
+ break;
+ case 'j':
+ /*
+ * use bzip2. Non standard option.
+ */
+ gzip_program = BZIP2_CMD;
+ break;
+ case 'k':
+ break;
+ case 'l':
+ /*
+ * use links instead of copies when possible
+ */
+ lflag = 1;
+ break;
+ case 'm':
+ /*
+ * preserve modification time
+ */
+ pmtime = 1;
+ break;
+ case 'o':
+ /*
+ * create an archive
+ */
+ act = ARCHIVE;
+ frmt = &(fsub[F_CPIO]);
+ break;
+ case 'p':
+ /*
+ * copy-pass mode
+ */
+ act = COPY;
+ break;
+ case 'r':
+ /*
+ * interactively rename files
+ */
+ iflag = 1;
+ break;
+ case 's':
+ /*
+ * swap bytes after reading data
+ */
+ break;
+ case 't':
+ /*
+ * list contents of archive
+ */
+ act = LIST;
+ listf = stdout;
+ break;
+ case 'u':
+ /*
+ * replace newer files
+ */
+ kflag = 0;
+ break;
+ case 'v':
+ /*
+ * verbose operation mode
+ */
+ vflag = 1;
+ break;
+ case 'z':
+ /*
+ * use gzip. Non standard option.
+ */
+ gzip_program = GZIP_CMD;
+ break;
+ case 'A':
+ /*
+ * append mode
+ */
+ act = APPND;
+ break;
+ case 'B':
+ /*
+ * Use 5120 byte block size
+ */
+ wrblksz = 5120;
+ break;
+ case 'C':
+ /*
+ * set block size in bytes
+ */
+ wrblksz = atoi(optarg);
+ break;
+ case 'E':
+ /*
+ * file with patterns to extract or list
+ */
+ if ((fp = fopen(optarg, "r")) == NULL) {
+ paxwarn(1, "Unable to open file '%s' for read", optarg);
+ cpio_usage();
+ }
+ while ((str = pax_getline(fp)) != NULL) {
+ pat_add(str, NULL);
+ }
+ fclose(fp);
+ if (getline_error) {
+ paxwarn(1, "Problem with file '%s'", optarg);
+ cpio_usage();
+ }
+ break;
+ case 'F':
+ case 'I':
+ case 'O':
+ /*
+ * filename where the archive is stored
+ */
+ if ((optarg[0] == '-') && (optarg[1]== '\0')) {
+ /*
+ * treat a - as stdin
+ */
+ arcname = NULL;
+ break;
+ }
+ arcname = optarg;
+ break;
+ case 'H':
+ /*
+ * specify an archive format on write
+ */
+ tmp.name = optarg;
+ n_fsub = sizeof(fsub)/sizeof(FSUB);
+ if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
+ n_fsub, sizeof(FSUB), c_frmt)) != NULL)
+ break;
+ paxwarn(1, "Unknown -H format: %s", optarg);
+ (void)fputs("cpio: Known -H formats are:", stderr);
+ for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i)
+ (void)fprintf(stderr, " %s", fsub[i].name);
+ (void)fputs("\n\n", stderr);
+ cpio_usage();
+ break;
+ case 'L':
+ /*
+ * follow symbolic links
+ */
+ Lflag = 1;
+ break;
+ case 'S':
+ /*
+ * swap halfwords after reading data
+ */
+ break;
+ case 'Z':
+ /*
+ * use compress. Non standard option.
+ */
+ gzip_program = COMPRESS_CMD;
+ break;
+ case '6':
+ /*
+ * process Version 6 cpio format
+ */
+ frmt = &(fsub[F_OCPIO]);
+ break;
+ case '?':
+ default:
+ cpio_usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * process the args as they are interpreted by the operation mode
+ */
+ switch (act) {
+ case LIST:
+ case EXTRACT:
+ while (*argv != NULL)
+ if (pat_add(*argv++, NULL) < 0)
+ cpio_usage();
+ break;
+ case COPY:
+ if (*argv == NULL) {
+ paxwarn(0, "Destination directory was not supplied");
+ cpio_usage();
+ }
+ dirptr = *argv;
+ if (mkpath(dirptr) < 0)
+ cpio_usage();
+ --argc;
+ ++argv;
+ /* FALL THROUGH */
+ case ARCHIVE:
+ case APPND:
+ if (*argv != NULL)
+ cpio_usage();
+ /*
+ * no read errors allowed on updates/append operation!
+ */
+ maxflt = 0;
+ while ((str = pax_getline(stdin)) != NULL) {
+ ftree_add(str, 0);
+ }
+ if (getline_error) {
+ paxwarn(1, "Problem while reading stdin");
+ cpio_usage();
+ }
+ break;
+ default:
+ cpio_usage();
+ break;
+ }
+}
+
+/*
+ * printflg()
+ * print out those invalid flag sets found to the user
+ */
+
+static void
+printflg(unsigned int flg)
+{
+ int nxt;
+ int pos = 0;
+
+ (void)fprintf(stderr,"%s: Invalid combination of options:", argv0);
+ while ((nxt = ffs(flg)) != 0) {
+ flg = flg >> nxt;
+ pos += nxt;
+ (void)fprintf(stderr, " -%c", flgch[pos-1]);
+ }
+ (void)putc('\n', stderr);
+}
+
+/*
+ * c_frmt()
+ * comparison routine used by bsearch to find the format specified
+ * by the user
+ */
+
+static int
+c_frmt(const void *a, const void *b)
+{
+ return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name));
+}
+
+/*
+ * opt_next()
+ * called by format specific options routines to get each format specific
+ * flag and value specified with -o
+ * Return:
+ * pointer to next OPLIST entry or NULL (end of list).
+ */
+
+OPLIST *
+opt_next(void)
+{
+ OPLIST *opt;
+
+ if ((opt = ophead) != NULL)
+ ophead = ophead->fow;
+ return(opt);
+}
+
+/*
+ * bad_opt()
+ * generic routine used to complain about a format specific options
+ * when the format does not support options.
+ */
+
+int
+bad_opt(void)
+{
+ OPLIST *opt;
+
+ if (ophead == NULL)
+ return(0);
+ /*
+ * print all we were given
+ */
+ paxwarn(1,"These format options are not supported");
+ while ((opt = opt_next()) != NULL) {
+ if (opt->separator == SEP_EQ) {
+ (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
+ } else if (opt->separator == SEP_COLONEQ ) {
+ (void)fprintf(stderr, "\t%s := %s\n", opt->name, opt->value);
+ } else { /* SEP_NONE */
+ (void)fprintf(stderr, "\t%s\n", opt->name);
+ }
+ }
+ pax_usage();
+ return(0);
+}
+
+/*
+ * opt_add()
+ * breaks the value supplied to -o into an option name and value. Options
+ * are given to -o in the form -o name-value,name=value
+ * multiple -o may be specified.
+ * Return:
+ * 0 if format in name=value format, -1 if -o is passed junk.
+ */
+
+int
+opt_add(const char *str)
+{
+ OPLIST *opt;
+ char *frpt;
+ char *pt;
+ char *dstr;
+ char *endpt;
+
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(0, "Invalid option name");
+ return(-1);
+ }
+ if ((dstr = strdup(str)) == NULL) {
+ paxwarn(0, "Unable to allocate space for option list");
+ return(-1);
+ }
+ frpt = dstr;
+
+ /*
+ * break into name and values pieces and stuff each one into a
+ * OPLIST structure. When we know the format, the format specific
+ * option function will go through this list
+ */
+ while ((frpt != NULL) && (*frpt != '\0')) {
+ if ((endpt = strchr(frpt, ',')) != NULL)
+ *endpt = '\0';
+ if ((pt = strchr(frpt, '=')) == NULL) {
+ paxwarn(0, "Invalid options format");
+ free(dstr);
+ return(-1);
+ }
+ if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
+ paxwarn(0, "Unable to allocate space for option list");
+ free(dstr);
+ return(-1);
+ }
+ *pt++ = '\0';
+ opt->name = frpt;
+ opt->value = pt;
+ opt->separator = SEP_EQ;
+ opt->fow = NULL;
+ if (endpt != NULL)
+ frpt = endpt + 1;
+ else
+ frpt = NULL;
+ if (ophead == NULL) {
+ optail = ophead = opt;
+ continue;
+ }
+ optail->fow = opt;
+ optail = opt;
+ }
+ return(0);
+}
+
+/*
+ * pax_format_opt_add()
+ * breaks the value supplied to -o into a option name and value. options
+ * are given to -o in the form -o name-value,name=value
+ * multiple -o may be specified.
+ * Return:
+ * 0 if format in name=value format, -1 if -o is passed junk
+ */
+
+int
+pax_format_opt_add(register char *str)
+{
+ register OPLIST *opt;
+ register char *frpt;
+ register char *pt;
+ register char *endpt;
+ register int separator;
+
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(0, "Invalid option name");
+ return(-1);
+ }
+ if ((str = strdup(str)) == NULL) {
+ paxwarn(0, "Unable to allocate space for option list");
+ return(-1);
+ }
+ frpt = str;
+
+ /*
+ * break into name and values pieces and stuff each one into a
+ * OPLIST structure. When we know the format, the format specific
+ * option function will go through this list
+ */
+ while ((frpt != NULL) && (*frpt != '\0')) {
+ if ((endpt = strchr(frpt, ',')) != NULL)
+ *endpt = '\0';
+ if ((pt = strstr(frpt, ":=")) != NULL) {
+ *pt++ = '\0';
+ pt++; /* beyond the := */
+ separator = SEP_COLONEQ;
+ } else if ((pt = strchr(frpt, '=')) != NULL) {
+ *pt++ = '\0';
+ separator = SEP_EQ;
+ } else {
+ /* keyword with no value */
+ separator = SEP_NONE;
+ }
+ if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
+ paxwarn(0, "Unable to allocate space for option list");
+ free(str);
+ return(-1);
+ }
+ opt->name = frpt;
+ opt->value = pt;
+ opt->separator = separator;
+ opt->fow = NULL;
+ if (endpt != NULL)
+ frpt = endpt + 1;
+ else
+ frpt = NULL;
+ if (ophead == NULL) {
+ optail = ophead = opt;
+ continue;
+ }
+ optail->fow = opt;
+ optail = opt;
+ }
+ return(0);
+}
+
+/*
+ * str_offt()
+ * Convert an expression of the following forms to an off_t > 0.
+ * 1) A positive decimal number.
+ * 2) A positive decimal number followed by a b (mult by 512).
+ * 3) A positive decimal number followed by a k (mult by 1024).
+ * 4) A positive decimal number followed by a m (mult by 512).
+ * 5) A positive decimal number followed by a w (mult by sizeof int)
+ * 6) Two or more positive decimal numbers (with/without k,b or w).
+ * separated by x (also * for backwards compatibility), specifying
+ * the product of the indicated values.
+ * Return:
+ * 0 for an error, a positive value o.w.
+ */
+
+static off_t
+str_offt(char *val)
+{
+ char *expr;
+ off_t num, t;
+
+# ifdef LONG_OFF_T
+ num = strtol(val, &expr, 0);
+ if ((num == LONG_MAX) || (num <= 0) || (expr == val))
+# else
+ num = strtoq(val, &expr, 0);
+ if ((num == QUAD_MAX) || (num <= 0) || (expr == val))
+# endif
+ return(0);
+
+ switch (*expr) {
+ case 'b':
+ t = num;
+ num *= 512;
+ if (t > num)
+ return(0);
+ ++expr;
+ break;
+ case 'k':
+ t = num;
+ num *= 1024;
+ if (t > num)
+ return(0);
+ ++expr;
+ break;
+ case 'm':
+ t = num;
+ num *= 1048576;
+ if (t > num)
+ return(0);
+ ++expr;
+ break;
+ case 'w':
+ t = num;
+ num *= sizeof(int);
+ if (t > num)
+ return(0);
+ ++expr;
+ break;
+ }
+
+ switch (*expr) {
+ case '\0':
+ break;
+ case '*':
+ case 'x':
+ t = num;
+ num *= str_offt(expr + 1);
+ if (t > num)
+ return(0);
+ break;
+ default:
+ return(0);
+ }
+ return(num);
+}
+
+char *
+pax_getline(FILE *f)
+{
+ char *name, *temp;
+ size_t len;
+
+ name = fgetln(f, &len);
+ if (!name) {
+ getline_error = ferror(f) ? GETLINE_FILE_CORRUPT : 0;
+ return(0);
+ }
+ if (name[len-1] != '\n')
+ len++;
+ temp = malloc(len);
+ if (!temp) {
+ getline_error = GETLINE_OUT_OF_MEM;
+ return(0);
+ }
+ memcpy(temp, name, len-1);
+ temp[len-1] = 0;
+ return(temp);
+}
+
+/*
+ * no_op()
+ * for those option functions where the archive format has nothing to do.
+ * Return:
+ * 0
+ */
+
+static int
+no_op(void)
+{
+ return(0);
+}
+
+/*
+ * pax_usage()
+ * print the usage summary to the user
+ */
+
+void
+pax_usage(void)
+{
+ (void)fputs(
+ "usage: pax [-0cdjnOvz] [-E limit] [-f archive] [-G group] [-s replstr]\n"
+ " [-T range] [-U user] [--insecure] [pattern ...]\n"
+ " pax -r [-0cDdijknOuvYZz] [-E limit] [-f archive] [-G group] [-o options]\n"
+ " [-p string] [-s replstr] [-T range] [-U user] [--insecure] [pattern ...]\n"
+ " pax -w [-0adHijLOPtuvXz] [-B bytes] [-b blocksize] [-f archive]\n"
+ " [-G group] [-o options] [-s replstr] [-T range] [-U user]\n"
+ " [-x format] [--insecure] [file ...]\n"
+ " pax -rw [-0DdHikLlnOPtuvXYZ] [-G group] [-p string] [-s replstr]\n"
+ " [-T range] [-U user] [--insecure] [file ...] directory\n",
+ stderr);
+ exit(1);
+}
+
+/*
+ * tar_usage()
+ * print the usage summary to the user
+ */
+
+void
+tar_usage(void)
+{
+ (void)fputs(
+ "usage: tar {crtux}[014578befHhjLmOoPpqsvwXZz]\n"
+ " [blocking-factor | archive | replstr] [-C directory] [-I file]\n"
+ " [file ...]\n"
+ " tar {-crtux} [-014578eHhjLmOoPpqvwXZz] [-b blocking-factor]\n"
+ " [-C directory] [-f archive] [-I file] [-s replstr] [file ...]\n",
+ stderr);
+ exit(1);
+}
+
+/*
+ * cpio_usage()
+ * print the usage summary to the user
+ */
+
+void
+cpio_usage(void)
+{
+ (void)fputs(
+ "usage: cpio -o [-AaBcjLvZz] [-C bytes] [-F archive] [-H format]\n"
+ " [-O archive] < name-list [> archive]\n"
+ " cpio -i [-6BbcdfjmrSstuvZz] [-C bytes] [-E file] [-F archive] [-H format]\n"
+ " [-I archive] [pattern ...] [< archive]\n"
+ " cpio -p [-adLlmuv] destination-directory < name-list\n",
+ stderr);
+ exit(1);
+}
diff --git a/file_cmds/pax/options.h b/file_cmds/pax/options.h
new file mode 100644
index 0000000..89eeaa5
--- /dev/null
+++ b/file_cmds/pax/options.h
@@ -0,0 +1,118 @@
+/* $OpenBSD: options.h,v 1.4 2003/06/13 17:51:14 millert Exp $ */
+/* $NetBSD: options.h,v 1.3 1995/03/21 09:07:32 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)options.h 8.2 (Berkeley) 4/18/94
+ */
+
+#ifndef _OPTIONS_H_
+#define _OPTIONS_H_
+
+/*
+ * argv[0] names. Used for tar and cpio emulation
+ */
+
+#define NM_TAR "tar"
+#define NM_CPIO "cpio"
+#define NM_PAX "pax"
+
+/*
+ * Constants used to specify the legal sets of flags in pax. For each major
+ * operation mode of pax, a set of illegal flags is defined. If any one of
+ * those illegal flags are found set, we scream and exit
+ */
+#define NONE "none"
+
+/*
+ * flags (one for each option).
+ */
+#define AF 0x00000001
+#define BF 0x00000002
+#define CF 0x00000004
+#define DF 0x00000008
+#define FF 0x00000010
+#define IF 0x00000020
+#define KF 0x00000040
+#define LF 0x00000080
+#define NF 0x00000100
+#define OF 0x00000200
+#define PF 0x00000400
+#define RF 0x00000800
+#define SF 0x00001000
+#define TF 0x00002000
+#define UF 0x00004000
+#define VF 0x00008000
+#define WF 0x00010000
+#define XF 0x00020000
+#define CBF 0x00040000 /* nonstandard extension */
+#define CDF 0x00080000 /* nonstandard extension */
+#define CEF 0x00100000 /* nonstandard extension */
+#define CGF 0x00200000 /* nonstandard extension */
+#define CHF 0x00400000 /* nonstandard extension */
+#define CLF 0x00800000 /* nonstandard extension */
+#define CPF 0x01000000 /* nonstandard extension */
+#define CTF 0x02000000 /* nonstandard extension */
+#define CUF 0x04000000 /* nonstandard extension */
+#define CXF 0x08000000
+#define CYF 0x10000000 /* nonstandard extension */
+#define CZF 0x20000000 /* nonstandard extension */
+#define C0F 0x40000000 /* nonstandard extension */
+
+/*
+ * ascii string indexed by bit position above (alter the above and you must
+ * alter this string) used to tell the user what flags caused us to complain
+ */
+#define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ0"
+
+/*
+ * legal pax operation bit patterns
+ */
+
+#define ISLIST(x) (((x) & (RF|WF)) == 0)
+#define ISEXTRACT(x) (((x) & (RF|WF)) == RF)
+#define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF)
+#define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF))
+#define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF))
+#define ISWRITE(x) (((x) & (RF|WF)) == WF)
+
+/*
+ * Illegal option flag subsets based on pax operation
+ */
+
+#define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF)
+#define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF)
+#define BDCOPY (AF|BF|FF|CBF|CEF)
+#define BDLIST (AF|BF|IF|KF|LF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF)
+
+#endif /* _OPTIONS_H_ */
diff --git a/file_cmds/pax/pat_rep.c b/file_cmds/pax/pat_rep.c
new file mode 100644
index 0000000..fa778b9
--- /dev/null
+++ b/file_cmds/pax/pat_rep.c
@@ -0,0 +1,1211 @@
+/* $OpenBSD: pat_rep.c,v 1.30 2005/08/05 08:30:10 djm Exp $ */
+/* $NetBSD: pat_rep.c,v 1.4 1995/03/21 09:07:33 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: pat_rep.c,v 1.30 2005/08/05 08:30:10 djm Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <regex.h>
+#include "pax.h"
+#include "pat_rep.h"
+#include "extern.h"
+
+/*
+ * routines to handle pattern matching, name modification (regular expression
+ * substitution and interactive renames), and destination name modification for
+ * copy (-rw). Both file name and link names are adjusted as required in these
+ * routines.
+ */
+
+#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */
+static PATTERN *pathead = NULL; /* file pattern match list head */
+static PATTERN *pattail = NULL; /* file pattern match list tail */
+static REPLACE *rephead = NULL; /* replacement string list head */
+static REPLACE *reptail = NULL; /* replacement string list tail */
+
+static int rep_name(char *, size_t, int *, int);
+int tty_rename(ARCHD *);
+static int fix_path(char *, int *, char *, int);
+static int fn_match(char *, char *, char **);
+#ifdef _HAVE_REGCOMP_
+static char* extract_equiv_pat(char *, char **);
+static int regex_match(char *, char *, char **);
+#endif
+static char * range_match(char *, int);
+static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *);
+
+/*
+ * rep_add()
+ * parses the -s replacement string; compiles the regular expression
+ * and stores the compiled value and it's replacement string together in
+ * replacement string list. Input to this function is of the form:
+ * /old/new/pg
+ * The first char in the string specifies the delimiter used by this
+ * replacement string. "Old" is a regular expression in "ed" format which
+ * is compiled by regcomp() and is applied to filenames. "new" is the
+ * substitution string; p and g are options flags for printing and global
+ * replacement (over the single filename)
+ * Return:
+ * 0 if a proper replacement string and regular expression was added to
+ * the list of replacement patterns; -1 otherwise.
+ */
+
+int
+rep_add(char *str)
+{
+ char *pt1;
+ char *pt2;
+ REPLACE *rep;
+ int res;
+ char rebuf[BUFSIZ];
+
+ /*
+ * throw out the bad parameters
+ */
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(1, "Empty replacement string");
+ return(-1);
+ }
+
+ /*
+ * first character in the string specifies what the delimiter is for
+ * this expression
+ */
+ for (pt1 = str+1; *pt1; pt1++) {
+ if (*pt1 == '\\') {
+ pt1++;
+ continue;
+ }
+ if (*pt1 == *str)
+ break;
+ }
+ if (*pt1 == '\0') {
+ paxwarn(1, "Invalid replacement string %s", str);
+ return(-1);
+ }
+
+ /*
+ * allocate space for the node that handles this replacement pattern
+ * and split out the regular expression and try to compile it
+ */
+ if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) {
+ paxwarn(1, "Unable to allocate memory for replacement string");
+ return(-1);
+ }
+
+ *pt1 = '\0';
+ if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) {
+ regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf));
+ paxwarn(1, "%s while compiling regular expression %s", rebuf, str);
+ (void)free((char *)rep);
+ return(-1);
+ }
+
+ /*
+ * put the delimiter back in case we need an error message and
+ * locate the delimiter at the end of the replacement string
+ * we then point the node at the new substitution string
+ */
+ *pt1++ = *str;
+ for (pt2 = pt1; *pt2; pt2++) {
+ if (*pt2 == '\\') {
+ pt2++;
+ continue;
+ }
+ if (*pt2 == *str)
+ break;
+ }
+ if (*pt2 == '\0') {
+ regfree(&(rep->rcmp));
+ (void)free((char *)rep);
+ paxwarn(1, "Invalid replacement string %s", str);
+ return(-1);
+ }
+
+ *pt2 = '\0';
+ rep->nstr = pt1;
+ pt1 = pt2++;
+ rep->flgs = 0;
+
+ /*
+ * set the options if any
+ */
+ while (*pt2 != '\0') {
+ switch (*pt2) {
+ case 'g':
+ case 'G':
+ rep->flgs |= GLOB;
+ break;
+ case 'p':
+ case 'P':
+ rep->flgs |= PRNT;
+ break;
+ default:
+ regfree(&(rep->rcmp));
+ (void)free((char *)rep);
+ *pt1 = *str;
+ paxwarn(1, "Invalid replacement string option %s", str);
+ return(-1);
+ }
+ ++pt2;
+ }
+
+ /*
+ * all done, link it in at the end
+ */
+ rep->fow = NULL;
+ if (rephead == NULL) {
+ reptail = rephead = rep;
+ return(0);
+ }
+ reptail->fow = rep;
+ reptail = rep;
+ return(0);
+}
+
+/*
+ * pat_add()
+ * add a pattern match to the pattern match list. Pattern matches are used
+ * to select which archive members are extracted. (They appear as
+ * arguments to pax in the list and read modes). If no patterns are
+ * supplied to pax, all members in the archive will be selected (and the
+ * pattern match list is empty).
+ * Return:
+ * 0 if the pattern was added to the list, -1 otherwise
+ */
+
+int
+pat_add(char *str, char *chdname)
+{
+ PATTERN *pt;
+
+ /*
+ * throw out the junk
+ */
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(1, "Empty pattern string");
+ return(-1);
+ }
+
+ /*
+ * allocate space for the pattern and store the pattern. the pattern is
+ * part of argv so do not bother to copy it, just point at it. Add the
+ * node to the end of the pattern list
+ */
+ if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) {
+ paxwarn(1, "Unable to allocate memory for pattern string");
+ return(-1);
+ }
+
+ pt->pstr = str;
+ pt->pend = NULL;
+ pt->plen = strlen(str);
+ pt->fow = NULL;
+ pt->flgs = 0;
+ pt->chdname = chdname;
+
+ if (pathead == NULL) {
+ pattail = pathead = pt;
+ return(0);
+ }
+ pattail->fow = pt;
+ pattail = pt;
+ return(0);
+}
+
+/*
+ * pat_chk()
+ * complain if any the user supplied pattern did not result in a match to
+ * a selected archive member.
+ */
+
+void
+pat_chk(void)
+{
+ PATTERN *pt;
+ int wban = 0;
+
+ /*
+ * walk down the list checking the flags to make sure MTCH was set,
+ * if not complain
+ */
+ for (pt = pathead; pt != NULL; pt = pt->fow) {
+ if (pt->flgs & MTCH)
+ continue;
+ if (!wban) {
+ paxwarn(1, "WARNING! These patterns were not matched:");
+ ++wban;
+ }
+ (void)fprintf(stderr, "%s\n", pt->pstr);
+ }
+}
+
+/*
+ * pat_sel()
+ * the archive member which matches a pattern was selected. Mark the
+ * pattern as having selected an archive member. arcn->pat points at the
+ * pattern that was matched. arcn->pat is set in pat_match()
+ *
+ * NOTE: When the -c option is used, we are called when there was no match
+ * by pat_match() (that means we did match before the inverted sense of
+ * the logic). Now this seems really strange at first, but with -c we
+ * need to keep track of those patterns that cause an archive member to NOT
+ * be selected (it found an archive member with a specified pattern)
+ * Return:
+ * 0 if the pattern pointed at by arcn->pat was tagged as creating a
+ * match, -1 otherwise.
+ */
+
+int
+pat_sel(ARCHD *arcn)
+{
+ PATTERN *pt;
+ PATTERN **ppt;
+ int len;
+
+ /*
+ * if no patterns just return
+ */
+ if ((pathead == NULL) || ((pt = arcn->pat) == NULL))
+ return(0);
+
+ /*
+ * when we are NOT limited to a single match per pattern mark the
+ * pattern and return
+ */
+ if (!nflag) {
+ pt->flgs |= MTCH;
+ return(0);
+ }
+
+ /*
+ * we reach this point only when we allow a single selected match per
+ * pattern, if the pattern matches a directory and we do not have -d
+ * (dflag) we are done with this pattern. We may also be handed a file
+ * in the subtree of a directory. in that case when we are operating
+ * with -d, this pattern was already selected and we are done
+ */
+ if (pt->flgs & DIR_MTCH)
+ return(0);
+
+ if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) {
+ /*
+ * ok we matched a directory and we are allowing
+ * subtree matches but because of the -n only its children will
+ * match. This is tagged as a DIR_MTCH type.
+ * WATCH IT, the code assumes that pt->pend points
+ * into arcn->name and arcn->name has not been modified.
+ * If not we will have a big mess. Yup this is another kludge
+ */
+
+ /*
+ * if this was a prefix match, remove trailing part of path
+ * so we can copy it. Future matches will be exact prefix match
+ */
+ if (pt->pend != NULL)
+ *pt->pend = '\0';
+
+ if ((pt->pstr = strdup(arcn->name)) == NULL) {
+ paxwarn(1, "Pattern select out of memory");
+ if (pt->pend != NULL)
+ *pt->pend = '/';
+ pt->pend = NULL;
+ return(-1);
+ }
+
+ /*
+ * put the trailing / back in the source string
+ */
+ if (pt->pend != NULL) {
+ *pt->pend = '/';
+ pt->pend = NULL;
+ }
+ pt->plen = strlen(pt->pstr);
+
+ /*
+ * strip off any trailing /, this should really never happen
+ */
+ len = pt->plen - 1;
+ if (*(pt->pstr + len) == '/') {
+ *(pt->pstr + len) = '\0';
+ pt->plen = len;
+ }
+ pt->flgs = DIR_MTCH | MTCH;
+ arcn->pat = pt;
+ return(0);
+ }
+
+ /*
+ * we are then done with this pattern, so we delete it from the list
+ * because it can never be used for another match.
+ * Seems kind of strange to do for a -c, but the pax spec is really
+ * vague on the interaction of -c, -n and -d. We assume that when -c
+ * and the pattern rejects a member (i.e. it matched it) it is done.
+ * In effect we place the order of the flags as having -c last.
+ */
+ pt = pathead;
+ ppt = &pathead;
+ while ((pt != NULL) && (pt != arcn->pat)) {
+ ppt = &(pt->fow);
+ pt = pt->fow;
+ }
+
+ if (pt == NULL) {
+ /*
+ * should never happen....
+ */
+ paxwarn(1, "Pattern list inconsistent");
+ return(-1);
+ }
+ *ppt = pt->fow;
+ (void)free((char *)pt);
+ arcn->pat = NULL;
+ return(0);
+}
+
+/*
+ * pat_match()
+ * see if this archive member matches any supplied pattern, if a match
+ * is found, arcn->pat is set to point at the potential pattern. Later if
+ * this archive member is "selected" we process and mark the pattern as
+ * one which matched a selected archive member (see pat_sel())
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be
+ * skipped and -1 if we are done with all patterns (and pax should quit
+ * looking for more members)
+ */
+
+int
+pat_match(ARCHD *arcn)
+{
+ PATTERN *pt;
+
+ arcn->pat = NULL;
+
+ /*
+ * if there are no more patterns and we have -n (and not -c) we are
+ * done. otherwise with no patterns to match, matches all
+ */
+ if (pathead == NULL) {
+ if (nflag && !cflag)
+ return(-1);
+ return(0);
+ }
+
+ /*
+ * have to search down the list one at a time looking for a match.
+ */
+ pt = pathead;
+ while (pt != NULL) {
+ /*
+ * check for a file name match unless we have DIR_MTCH set in
+ * this pattern then we want a prefix match
+ */
+ if (pt->flgs & DIR_MTCH) {
+ /*
+ * this pattern was matched before to a directory
+ * as we must have -n set for this (but not -d). We can
+ * only match CHILDREN of that directory so we must use
+ * an exact prefix match (no wildcards).
+ */
+ if ((arcn->name[pt->plen] == '/') &&
+ (strncmp(pt->pstr, arcn->name, pt->plen) == 0))
+ break;
+ } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0)
+ break;
+ pt = pt->fow;
+ }
+
+ /*
+ * return the result, remember that cflag (-c) inverts the sense of a
+ * match
+ */
+ if (pt == NULL)
+ return(cflag ? 0 : 1);
+
+ /*
+ * we had a match, now when we invert the sense (-c) we reject this
+ * member. However we have to tag the pattern a being successful, (in a
+ * match, not in selecting an archive member) so we call pat_sel() here.
+ */
+ arcn->pat = pt;
+ if (!cflag)
+ return(0);
+
+ if (pat_sel(arcn) < 0)
+ return(-1);
+ arcn->pat = NULL;
+ return(1);
+}
+
+/*
+ * fn_match()
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be
+ * skipped and -1 if we are done with all patterns (and pax should quit
+ * looking for more members)
+ * Note: *pend may be changed to show where the prefix ends.
+ */
+
+static int
+fn_match(char *pattern, char *string, char **pend)
+{
+ char c;
+ char test;
+#ifdef _HAVE_REGCOMP_
+ char *equiv_pat;
+ char *pat_pend = NULL;
+#endif
+
+ *pend = NULL;
+ for (;;) {
+ switch (c = *pattern++) {
+ case '\0':
+ /*
+ * Ok we found an exact match
+ */
+ if (*string == '\0')
+ return(0);
+
+ /*
+ * Check if it is a prefix match
+ */
+ if ((dflag == 1) || (*string != '/'))
+ return(-1);
+
+ /*
+ * It is a prefix match, remember where the trailing
+ * / is located
+ */
+ *pend = string;
+ return(0);
+ case '?':
+ if ((*string++) == '\0')
+ return (-1);
+ break;
+ case '*':
+ c = *pattern;
+ /*
+ * Collapse multiple *'s.
+ */
+ while (c == '*')
+ c = *++pattern;
+
+ /*
+ * Optimized hack for pattern with a * at the end
+ */
+ if (c == '\0')
+ return (0);
+
+ /*
+ * General case, use recursion.
+ */
+ while ((*string) != '\0') {
+ if (!fn_match(pattern, string, pend))
+ return (0);
+ ++string;
+ }
+ return (-1);
+ case '[':
+ /*
+ * range match
+ */
+#ifdef _HAVE_REGCOMP_
+ /*
+ * Check for equivalence class and use regex_match to
+ * handle this case. Note pattern should include the
+ * opening bracket '['
+ */
+ equiv_pat = extract_equiv_pat(pattern-1, &pat_pend);
+ if (equiv_pat) {
+ if (regex_match(equiv_pat, string, &string) == -1) {
+ free (equiv_pat);
+ return (-1);
+ }
+
+ free(equiv_pat);
+
+ /*
+ * Update the pattern string
+ */
+ pattern = pat_pend;
+ break;
+ }
+#endif
+ if (((test = *string++) == '\0') ||
+ ((pattern = range_match(pattern, test)) == NULL))
+ return (-1);
+ break;
+
+ case '\\':
+ default:
+ if (c != *string++)
+ return (-1);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+#ifdef _HAVE_REGCOMP_
+static char*
+extract_equiv_pat(char *pattern, char **pend)
+{
+ int pat_len = 2;
+ int found = 0;
+ int is_double_bracket = 0;
+ char* equiv_pat = NULL;
+
+ if (*pattern == '\0' || pattern[1] == '\0' || pattern[2] == '\0')
+ return NULL;
+
+ /*
+ * check if the pattern is
+ * "[= =]", "[[= =][= =]]", "[: :]", or "[[: :][: :]]"
+ * note that the full "pattern" string needs to be passed in
+ */
+ is_double_bracket = (*pattern == '[' && pattern[1] == '[');
+ if (!(*pattern == '[') && !is_double_bracket) {
+ return NULL;
+ }
+
+ pattern ++;
+
+ if (is_double_bracket) {
+ pattern ++;
+ pat_len ++;
+ }
+
+ if (!(*pattern == ':') && !(*pattern == '=')) {
+ return NULL;
+ }
+
+ pattern ++;
+
+
+ for(; *pattern != '\0'; pat_len++, pattern++) {
+ if (!is_double_bracket) {
+ if ((*pattern == '=' || *pattern == ':')
+ && pattern[1] == ']') {
+ found = 1;
+ pattern += 2;
+ pat_len += 2;
+ break;
+ }
+
+ } else {
+ if ((*pattern == '=' || *pattern == ':')
+ && pattern[1] == ']' && pattern[2] == ']') {
+ found = 1;
+ pattern += 3;
+ pat_len += 3;
+ break;
+ }
+
+ }
+ }
+
+ if (!found) {
+ return NULL;
+ }
+
+ equiv_pat = strndup(pattern-pat_len, pat_len);
+
+ if (equiv_pat == NULL) {
+ paxwarn(1, "Out of memory");
+ return NULL;
+ }
+
+ /*
+ * set pend to the remaining pattern to be matched
+ */
+ if (pend != NULL) {
+ *pend = pattern;
+ }
+
+ return equiv_pat;
+}
+
+static int
+regex_match(char *pattern, char *string, char **pend)
+{
+ int res;
+ regex_t preg;
+ regmatch_t pmatch;
+ char rebuf[BUFSIZ];
+
+ if ((res = regcomp(&(preg), pattern, REG_EXTENDED)) != 0) {
+ regerror(res, &(preg), rebuf, sizeof(rebuf));
+ paxwarn(1, "%s while compiling pattern %s", rebuf, pattern);
+ return(-1);
+ }
+
+ if (regexec(&(preg), string, 1, &(pmatch), 0) != 0) {
+ regfree(&(preg));
+ return(-1);
+ }
+
+ regfree(&(preg));
+
+ /*
+ * starting position of the match must be 0
+ */
+ if (pmatch.rm_so != 0) {
+ return(-1);
+ }
+
+ /*
+ * set pend to the remaining string to be matched
+ */
+ if (pend != NULL) {
+ *pend = string + pmatch.rm_eo;
+ }
+
+ return(0);
+}
+#endif
+
+static char *
+range_match(char *pattern, int test)
+{
+ char c;
+ char c2;
+ int negate;
+ int ok = 0;
+
+ if ((negate = (*pattern == '!')) != 0)
+ ++pattern;
+
+ while ((c = *pattern++) != ']') {
+ /*
+ * Illegal pattern
+ */
+ if (c == '\0')
+ return (NULL);
+
+ if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') &&
+ (c2 != ']')) {
+ if ((c <= test) && (test <= c2))
+ ok = 1;
+ pattern += 2;
+ } else if (c == test)
+ ok = 1;
+ }
+ return (ok == negate ? NULL : pattern);
+}
+
+/*
+ * mod_name()
+ * modify a selected file name. first attempt to apply replacement string
+ * expressions, then apply interactive file rename. We apply replacement
+ * string expressions to both filenames and file links (if we didn't the
+ * links would point to the wrong place, and we could never be able to
+ * move an archive that has a file link in it). When we rename files
+ * interactively, we store that mapping (old name to user input name) so
+ * if we spot any file links to the old file name in the future, we will
+ * know exactly how to fix the file link.
+ * Return:
+ * 0 continue to process file, 1 skip this file, -1 pax is finished
+ */
+
+int
+mod_name(ARCHD *arcn)
+{
+ int res = 0;
+
+ /*
+ * Strip off leading '/' if appropriate.
+ * Currently, this option is only set for the tar format.
+ */
+ while (rmleadslash && arcn->name[0] == '/') {
+ if (arcn->name[1] == '\0') {
+ arcn->name[0] = '.';
+ } else {
+ (void)memmove(arcn->name, &arcn->name[1],
+ strlen(arcn->name));
+ arcn->nlen--;
+ }
+ if (rmleadslash < 2) {
+ rmleadslash = 2;
+ paxwarn(0, "Removing leading / from absolute path names in the archive");
+ }
+ }
+ while (rmleadslash && arcn->ln_name[0] == '/' &&
+ (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) {
+ if (arcn->ln_name[1] == '\0') {
+ arcn->ln_name[0] = '.';
+ } else {
+ (void)memmove(arcn->ln_name, &arcn->ln_name[1],
+ strlen(arcn->ln_name));
+ arcn->ln_nlen--;
+ }
+ if (rmleadslash < 2) {
+ rmleadslash = 2;
+ paxwarn(0, "Removing leading / from absolute path names in the archive");
+ }
+ }
+
+ /*
+ * IMPORTANT: We have a problem. what do we do with symlinks?
+ * Modifying a hard link name makes sense, as we know the file it
+ * points at should have been seen already in the archive (and if it
+ * wasn't seen because of a read error or a bad archive, we lose
+ * anyway). But there are no such requirements for symlinks. On one
+ * hand the symlink that refers to a file in the archive will have to
+ * be modified to so it will still work at its new location in the
+ * file system. On the other hand a symlink that points elsewhere (and
+ * should continue to do so) should not be modified. There is clearly
+ * no perfect solution here. So we handle them like hardlinks. Clearly
+ * a replacement made by the interactive rename mapping is very likely
+ * to be correct since it applies to a single file and is an exact
+ * match. The regular expression replacements are a little harder to
+ * justify though. We claim that the symlink name is only likely
+ * to be replaced when it points within the file tree being moved and
+ * in that case it should be modified. what we really need to do is to
+ * call an oracle here. :)
+ */
+ if (rephead != NULL) {
+ /*
+ * we have replacement strings, modify the name and the link
+ * name if any.
+ */
+ if ((res = rep_name(arcn->name, sizeof(arcn->name), &(arcn->nlen), 1)) != 0)
+ return(res);
+
+ if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
+ (arcn->type == PAX_HRG)) &&
+ ((res = rep_name(arcn->ln_name, sizeof(arcn->ln_name), &(arcn->ln_nlen), 0)) != 0))
+ return(res);
+ }
+
+ if (iflag) {
+ /*
+ * perform interactive file rename, then map the link if any
+ */
+ if ((res = tty_rename(arcn)) != 0)
+ return(res);
+ if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
+ (arcn->type == PAX_HRG))
+ sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name));
+ }
+ return(res);
+}
+
+/*
+ * tty_rename()
+ * Prompt the user for a replacement file name. A "." keeps the old name,
+ * a empty line skips the file, and an EOF on reading the tty, will cause
+ * pax to stop processing and exit. Otherwise the file name input, replaces
+ * the old one.
+ * Return:
+ * 0 process this file, 1 skip this file, -1 we need to exit pax
+ */
+
+int
+tty_rename(ARCHD *arcn)
+{
+ char tmpname[PAXPATHLEN+2];
+ int res;
+
+ /*
+ * prompt user for the replacement name for a file, keep trying until
+ * we get some reasonable input. Archives may have more than one file
+ * on them with the same name (from updates etc). We print verbose info
+ * on the file so the user knows what is up.
+ */
+ tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0);
+
+ for (;;) {
+ ls_tty(arcn);
+ tty_prnt("Input new name, or a \".\" to keep the old name, ");
+ tty_prnt("or a \"return\" to skip this file.\n");
+ tty_prnt("Input > ");
+ if (tty_read(tmpname, sizeof(tmpname)) < 0)
+ return(-1);
+ if (strcmp(tmpname, "..") == 0) {
+ tty_prnt("Try again, illegal file name: ..\n");
+ continue;
+ }
+ if (strlen(tmpname) > PAXPATHLEN) {
+ tty_prnt("Try again, file name too long\n");
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * empty file name, skips this file. a "." leaves it alone
+ */
+ if (tmpname[0] == '\0') {
+ tty_prnt("Skipping file.\n");
+ return(1);
+ }
+ if ((tmpname[0] == '.') && (tmpname[1] == '\0')) {
+ tty_prnt("Processing continues, name unchanged.\n");
+ return(0);
+ }
+
+ /*
+ * ok the name changed. We may run into links that point at this
+ * file later. we have to remember where the user sent the file
+ * in order to repair any links.
+ */
+ tty_prnt("Processing continues, name changed to: %s\n", tmpname);
+ res = add_name(arcn->name, arcn->nlen, tmpname);
+ arcn->nlen = strlcpy(arcn->name, tmpname, sizeof(arcn->name));
+ if (arcn->nlen >= sizeof(arcn->name))
+ arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */
+ if (res < 0)
+ return(-1);
+ return(0);
+}
+
+/*
+ * set_dest()
+ * fix up the file name and the link name (if any) so this file will land
+ * in the destination directory (used during copy() -rw).
+ * Return:
+ * 0 if ok, -1 if failure (name too long)
+ */
+
+int
+set_dest(ARCHD *arcn, char *dest_dir, int dir_len)
+{
+ if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0)
+ return(-1);
+
+ /*
+ * It is really hard to deal with symlinks here, we cannot be sure
+ * if the name they point was moved (or will be moved). It is best to
+ * leave them alone.
+ */
+ if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG))
+ return(0);
+
+ if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0)
+ return(-1);
+ return(0);
+}
+
+/*
+ * fix_path
+ * concatenate dir_name and or_name and store the result in or_name (if
+ * it fits). This is one ugly function.
+ * Return:
+ * 0 if ok, -1 if the final name is too long
+ */
+
+static int
+fix_path(char *or_name, int *or_len, char *dir_name, int dir_len)
+{
+ char *src;
+ char *dest;
+ char *start;
+ int len;
+
+ /*
+ * we shift the or_name to the right enough to tack in the dir_name
+ * at the front. We make sure we have enough space for it all before
+ * we start. since dest always ends in a slash, we skip of or_name
+ * if it also starts with one.
+ */
+ start = or_name;
+ src = start + *or_len;
+ dest = src + dir_len;
+ if (*start == '/') {
+ ++start;
+ --dest;
+ }
+ if ((len = dest - or_name) > PAXPATHLEN) {
+ paxwarn(1, "File name %s/%s, too long", dir_name, start);
+ return(-1);
+ }
+ *or_len = len;
+
+ /*
+ * enough space, shift
+ */
+ while (src >= start)
+ *dest-- = *src--;
+ src = dir_name + dir_len - 1;
+
+ /*
+ * splice in the destination directory name
+ */
+ while (src >= dir_name)
+ *dest-- = *src--;
+
+ *(or_name + len) = '\0';
+ return(0);
+}
+
+/*
+ * rep_name()
+ * walk down the list of replacement strings applying each one in order.
+ * when we find one with a successful substitution, we modify the name
+ * as specified. if required, we print the results. if the resulting name
+ * is empty, we will skip this archive member. We use the regexp(3)
+ * routines (regexp() ought to win a prize as having the most cryptic
+ * library function manual page).
+ * --Parameters--
+ * name is the file name we are going to apply the regular expressions to
+ * (and may be modified)
+ * nsize is the size of the name buffer.
+ * nlen is the length of this name (and is modified to hold the length of
+ * the final string).
+ * prnt is a flag that says whether to print the final result.
+ * Return:
+ * 0 if substitution was successful, 1 if we are to skip the file (the name
+ * ended up empty)
+ */
+
+static int
+rep_name(char *name, size_t nsize, int *nlen, int prnt)
+{
+ REPLACE *pt;
+ char *inpt;
+ char *outpt;
+ char *endpt;
+ char *rpt;
+ int found = 0;
+ int res;
+ regmatch_t pm[MAXSUBEXP];
+ char nname[PAXPATHLEN+1]; /* final result of all replacements */
+ char buf1[PAXPATHLEN+1]; /* where we work on the name */
+
+ /*
+ * copy the name into buf1, where we will work on it. We need to keep
+ * the orig string around so we can print out the result of the final
+ * replacement. We build up the final result in nname. inpt points at
+ * the string we apply the regular expression to. prnt is used to
+ * suppress printing when we handle replacements on the link field
+ * (the user already saw that substitution go by)
+ */
+ pt = rephead;
+ (void)strlcpy(buf1, name, sizeof(buf1));
+ inpt = buf1;
+ outpt = nname;
+ endpt = outpt + PAXPATHLEN;
+
+ /*
+ * try each replacement string in order
+ */
+ while (pt != NULL) {
+ do {
+ char *oinpt = inpt;
+ /*
+ * check for a successful substitution, if not go to
+ * the next pattern, or cleanup if we were global
+ */
+ if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0)
+ break;
+
+ /*
+ * ok we found one. We have three parts, the prefix
+ * which did not match, the section that did and the
+ * tail (that also did not match). Copy the prefix to
+ * the final output buffer (watching to make sure we
+ * do not create a string too long).
+ */
+ found = 1;
+ rpt = inpt + pm[0].rm_so;
+
+ while ((inpt < rpt) && (outpt < endpt))
+ *outpt++ = *inpt++;
+ if (outpt == endpt)
+ break;
+
+ /*
+ * for the second part (which matched the regular
+ * expression) apply the substitution using the
+ * replacement string and place it the prefix in the
+ * final output. If we have problems, skip it.
+ */
+ if ((res = resub(&(pt->rcmp),pm,pt->nstr,oinpt,outpt,endpt))
+ < 0) {
+ if (prnt)
+ paxwarn(1, "Replacement name error %s",
+ name);
+ return(1);
+ }
+ outpt += res;
+
+ /*
+ * we set up to look again starting at the first
+ * character in the tail (of the input string right
+ * after the last character matched by the regular
+ * expression (inpt always points at the first char in
+ * the string to process). If we are not doing a global
+ * substitution, we will use inpt to copy the tail to
+ * the final result. Make sure we do not overrun the
+ * output buffer
+ */
+ inpt += pm[0].rm_eo - pm[0].rm_so;
+
+ if ((outpt == endpt) || (*inpt == '\0'))
+ break;
+
+ /*
+ * if the user wants global we keep trying to
+ * substitute until it fails, then we are done.
+ */
+ } while (pt->flgs & GLOB);
+
+ if (found)
+ break;
+
+ /*
+ * a successful substitution did NOT occur, try the next one
+ */
+ pt = pt->fow;
+ }
+
+ if (found) {
+ /*
+ * we had a substitution, copy the last tail piece (if there is
+ * room) to the final result
+ */
+ while ((outpt < endpt) && (*inpt != '\0'))
+ *outpt++ = *inpt++;
+
+ *outpt = '\0';
+ if ((outpt == endpt) && (*inpt != '\0')) {
+ if (prnt)
+ paxwarn(1,"Replacement name too long %s >> %s",
+ name, nname);
+ return(1);
+ }
+
+ /*
+ * inform the user of the result if wanted
+ */
+ if (prnt && (pt->flgs & PRNT)) {
+ if (*nname == '\0')
+ (void)fprintf(stderr,"%s >> <empty string>\n",
+ name);
+ else
+ (void)fprintf(stderr,"%s >> %s\n", name, nname);
+ }
+
+ /*
+ * if empty inform the caller this file is to be skipped
+ * otherwise copy the new name over the orig name and return
+ */
+ if (*nname == '\0')
+ return(1);
+ *nlen = strlcpy(name, nname, nsize);
+ }
+ return(0);
+}
+
+/*
+ * resub()
+ * apply the replacement to the matched expression. expand out the old
+ * style ed(1) subexpression expansion.
+ * Return:
+ * -1 if error, or the number of characters added to the destination.
+ */
+
+static int
+resub(regex_t *rp, regmatch_t *pm, char *src, char *inpt, char *dest,
+ char *destend)
+{
+ char *spt;
+ char *dpt;
+ char c;
+ regmatch_t *pmpt;
+ int len;
+ int subexcnt;
+
+ spt = src;
+ dpt = dest;
+ subexcnt = rp->re_nsub;
+ while ((dpt < destend) && ((c = *spt++) != '\0')) {
+ /*
+ * see if we just have an ordinary replacement character
+ * or we refer to a subexpression.
+ */
+ if (c == '&') {
+ pmpt = pm;
+ } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) {
+ /*
+ * make sure there is a subexpression as specified
+ */
+ if ((len = *spt++ - '0') > subexcnt)
+ return(-1);
+ pmpt = pm + len;
+ } else {
+ /*
+ * Ordinary character, just copy it
+ */
+ if ((c == '\\') && (*spt != '\0'))
+ c = *spt++;
+ *dpt++ = c;
+ continue;
+ }
+
+ /*
+ * continue if the subexpression is bogus
+ */
+ if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) ||
+ ((len = (int)(pmpt->rm_eo - pmpt->rm_so)) <= 0))
+ continue;
+
+ /*
+ * copy the subexpression to the destination.
+ * fail if we run out of space or the match string is damaged
+ */
+ if (len > (destend - dpt))
+ len = destend - dpt;
+ strncpy(dpt, inpt + pmpt->rm_so, len);
+ dpt += len;
+ }
+ return(dpt - dest);
+}
diff --git a/file_cmds/pax/pat_rep.h b/file_cmds/pax/pat_rep.h
new file mode 100644
index 0000000..4d65a8e
--- /dev/null
+++ b/file_cmds/pax/pat_rep.h
@@ -0,0 +1,56 @@
+/* $OpenBSD: pat_rep.h,v 1.4 2003/06/02 23:32:08 millert Exp $ */
+/* $NetBSD: pat_rep.h,v 1.3 1995/03/21 09:07:35 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pat_rep.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _PAT_REP_H_
+#define _PAT_REP_H_
+
+/*
+ * data structure for storing user supplied replacement strings (-s)
+ */
+typedef struct replace {
+ char *nstr; /* the new string we will substitute with */
+ regex_t rcmp; /* compiled regular expression used to match */
+ int flgs; /* print conversions? global in operation? */
+#define PRNT 0x1
+#define GLOB 0x2
+ struct replace *fow; /* pointer to next pattern */
+} REPLACE;
+
+int tty_rename(ARCHD *); /* used for -o invalid=rename recovery */
+
+#endif /* _PAT_REP_H_ */
diff --git a/file_cmds/pax/pax.1 b/file_cmds/pax/pax.1
new file mode 100644
index 0000000..e79cc4b
--- /dev/null
+++ b/file_cmds/pax/pax.1
@@ -0,0 +1,1185 @@
+.\" $OpenBSD: pax.1,v 1.54 2008/06/11 07:42:50 jmc 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.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Keith Muller of the University of California, San Diego.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pax.1 8.4 (Berkeley) 4/18/94
+.\"
+.Dd $Mdocdate: June 11 2008 $
+.Dt PAX 1
+.Os
+.Sh NAME
+.Nm pax
+.Nd read and write file archives and copy directory hierarchies
+.Sh SYNOPSIS
+.Bk -words
+.Nm pax
+.Op Fl 0cdjnOvz
+.Op Fl E Ar limit
+.Op Fl f Ar archive
+.Op Fl G Ar group
+.Op Fl s Ar replstr
+.Op Fl T Ar range
+.Op Fl U Ar user
+.Op Ar pattern ...
+.Nm pax
+.Fl r
+.Op Fl 0cDdijknOuvYZz
+.Op Fl E Ar limit
+.Op Fl f Ar archive
+.Op Fl G Ar group
+.Op Fl o Ar options
+.Op Fl p Ar string
+.Op Fl s Ar replstr
+.Op Fl T Ar range
+.Op Fl U Ar user
+.Op Ar pattern ...
+.Nm pax
+.Fl w
+.Op Fl 0adHijLOPtuvXz
+.Op Fl B Ar bytes
+.Op Fl b Ar blocksize
+.Op Fl f Ar archive
+.Op Fl G Ar group
+.Op Fl o Ar options
+.Op Fl s Ar replstr
+.Op Fl T Ar range
+.Op Fl U Ar user
+.Op Fl x Ar format
+.Op Ar file ...
+.Nm pax
+.Fl rw
+.Op Fl 0DdHijkLlnOPtuvXYZ
+.Op Fl G Ar group
+.Op Fl p Ar string
+.Op Fl s Ar replstr
+.Op Fl T Ar range
+.Op Fl U Ar user
+.Op Ar file ...
+.Ar directory
+.Ek
+.Sh DESCRIPTION
+.Nm
+will read, write, and list the members of an archive file
+and will copy directory hierarchies.
+.Nm
+operation is independent of the specific archive format
+and supports a wide variety of different archive formats.
+A list of supported archive formats can be found under the description of the
+.Fl x
+option.
+.Pp
+The presence of the
+.Fl r
+and the
+.Fl w
+options specifies which of the following functional modes
+.Nm
+will operate under:
+.Em list , read , write ,
+and
+.Em copy .
+.Bl -tag -width 6n
+.It \*(Ltnone\*(Gt
+.Em List .
+.Nm
+will write to standard output
+a table of contents of the members of the archive file read from
+standard input, whose pathnames match the specified
+.Ar pattern
+arguments.
+The table of contents contains one filename per line
+and is written using single line buffering.
+.It Fl r
+.Em Read .
+.Nm
+extracts the members of the archive file read from the standard input,
+with pathnames matching the specified
+.Ar pattern
+arguments.
+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.
+Extracted files are created either at absolute paths (those that begin
+with a / character) or relative to the current file hierarchy unless the
+.Fl s
+option is used to remove leading slashes or add a relative path prefix.
+Files being extracted to absolute paths may overwrite
+files outside of the current working directory,
+so care should be taken when extracting untrusted archives.
+The setting of ownership, access and modification times, and file mode of
+the extracted files are discussed in more detail under the
+.Fl p
+option.
+.It Fl w
+.Em Write .
+.Nm
+writes an archive containing the
+.Ar file
+operands to standard output
+using the specified archive format.
+When no
+.Ar file
+operands are specified, a list of files to copy with one per line is read from
+standard input.
+When a
+.Ar file
+operand is also a directory, the entire file hierarchy rooted
+at that directory will be included.
+.It Fl rw
+.Em Copy .
+.Nm
+copies the
+.Ar file
+operands to the destination
+.Ar directory .
+When no
+.Ar file
+operands are specified, a list of files to copy with one per line is read from
+the standard input.
+When a
+.Ar file
+operand is also a directory the entire file
+hierarchy rooted at that directory will be included.
+The effect of the
+.Em copy
+is as if the copied files were written to an archive file and then
+subsequently extracted, except that there may be hard links between
+the original and the copied files (see the
+.Fl l
+option below).
+.Pp
+.Sy Warning :
+The destination
+.Ar directory
+must not be one of the
+.Ar file
+operands or a member of a file hierarchy rooted at one of the
+.Ar file
+operands.
+The result of a
+.Em copy
+under these conditions is unpredictable.
+.El
+.Pp
+While processing a damaged archive during a
+.Em read
+or
+.Em list
+operation,
+.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).
+.Pp
+The
+.Ar directory
+operand specifies a destination directory pathname.
+If the
+.Ar directory
+operand does not exist, or it is not writable by the user,
+or it is not of type directory,
+.Nm
+will exit with a non-zero exit status.
+.Pp
+The
+.Ar pattern
+operand is used to select one or more pathnames of archive members.
+Archive members are selected using the pattern matching notation described
+by
+.Xr glob 3 .
+When the
+.Ar pattern
+operand is not supplied, all members of the archive will be selected.
+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
+will write these
+.Ar pattern
+operands in a diagnostic message to standard error
+and then exit with a non-zero exit status.
+.Pp
+The
+.Ar file
+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
+will write these
+.Ar file
+operand pathnames in a diagnostic message to standard error
+and then exit with a non-zero exit status.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 0
+Use the NUL
+.Pq Ql \e0
+character as a pathname terminator, instead of newline
+.Pq Ql \en .
+This applies only to the pathnames read from standard input in
+the write and copy modes,
+and to the pathnames written to standard output in list mode.
+This option is expected to be used in concert with the
+.Fl print0
+function in
+.Xr find 1
+or the
+.Fl 0
+flag in
+.Xr xargs 1 .
+.It Fl a
+Append the given
+.Ar file
+operands
+to the end of an archive that was previously written.
+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
+to exit immediately
+with a non-zero exit status.
+The blocking size used in the archive volume where writing starts
+will continue to be used for the remainder of that archive volume.
+.Pp
+.Sy Warning :
+Many storage devices are not able to support the operations necessary
+to perform an append operation.
+Any attempt to append to an archive stored on such a device may damage the
+archive or have other unpredictable results.
+Tape drives in particular are more likely to not support an append operation.
+An archive stored in a regular file system file or on a disk device will
+usually support an append operation.
+.It Fl B Ar bytes
+Limit the number of bytes written to a single archive volume to
+.Ar bytes .
+The
+.Ar bytes
+limit can end with
+.Sq Li m ,
+.Sq Li k ,
+or
+.Sq Li b
+to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively.
+A pair of
+.Ar bytes
+limits can be separated by
+.Sq Li x
+to indicate a product.
+.Pp
+.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).
+The use of this option with a floppy or hard disk is not recommended.
+.It Fl b Ar blocksize
+When
+.Em writing
+an archive,
+block the output at a positive decimal integer number of
+bytes per write to the archive file.
+The
+.Ar blocksize
+must be a multiple of 512 bytes with a maximum of 64512 bytes.
+Archive block sizes larger than 32256 bytes violate the
+.Tn POSIX
+standard and will not be portable to all systems.
+A
+.Ar blocksize
+can end with
+.Sq Li k
+or
+.Sq Li b
+to specify multiplication by 1024 (1K) or 512, respectively.
+A pair of
+.Ar blocksizes
+can be separated by
+.Sq Li x
+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
+.Ar blocksize
+is dependent on the specific archive format being used (see the
+.Fl x
+option).
+.It Fl c
+Match all file or archive members
+.Em except
+those specified by the
+.Ar pattern
+and
+.Ar file
+operands.
+.It Fl D
+This option is the same as the
+.Fl u
+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
+.Ar directory .
+.It Fl d
+Cause files of type directory being copied or archived, or archive members of
+type directory being extracted, to match only the directory file or archive
+member and not the file hierarchy rooted at the directory.
+.It Fl E Ar limit
+Limit the number of consecutive read faults while trying to read a flawed
+archive to
+.Ar limit .
+With a positive
+.Ar limit ,
+.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
+to stop operation after the first read error is detected on an archive volume.
+A
+.Ar limit
+of
+.Li NONE
+will cause
+.Nm
+to attempt to recover from read errors forever.
+The default
+.Ar limit
+is a small positive number of retries.
+.Pp
+.Em Warning :
+Using this option with
+.Li NONE
+should be used with extreme caution as
+.Nm
+may get stuck in an infinite loop on a very badly flawed archive.
+.It Fl f Ar archive
+Specify
+.Ar archive
+as the pathname of the input or output archive, overriding the default
+standard input (for
+.Em list
+and
+.Em read )
+or standard output
+(for
+.Em write ) .
+A single archive may span multiple files and different archive devices.
+When required,
+.Nm
+will prompt for the pathname of the file or device of the next volume in the
+archive.
+.It Fl G Ar group
+Select a file based on its
+.Ar group
+name, or when starting with a
+.Cm # ,
+a numeric GID.
+A
+.Ql \e
+can be used to escape the
+.Cm # .
+Multiple
+.Fl G
+options may be supplied and checking stops with the first match.
+.It Fl H
+Follow only command-line symbolic links while performing a physical file
+system traversal.
+.It Fl i
+Interactively rename files or archive members.
+For each archive member matching a
+.Ar pattern
+operand or each file matching a
+.Ar file
+operand,
+.Nm
+will prompt to
+.Pa /dev/tty
+giving the name of the file, its file mode, and its modification time.
+.Nm
+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
+will immediately exit with a non-zero exit status if
+.Dv EOF
+is encountered when reading a response or if
+.Pa /dev/tty
+cannot be opened for reading and writing.
+.It Fl j
+Use bzip2 to compress (decompress) the archive while writing (reading).
+The bzip2 utility must be installed separately.
+Incompatible with
+.Fl a .
+.It Fl k
+Do not overwrite existing files.
+.It Fl L
+Follow all symbolic links to perform a logical file system traversal.
+.It Fl l
+(The lowercase letter
+.Dq ell . )
+Link files.
+In the
+.Em copy
+mode
+.Pq Fl r Fl w ,
+hard links are made between the source and destination file hierarchies
+whenever possible.
+.It Fl n
+Select the first archive member that matches each
+.Ar pattern
+operand.
+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
+is also specified).
+.It Fl O
+Force the archive to be one volume.
+If a volume ends prematurely,
+.Nm
+will not prompt for a new volume.
+This option can be useful for
+automated tasks where error recovery cannot be performed by a human.
+.It Fl o Ar options
+Information to modify the algorithm for extracting or writing archive files
+which is specific to the archive format specified by
+.Fl x .
+In general,
+.Ar options
+take the form:
+.Ar name Ns = Ns Ar value .
+.Pp
+The following options are available for the old
+.Bx
+.Em tar
+format:
+.Pp
+.Bl -tag -width Ds -compact
+.It Cm nodir
+.It Cm write_opt=nodir
+When writing archives, omit the storage of directories.
+.El
+.It Fl P
+Do not follow symbolic links, perform a physical file system traversal.
+This is the default mode.
+.It Fl p Ar string
+Specify one or more file characteristic options (privileges).
+The
+.Ar string
+option-argument is a string specifying file characteristics to be retained or
+discarded on extraction.
+The string consists of the specification characters
+.Cm a , e , m , o ,
+and
+.Cm p .
+Multiple characteristics can be concatenated within the same string
+and multiple
+.Fl p
+options can be specified.
+The meanings of the specification characters are as follows:
+.Bl -tag -width 2n
+.It Cm a
+Do not preserve file access times.
+By default, file access times are preserved whenever possible.
+.It Cm e
+.Dq Preserve everything ,
+the user ID, group ID, file mode bits,
+file access time, and file modification time.
+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
+.Cm e
+flag is the sum of the
+.Cm o
+and
+.Cm p
+flags.
+.It Cm m
+Do not preserve file modification times.
+By default, file modification times are preserved whenever possible.
+.It Cm o
+Preserve the user ID and group ID.
+.It Cm p
+.Dq Preserve
+the file mode bits.
+This is intended to be used by a
+.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
+disable this and use the time of extraction instead.
+.El
+.Pp
+In the preceding list,
+.Sq preserve
+indicates that an attribute stored in the archive is given to the
+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
+.Cm e
+nor the
+.Cm o
+specification character is specified, or the user ID and group ID are not
+preserved for any reason,
+.Nm
+will not set the
+.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
+will write a diagnostic message to standard error.
+Failure to preserve these items will affect the final exit status,
+but will not cause the extracted file to be deleted.
+If the file characteristic letters in any of the string option-arguments are
+duplicated or conflict with each other, the one(s) given last will take
+precedence.
+For example, if
+.Fl p Ar eme
+is specified, file modification times are still preserved.
+.It Fl r
+Read an archive file from standard input
+and extract the specified
+.Ar file
+operands.
+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
+.Tn OR
+of
+.Dv S_IRWXU , S_IRWXG ,
+and
+.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
+will write a diagnostic message to standard error
+and exit with a non-zero exit status at the completion of operation.
+.It Fl s Ar replstr
+Modify the archive member names according to the substitution expression
+.Ar replstr ,
+using the syntax of the
+.Xr ed 1
+utility regular expressions.
+.Ar file
+or
+.Ar pattern
+arguments may be given to restrict the list of archive members to those
+specified.
+.Pp
+The format of these regular expressions is:
+.Pp
+.Dl /old/new/[gp]
+.Pp
+As in
+.Xr ed 1 ,
+.Ar old
+is a basic regular expression (see
+.Xr re_format 7 )
+and
+.Ar new
+can contain an ampersand
+.Pq Ql & ,
+.Ql \e Ns Em n
+(where
+.Em n
+is a digit) back-references,
+or subexpression matching.
+The
+.Ar old
+string may also contain newline characters.
+Any non-null character can be used as a delimiter
+.Po
+.Ql /
+is shown here
+.Pc .
+Multiple
+.Fl s
+expressions can be specified.
+The expressions are applied in the order they are specified on the
+command line, terminating with the first successful substitution.
+.Pp
+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
+.Cm g
+option.
+The optional trailing
+.Cm p
+will cause the final result of a successful substitution to be written to
+standard error in the following format:
+.Pp
+.D1 Em original-pathname No \*(Gt\*(Gt Em new-pathname
+.Pp
+File or archive member names that substitute to the empty string
+are not selected and will be skipped.
+.It Fl T Ar range
+Allow files to be selected based on a file modification or inode change
+time falling within the specified time range.
+The range has the format:
+.Sm off
+.Bd -filled -offset indent
+.Oo Ar from_date Oc Oo ,
+.Ar to_date Oc Oo /
+.Oo Cm c Oc Op Cm m Oc
+.Ed
+.Sm on
+.Pp
+The dates specified by
+.Ar from_date
+to
+.Ar to_date
+are inclusive.
+If only a
+.Ar from_date
+is supplied, all files with a modification or inode change time
+equal to or younger are selected.
+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
+.Ar from_date
+is equal to the
+.Ar to_date ,
+only files with a modification or inode change time of exactly that
+time will be selected.
+.Pp
+When
+.Nm
+is in the
+.Em write
+or
+.Em copy
+mode, the optional trailing field
+.Oo Cm c Oc Op Cm m
+can be used to determine which file time (inode change, file modification or
+both) are used in the comparison.
+If neither is specified, the default is to use file modification time only.
+The
+.Cm m
+specifies the comparison of file modification time (the time when
+the file was last written).
+The
+.Cm 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
+.Cm c
+and
+.Cm m
+are both specified, then the modification and inode change times are
+both compared.
+.Pp
+The inode change time comparison is useful in selecting files whose
+attributes were recently changed or selecting files which were recently
+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
+is used to create a time based incremental archive (only files that were
+changed during a specified time range will be archived).
+.Pp
+A time range is made up of six different fields and each field must contain two
+digits.
+The format is:
+.Pp
+.Dl [[[[[cc]yy]mm]dd]HH]MM[.SS]
+.Pp
+Where
+.Ar cc
+is the first two digits of the year (the century),
+.Ar yy
+is the last two digits of the year,
+the first
+.Ar mm
+is the month (from 01 to 12),
+.Ar dd
+is the day of the month (from 01 to 31),
+.Ar HH
+is the hour of the day (from 00 to 23),
+.Ar MM
+is the minute (from 00 to 59),
+and
+.Ar SS
+is the seconds (from 00 to 59).
+The minute field
+.Ar MM
+is required, while the other fields are optional and must be added in the
+following order:
+.Ar HH , dd , mm ,
+.Ar yy , cc .
+.Pp
+The
+.Ar SS
+field may be added independently of the other fields.
+Time ranges are relative to the current time, so
+.Ic -T 1234/cm
+would select all files with a modification or inode change time
+of 12:34 PM today or later.
+Multiple
+.Fl T
+time range can be supplied and checking stops with the first match.
+.It Fl t
+Reset the access times of any file or directory read or accessed by
+.Nm
+to be the same as they were before being read or accessed by
+.Nm pax .
+.It Fl U Ar user
+Select a file based on its
+.Ar user
+name, or when starting with a
+.Cm # ,
+a numeric UID.
+A
+.Ql \e
+can be used to escape the
+.Cm # .
+Multiple
+.Fl U
+options may be supplied and checking stops with the first match.
+.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.
+During
+.Em read ,
+an archive member with the same name as a file in the file system will be
+extracted if the archive member is newer than the file.
+During
+.Em write ,
+a file system member with the same name as an archive member will be
+written to the archive if it is newer than the archive member.
+During
+.Em copy ,
+the file in the destination hierarchy is replaced by the file in the source
+hierarchy or by a link to the file in the source hierarchy if the file in
+the source hierarchy is newer.
+.It Fl v
+During a
+.Em list
+operation, produce a verbose table of contents using the format of the
+.Xr ls 1
+utility with the
+.Fl l
+option.
+For pathnames representing a hard link to a previous member of the archive,
+the output has the format:
+.Pp
+.Dl Em ls -l listing Li == Em link-name
+.Pp
+For pathnames representing a symbolic link, the output has the format:
+.Pp
+.Dl Em ls -l listing Li =\*(Gt Em link-name
+.Pp
+Where
+.Em 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
+.Po Em read , write , No and Em copy
+.Pc ,
+pathnames are written and flushed to standard error
+without a trailing newline
+as soon as processing begins on that file or
+archive member.
+The trailing newline
+is not buffered and is written only after the file has been read or written.
+.It Fl w
+Write files to the standard output
+in the specified archive format.
+When no
+.Ar file
+operands are specified, standard input
+is read for a list of pathnames with one per line without any leading or
+trailing
+.Aq blanks .
+.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
+field as described in
+.Xr stat 2
+for more information about device IDs.
+.It Fl x Ar format
+Specify the output archive format, with the default format being
+.Cm ustar .
+.Nm
+currently supports the following formats:
+.Bl -tag -width "sv4cpio"
+.It Cm bcpio
+The old binary cpio format.
+The default blocksize for this format is 5120 bytes.
+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
+and is repaired.
+.It Cm cpio
+The extended cpio interchange format specified in the
+.St -p1003.2
+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
+and is repaired.
+.It Cm 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
+and is repaired.
+.It Cm 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
+and is repaired.
+.It Cm tar
+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 ,
+and
+.Em directories
+will be archived (other file system types are not supported).
+For backwards compatibility with even older tar formats, a
+.Fl o
+option can be used when writing an archive to omit the storage of directories.
+This option takes the form:
+.Pp
+.Dl Fl o Cm write_opt=nodir
+.It Cm ustar
+The extended tar interchange format specified in the
+.St -p1003.2
+standard.
+The default blocksize for this format is 10240 bytes.
+Filenames stored by this format must be 100 characters or less in length;
+the total pathname must be 255 characters or less.
+.El
+.Pp
+.Nm
+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 Y
+This option is the same as the
+.Fl D
+option, except that the inode change time is checked using the
+pathname created after all the file name modifications have completed.
+.It Fl Z
+This option is the same as the
+.Fl u
+option, except that the modification time is checked using the
+pathname created after all the file name modifications have completed.
+.It Fl z
+Use
+.Xr gzip 1
+to compress (decompress) the archive while writing (reading).
+Incompatible with
+.Fl a .
+.It Fl -insecure
+Normally
+.Nm
+ignores filenames or symbolic links that contain
+.Dq ..
+as a path component.
+With this option,
+files that contain
+.Dq ..
+can be processed.
+.El
+.Pp
+The options that operate on the names of files or archive members
+.Po Fl c ,
+.Fl i ,
+.Fl j ,
+.Fl n ,
+.Fl s ,
+.Fl u ,
+.Fl v ,
+.Fl D ,
+.Fl G ,
+.Fl T ,
+.Fl U ,
+.Fl Y ,
+and
+.Fl Z
+.Pc
+interact as follows.
+.Pp
+When extracting files during a
+.Em read
+operation, archive members are
+.Sq selected ,
+based only on the user specified pattern operands as modified by the
+.Fl c ,
+.Fl n ,
+.Fl u ,
+.Fl D ,
+.Fl G ,
+.Fl T ,
+.Fl U
+options.
+Then any
+.Fl s
+and
+.Fl i
+options will modify in that order, the names of these selected files.
+Then the
+.Fl Y
+and
+.Fl Z
+options will be applied based on the final pathname.
+Finally, the
+.Fl v
+option will write the names resulting from these modifications.
+.Pp
+When archiving files during a
+.Em write
+operation, or copying files during a
+.Em copy
+operation, archive members are
+.Sq selected ,
+based only on the user specified pathnames as modified by the
+.Fl n ,
+.Fl u ,
+.Fl D ,
+.Fl G ,
+.Fl T ,
+and
+.Fl U
+options (the
+.Fl D
+option only applies during a copy operation).
+Then any
+.Fl s
+and
+.Fl i
+options will modify in that order, the names of these selected files.
+Then during a
+.Em copy
+operation the
+.Fl Y
+and the
+.Fl Z
+options will be applied based on the final pathname.
+Finally, the
+.Fl v
+option will write the names resulting from these modifications.
+.Pp
+When one or both of the
+.Fl u
+or
+.Fl D
+options are specified along with the
+.Fl n
+option, a file is not considered selected unless it is newer
+than the file to which it is compared.
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev TMPDIR
+Path in which to store temporary files.
+.El
+.Sh EXAMPLES
+Copy the contents of the current directory to the device
+.Pa /dev/rst0 :
+.Pp
+.Dl $ pax -w -f /dev/rst0 \&.
+.Pp
+Give the verbose table of contents for an archive stored in
+.Pa filename :
+.Pp
+.Dl $ pax -v -f filename
+.Pp
+This sequence of commands will copy the entire
+.Pa olddir
+directory hierarchy to
+.Pa newdir :
+.Bd -literal -offset indent
+$ mkdir newdir
+$ cd olddir
+$ pax -rw . ../newdir
+.Ed
+.Pp
+Extract files from the archive
+.Pa a.pax .
+Files rooted in
+.Pa /usr
+are extracted relative to the current working directory;
+all other files are extracted to their unmodified path.
+.Pp
+.Dl $ pax -r -s ',^/usr/,,' -f a.pax
+.Pp
+This can be used to interactively select the files to copy from the
+current directory to
+.Pa dest_dir :
+.Pp
+.Dl $ pax -rw -i \&. dest_dir
+.Pp
+Extract all files from the archive
+.Pa a.pax
+which are owned by
+.Em root
+with group
+.Em bin
+and preserve all file permissions:
+.Pp
+.Dl "$ pax -r -pe -U root -G bin -f a.pax"
+.Pp
+Update (and list) only those files in the destination directory
+.Pa /backup
+which are older (less recent inode change or file modification times) than
+files with the same name found in the source file tree
+.Pa home :
+.Pp
+.Dl "$ pax -r -w -v -Y -Z home /backup"
+.Sh DIAGNOSTICS
+.Nm
+will exit with one of the following values:
+.Bl -tag -width 2n -offset indent
+.It 0
+All files were processed successfully.
+.It 1
+An error occurred.
+.El
+.Pp
+Whenever
+.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
+.Fl p
+option is specified, a diagnostic message is written to standard error
+and a non-zero exit status will be returned, but processing will continue.
+In the case where
+.Nm
+cannot create a link to a file,
+.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
+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
+may have only partially created the archive, which may violate the specific
+archive format specification.
+.Pp
+If while doing a
+.Em copy ,
+.Nm
+detects a file is about to overwrite itself, the file is not copied,
+a diagnostic message is written to standard error
+and when
+.Nm
+completes it will exit with a non-zero exit status.
+.Sh SEE ALSO
+.Xr cpio 1 ,
+.Xr tar 1
+.Pp
+"Archiving with Pax", Dru Lavigne, ONLamp.com BSD DevCenter,
+http://www.onlamp.com/pub/a/bsd/2002/08/22/FreeBSD_Basics.html
+.Pp
+pax(1) manual page,
+http://heirloom.sourceforge.net/man/pax.1.html
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2004
+specification.
+.Pp
+The flags
+.Op Fl 0BDEGHjLOPTUYZz ,
+the archive formats
+.Em bcpio ,
+.Em sv4cpio ,
+.Em sv4crc ,
+.Em tar ,
+and the flawed archive handling during
+.Em list
+and
+.Em read
+operations
+are extensions to that specification.
+.Sh AUTHORS
+Keith Muller at the University of California, San Diego.
diff --git a/file_cmds/pax/pax.c b/file_cmds/pax/pax.c
new file mode 100644
index 0000000..ec180b7
--- /dev/null
+++ b/file_cmds/pax/pax.c
@@ -0,0 +1,446 @@
+/* $OpenBSD: pax.c,v 1.28 2005/08/04 10:02:44 mpf Exp $ */
+/* $NetBSD: pax.c,v 1.5 1996/03/26 23:54:20 mrg Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: pax.c,v 1.28 2005/08/04 10:02:44 mpf Exp $";
+#endif
+#endif /* not lint */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <locale.h>
+#include "pax.h"
+#include "extern.h"
+static int gen_init(void);
+
+/*
+ * PAX main routines, general globals and some simple start up routines
+ */
+
+/*
+ * Variables that can be accessed by any routine within pax
+ */
+int act = DEFOP; /* read/write/append/copy */
+const FSUB *frmt = NULL; /* archive format type */
+int cflag; /* match all EXCEPT pattern/file */
+int cwdfd; /* starting cwd */
+int dflag; /* directory member match only */
+int iflag; /* interactive file/archive rename */
+int kflag; /* do not overwrite existing files */
+int lflag; /* use hard links when possible */
+int nflag; /* select first archive member match */
+int tflag; /* restore access time after read */
+int uflag; /* ignore older modification time files */
+int vflag; /* produce verbose output */
+int Dflag; /* same as uflag except inode change time */
+int Hflag; /* follow command line symlinks (write only) */
+int Lflag; /* follow symlinks when writing */
+int Xflag; /* archive files with same device id only */
+int Yflag; /* same as Dflag except after name mode */
+int Zflag; /* same as uflag except after name mode */
+int zeroflag; /* use \0 as pathname terminator */
+int vfpart; /* is partial verbose output in progress */
+int patime = 1; /* preserve file access time */
+int pmtime = 1; /* preserve file modification times */
+int nodirs; /* do not create directories as needed */
+int pmode; /* preserve file mode bits */
+int pids; /* preserve file uid/gid */
+int rmleadslash = 0; /* remove leading '/' from pathnames */
+int secure = 1; /* don't extract names that contain .. */
+int exit_val; /* exit value */
+int docrc; /* check/create file crc */
+char *dirptr; /* destination dir in a copy */
+char *ltmfrmt; /* -v locale time format (if any) */
+char *argv0; /* root of argv[0] */
+sigset_t s_mask; /* signal mask for cleanup critical sect */
+FILE *listf; /* file pointer to print file list to */
+char *tempfile; /* tempfile to use for mkstemp(3) */
+char *tempbase; /* basename of tempfile to use for mkstemp(3) */
+
+/*
+ * PAX - Portable Archive Interchange
+ *
+ * A utility to read, write, and write lists of the members of archive
+ * files and copy directory hierarchies. A variety of archive formats
+ * are supported (some are described in POSIX 1003.1 10.1):
+ *
+ * ustar - 10.1.1 extended tar interchange format
+ * cpio - 10.1.2 extended cpio interchange format
+ * tar - old BSD 4.3 tar format
+ * binary cpio - old cpio with binary header format
+ * sysVR4 cpio - with and without CRC
+ *
+ * This version is a superset of IEEE Std 1003.2b-d3
+ *
+ * Summary of Extensions to the IEEE Standard:
+ *
+ * 1 READ ENHANCEMENTS
+ * 1.1 Operations which read archives will continue to operate even when
+ * processing archives which may be damaged, truncated, or fail to meet
+ * format specs in several different ways. Damaged sections of archives
+ * are detected and avoided if possible. Attempts will be made to resync
+ * archive read operations even with badly damaged media.
+ * 1.2 Blocksize requirements are not strictly enforced on archive read.
+ * Tapes which have variable sized records can be read without errors.
+ * 1.3 The user can specify via the non-standard option flag -E if error
+ * resync operation should stop on a media error, try a specified number
+ * of times to correct, or try to correct forever.
+ * 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks
+ * of all zeros will be restored with holes appropriate for the target
+ * filesystem
+ * 1.5 The user is notified whenever something is found during archive
+ * read operations which violates spec (but the read will continue).
+ * 1.6 Multiple archive volumes can be read and may span over different
+ * archive devices
+ * 1.7 Rigidly restores all file attributes exactly as they are stored on the
+ * archive.
+ * 1.8 Modification change time ranges can be specified via multiple -T
+ * options. These allow a user to select files whose modification time
+ * lies within a specific time range.
+ * 1.9 Files can be selected based on owner (user name or uid) via one or more
+ * -U options.
+ * 1.10 Files can be selected based on group (group name or gid) via one o
+ * more -G options.
+ * 1.11 File modification time can be checked against existing file after
+ * name modification (-Z)
+ *
+ * 2 WRITE ENHANCEMENTS
+ * 2.1 Write operation will stop instead of allowing a user to create a flawed
+ * flawed archive (due to any problem).
+ * 2.2 Archives written by pax are forced to strictly conform to both the
+ * archive and pax the specific format specifications.
+ * 2.3 Blocking size and format is rigidly enforced on writes.
+ * 2.4 Formats which may exhibit header overflow problems (they have fields
+ * too small for large file systems, such as inode number storage), use
+ * routines designed to repair this problem. These techniques still
+ * conform to both pax and format specifications, but no longer truncate
+ * these fields. This removes any restrictions on using these archive
+ * formats on large file systems.
+ * 2.5 Multiple archive volumes can be written and may span over different
+ * archive devices
+ * 2.6 A archive volume record limit allows the user to specify the number
+ * of bytes stored on an archive volume. When reached the user is
+ * prompted for the next archive volume. This is specified with the
+ * non-standard -B flag. The limit is rounded up to the next blocksize.
+ * 2.7 All archive padding during write use zero filled sections. This makes
+ * it much easier to pull data out of flawed archive during read
+ * operations.
+ * 2.8 Access time reset with the -t applies to all file nodes (including
+ * directories).
+ * 2.9 Symbolic links can be followed with -L (optional in the spec).
+ * 2.10 Modification or inode change time ranges can be specified via
+ * multiple -T options. These allow a user to select files whose
+ * modification or inode change time lies within a specific time range.
+ * 2.11 Files can be selected based on owner (user name or uid) via one or more
+ * -U options.
+ * 2.12 Files can be selected based on group (group name or gid) via one o
+ * more -G options.
+ * 2.13 Symlinks which appear on the command line can be followed (without
+ * following other symlinks; -H flag)
+ *
+ * 3 COPY ENHANCEMENTS
+ * 3.1 Sparse files (lseek holes) can be copied without expanding the holes
+ * into zero filled blocks. The file copy is created with holes which are
+ * appropriate for the target filesystem
+ * 3.2 Access time as well as modification time on copied file trees can be
+ * preserved with the appropriate -p options.
+ * 3.3 Access time reset with the -t applies to all file nodes (including
+ * directories).
+ * 3.4 Symbolic links can be followed with -L (optional in the spec).
+ * 3.5 Modification or inode change time ranges can be specified via
+ * multiple -T options. These allow a user to select files whose
+ * modification or inode change time lies within a specific time range.
+ * 3.6 Files can be selected based on owner (user name or uid) via one or more
+ * -U options.
+ * 3.7 Files can be selected based on group (group name or gid) via one o
+ * more -G options.
+ * 3.8 Symlinks which appear on the command line can be followed (without
+ * following other symlinks; -H flag)
+ * 3.9 File inode change time can be checked against existing file before
+ * name modification (-D)
+ * 3.10 File inode change time can be checked against existing file after
+ * name modification (-Y)
+ * 3.11 File modification time can be checked against existing file after
+ * name modification (-Z)
+ *
+ * 4 GENERAL ENHANCEMENTS
+ * 4.1 Internal structure is designed to isolate format dependent and
+ * independent functions. Formats are selected via a format driver table.
+ * This encourages the addition of new archive formats by only having to
+ * write those routines which id, read and write the archive header.
+ */
+
+/*
+ * main()
+ * parse options, set up and operate as specified by the user.
+ * any operational flaw will set exit_val to non-zero
+ * Return: 0 if ok, 1 otherwise
+ */
+
+int
+main(int argc, char **argv)
+{
+ char *tmpdir;
+ size_t tdlen;
+#ifdef _HAVE_REGCOMP_
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_COLLATE, "");
+#endif
+
+ listf = stderr;
+ /*
+ * Keep a reference to cwd, so we can always come back home.
+ */
+ cwdfd = open(".", O_RDONLY);
+ if (cwdfd < 0) {
+ syswarn(1, errno, "Can't open current working directory.");
+ return(exit_val);
+ }
+
+ if (updatepath() == -1)
+ return exit_val;
+ /*
+ * Where should we put temporary files?
+ */
+ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
+ tmpdir = _PATH_TMP;
+ tdlen = strlen(tmpdir);
+ while (tdlen > 0 && tmpdir[tdlen - 1] == '/')
+ tdlen--;
+ tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE));
+ if (tempfile == NULL) {
+ paxwarn(1, "Cannot allocate memory for temp file name.");
+ return(exit_val);
+ }
+ if (tdlen)
+ memcpy(tempfile, tmpdir, tdlen);
+ tempbase = tempfile + tdlen;
+ *tempbase++ = '/';
+
+ /*
+ * parse options, determine operational mode, general init
+ */
+ options(argc, argv);
+ if ((gen_init() < 0) || (tty_init() < 0))
+ return(exit_val);
+
+ /*
+ * select a primary operation mode
+ */
+ switch (act) {
+ case EXTRACT:
+ extract();
+ break;
+ case ARCHIVE:
+ archive();
+ break;
+ case APPND:
+ if (gzip_program != NULL)
+ errx(1, "can not gzip while appending");
+ append();
+ break;
+ case COPY:
+ copy();
+ break;
+ default:
+ case LIST:
+ list();
+ break;
+ }
+ return(exit_val);
+}
+
+/*
+ * sig_cleanup()
+ * when interrupted we try to do whatever delayed processing we can.
+ * This is not critical, but we really ought to limit our damage when we
+ * are aborted by the user.
+ * Return:
+ * never....
+ */
+
+void
+sig_cleanup(int which_sig)
+{
+ /* XXX signal races */
+
+ /*
+ * restore modes and times for any dirs we may have created
+ * or any dirs we may have read. Set vflag and vfpart so the user
+ * will clearly see the message on a line by itself.
+ */
+ vflag = vfpart = 1;
+ if (which_sig == SIGXCPU)
+ paxwarn(0, "Cpu time limit reached, cleaning up.");
+ else
+ paxwarn(0, "Signal caught, cleaning up.");
+
+ ar_close();
+ proc_dir();
+ if (tflag)
+ atdir_end();
+ exit(1);
+}
+
+/*
+ * gen_init()
+ * general setup routines. Not all are required, but they really help
+ * when dealing with a medium to large sized archives.
+ */
+
+static int
+gen_init(void)
+{
+ struct rlimit reslimit;
+ struct sigaction n_hand;
+ struct sigaction o_hand;
+
+ /*
+ * Really needed to handle large archives. We can run out of memory for
+ * internal tables really fast when we have a whole lot of files...
+ */
+ if (getrlimit(RLIMIT_DATA , &reslimit) == 0){
+ reslimit.rlim_cur = reslimit.rlim_max;
+ (void)setrlimit(RLIMIT_DATA , &reslimit);
+ }
+
+ /*
+ * should file size limits be waived? if the os limits us, this is
+ * needed if we want to write a large archive
+ */
+ if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){
+ reslimit.rlim_cur = reslimit.rlim_max;
+ (void)setrlimit(RLIMIT_FSIZE , &reslimit);
+ }
+
+ /*
+ * increase the size the stack can grow to
+ */
+ if (getrlimit(RLIMIT_STACK , &reslimit) == 0){
+ reslimit.rlim_cur = reslimit.rlim_max;
+ (void)setrlimit(RLIMIT_STACK , &reslimit);
+ }
+
+ /*
+ * not really needed, but doesn't hurt
+ */
+ if (getrlimit(RLIMIT_RSS , &reslimit) == 0){
+ reslimit.rlim_cur = reslimit.rlim_max;
+ (void)setrlimit(RLIMIT_RSS , &reslimit);
+ }
+
+ /*
+ * Handle posix locale
+ *
+ * set user defines time printing format for -v option
+ */
+ ltmfrmt = getenv("LC_TIME");
+
+ /*
+ * signal handling to reset stored directory times and modes. Since
+ * we deal with broken pipes via failed writes we ignore it. We also
+ * deal with any file size limit through failed writes. Cpu time
+ * limits are caught and a cleanup is forced.
+ */
+ if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) ||
+ (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) ||
+ (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) ||
+ (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) {
+ paxwarn(1, "Unable to set up signal mask");
+ return(-1);
+ }
+ memset(&n_hand, 0, sizeof n_hand);
+ n_hand.sa_mask = s_mask;
+ n_hand.sa_flags = 0;
+ n_hand.sa_handler = sig_cleanup;
+
+ if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) &&
+ (o_hand.sa_handler == SIG_IGN) &&
+ (sigaction(SIGHUP, &o_hand, &o_hand) < 0))
+ goto out;
+
+ if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) &&
+ (o_hand.sa_handler == SIG_IGN) &&
+ (sigaction(SIGTERM, &o_hand, &o_hand) < 0))
+ goto out;
+
+ if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) &&
+ (o_hand.sa_handler == SIG_IGN) &&
+ (sigaction(SIGINT, &o_hand, &o_hand) < 0))
+ goto out;
+
+ if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) &&
+ (o_hand.sa_handler == SIG_IGN) &&
+ (sigaction(SIGQUIT, &o_hand, &o_hand) < 0))
+ goto out;
+
+ if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) &&
+ (o_hand.sa_handler == SIG_IGN) &&
+ (sigaction(SIGXCPU, &o_hand, &o_hand) < 0))
+ goto out;
+
+ n_hand.sa_handler = SIG_IGN;
+ if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0) ||
+ (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0))
+ goto out;
+ return(0);
+
+ out:
+ syswarn(1, errno, "Unable to set up signal handler");
+ return(-1);
+}
diff --git a/file_cmds/pax/pax.h b/file_cmds/pax/pax.h
new file mode 100644
index 0000000..1caff0c
--- /dev/null
+++ b/file_cmds/pax/pax.h
@@ -0,0 +1,255 @@
+/* $OpenBSD: pax.h,v 1.17 2005/11/09 19:59:06 otto Exp $ */
+/* $NetBSD: pax.h,v 1.3 1995/03/21 09:07:41 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pax.h 8.2 (Berkeley) 4/18/94
+ */
+
+#ifndef _PAX_H_
+#define _PAX_H_
+
+/*
+ * BSD PAX global data structures and constants.
+ */
+
+#define MAXBLK 64512 /* MAX blocksize supported (posix SPEC) */
+ /* WARNING: increasing MAXBLK past 32256 */
+ /* will violate posix spec. */
+#define MAXBLK_POSIX 32256 /* MAX blocksize supported as per POSIX */
+#define BLKMULT 512 /* blocksize must be even mult of 512 bytes */
+ /* Don't even think of changing this */
+#define DEVBLK 8192 /* default read blksize for devices */
+#define FILEBLK 10240 /* default read blksize for files */
+#define PAXPATHLEN 3072 /* maximum path length for pax. MUST be */
+ /* longer than the system MAXPATHLEN */
+
+/*
+ * Pax modes of operation
+ */
+#define LIST 0 /* List the file in an archive */
+#define EXTRACT 1 /* extract the files in an archive */
+#define ARCHIVE 2 /* write a new archive */
+#define APPND 3 /* append to the end of an archive */
+#define COPY 4 /* copy files to destination dir */
+#define DEFOP LIST /* if no flags default is to LIST */
+
+/*
+ * Device type of the current archive volume
+ */
+#define ISREG 0 /* regular file */
+#define ISCHR 1 /* character device */
+#define ISBLK 2 /* block device */
+#define ISTAPE 3 /* tape drive */
+#define ISPIPE 4 /* pipe/socket */
+
+/*
+ * Pattern matching structure
+ *
+ * Used to store command line patterns
+ */
+typedef struct pattern {
+ char *pstr; /* pattern to match, user supplied */
+ char *pend; /* end of a prefix match */
+ char *chdname; /* the dir to change to if not NULL. */
+ int plen; /* length of pstr */
+ int flgs; /* processing/state flags */
+#define MTCH 0x1 /* pattern has been matched */
+#define DIR_MTCH 0x2 /* pattern matched a directory */
+ struct pattern *fow; /* next pattern */
+} PATTERN;
+
+/*
+ * General Archive Structure (used internal to pax)
+ *
+ * This structure is used to pass information about archive members between
+ * the format independent routines and the format specific routines. When
+ * new archive formats are added, they must accept requests and supply info
+ * encoded in a structure of this type. The name fields are declared statically
+ * here, as there is only ONE of these floating around, size is not a major
+ * consideration. Eventually converting the name fields to a dynamic length
+ * may be required if and when the supporting operating system removes all
+ * restrictions on the length of pathnames it will resolve.
+ */
+typedef struct {
+ int nlen; /* file name length */
+ char name[PAXPATHLEN+1]; /* file name */
+ int ln_nlen; /* link name length */
+ char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */
+ char *org_name; /* orig name in file system */
+ PATTERN *pat; /* ptr to pattern match (if any) */
+ struct stat sb; /* stat buffer see stat(2) */
+ off_t pad; /* bytes of padding after file xfer */
+ off_t skip; /* bytes of real data after header */
+ /* IMPORTANT. The st_size field does */
+ /* not always indicate the amount of */
+ /* data following the header. */
+ u_int32_t crc; /* file crc */
+ int type; /* type of file node */
+#define PAX_DIR 1 /* directory */
+#define PAX_CHR 2 /* character device */
+#define PAX_BLK 3 /* block device */
+#define PAX_REG 4 /* regular file */
+#define PAX_SLK 5 /* symbolic link */
+#define PAX_SCK 6 /* socket */
+#define PAX_FIF 7 /* fifo */
+#define PAX_HLK 8 /* hard link */
+#define PAX_HRG 9 /* hard link to a regular file */
+#define PAX_CTG 10 /* high performance file */
+#define PAX_GLL 11 /* GNU long symlink */
+#define PAX_GLF 12 /* GNU long file */
+} ARCHD;
+
+/*
+ * Format Specific Routine Table
+ *
+ * The format specific routine table allows new archive formats to be quickly
+ * added. Overall pax operation is independent of the actual format used to
+ * form the archive. Only those routines which deal directly with the archive
+ * are tailored to the oddities of the specific format. All other routines are
+ * independent of the archive format. Data flow in and out of the format
+ * dependent routines pass pointers to ARCHD structure (described below).
+ */
+typedef struct {
+ char *name; /* name of format, this is the name the user */
+ /* gives to -x option to select it. */
+ int bsz; /* default block size. used when the user */
+ /* does not specify a blocksize for writing */
+ /* Appends continue to with the blocksize */
+ /* the archive is currently using. */
+ int hsz; /* Header size in bytes. this is the size of */
+ /* the smallest header this format supports. */
+ /* Headers are assumed to fit in a BLKMULT. */
+ /* If they are bigger, get_head() and */
+ /* get_arc() must be adjusted */
+ int udev; /* does append require unique dev/ino? some */
+ /* formats use the device and inode fields */
+ /* to specify hard links. when members in */
+ /* the archive have the same inode/dev they */
+ /* are assumed to be hard links. During */
+ /* append we may have to generate unique ids */
+ /* to avoid creating incorrect hard links */
+ int hlk; /* does archive store hard links info? if */
+ /* not, we do not bother to look for them */
+ /* during archive write operations */
+ int blkalgn; /* writes must be aligned to blkalgn boundary */
+ int inhead; /* is the trailer encoded in a valid header? */
+ /* if not, trailers are assumed to be found */
+ /* in invalid headers (i.e like tar) */
+ int (*id)(char *, /* checks if a buffer is a valid header */
+ int); /* returns 1 if it is, o.w. returns a 0 */
+ int (*st_rd)(void); /* initialize routine for read. so format */
+ /* can set up tables etc before it starts */
+ /* reading an archive */
+ int (*rd)(ARCHD *, /* read header routine. passed a pointer to */
+ char *); /* ARCHD. It must extract the info from the */
+ /* format and store it in the ARCHD struct. */
+ /* This routine is expected to fill all the */
+ /* fields in the ARCHD (including stat buf) */
+ /* 0 is returned when a valid header is */
+ /* found. -1 when not valid. This routine */
+ /* set the skip and pad fields so the format */
+ /* independent routines know the amount of */
+ /* padding and the number of bytes of data */
+ /* which follow the header. This info is */
+ /* used skip to the next file header */
+ off_t (*end_rd)(void); /* read cleanup. Allows format to clean up */
+ /* and MUST RETURN THE LENGTH OF THE TRAILER */
+ /* RECORD (so append knows how many bytes */
+ /* to move back to rewrite the trailer) */
+ int (*st_wr)(void); /* initialize routine for write operations */
+ int (*wr)(ARCHD *); /* write archive header. Passed an ARCHD */
+ /* filled with the specs on the next file to */
+ /* archived. Returns a 1 if no file data is */
+ /* is to be stored; 0 if file data is to be */
+ /* added. A -1 is returned if a write */
+ /* operation to the archive failed. this */
+ /* function sets the skip and pad fields so */
+ /* the proper padding can be added after */
+ /* file data. This routine must NEVER write */
+ /* a flawed archive header. */
+ int (*end_wr)(void); /* end write. write the trailer and do any */
+ /* other format specific functions needed */
+ /* at the end of an archive write */
+ int (*trail)(ARCHD *, /* returns 0 if a valid trailer, -1 if not */
+ char *, int, /* For formats which encode the trailer */
+ int *); /* outside of a valid header, a return value */
+ /* of 1 indicates that the block passed to */
+ /* it can never contain a valid header (skip */
+ /* this block, no point in looking at it) */
+ /* CAUTION: parameters to this function are */
+ /* different for trailers inside or outside */
+ /* of headers. See get_head() for details */
+ int (*rd_data)(ARCHD *, /* read/process file data from the archive */
+ int, off_t *);
+ int (*wr_data)(ARCHD *, /* write/process file data to the archive */
+ int, off_t *);
+ int (*options)(void); /* process format specific options (-o) */
+} FSUB;
+
+/*
+ * Format Specific Options List
+ *
+ * Used to pass format options to the format options handler
+ */
+typedef struct oplist {
+ char *name; /* option variable name e.g. name= */
+ char *value; /* value for option variable */
+ struct oplist *fow; /* next option */
+ int separator; /* 2 means := separator; 1 means = separator
+ 0 means no separator */
+} OPLIST;
+#define SEP_COLONEQ 2
+#define SEP_EQ 1
+#define SEP_NONE 0
+
+/*
+ * General Macros
+ */
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#define MAJOR(x) major(x)
+#define MINOR(x) minor(x)
+#define TODEV(x, y) makedev((x), (y))
+
+/*
+ * General Defines
+ */
+#define HEX 16
+#define OCT 8
+#define _PAX_ 1
+#define _HAVE_REGCOMP_ 1
+#define _TFILE_BASE "paxXXXXXXXXXX"
+
+#endif /* _PAX_H_ */
diff --git a/file_cmds/pax/pax_format.c b/file_cmds/pax/pax_format.c
new file mode 100644
index 0000000..fbacdff
--- /dev/null
+++ b/file_cmds/pax/pax_format.c
@@ -0,0 +1,1624 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
+#else
+static const char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tar.c,v 1.34 2004/10/23 19:34:14 otto Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "extern.h"
+#include "tar.h"
+#include <fnmatch.h>
+#include <regex.h>
+#include "pat_rep.h"
+#include <errno.h>
+
+/*
+ * This file implements the -x pax format support; it is incomplete.
+ * Known missing features include:
+ * many -o options for "copy" mode are not implemented (only path=)
+ * many format specifiers for -o listopt are not implemented
+ * -o listopt option should work for all archive formats, not just -x pax
+ * This file was originally derived from the file tar.c. You should
+ * 'diff' it to that file to see how much of the -x pax format has been implemented.
+ */
+
+char pax_eh_datablk[4*1024];
+int pax_read_or_list_mode = 0;
+int want_a_m_time_headers = 0;
+int want_linkdata = 0;
+
+int pax_invalid_action = 0;
+char * pax_invalid_action_write_path = NULL;
+char * pax_invalid_action_write_cwd = NULL;
+
+char
+ *path_g, *path_x, *path_g_current, *path_x_current,
+ *uname_g, *uname_x, *uname_g_current, *uname_x_current,
+ *gname_g, *gname_x, *gname_g_current, *gname_x_current,
+ *comment_g, *comment_x, *comment_g_current, *comment_x_current,
+ *charset_g, *charset_x, *charset_g_current, *charset_x_current,
+ *atime_g, *atime_x, *atime_g_current, *atime_x_current,
+ *gid_g, *gid_x, *gid_g_current, *gid_x_current,
+ *linkpath_g, *linkpath_x, *linkpath_g_current, *linkpath_x_current,
+ *mtime_g, *mtime_x, *mtime_g_current, *mtime_x_current,
+ *size_g, *size_x, *size_g_current, *size_x_current,
+ *uid_g, *uid_x, *uid_g_current, *uid_x_current;
+
+char *header_name_g_requested = NULL,
+ *header_name_x_requested = NULL;
+
+char *header_name_g = "/tmp/GlobalHead.%p.%n",
+ *header_name_x = "%d/PaxHeaders.%p/%f";
+
+int nglobal_headers = 0;
+char *pax_list_opt_format;
+
+#define O_OPTION_ACTION_NOTIMPL 0
+#define O_OPTION_ACTION_INVALID 1
+#define O_OPTION_ACTION_DELETE 2
+#define O_OPTION_ACTION_STORE_HEADER 3
+#define O_OPTION_ACTION_TIMES 4
+#define O_OPTION_ACTION_HEADER_NAME 5
+#define O_OPTION_ACTION_LISTOPT 6
+#define O_OPTION_ACTION_LINKDATA 7
+
+#define O_OPTION_ACTION_IGNORE 8
+#define O_OPTION_ACTION_ERROR 9
+#define O_OPTION_ACTION_STORE_HEADER2 10
+
+#define ATTRSRC_FROM_NOWHERE 0
+#define ATTRSRC_FROM_X_O_OPTION 1
+#define ATTRSRC_FROM_G_O_OPTION 2
+#define ATTRSRC_FROM_X_HEADER 3
+#define ATTRSRC_FROM_G_HEADER 4
+
+#define KW_PATH_CASE 0
+#define KW_SKIP_CASE -1
+#define KW_ATIME_CASE -2
+
+typedef struct {
+ char * name;
+ int len;
+ int active; /* 1 means active, 0 means deleted via -o delete= */
+ int cmdline_action;
+ int header_action;
+ /* next 2 entries only used by store_header actions */
+ char ** g_value; /* -o keyword= value */
+ char ** x_value; /* -o keyword:= value */
+ char ** g_value_current; /* keyword= value found in Global extended header */
+ char ** x_value_current; /* keyword= value found in extended header */
+ int header_inx; /* starting index of header field this keyword represents */
+ int header_len; /* length of header field this keyword represents */
+ /* If negative, special cases line path= */
+} O_OPTION_TYPE;
+
+O_OPTION_TYPE o_option_table[] = {
+ { "atime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
+ &atime_g, &atime_x, &atime_g_current, &atime_x_current, 0, KW_ATIME_CASE },
+ { "charset", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE,
+ &charset_g, &charset_x, &charset_g_current, &charset_x_current, 0, KW_SKIP_CASE },
+ { "comment", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE,
+ &comment_g, &comment_x, &comment_g_current, &comment_x_current, 0, KW_SKIP_CASE },
+ { "gid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
+ &gid_g, &gid_x, &gid_g_current, &gid_x_current , 116, 8 },
+ { "gname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
+ &gname_g, &gname_x, &gname_g_current, &gname_x_current, 297, 32 },
+ { "linkpath", 8, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
+ &linkpath_g, &linkpath_x, &linkpath_g_current, &linkpath_x_current, 0, KW_SKIP_CASE },
+ { "mtime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
+ &mtime_g, &mtime_x, &mtime_g_current, &mtime_x_current, 136, KW_SKIP_CASE },
+ { "path", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
+ &path_g, &path_x, &path_g_current, &path_x_current, 0, KW_PATH_CASE },
+ { "size", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER,
+ &size_g, &size_x, &size_g_current, &size_x_current, 124, KW_SKIP_CASE },
+ { "uid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
+ &uid_g, &uid_x, &uid_g_current, &uid_x_current, 108, 8 },
+ { "uname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2,
+ &uname_g, &uname_x, &uname_g_current, &uname_x_current, 265, 32 },
+
+ { "exthdr.name", 11, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR,
+ &header_name_x, &header_name_x_requested, NULL, NULL, 0, KW_SKIP_CASE },
+ { "globexthdr.name", 15, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR,
+ &header_name_g, &header_name_g_requested, NULL, NULL, 0, KW_SKIP_CASE },
+
+ { "delete", 6, 1, O_OPTION_ACTION_DELETE, O_OPTION_ACTION_ERROR,
+ NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
+ { "invalid", 7, 1, O_OPTION_ACTION_INVALID, O_OPTION_ACTION_ERROR,
+ NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
+ { "linkdata", 8, 1, O_OPTION_ACTION_LINKDATA, O_OPTION_ACTION_ERROR,
+ NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE }, /* Test 241 */
+ { "listopt", 7, 1, O_OPTION_ACTION_LISTOPT, O_OPTION_ACTION_ERROR,
+ &pax_list_opt_format, NULL, NULL, NULL, 0, KW_SKIP_CASE }, /* Test 242 */
+ /* Note: listopt is supposed to apply for all formats, not just -x pax only */
+ { "times", 5, 1, O_OPTION_ACTION_TIMES, O_OPTION_ACTION_ERROR,
+ NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE },
+};
+
+int ext_header_inx,
+ global_ext_header_inx;
+
+/* Make these tables big enough to handle lots of -o options, not just one per table entry */
+int ext_header_entry [4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)],
+ global_ext_header_entry[4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)];
+
+/*
+ * Routines for reading, writing and header identify of various versions of pax
+ */
+
+static size_t expandname(char *, size_t, char **, const char *, size_t);
+static u_long pax_chksm(char *, int);
+static char *name_split(char *, int);
+static int ul_oct(u_long, char *, int, int);
+#ifndef LONG_OFF_T
+static int uqd_oct(u_quad_t, char *, int, int);
+#endif
+
+static uid_t uid_nobody;
+static uid_t uid_warn;
+static gid_t gid_nobody;
+static gid_t gid_warn;
+
+/*
+ * Routines common to all versions of pax
+ */
+
+/*
+ * ul_oct()
+ * convert an unsigned long to an octal string. many oddball field
+ * termination characters are used by the various versions of tar in the
+ * different fields. term selects which kind to use. str is '0' padded
+ * at the front to len. we are unable to use only one format as many old
+ * tar readers are very cranky about this.
+ * Return:
+ * 0 if the number fit into the string, -1 otherwise
+ */
+
+static int
+ul_oct(u_long val, char *str, int len, int term)
+{
+ char *pt;
+
+ /*
+ * term selects the appropriate character(s) for the end of the string
+ */
+ pt = str + len - 1;
+ switch (term) {
+ case 3:
+ *pt-- = '\0';
+ break;
+ case 2:
+ *pt-- = ' ';
+ *pt-- = '\0';
+ break;
+ case 1:
+ *pt-- = ' ';
+ break;
+ case 0:
+ default:
+ *pt-- = '\0';
+ *pt-- = ' ';
+ break;
+ }
+
+ /*
+ * convert and blank pad if there is space
+ */
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = val >> 3) == (u_long)0)
+ break;
+ }
+
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_long)0)
+ return(-1);
+ return(0);
+}
+
+#ifndef LONG_OFF_T
+/*
+ * uqd_oct()
+ * convert an u_quad_t to an octal string. one of many oddball field
+ * termination characters are used by the various versions of tar in the
+ * different fields. term selects which kind to use. str is '0' padded
+ * at the front to len. we are unable to use only one format as many old
+ * tar readers are very cranky about this.
+ * Return:
+ * 0 if the number fit into the string, -1 otherwise
+ */
+
+static int
+uqd_oct(u_quad_t val, char *str, int len, int term)
+{
+ char *pt;
+
+ /*
+ * term selects the appropriate character(s) for the end of the string
+ */
+ pt = str + len - 1;
+ switch (term) {
+ case 3:
+ *pt-- = '\0';
+ break;
+ case 2:
+ *pt-- = ' ';
+ *pt-- = '\0';
+ break;
+ case 1:
+ *pt-- = ' ';
+ break;
+ case 0:
+ default:
+ *pt-- = '\0';
+ *pt-- = ' ';
+ break;
+ }
+
+ /*
+ * convert and blank pad if there is space
+ */
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = val >> 3) == 0)
+ break;
+ }
+
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_quad_t)0)
+ return(-1);
+ return(0);
+}
+#endif
+
+/*
+ * pax_chksm()
+ * calculate the checksum for a pax block counting the checksum field as
+ * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks).
+ * NOTE: we use len to short circuit summing 0's on write since we ALWAYS
+ * pad headers with 0.
+ * Return:
+ * unsigned long checksum
+ */
+
+static u_long
+pax_chksm(char *blk, int len)
+{
+ char *stop;
+ char *pt;
+ u_long chksm = BLNKSUM; /* initial value is checksum field sum */
+
+ /*
+ * add the part of the block before the checksum field
+ */
+ pt = blk;
+ stop = blk + CHK_OFFSET;
+ while (pt < stop)
+ chksm += (u_long)(*pt++ & 0xff);
+ /*
+ * move past the checksum field and keep going, spec counts the
+ * checksum field as the sum of 8 blanks (which is pre-computed as
+ * BLNKSUM).
+ * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding
+ * starts, no point in summing zero's)
+ */
+ pt += CHK_LEN;
+ stop = blk + len;
+ while (pt < stop)
+ chksm += (u_long)(*pt++ & 0xff);
+ return(chksm);
+}
+
+void
+pax_format_list_output(ARCHD *arcn, time_t now, FILE *fp, int term)
+{
+ /* parse specified listopt format */
+ char *nextpercent, *nextchar;
+ char buf[4*1024];
+ int pos, cpylen;
+ char *fname;
+
+ nextpercent = strchr(pax_list_opt_format,'%');
+ if (nextpercent==NULL) {
+ /* Strange case: no specifiers? */
+ safe_print(pax_list_opt_format, fp);
+ (void)putc(term, fp);
+ (void)fflush(fp);
+ return;
+ }
+ pos = nextpercent-pax_list_opt_format;
+ memcpy(buf,pax_list_opt_format, pos);
+ while (nextpercent++) {
+ switch (*nextpercent) {
+ case 'F':
+ fname = arcn->name;
+ cpylen = strlen(fname);
+ memcpy(&buf[pos],fname,cpylen);
+ pos+= cpylen;
+ break;
+ case 'D':
+ case 'T':
+ case 'M':
+ case 'L':
+ default:
+ paxwarn(1, "Unimplemented listopt format: %c",*nextpercent);
+ break;
+ }
+ nextpercent++;
+ if (*nextpercent=='\0') {
+ break;
+ }
+ nextchar = nextpercent;
+ nextpercent = strchr(nextpercent,'%');
+ if (nextpercent==NULL) {
+ cpylen = strlen(nextchar);
+ } else {
+ cpylen = nextpercent - nextchar;
+ }
+ memcpy(&buf[pos],nextchar, cpylen);
+ pos += cpylen;
+ }
+ buf[pos]='\0';
+ safe_print(&buf[0], fp);
+ (void)putc(term, fp);
+ (void)fflush(fp);
+ return;
+}
+
+void
+cleanup_pax_invalid_action()
+{
+ switch (pax_invalid_action) {
+ case PAX_INVALID_ACTION_BYPASS:
+ case PAX_INVALID_ACTION_RENAME:
+ break;
+ case PAX_INVALID_ACTION_WRITE:
+ pax_invalid_action_write_path = NULL;
+ if (pax_invalid_action_write_cwd) {
+ free(pax_invalid_action_write_cwd);
+ pax_invalid_action_write_cwd = NULL;
+ }
+ break;
+ case PAX_INVALID_ACTION_UTF8:
+ default:
+ paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
+ }
+}
+
+void
+record_pax_invalid_action_results(ARCHD * arcn, char * fixed_path)
+{
+ switch (pax_invalid_action) {
+ case PAX_INVALID_ACTION_BYPASS:
+ case PAX_INVALID_ACTION_RENAME:
+ break;
+ case PAX_INVALID_ACTION_WRITE:
+ pax_invalid_action_write_path = fixed_path;
+ pax_invalid_action_write_cwd = strdup(arcn->name);
+ pax_invalid_action_write_cwd[fixed_path-arcn->name-1] = '\0';
+ break;
+ case PAX_INVALID_ACTION_UTF8:
+ default:
+ paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
+ }
+}
+
+int
+perform_pax_invalid_action(ARCHD * arcn, int err)
+{
+ int rc = 0;
+ switch (pax_invalid_action) {
+ case PAX_INVALID_ACTION_BYPASS:
+ rc = -1;
+ break;
+ case PAX_INVALID_ACTION_RENAME:
+ rc = tty_rename(arcn);
+ break;
+ case PAX_INVALID_ACTION_WRITE:
+ pax_invalid_action_write_path = NULL;
+ pax_invalid_action_write_cwd = NULL;
+ rc = 2;
+ break;
+ case PAX_INVALID_ACTION_UTF8:
+ default:
+ paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action);
+ rc = -1; /* do nothing? */
+ }
+ return rc;
+}
+
+static void
+delete_keywords(char * pattern)
+{
+ int i;
+ /* loop over all keywords, marking any matched as deleted */
+ for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
+ if (fnmatch(pattern, o_option_table[i].name, 0) == 0) {
+ /* Found option: mark deleted */
+ o_option_table[i].active = 0;
+ }
+ }
+}
+
+/*
+ * pax_opt()
+ * handle pax format specific -o options
+ * Return:
+ * 0 if ok -1 otherwise
+ */
+
+int
+pax_opt(void)
+{
+ OPLIST *opt;
+ int got_option = 0;
+
+ while ((opt = opt_next()) != NULL) {
+ int i;
+ got_option = -1;
+ pax_invalid_action = PAX_INVALID_ACTION_BYPASS; /* Default for pax format */
+ /* look up opt->name */
+ for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
+ if (strncasecmp(opt->name, o_option_table[i].name, o_option_table[i].len) == 0) {
+ /* Found option: see if already set */
+ /* Save it away */
+ got_option = 1;
+ switch (o_option_table[i].cmdline_action) {
+ case O_OPTION_ACTION_INVALID:
+ if (opt->separator != SEP_EQ) {
+ paxwarn(1,"-o %s= option requires '=' separator: option ignored",
+ opt->name);
+ break;
+ }
+ if (opt->value) {
+ if (strncasecmp(opt->value,"bypass",6) == 0) {
+ pax_invalid_action = PAX_INVALID_ACTION_BYPASS;
+ } else if (strncasecmp(opt->value,"rename",6) == 0) {
+ pax_invalid_action = PAX_INVALID_ACTION_RENAME;
+ } else if (strncasecmp(opt->value,"UTF-8",5) == 0) {
+ pax_invalid_action = PAX_INVALID_ACTION_UTF8;
+ } else if (strncasecmp(opt->value,"write",5) == 0) {
+ pax_invalid_action = PAX_INVALID_ACTION_WRITE;
+ } else {
+ paxwarn(1,"Invalid action %s not recognized: option ignored",
+ opt->value);
+ }
+ } else {
+ paxwarn(1,"Invalid action RHS not specified: option ignored");
+ }
+ break;
+ case O_OPTION_ACTION_DELETE:
+ if (opt->separator != SEP_EQ) {
+ paxwarn(1,"-o %s= option requires '=' separator: option ignored",
+ opt->name);
+ break;
+ }
+ /* Mark all matches as deleted */
+ /* can have multiple -o delete= patterns */
+ delete_keywords(opt->value);
+ break;
+ case O_OPTION_ACTION_STORE_HEADER2:
+ if(pax_read_or_list_mode) pids = 1; /* Force -p o for these options */
+ case O_OPTION_ACTION_STORE_HEADER:
+ if (o_option_table[i].g_value == NULL ||
+ o_option_table[i].x_value == NULL ) {
+ paxwarn(1,"-o option not implemented: %s=%s",
+ opt->name, opt->value);
+ } else {
+ if (opt->separator == SEP_EQ) {
+ *(o_option_table[i].g_value) = opt->value;
+ global_ext_header_entry[global_ext_header_inx++] = i;
+ } else if (opt->separator == SEP_COLONEQ ) {
+ *(o_option_table[i].x_value) = opt->value;
+ ext_header_entry [ext_header_inx++] = i;
+ } else { /* SEP_NONE */
+ paxwarn(1,"-o %s option is missing value", opt->name);
+ }
+ }
+ break;
+ case O_OPTION_ACTION_TIMES:
+ if (opt->separator != SEP_NONE) {
+ paxwarn(1,"-o %s option takes no value: option ignored", opt->name);
+ break;
+ }
+ want_a_m_time_headers = 1;
+ break;
+ case O_OPTION_ACTION_LINKDATA:
+ if (opt->separator != SEP_NONE) {
+ paxwarn(1,"-o %s option takes no value: option ignored", opt->name);
+ break;
+ }
+ want_linkdata = 1;
+ break;
+ case O_OPTION_ACTION_HEADER_NAME:
+ if (opt->separator != SEP_EQ) {
+ paxwarn(1,"-o %s= option requires '=' separator: option ignored",
+ opt->name);
+ break;
+ }
+ *(o_option_table[i].g_value) = opt->value;
+ *(o_option_table[i].x_value) = "YES";
+ break;
+ case O_OPTION_ACTION_LISTOPT:
+ if (opt->separator != SEP_EQ) {
+ paxwarn(1,"-o %s= option requires '=' separator: option ignored",
+ opt->name);
+ break;
+ }
+ *(o_option_table[i].g_value) = opt->value;
+ break;
+ case O_OPTION_ACTION_NOTIMPL:
+ default:
+ paxwarn(1,"pax format -o option not yet implemented: %s=%s",
+ opt->name, opt->value);
+ break;
+ }
+ break;
+ }
+ }
+ if (got_option == -1) {
+ paxwarn(1,"pax format -o option not recognized: %s=%s",
+ opt->name, opt->value);
+ }
+ }
+ return(0);
+}
+
+static int
+expand_extended_headers(ARCHD *arcn, HD_USTAR *hd)
+{
+ char mybuf[BLKMULT];
+ HD_USTAR *myhd;
+ char * current_value;
+ int path_replaced = 0;
+ int i, len;
+
+ myhd = hd;
+ while (myhd->typeflag == PAXGTYPE || myhd->typeflag == PAXXTYPE) {
+ char *name, *str;
+ int size, nbytes, inx;
+ size = asc_ul(myhd->size, sizeof(myhd->size), OCT);
+ if (size > sizeof(mybuf)) {
+ paxwarn(1,"extended header buffer overflow");
+ exit(1);
+ }
+ nbytes = rd_wrbuf(mybuf, size);
+ if (nbytes != size) {
+ paxwarn(1,"extended header data read failure: nbytes=%d, size=%d\n",
+ nbytes, size);
+ exit(1);
+ }
+ /*
+ printf("Read 1 extended header: type=%c, size=%d\n",
+ myhd->typeflag, size);
+ */
+ inx=0;
+ /* loop over buffer collecting attributes */
+ while (nbytes > 0) {
+ int got_option = -1;
+ int nentries = sscanf(&mybuf[inx],"%d ", &len);
+ if (nentries != 1) {
+ paxwarn(1,"Extended header failure: length");
+ exit(1);
+ }
+ if (len < 0 || (inx+len-1 >= sizeof(mybuf))) {
+ paxwarn(1, "Extended header failure: invalid length (%d)", len);
+ exit(1);
+ }
+ if (mybuf[inx+len-1] != '\n') {
+ paxwarn(1,"Extended header failure: missed newline");
+ exit(1);
+ } else
+ mybuf[inx+len-1] = '\0';
+ name = strchr(&mybuf[inx],' ');
+ if (name) name++;
+ else {
+ paxwarn(1,"Extended header failure: missing space");
+ exit(1);
+ }
+ str = strchr(name,'=');
+ if (str) {
+ *str++='\0'; /* end of name */
+ } else {
+ paxwarn(1,"Extended header failure: missing RHS string");
+ exit(1);
+ }
+ for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
+ if (strncasecmp(name, o_option_table[i].name, o_option_table[i].len) == 0) {
+ /* Found option: see if already set TBD */
+ /* Save it away */
+ got_option = i;
+ break;
+ }
+ }
+ if (got_option == -1) {
+ paxwarn(1,"Unrecognized header keyword: %s",name);
+ } else {
+ /* Determine precedence of -o and header attributes */
+ int found_value = ATTRSRC_FROM_NOWHERE;
+ current_value = NULL;
+ if (myhd->typeflag == PAXXTYPE) {
+ if (*o_option_table[got_option].x_value) {
+ current_value = *o_option_table[got_option].x_value;
+ found_value = ATTRSRC_FROM_X_O_OPTION;
+ } else {
+ current_value = str;
+ found_value = ATTRSRC_FROM_X_HEADER;
+ }
+ } else if (myhd->typeflag == PAXGTYPE) {
+ if (*o_option_table[got_option].g_value) {
+ current_value = *o_option_table[got_option].g_value;
+ found_value = ATTRSRC_FROM_G_O_OPTION;
+ } else {
+ current_value = str;
+ found_value = ATTRSRC_FROM_G_HEADER;
+ }
+ } else {
+ paxwarn(1,"Unsupported header type:%c",myhd->typeflag);
+ }
+ if (current_value) {
+ /* Save this attribute value for use later */
+ switch (o_option_table[got_option].header_action) {
+ case O_OPTION_ACTION_IGNORE:
+ paxwarn(1,"ignoring header keyword: %s",name);
+ break;
+ case O_OPTION_ACTION_STORE_HEADER2:
+ case O_OPTION_ACTION_STORE_HEADER:
+ switch (found_value) {
+ case ATTRSRC_FROM_NOWHERE: /* shouldn't happen */
+ paxwarn(1, "internal error: value from nowhere");
+ break;
+ case ATTRSRC_FROM_X_O_OPTION:
+ case ATTRSRC_FROM_G_O_OPTION:
+ break;
+ case ATTRSRC_FROM_X_HEADER:
+ current_value = strdup(current_value);
+ if(*o_option_table[got_option].x_value_current)
+ free(*o_option_table[got_option].x_value_current);
+ *o_option_table[got_option].x_value_current = current_value;
+ break;
+ case ATTRSRC_FROM_G_HEADER:
+ current_value = strdup(current_value);
+ if(*o_option_table[got_option].g_value_current)
+ free(*o_option_table[got_option].g_value_current);
+ *o_option_table[got_option].g_value_current = current_value;
+ break;
+ }
+ break;
+ case O_OPTION_ACTION_ERROR:
+ default:
+ paxwarn(1,"Unsupported extended header attribute: %s=%s",
+ name, str);
+ }
+ }
+ }
+ inx+=len;
+ nbytes -= len;
+ }
+
+ /* position file at next header */
+ (void)rd_skip(TAR_PAD(size));
+
+ /* read next header */
+ nbytes = rd_wrbuf(mybuf, frmt->hsz);
+ if (nbytes != frmt->hsz) {
+ paxwarn(1,"extended header read failure: nbytes=%d, size=%d\n",
+ nbytes, frmt->hsz);
+ }
+ myhd = ((HD_USTAR *)mybuf);
+ /* repeat until no more extended headers */
+ }
+
+ /* The header about to be returned must now be updated using all the extended
+ header values collected and any command line options */
+ /* Acceleration: check during command option processing. If there are no -o
+ options, and no changes from any header, do not need to run through this loop. */
+
+ for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
+ int header_len, free_it;
+ if (!o_option_table[i].active) {
+ continue; /* deleted keywords */
+ }
+ header_len = o_option_table[i].header_len;
+ if (header_len == KW_SKIP_CASE) {
+ continue;
+ }
+ free_it = 0;
+ /* Calculate values for all non-skip keywords */
+ current_value = NULL;
+ if (o_option_table[i].x_value) {
+ current_value = *o_option_table[i].x_value;
+ }
+ if (!current_value) { /* No -o := */
+ if (o_option_table[i].x_value_current) {
+ current_value = *o_option_table[i].x_value_current;
+ }
+ if (current_value) {
+ /* Must remove it: x header values not valid beyond this header */
+ *o_option_table[i].x_value_current = NULL;
+ free_it = 1;
+ } else { /* No x values, try globals */
+ current_value = *o_option_table[i].g_value;
+ if (!current_value) {
+ current_value = *o_option_table[i].g_value_current;
+ }
+ }
+ }
+ if (current_value) {
+ /* Update current header with this value */
+ /*
+ printf ("Found current_value:%s for %s, pids=%d\n",
+ current_value, o_option_table[i].name, pids);
+ */
+ len = strlen(current_value);
+ if (header_len == KW_ATIME_CASE) {
+ time_t asecs = strtoul(current_value, NULL, 10);
+ arcn->sb.st_atimespec.tv_sec = asecs;
+ } else if (header_len == KW_PATH_CASE) { /* Special case for path keyword */
+ path_replaced = 1;
+ arcn->nlen = len;
+ strlcpy(arcn->name,current_value,sizeof(arcn->name));
+ } else if (header_len >= 0) { // Skip negative values
+ if (len > header_len) {
+ paxwarn(1," length of string from extended header bigger than header field:"
+ " THAT won't work!\n");
+ } else {
+ char * p = (char *) myhd;
+ memcpy(&p[o_option_table[i].header_inx],
+ current_value, len);
+ if (len != header_len) {
+ /* pad with ? */
+ p[o_option_table[i].header_inx+len] = '\0';
+ }
+ }
+ }
+ if (free_it) {
+ free(current_value);
+ }
+ }
+ }
+
+ if (myhd==hd) return(path_replaced);
+
+ /* must put new header into memory of original */
+ memcpy(hd, myhd, sizeof(HD_USTAR));
+
+ return(path_replaced);
+}
+
+/*
+ * pax_id()
+ * determine if a block given to us is a valid pax header. We have to
+ * be on the lookout for those pesky blocks of all zero's
+ * Return:
+ * 0 if a ustar header, -1 otherwise
+ */
+
+int
+pax_id(char *blk, int size)
+{
+ HD_USTAR *hd;
+
+ if (size < BLKMULT)
+ return(-1);
+ hd = (HD_USTAR *)blk;
+
+ /*
+ * check for block of zero's first, a simple and fast test then check
+ * ustar magic cookie. We should use TMAGLEN, but some USTAR archive
+ * programs are fouled up and create archives missing the \0. Last we
+ * check the checksum. If ok we have to assume it is a valid header.
+ */
+ if (hd->name[0] == '\0')
+ return(-1);
+ if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
+ return(-1);
+ if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != pax_chksm(blk,BLKMULT))
+ return(-1);
+ if ((hd->typeflag != PAXXTYPE) && (hd->typeflag != PAXGTYPE)) {
+ /* Not explicitly pax format, but at least ustar */
+ if (act==LIST || act==EXTRACT) {
+ /* Although insufficient evidence, call it pax format */
+ return(0);
+ }
+ return(-1);
+ }
+ pax_invalid_action = PAX_INVALID_ACTION_BYPASS; /* Default for pax format */
+ return(0);
+}
+
+/*
+ * pax_rd()
+ * extract the values out of block already determined to be a pax header.
+ * store the values in the ARCHD parameter.
+ * Return:
+ * 0
+ */
+
+int
+pax_rd(ARCHD *arcn, char *buf)
+{
+ HD_USTAR *hd;
+ int cnt = 0;
+ int check_path;
+ dev_t devmajor;
+ dev_t devminor;
+
+ /*
+ * we only get proper sized buffers
+ */
+ if (pax_id(buf, BLKMULT) < 0)
+ return(-1);
+
+ memset(arcn, 0, sizeof(*arcn));
+ arcn->org_name = arcn->name;
+ arcn->sb.st_nlink = 1;
+ hd = (HD_USTAR *)buf;
+
+ check_path = expand_extended_headers(arcn, hd);
+
+ if (check_path) {
+ /*
+ * pathname derived from extended head or -o option;
+ * full name is in one string, but length may exceed
+ * max path so be careful.
+ */
+ if (arcn->nlen > sizeof(arcn->name)) {
+ paxwarn(1,"pathname from extended header info doesn't fit! (len=%d)\n",
+ arcn->nlen);
+ }
+ } else {
+ /*
+ * see if the filename is split into two parts. if so, join the parts.
+ * we copy the prefix first and add a / between the prefix and name.
+ */
+ char *dest = arcn->name;
+ if (*(hd->prefix) != '\0') {
+ cnt = strlcpy(dest, hd->prefix, sizeof(arcn->name) - 1);
+ dest += cnt;
+ *dest++ = '/';
+ cnt++;
+ } else {
+ cnt = 0;
+ }
+
+ if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
+ arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt,
+ &gnu_name_string, hd->name, sizeof(hd->name));
+ arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
+ &gnu_link_string, hd->linkname, sizeof(hd->linkname));
+ }
+ }
+
+ /*
+ * follow the spec to the letter. we should only have mode bits, strip
+ * off all other crud we may be passed.
+ */
+ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) &
+ 0xfff);
+#ifdef LONG_OFF_T
+ arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#else
+ arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
+ /* When we have extended header for size, prefer it over hd->size */
+ if (size_x_current) {
+ sscanf(size_x_current, "%lld", &arcn->sb.st_size);
+ }
+#endif
+ arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
+ if (arcn->sb.st_atimespec.tv_sec == 0) { // Can be set from header
+ arcn->sb.st_atime = arcn->sb.st_mtime;
+ }
+ arcn->sb.st_ctime = arcn->sb.st_mtime;
+
+ /*
+ * If we can find the ascii names for gname and uname in the password
+ * and group files we will use the uid's and gid they bind. Otherwise
+ * we use the uid and gid values stored in the header. (This is what
+ * the posix spec wants).
+ */
+ hd->gname[sizeof(hd->gname) - 1] = '\0';
+ if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0)
+ arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
+ hd->uname[sizeof(hd->uname) - 1] = '\0';
+ if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0)
+ arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
+
+ /*
+ * set the defaults, these may be changed depending on the file type
+ */
+ arcn->pad = 0;
+ arcn->skip = 0;
+ arcn->sb.st_rdev = (dev_t)0;
+
+ /*
+ * set the mode and PAX type according to the typeflag in the header
+ */
+ switch (hd->typeflag) {
+ case FIFOTYPE:
+ arcn->type = PAX_FIF;
+ arcn->sb.st_mode |= S_IFIFO;
+ break;
+ case DIRTYPE:
+ arcn->type = PAX_DIR;
+ arcn->sb.st_mode |= S_IFDIR;
+ arcn->sb.st_nlink = 2;
+
+ /*
+ * Some programs that create pax archives append a '/'
+ * to the pathname for directories. This clearly violates
+ * pax specs, but we will silently strip it off anyway.
+ */
+ if (arcn->name[arcn->nlen - 1] == '/')
+ arcn->name[--arcn->nlen] = '\0';
+ break;
+ case BLKTYPE:
+ case CHRTYPE:
+ /*
+ * this type requires the rdev field to be set.
+ */
+ if (hd->typeflag == BLKTYPE) {
+ arcn->type = PAX_BLK;
+ arcn->sb.st_mode |= S_IFBLK;
+ } else {
+ arcn->type = PAX_CHR;
+ arcn->sb.st_mode |= S_IFCHR;
+ }
+ devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT);
+ devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT);
+ arcn->sb.st_rdev = TODEV(devmajor, devminor);
+ break;
+ case SYMTYPE:
+ case LNKTYPE:
+ if (hd->typeflag == SYMTYPE) {
+ arcn->type = PAX_SLK;
+ arcn->sb.st_mode |= S_IFLNK;
+ } else {
+ arcn->type = PAX_HLK;
+ /*
+ * so printing looks better
+ */
+ arcn->sb.st_mode |= S_IFREG;
+ arcn->sb.st_nlink = 2;
+ }
+ break;
+ case LONGLINKTYPE:
+ case LONGNAMETYPE:
+ /*
+ * GNU long link/file; we tag these here and let the
+ * pax internals deal with it -- too ugly otherwise.
+ */
+ arcn->type =
+ hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ break;
+ case CONTTYPE:
+ case AREGTYPE:
+ case REGTYPE:
+ default:
+ /*
+ * these types have file data that follows. Set the skip and
+ * pad fields.
+ */
+ arcn->type = PAX_REG;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ arcn->sb.st_mode |= S_IFREG;
+ break;
+ }
+ return(0);
+}
+
+void
+adjust_copy_for_pax_options(ARCHD * arcn)
+{
+ /* Because ext_header options take precedence over global_header options, apply
+ global options first, then override with any extended header options */
+ int i;
+ if (global_ext_header_inx) {
+ for (i=0; i < global_ext_header_inx; i++) {
+ if (!o_option_table[global_ext_header_entry[i]].active) continue; /* deleted keywords */
+ if (strcmp(o_option_table[global_ext_header_entry[i]].name, "path")==0) {
+ strlcpy(arcn->name,*(o_option_table[global_ext_header_entry[i]].g_value),
+ sizeof(arcn->name));
+ arcn->nlen = strlen(*(o_option_table[global_ext_header_entry[i]].g_value));
+ } else { /* only handle path for now: others TBD */
+ paxwarn(1, "adjust arcn for global extended header options not implemented:%d", i);
+ }
+ }
+ }
+ if (ext_header_inx) {
+ for (i=0; i < ext_header_inx; i++) {
+ if (!o_option_table[ext_header_entry[i]].active) continue; /* deleted keywords */
+ if (strcmp(o_option_table[ext_header_entry[i]].name, "path")==0) {
+ strlcpy(arcn->name,*(o_option_table[ext_header_entry[i]].x_value),
+ sizeof(arcn->name));
+ arcn->nlen = strlen(*(o_option_table[ext_header_entry[i]].x_value));
+ } else { /* only handle path for now: others TBD */
+ paxwarn(1, "adjust arcn for extended header options not implemented:%d", i);
+ }
+ }
+ }
+ if (want_a_m_time_headers) {
+ /* TBD */
+ }
+}
+
+static int
+emit_extended_header_record(int len, int total_len, int head_type,
+ char * name, char * value)
+{
+ if (total_len + len > sizeof(pax_eh_datablk)) {
+ paxwarn(1,"extended header buffer overflow for header type '%c': %d",
+ head_type, total_len+len);
+ } else {
+ sprintf(&pax_eh_datablk[total_len],"%d %s=%s\n", len, name, value);
+ total_len += len;
+ }
+ return (total_len);
+}
+
+__attribute__((__malloc__))
+static char *
+substitute_percent(char * header, char * filename)
+{
+ char *nextpercent, *nextchar;
+ char buf[4*1024];
+ int pos, cpylen;
+ char *dname, *fname;
+
+ nextpercent = strchr(header,'%');
+ if (nextpercent==NULL) return strdup(header);
+ pos = nextpercent-header;
+ memcpy(buf,header, pos);
+ while (nextpercent++) {
+ switch (*nextpercent) {
+ case '%':
+ buf[pos++]='%'; /* just skip it */
+ break;
+ case 'd':
+ dname = strrchr(filename,'/');
+ if (dname==NULL) {
+ cpylen = 1;
+ dname = ".";
+ } else {
+ cpylen = dname-filename;
+ dname = filename;
+ }
+ memcpy(&buf[pos],dname,cpylen);
+ pos+= cpylen;
+ break;
+ case 'f':
+ fname = strrchr(filename,'/');
+ if (fname==NULL) {
+ fname = filename;
+ } else {
+ fname++;
+ }
+ cpylen = strlen(fname);
+ memcpy(&buf[pos],fname,cpylen);
+ pos+= cpylen;
+ break;
+ case 'n':
+ pos += sprintf (&buf[pos],"%d",nglobal_headers);
+ break;
+ case 'p':
+ pos += sprintf (&buf[pos],"%d",getpid());
+ break;
+ default:
+ paxwarn(1,"header format substitution failed: '%c'", *nextpercent);
+ return strdup(header);
+ }
+ nextpercent++;
+ if (*nextpercent=='\0') {
+ break;
+ }
+ nextchar = nextpercent;
+ nextpercent = strchr(nextpercent,'%');
+ if (nextpercent==NULL) {
+ cpylen = strlen(nextchar);
+ } else {
+ cpylen = nextpercent - nextchar;
+ }
+ memcpy(&buf[pos],nextchar, cpylen);
+ pos += cpylen;
+ }
+ buf[pos]='\0';
+ return (strdup(&buf[0]));
+}
+
+static int
+generate_pax_ext_header_and_data(ARCHD *arcn, int nfields, int *table,
+ char header_type, char * header_name, char * header_name_requested)
+{
+ HD_USTAR *hd;
+ char hdblk[sizeof(HD_USTAR)];
+ u_long records_size;
+ int term_char, i, len, total_len;
+ char * str, *name;
+
+ if (nfields == 0 && (header_name_requested == NULL)) {
+ if (header_type==PAXXTYPE) {
+ if (!want_a_m_time_headers) return (0);
+ } else
+ return (0);
+ }
+
+ /* There might be no fields but a header with a specific name or
+ times might be wanted */
+
+ term_char = 1;
+ memset(hdblk, 0, sizeof(hdblk));
+ hd = (HD_USTAR *)hdblk;
+ memset(pax_eh_datablk, 0, sizeof(pax_eh_datablk));
+
+ /* generate header */
+ hd->typeflag = header_type;
+
+ /* These fields appear to be necessary to be able to treat extended headers
+ like files in older versions of pax */
+ ul_oct((u_long)0444, hd->mode, sizeof(hd->mode), term_char);
+ strncpy(hd->magic, TMAGIC, TMAGLEN);
+ strncpy(hd->version, TVERSION, TVERSLEN);
+ ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char);
+
+ /* compute size of data */
+ total_len = 0;
+ for (i=0; i < nfields; i++) {
+ if (!o_option_table[table[i]].active) continue; /* deleted keywords */
+ name = o_option_table[table[i]].name;
+ if (header_type == PAXXTYPE) {
+ str = *(o_option_table[table[i]].x_value);
+ } else {
+ str = *(o_option_table[table[i]].g_value);
+ }
+ if (str==NULL) {
+ paxwarn(1,"Missing option value for %s", name);
+ continue;
+ }
+ len = strlen(str) + o_option_table[table[i]].len + 3;
+ if (len < 9) len++;
+ else if (len < 98) len = len + 2;
+ else if (len < 997) len = len + 3;
+ else if (len < 9996) len = len + 4;
+ else {
+ paxwarn(1,"extended header data too long for header type '%c': %d",
+ header_type, len);
+ }
+ total_len = emit_extended_header_record(len, total_len,
+ header_type, name, str);
+ }
+
+ if ((header_type == PAXXTYPE) && want_a_m_time_headers) {
+ char time_buffer[12];
+ memset(time_buffer,0,sizeof(time_buffer));
+ sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_atime);
+ /* 3 chars + strlen("atime") + time + # chars in len */
+ len = 3 + 5 + strlen(&time_buffer[0]) + 2;
+ total_len = emit_extended_header_record(len, total_len,
+ header_type, "atime", &time_buffer[0]);
+ memset(time_buffer,0,sizeof(time_buffer));
+ sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_mtime);
+ /* 3 chars + strlen("mtime") + time + # chars in len */
+ len = 3 + 5 + strlen(&time_buffer[0]) + 2;
+ total_len = emit_extended_header_record(len, total_len,
+ header_type, "mtime", &time_buffer[0]);
+ }
+
+ /* Check if all fields were deleted: might not need to generate anything */
+ if ((total_len==0) && (header_name_requested == NULL)) return (0);
+
+ if (header_type == PAXGTYPE) nglobal_headers++;
+ /* substitution of fields in header_name */
+ header_name = substitute_percent(header_name, arcn->name);
+ if (strlen(header_name) == sizeof(hd->name)) { /* must account for name just fits in buffer */
+ strncpy(hd->name, header_name, sizeof(hd->name));
+ } else {
+ strlcpy(hd->name, header_name, sizeof(hd->name));
+ }
+
+ free(header_name);
+ header_name = NULL;
+ records_size = (u_long)total_len;
+ if (ul_oct(records_size, hd->size, sizeof(hd->size), term_char)) {
+ paxwarn(1,"extended header data too long for header type '%c'", header_type);
+ return(1);
+ }
+
+ if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, sizeof(hd->chksum), term_char)) {
+ paxwarn(1,"extended header data checksum failed: header type '%c'", header_type);
+ return(1);
+ }
+
+ /* write out header */
+ if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
+ return(-1);
+ if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
+ return(-1);
+ /* write out header data */
+ if (total_len > 0) {
+ if (wr_rdbuf(pax_eh_datablk, total_len) < 0)
+ return(-1);
+ if (wr_skip((off_t)(BLKMULT - total_len)) < 0)
+ return(-1);
+ /*
+ printf("data written:\n%s",&pax_eh_datablk[0]);
+ */
+ }
+
+ /*
+ paxwarn(0,"extended header and data written: header type '%c', #items: %d, %d characters",
+ header_type, nfields, records_size);
+ */
+ return (0);
+}
+
+/*
+ * pax_wr()
+ * write a pax header for the file specified in the ARCHD to the archive
+ * Have to check for file types that cannot be stored and file names that
+ * are too long. Be careful of the term (last arg) to ul_oct, we only use
+ * '\0' for the termination character (this is different than picky tar)
+ * ASSUMED: space after header in header block is zero filled
+ * Return:
+ * 0 if file has data to be written after the header, 1 if file has NO
+ * data to write after the header, -1 if archive write failed
+ */
+
+int
+pax_wr(ARCHD *arcn)
+{
+ HD_USTAR *hd;
+ char *pt;
+ char hdblk[sizeof(HD_USTAR)];
+ mode_t mode12only;
+ int term_char=3; /* orignal setting */
+ term_char=1; /* To pass conformance tests 274, 301 */
+ const char *size_header_name = "size";
+ char size_value[100];
+ bzero(size_value, sizeof(size_value));
+
+ /*
+ * check for those file system types pax cannot store
+ */
+ if (arcn->type == PAX_SCK) {
+ paxwarn(1, "Pax cannot archive a socket %s", arcn->org_name);
+ return(1);
+ }
+
+ /*
+ * check the length of the linkname
+ */
+ if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
+ (arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){
+ paxwarn(1, "Link name too long for pax %s", arcn->ln_name);
+ /*
+ * Conformance: test pax:285 wants error code to be non-zero, and
+ * test tar:12 wants error code from pax to be 0
+ */
+ return(1);
+ }
+
+ /*
+ * split the path name into prefix and name fields (if needed). if
+ * pt != arcn->name, the name has to be split
+ */
+ if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
+ paxwarn(1, "File name too long for pax %s", arcn->name);
+ return(1);
+ }
+
+ generate_pax_ext_header_and_data(arcn, global_ext_header_inx, &global_ext_header_entry[0],
+ PAXGTYPE, header_name_g, header_name_g_requested);
+ generate_pax_ext_header_and_data(arcn, ext_header_inx, &ext_header_entry[0],
+ PAXXTYPE, header_name_x, header_name_x_requested);
+
+ /*
+ * zero out the header so we don't have to worry about zero fill below
+ */
+ memset(hdblk, 0, sizeof(hdblk));
+ hd = (HD_USTAR *)hdblk;
+ arcn->pad = 0L;
+ /* To pass conformance tests 274/301, always set these fields to "zero" */
+ ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char);
+ ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char);
+
+ /*
+ * split the name, or zero out the prefix
+ */
+ if (pt != arcn->name) {
+ /*
+ * name was split, pt points at the / where the split is to
+ * occur, we remove the / and copy the first part to the prefix
+ */
+ *pt = '\0';
+ strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix));
+ *pt++ = '/';
+ }
+
+ /*
+ * copy the name part. this may be the whole path or the part after
+ * the prefix
+ */
+ if (strlen(pt) == sizeof(hd->name)) { /* must account for name just fits in buffer */
+ strncpy(hd->name, pt, sizeof(hd->name));
+ } else {
+ strlcpy(hd->name, pt, sizeof(hd->name));
+ }
+
+ /*
+ * set the fields in the header that are type dependent
+ */
+ switch (arcn->type) {
+ case PAX_DIR:
+ hd->typeflag = DIRTYPE;
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_CHR:
+ case PAX_BLK:
+ if (arcn->type == PAX_CHR)
+ hd->typeflag = CHRTYPE;
+ else
+ hd->typeflag = BLKTYPE;
+ if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
+ sizeof(hd->devmajor), term_char) ||
+ ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
+ sizeof(hd->devminor), term_char) ||
+ ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_FIF:
+ hd->typeflag = FIFOTYPE;
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_SLK:
+ case PAX_HLK:
+ case PAX_HRG:
+ if (arcn->type == PAX_SLK)
+ hd->typeflag = SYMTYPE;
+ else
+ hd->typeflag = LNKTYPE;
+ if (strlen(arcn->ln_name) == sizeof(hd->linkname)) { /* must account for name just fits in buffer */
+ strncpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ } else {
+ strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ }
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_REG:
+ case PAX_CTG:
+ default:
+ /*
+ * file data with this type, set the padding
+ */
+ if (arcn->type == PAX_CTG)
+ hd->typeflag = CONTTYPE;
+ else
+ hd->typeflag = REGTYPE;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+# ifdef LONG_OFF_T
+ if (ul_oct((u_long)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), term_char)) {
+# else
+ if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), term_char)) {
+# endif
+ /*
+ * Insert an extended header for size=<arcn->sb.st_size> since
+ * octal range of 12 byte string cannot fit > 8GiB files in header.
+ * This fixes Conformance test pax.343
+ */
+ int i;
+ snprintf(size_value, sizeof(size_value), "%lld", arcn->sb.st_size);
+ for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) {
+ if (strncasecmp(size_header_name, o_option_table[i].name, o_option_table[i].len) == 0) {
+ size_x = size_value;
+ ext_header_entry[ext_header_inx++] = i;
+ }
+ }
+ generate_pax_ext_header_and_data(arcn, ext_header_inx, &ext_header_entry[0],
+ PAXXTYPE, header_name_x, header_name_x_requested);
+ }
+ break;
+ }
+
+ strncpy(hd->magic, TMAGIC, TMAGLEN);
+ strncpy(hd->version, TVERSION, TVERSLEN);
+
+ /*
+ * set the remaining fields. Some versions want all 16 bits of mode
+ * we better humor them (they really do not meet spec though)....
+ */
+ if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), term_char)) {
+ if (uid_nobody == 0) {
+ if (uid_name("nobody", &uid_nobody) == -1)
+ goto out;
+ }
+ if (uid_warn != arcn->sb.st_uid) {
+ uid_warn = arcn->sb.st_uid;
+ paxwarn(1,
+ "Pax header field is too small for uid %lu, "
+ "using nobody", (u_long)arcn->sb.st_uid);
+ }
+ if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), term_char))
+ goto out;
+ }
+ if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), term_char)) {
+ if (gid_nobody == 0) {
+ if (gid_name("nobody", &gid_nobody) == -1)
+ goto out;
+ }
+ if (gid_warn != arcn->sb.st_gid) {
+ gid_warn = arcn->sb.st_gid;
+ paxwarn(1,
+ "Pax header field is too small for gid %lu, "
+ "using nobody", (u_long)arcn->sb.st_gid);
+ }
+ if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), term_char))
+ goto out;
+ }
+ /* However, Unix conformance tests do not like MORE than 12 mode bits:
+ remove all beyond (see definition of stat.st_mode structure) */
+ mode12only = ((u_long)arcn->sb.st_mode) & 0x00000fff;
+ if (ul_oct((u_long)mode12only, hd->mode, sizeof(hd->mode), term_char) ||
+ ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char))
+ goto out;
+ strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname));
+ strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname));
+
+ /*
+ * calculate and store the checksum write the header to the archive
+ * return 0 tells the caller to now write the file data, 1 says no data
+ * needs to be written
+ */
+ if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
+ sizeof(hd->chksum), term_char))
+ goto out;
+ if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
+ return(-1);
+ if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
+ return(-1);
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
+ return(0);
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1, "Pax header field is too small for %s", arcn->org_name);
+ return(1);
+}
+
+/*
+ * name_split()
+ * see if the name has to be split for storage in a ustar header. We try
+ * to fit the entire name in the name field without splitting if we can.
+ * The split point is always at a /
+ * Return
+ * character pointer to split point (always the / that is to be removed
+ * if the split is not needed, the points is set to the start of the file
+ * name (it would violate the spec to split there). A NULL is returned if
+ * the file name is too long
+ */
+
+static char *
+name_split(char *name, int len)
+{
+ char *start;
+
+ /*
+ * check to see if the file name is small enough to fit in the name
+ * field. if so just return a pointer to the name.
+ */
+ if (len <= TNMSZ)
+ return(name);
+ if (len > (TPFSZ + TNMSZ))
+ return(NULL);
+
+ /*
+ * we start looking at the biggest sized piece that fits in the name
+ * field. We walk forward looking for a slash to split at. The idea is
+ * to find the biggest piece to fit in the name field (or the smallest
+ * prefix we can find)
+ */
+ start = name + len - TNMSZ -1;
+ if ((*start == '/') && (start == name))
+ ++start; /* 101 byte paths with leading '/' are dinged otherwise */
+ while ((*start != '\0') && (*start != '/'))
+ ++start;
+
+ /*
+ * if we hit the end of the string, this name cannot be split, so we
+ * cannot store this file.
+ */
+ if (*start == '\0')
+ return(NULL);
+ len = start - name;
+
+ /*
+ * NOTE: /str where the length of str == TNMSZ can not be stored under
+ * the p1003.1-1990 spec for ustar. We could force a prefix of / and
+ * the file would then expand on extract to //str. The len == 0 below
+ * makes this special case follow the spec to the letter.
+ */
+ if ((len >= TPFSZ) || (len == 0))
+ return(NULL);
+
+ /*
+ * ok have a split point, return it to the caller
+ */
+ return(start);
+}
+
+static size_t
+expandname(char *buf, size_t len, char **gnu_name, const char *name, size_t name_len)
+{
+ size_t nlen;
+
+ if (*gnu_name) {
+ if ((nlen = strlcpy(buf, *gnu_name, len)) >= len)
+ nlen = len - 1;
+ free(*gnu_name);
+ *gnu_name = NULL;
+ } else {
+ if (name_len < len) {
+ /* name may not be null terminated: it might be as big as the
+ field, so copy is limited to the max size of the header field */
+ if ((nlen = strlcpy(buf, name, name_len+1)) >= name_len+1)
+ nlen = name_len;
+ } else {
+ if ((nlen = strlcpy(buf, name, len)) >= len)
+ nlen = len - 1;
+ }
+ }
+ return(nlen);
+}
diff --git a/file_cmds/pax/pax_format.h b/file_cmds/pax/pax_format.h
new file mode 100644
index 0000000..4ac4094
--- /dev/null
+++ b/file_cmds/pax/pax_format.h
@@ -0,0 +1,158 @@
+/* $OpenBSD: tar.h,v 1.7 2003/06/02 23:32:09 millert Exp $ */
+/* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tar.h 8.2 (Berkeley) 4/18/94
+ */
+
+#ifndef _PAX_FORMAT_H_
+#define _PAX_FORMAT_H_
+
+/*
+ * defines and data structures common to all tar formats
+ */
+#define CHK_LEN 8 /* length of checksum field */
+#define TNMSZ 100 /* size of name field */
+#ifdef _PAX_
+#define NULLCNT 2 /* number of null blocks in trailer */
+#define CHK_OFFSET 148 /* start of chksum field */
+#define BLNKSUM 256L /* sum of checksum field using ' ' */
+#endif /* _PAX_ */
+
+/*
+ * Values used in typeflag field in all tar formats
+ * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers)
+ */
+#define REGTYPE '0' /* Regular File */
+#define AREGTYPE '\0' /* Regular File */
+#define LNKTYPE '1' /* Link */
+#define SYMTYPE '2' /* Symlink */
+#define CHRTYPE '3' /* Character Special File */
+#define BLKTYPE '4' /* Block Special File */
+#define DIRTYPE '5' /* Directory */
+#define FIFOTYPE '6' /* FIFO */
+#define CONTTYPE '7' /* high perf file */
+
+/*
+ * GNU tar compatibility;
+ */
+#define LONGLINKTYPE 'K' /* Long Symlink */
+#define LONGNAMETYPE 'L' /* Long File */
+
+/*
+ * Mode field encoding of the different file types - values in octal
+ */
+#define TSUID 04000 /* Set UID on execution */
+#define TSGID 02000 /* Set GID on execution */
+#define TSVTX 01000 /* Reserved */
+#define TUREAD 00400 /* Read by owner */
+#define TUWRITE 00200 /* Write by owner */
+#define TUEXEC 00100 /* Execute/Search by owner */
+#define TGREAD 00040 /* Read by group */
+#define TGWRITE 00020 /* Write by group */
+#define TGEXEC 00010 /* Execute/Search by group */
+#define TOREAD 00004 /* Read by other */
+#define TOWRITE 00002 /* Write by other */
+#define TOEXEC 00001 /* Execute/Search by other */
+
+#ifdef _PAX_
+/*
+ * Pad with a bit mask, much faster than doing a mod but only works on powers
+ * of 2. Macro below is for block of 512 bytes.
+ */
+#define TAR_PAD(x) ((512 - ((x) & 511)) & 511)
+#endif /* _PAX_ */
+
+/*
+ * structure of an old tar header as it appeared in BSD releases
+ */
+typedef struct {
+ char name[TNMSZ]; /* name of entry */
+ char mode[8]; /* mode */
+ char uid[8]; /* uid */
+ char gid[8]; /* gid */
+ char size[12]; /* size */
+ char mtime[12]; /* modification time */
+ char chksum[CHK_LEN]; /* checksum */
+ char linkflag; /* norm, hard, or sym. */
+ char linkname[TNMSZ]; /* linked to name */
+} HD_TAR;
+
+#ifdef _PAX_
+/*
+ * -o options for BSD tar to not write directories to the archive
+ */
+#define TAR_NODIR "nodir"
+#define TAR_OPTION "write_opt"
+
+/*
+ * default device names
+ */
+#define DEV_0 "/dev/rst0"
+#define DEV_1 "/dev/rst1"
+#define DEV_4 "/dev/rst4"
+#define DEV_5 "/dev/rst5"
+#define DEV_7 "/dev/rst7"
+#define DEV_8 "/dev/rst8"
+#endif /* _PAX_ */
+
+/*
+ * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990
+ */
+#define TPFSZ 155
+#define TMAGIC "ustar" /* ustar and a null */
+#define TMAGLEN 6
+#define TVERSION "00" /* 00 and no null */
+#define TVERSLEN 2
+
+typedef struct {
+ char name[TNMSZ]; /* name of entry */
+ char mode[8]; /* mode */
+ char uid[8]; /* uid */
+ char gid[8]; /* gid */
+ char size[12]; /* size */
+ char mtime[12]; /* modification time */
+ char chksum[CHK_LEN]; /* checksum */
+ char typeflag; /* type of file. */
+ char linkname[TNMSZ]; /* linked to name */
+ char magic[TMAGLEN]; /* magic cookie */
+ char version[TVERSLEN]; /* version */
+ char uname[32]; /* ascii owner name */
+ char gname[32]; /* ascii group name */
+ char devmajor[8]; /* major device number */
+ char devminor[8]; /* minor device number */
+ char prefix[TPFSZ]; /* linked to name */
+} HD_USTAR;
+
+#endif /* _PAX_FORMAT_H_ */
diff --git a/file_cmds/pax/sel_subs.c b/file_cmds/pax/sel_subs.c
new file mode 100644
index 0000000..008479b
--- /dev/null
+++ b/file_cmds/pax/sel_subs.c
@@ -0,0 +1,614 @@
+/* $OpenBSD: sel_subs.c,v 1.18 2004/04/16 22:50:23 deraadt Exp $ */
+/* $NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: sel_subs.c,v 1.18 2004/04/16 22:50:23 deraadt Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include "pax.h"
+#include "sel_subs.h"
+#include "extern.h"
+
+static int str_sec(const char *, time_t *);
+static int usr_match(ARCHD *);
+static int grp_match(ARCHD *);
+static int trng_match(ARCHD *);
+
+static TIME_RNG *trhead = NULL; /* time range list head */
+static TIME_RNG *trtail = NULL; /* time range list tail */
+static USRT **usrtb = NULL; /* user selection table */
+static GRPT **grptb = NULL; /* group selection table */
+
+/*
+ * Routines for selection of archive members
+ */
+
+/*
+ * sel_chk()
+ * check if this file matches a specified uid, gid or time range
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be skipped
+ */
+
+int
+sel_chk(ARCHD *arcn)
+{
+ if (((usrtb != NULL) && usr_match(arcn)) ||
+ ((grptb != NULL) && grp_match(arcn)) ||
+ ((trhead != NULL) && trng_match(arcn)))
+ return(1);
+ return(0);
+}
+
+/*
+ * User/group selection routines
+ *
+ * Routines to handle user selection of files based on the file uid/gid. To
+ * add an entry, the user supplies either the name or the uid/gid starting with
+ * a # on the command line. A \# will escape the #.
+ */
+
+/*
+ * usr_add()
+ * add a user match to the user match hash table
+ * Return:
+ * 0 if added ok, -1 otherwise;
+ */
+
+int
+usr_add(char *str)
+{
+ u_int indx;
+ USRT *pt;
+ struct passwd *pw;
+ uid_t uid;
+
+ /*
+ * create the table if it doesn't exist
+ */
+ if ((str == NULL) || (*str == '\0'))
+ return(-1);
+ if ((usrtb == NULL) &&
+ ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) {
+ paxwarn(1, "Unable to allocate memory for user selection table");
+ return(-1);
+ }
+
+ /*
+ * figure out user spec
+ */
+ if (str[0] != '#') {
+ /*
+ * it is a user name, \# escapes # as first char in user name
+ */
+ if ((str[0] == '\\') && (str[1] == '#'))
+ ++str;
+ if ((pw = getpwnam(str)) == NULL) {
+ paxwarn(1, "Unable to find uid for user: %s", str);
+ return(-1);
+ }
+ uid = (uid_t)pw->pw_uid;
+ } else
+ uid = (uid_t)strtoul(str+1, NULL, 10);
+ endpwent();
+
+ /*
+ * hash it and go down the hash chain (if any) looking for it
+ */
+ indx = ((unsigned)uid) % USR_TB_SZ;
+ if ((pt = usrtb[indx]) != NULL) {
+ while (pt != NULL) {
+ if (pt->uid == uid)
+ return(0);
+ pt = pt->fow;
+ }
+ }
+
+ /*
+ * uid is not yet in the table, add it to the front of the chain
+ */
+ if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) {
+ pt->uid = uid;
+ pt->fow = usrtb[indx];
+ usrtb[indx] = pt;
+ return(0);
+ }
+ paxwarn(1, "User selection table out of memory");
+ return(-1);
+}
+
+/*
+ * usr_match()
+ * check if this files uid matches a selected uid.
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be skipped
+ */
+
+static int
+usr_match(ARCHD *arcn)
+{
+ USRT *pt;
+
+ /*
+ * hash and look for it in the table
+ */
+ pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ];
+ while (pt != NULL) {
+ if (pt->uid == arcn->sb.st_uid)
+ return(0);
+ pt = pt->fow;
+ }
+
+ /*
+ * not found
+ */
+ return(1);
+}
+
+/*
+ * grp_add()
+ * add a group match to the group match hash table
+ * Return:
+ * 0 if added ok, -1 otherwise;
+ */
+
+int
+grp_add(char *str)
+{
+ u_int indx;
+ GRPT *pt;
+ struct group *gr;
+ gid_t gid;
+
+ /*
+ * create the table if it doesn't exist
+ */
+ if ((str == NULL) || (*str == '\0'))
+ return(-1);
+ if ((grptb == NULL) &&
+ ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) {
+ paxwarn(1, "Unable to allocate memory fo group selection table");
+ return(-1);
+ }
+
+ /*
+ * figure out user spec
+ */
+ if (str[0] != '#') {
+ /*
+ * it is a group name, \# escapes # as first char in group name
+ */
+ if ((str[0] == '\\') && (str[1] == '#'))
+ ++str;
+ if ((gr = getgrnam(str)) == NULL) {
+ paxwarn(1,"Cannot determine gid for group name: %s", str);
+ return(-1);
+ }
+ gid = (gid_t)gr->gr_gid;
+ } else
+ gid = (gid_t)strtoul(str+1, NULL, 10);
+ endgrent();
+
+ /*
+ * hash it and go down the hash chain (if any) looking for it
+ */
+ indx = ((unsigned)gid) % GRP_TB_SZ;
+ if ((pt = grptb[indx]) != NULL) {
+ while (pt != NULL) {
+ if (pt->gid == gid)
+ return(0);
+ pt = pt->fow;
+ }
+ }
+
+ /*
+ * gid not in the table, add it to the front of the chain
+ */
+ if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) {
+ pt->gid = gid;
+ pt->fow = grptb[indx];
+ grptb[indx] = pt;
+ return(0);
+ }
+ paxwarn(1, "Group selection table out of memory");
+ return(-1);
+}
+
+/*
+ * grp_match()
+ * check if this files gid matches a selected gid.
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be skipped
+ */
+
+static int
+grp_match(ARCHD *arcn)
+{
+ GRPT *pt;
+
+ /*
+ * hash and look for it in the table
+ */
+ pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ];
+ while (pt != NULL) {
+ if (pt->gid == arcn->sb.st_gid)
+ return(0);
+ pt = pt->fow;
+ }
+
+ /*
+ * not found
+ */
+ return(1);
+}
+
+/*
+ * Time range selection routines
+ *
+ * Routines to handle user selection of files based on the modification and/or
+ * inode change time falling within a specified time range (the non-standard
+ * -T flag). The user may specify any number of different file time ranges.
+ * Time ranges are checked one at a time until a match is found (if at all).
+ * If the file has a mtime (and/or ctime) which lies within one of the time
+ * ranges, the file is selected. Time ranges may have a lower and/or a upper
+ * value. These ranges are inclusive. When no time ranges are supplied to pax
+ * with the -T option, all members in the archive will be selected by the time
+ * range routines. When only a lower range is supplied, only files with a
+ * mtime (and/or ctime) equal to or younger are selected. When only a upper
+ * range is supplied, only files with a mtime (and/or ctime) equal to or older
+ * are selected. When the lower time range is equal to the upper time range,
+ * only files with a mtime (or ctime) of exactly that time are selected.
+ */
+
+/*
+ * trng_add()
+ * add a time range match to the time range list.
+ * This is a non-standard pax option. Lower and upper ranges are in the
+ * format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated.
+ * Time ranges are based on current time, so 1234 would specify a time of
+ * 12:34 today.
+ * Return:
+ * 0 if the time range was added to the list, -1 otherwise
+ */
+
+int
+trng_add(char *str)
+{
+ TIME_RNG *pt;
+ char *up_pt = NULL;
+ char *stpt;
+ char *flgpt;
+ int dot = 0;
+
+ /*
+ * throw out the badly formed time ranges
+ */
+ if ((str == NULL) || (*str == '\0')) {
+ paxwarn(1, "Empty time range string");
+ return(-1);
+ }
+
+ /*
+ * locate optional flags suffix /{cm}.
+ */
+ if ((flgpt = strrchr(str, '/')) != NULL)
+ *flgpt++ = '\0';
+
+ for (stpt = str; *stpt != '\0'; ++stpt) {
+ if ((*stpt >= '0') && (*stpt <= '9'))
+ continue;
+ if ((*stpt == ',') && (up_pt == NULL)) {
+ *stpt = '\0';
+ up_pt = stpt + 1;
+ dot = 0;
+ continue;
+ }
+
+ /*
+ * allow only one dot per range (secs)
+ */
+ if ((*stpt == '.') && (!dot)) {
+ ++dot;
+ continue;
+ }
+ paxwarn(1, "Improperly specified time range: %s", str);
+ goto out;
+ }
+
+ /*
+ * allocate space for the time range and store the limits
+ */
+ if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) {
+ paxwarn(1, "Unable to allocate memory for time range");
+ return(-1);
+ }
+
+ /*
+ * by default we only will check file mtime, but user can specify
+ * mtime, ctime (inode change time) or both.
+ */
+ if ((flgpt == NULL) || (*flgpt == '\0'))
+ pt->flgs = CMPMTME;
+ else {
+ pt->flgs = 0;
+ while (*flgpt != '\0') {
+ switch (*flgpt) {
+ case 'M':
+ case 'm':
+ pt->flgs |= CMPMTME;
+ break;
+ case 'C':
+ case 'c':
+ pt->flgs |= CMPCTME;
+ break;
+ default:
+ paxwarn(1, "Bad option %c with time range %s",
+ *flgpt, str);
+ free(pt);
+ goto out;
+ }
+ ++flgpt;
+ }
+ }
+
+ /*
+ * start off with the current time
+ */
+ pt->low_time = pt->high_time = time(NULL);
+ if (*str != '\0') {
+ /*
+ * add lower limit
+ */
+ if (str_sec(str, &(pt->low_time)) < 0) {
+ paxwarn(1, "Illegal lower time range %s", str);
+ (void)free((char *)pt);
+ goto out;
+ }
+ pt->flgs |= HASLOW;
+ }
+
+ if ((up_pt != NULL) && (*up_pt != '\0')) {
+ /*
+ * add upper limit
+ */
+ if (str_sec(up_pt, &(pt->high_time)) < 0) {
+ paxwarn(1, "Illegal upper time range %s", up_pt);
+ (void)free((char *)pt);
+ goto out;
+ }
+ pt->flgs |= HASHIGH;
+
+ /*
+ * check that the upper and lower do not overlap
+ */
+ if (pt->flgs & HASLOW) {
+ if (pt->low_time > pt->high_time) {
+ paxwarn(1, "Upper %s and lower %s time overlap",
+ up_pt, str);
+ (void)free((char *)pt);
+ return(-1);
+ }
+ }
+ }
+
+ pt->fow = NULL;
+ if (trhead == NULL) {
+ trtail = trhead = pt;
+ return(0);
+ }
+ trtail->fow = pt;
+ trtail = pt;
+ return(0);
+
+ out:
+ paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]");
+ return(-1);
+}
+
+/*
+ * trng_match()
+ * check if this files mtime/ctime falls within any supplied time range.
+ * Return:
+ * 0 if this archive member should be processed, 1 if it should be skipped
+ */
+
+static int
+trng_match(ARCHD *arcn)
+{
+ TIME_RNG *pt;
+
+ /*
+ * have to search down the list one at a time looking for a match.
+ * remember time range limits are inclusive.
+ */
+ pt = trhead;
+ while (pt != NULL) {
+ switch (pt->flgs & CMPBOTH) {
+ case CMPBOTH:
+ /*
+ * user wants both mtime and ctime checked for this
+ * time range
+ */
+ if (((pt->flgs & HASLOW) &&
+ (arcn->sb.st_mtime < pt->low_time) &&
+ (arcn->sb.st_ctime < pt->low_time)) ||
+ ((pt->flgs & HASHIGH) &&
+ (arcn->sb.st_mtime > pt->high_time) &&
+ (arcn->sb.st_ctime > pt->high_time))) {
+ pt = pt->fow;
+ continue;
+ }
+ break;
+ case CMPCTME:
+ /*
+ * user wants only ctime checked for this time range
+ */
+ if (((pt->flgs & HASLOW) &&
+ (arcn->sb.st_ctime < pt->low_time)) ||
+ ((pt->flgs & HASHIGH) &&
+ (arcn->sb.st_ctime > pt->high_time))) {
+ pt = pt->fow;
+ continue;
+ }
+ break;
+ case CMPMTME:
+ default:
+ /*
+ * user wants only mtime checked for this time range
+ */
+ if (((pt->flgs & HASLOW) &&
+ (arcn->sb.st_mtime < pt->low_time)) ||
+ ((pt->flgs & HASHIGH) &&
+ (arcn->sb.st_mtime > pt->high_time))) {
+ pt = pt->fow;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (pt == NULL)
+ return(1);
+ return(0);
+}
+
+/*
+ * str_sec()
+ * Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to
+ * seconds UTC. Tval already has current time loaded into it at entry.
+ * Return:
+ * 0 if converted ok, -1 otherwise
+ */
+
+static int
+str_sec(const char *p, time_t *tval)
+{
+ struct tm *lt;
+ const char *dot, *t;
+ size_t len;
+ int bigyear;
+ int yearset;
+
+ yearset = 0;
+ len = strlen(p);
+
+ for (t = p, dot = NULL; *t; ++t) {
+ if (isdigit(*t))
+ continue;
+ if (*t == '.' && dot == NULL) {
+ dot = t;
+ continue;
+ }
+ return(-1);
+ }
+
+ lt = localtime(tval);
+
+ if (dot != NULL) { /* .SS */
+ if (strlen(++dot) != 2)
+ return(-1);
+ lt->tm_sec = ATOI2(dot);
+ if (lt->tm_sec > 61)
+ return(-1);
+ len -= 3;
+ } else
+ lt->tm_sec = 0;
+
+ switch (len) {
+ case 12: /* cc */
+ bigyear = ATOI2(p);
+ lt->tm_year = (bigyear * 100) - TM_YEAR_BASE;
+ yearset = 1;
+ /* FALLTHROUGH */
+ case 10: /* yy */
+ if (yearset) {
+ lt->tm_year += ATOI2(p);
+ } else {
+ lt->tm_year = ATOI2(p);
+ if (lt->tm_year < 69) /* hack for 2000 ;-} */
+ lt->tm_year += (2000 - TM_YEAR_BASE);
+ else
+ lt->tm_year += (1900 - TM_YEAR_BASE);
+ }
+ /* FALLTHROUGH */
+ case 8: /* mm */
+ lt->tm_mon = ATOI2(p);
+ if ((lt->tm_mon > 12) || !lt->tm_mon)
+ return(-1);
+ --lt->tm_mon; /* time struct is 0 - 11 */
+ /* FALLTHROUGH */
+ case 6: /* dd */
+ lt->tm_mday = ATOI2(p);
+ if ((lt->tm_mday > 31) || !lt->tm_mday)
+ return(-1);
+ /* FALLTHROUGH */
+ case 4: /* HH */
+ lt->tm_hour = ATOI2(p);
+ if (lt->tm_hour > 23)
+ return(-1);
+ /* FALLTHROUGH */
+ case 2: /* MM */
+ lt->tm_min = ATOI2(p);
+ if (lt->tm_min > 59)
+ return(-1);
+ break;
+ default:
+ return(-1);
+ }
+
+ /* convert broken-down time to UTC clock time seconds */
+ if ((*tval = mktime(lt)) == -1)
+ return(-1);
+ return(0);
+}
diff --git a/file_cmds/pax/sel_subs.h b/file_cmds/pax/sel_subs.h
new file mode 100644
index 0000000..4516545
--- /dev/null
+++ b/file_cmds/pax/sel_subs.h
@@ -0,0 +1,77 @@
+/* $OpenBSD: sel_subs.h,v 1.4 2003/06/02 23:32:09 millert Exp $ */
+/* $NetBSD: sel_subs.h,v 1.3 1995/03/21 09:07:44 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _SEL_SUBS_H_
+#define _SEL_SUBS_H_
+
+/*
+ * data structure for storing uid/grp selects (-U, -G non standard options)
+ */
+
+#define USR_TB_SZ 317 /* user selection table size */
+#define GRP_TB_SZ 317 /* user selection table size */
+
+typedef struct usrt {
+ uid_t uid;
+ struct usrt *fow; /* next uid */
+} USRT;
+
+typedef struct grpt {
+ gid_t gid;
+ struct grpt *fow; /* next gid */
+} GRPT;
+
+/*
+ * data structure for storing user supplied time ranges (-T option)
+ */
+
+#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
+
+typedef struct time_rng {
+ time_t low_time; /* lower inclusive time limit */
+ time_t high_time; /* higher inclusive time limit */
+ int flgs; /* option flags */
+#define HASLOW 0x01 /* has lower time limit */
+#define HASHIGH 0x02 /* has higher time limit */
+#define CMPMTME 0x04 /* compare file modification time */
+#define CMPCTME 0x08 /* compare inode change time */
+#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */
+ struct time_rng *fow; /* next pattern */
+} TIME_RNG;
+
+#endif /* _SEL_SUBS_H_ */
diff --git a/file_cmds/pax/tables.c b/file_cmds/pax/tables.c
new file mode 100644
index 0000000..f45d291
--- /dev/null
+++ b/file_cmds/pax/tables.c
@@ -0,0 +1,1278 @@
+/* $OpenBSD: tables.c,v 1.25 2007/09/02 15:19:08 deraadt Exp $ */
+/* $NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 5/31/93";
+#else
+__used static const char rcsid[] = "$OpenBSD: tables.c,v 1.25 2007/09/02 15:19:08 deraadt Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "tables.h"
+#include "extern.h"
+
+/*
+ * Routines for controlling the contents of all the different databases pax
+ * keeps. Tables are dynamically created only when they are needed. The
+ * goal was speed and the ability to work with HUGE archives. The databases
+ * were kept simple, but do have complex rules for when the contents change.
+ * As of this writing, the posix library functions were more complex than
+ * needed for this application (pax databases have very short lifetimes and
+ * do not survive after pax is finished). Pax is required to handle very
+ * large archives. These database routines carefully combine memory usage and
+ * temporary file storage in ways which will not significantly impact runtime
+ * performance while allowing the largest possible archives to be handled.
+ * Trying to force the fit to the posix database routines was not considered
+ * time well spent.
+ */
+
+static HRDLNK **ltab = NULL; /* hard link table for detecting hard links */
+static FTM **ftab = NULL; /* file time table for updating arch */
+static NAMT **ntab = NULL; /* interactive rename storage table */
+static DEVT **dtab = NULL; /* device/inode mapping tables */
+static ATDIR **atab = NULL; /* file tree directory time reset table */
+static DIRDATA *dirp = NULL; /* storage for setting created dir time/mode */
+static size_t dirsize; /* size of dirp table */
+static long dircnt = 0; /* entries in dir time/mode storage */
+static int ffd = -1; /* tmp file for file time table name storage */
+
+static DEVT *chk_dev(dev_t, int);
+
+/*
+ * hard link table routines
+ *
+ * The hard link table tries to detect hard links to files using the device and
+ * inode values. We do this when writing an archive, so we can tell the format
+ * write routine that this file is a hard link to another file. The format
+ * write routine then can store this file in whatever way it wants (as a hard
+ * link if the format supports that like tar, or ignore this info like cpio).
+ * (Actually a field in the format driver table tells us if the format wants
+ * hard link info. if not, we do not waste time looking for them). We also use
+ * the same table when reading an archive. In that situation, this table is
+ * used by the format read routine to detect hard links from stored dev and
+ * inode numbers (like cpio). This will allow pax to create a link when one
+ * can be detected by the archive format.
+ */
+
+/*
+ * lnk_start
+ * Creates the hard link table.
+ * Return:
+ * 0 if created, -1 if failure
+ */
+
+int
+lnk_start(void)
+{
+ if (ltab != NULL)
+ return(0);
+ if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) {
+ paxwarn(1, "Cannot allocate memory for hard link table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * chk_lnk()
+ * Looks up entry in hard link hash table. If found, it copies the name
+ * of the file it is linked to (we already saw that file) into ln_name.
+ * lnkcnt is decremented and if goes to 1 the node is deleted from the
+ * database. (We have seen all the links to this file). If not found,
+ * we add the file to the database if it has the potential for having
+ * hard links to other files we may process (it has a link count > 1)
+ * Return:
+ * if found returns 1; if not found returns 0; -1 on error
+ */
+
+int
+chk_lnk(ARCHD *arcn)
+{
+ HRDLNK *pt;
+ HRDLNK **ppt;
+ u_int indx;
+
+ if (ltab == NULL)
+ return(-1);
+ /*
+ * ignore those nodes that cannot have hard links
+ */
+ if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1))
+ return(0);
+
+ /*
+ * hash inode number and look for this file
+ */
+ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
+ if ((pt = ltab[indx]) != NULL) {
+ /*
+ * its hash chain in not empty, walk down looking for it
+ */
+ ppt = &(ltab[indx]);
+ while (pt != NULL) {
+ if ((pt->ino == arcn->sb.st_ino) &&
+ (pt->dev == arcn->sb.st_dev))
+ break;
+ ppt = &(pt->fow);
+ pt = pt->fow;
+ }
+
+ if (pt != NULL) {
+ /*
+ * found a link. set the node type and copy in the
+ * name of the file it is to link to. we need to
+ * handle hardlinks to regular files differently than
+ * other links.
+ */
+ arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name,
+ sizeof(arcn->ln_name));
+ /* XXX truncate? */
+ if (arcn->nlen >= sizeof(arcn->name))
+ arcn->nlen = sizeof(arcn->name) - 1;
+ if (arcn->type == PAX_REG)
+ arcn->type = PAX_HRG;
+ else
+ arcn->type = PAX_HLK;
+
+ /*
+ * if we have found all the links to this file, remove
+ * it from the database
+ */
+ if (--pt->nlink <= 1) {
+ *ppt = pt->fow;
+ (void)free((char *)pt->name);
+ (void)free((char *)pt);
+ }
+ return(1);
+ }
+ }
+
+ /*
+ * we never saw this file before. It has links so we add it to the
+ * front of this hash chain
+ */
+ if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) {
+ if ((pt->name = strdup(arcn->name)) != NULL) {
+ pt->dev = arcn->sb.st_dev;
+ pt->ino = arcn->sb.st_ino;
+ pt->nlink = arcn->sb.st_nlink;
+ pt->fow = ltab[indx];
+ ltab[indx] = pt;
+ return(0);
+ }
+ (void)free((char *)pt);
+ }
+
+ paxwarn(1, "Hard link table out of memory");
+ return(-1);
+}
+
+/*
+ * purg_lnk
+ * remove reference for a file that we may have added to the data base as
+ * a potential source for hard links. We ended up not using the file, so
+ * we do not want to accidently point another file at it later on.
+ */
+
+void
+purg_lnk(ARCHD *arcn)
+{
+ HRDLNK *pt;
+ HRDLNK **ppt;
+ u_int indx;
+
+ if (ltab == NULL)
+ return;
+ /*
+ * do not bother to look if it could not be in the database
+ */
+ if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) ||
+ (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+ return;
+
+ /*
+ * find the hash chain for this inode value, if empty return
+ */
+ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ;
+ if ((pt = ltab[indx]) == NULL)
+ return;
+
+ /*
+ * walk down the list looking for the inode/dev pair, unlink and
+ * free if found
+ */
+ ppt = &(ltab[indx]);
+ while (pt != NULL) {
+ if ((pt->ino == arcn->sb.st_ino) &&
+ (pt->dev == arcn->sb.st_dev))
+ break;
+ ppt = &(pt->fow);
+ pt = pt->fow;
+ }
+ if (pt == NULL)
+ return;
+
+ /*
+ * remove and free it
+ */
+ *ppt = pt->fow;
+ (void)free((char *)pt->name);
+ (void)free((char *)pt);
+}
+
+/*
+ * lnk_end()
+ * pull apart a existing link table so we can reuse it. We do this between
+ * read and write phases of append with update. (The format may have
+ * used the link table, and we need to start with a fresh table for the
+ * write phase
+ */
+
+void
+lnk_end(void)
+{
+ int i;
+ HRDLNK *pt;
+ HRDLNK *ppt;
+
+ if (ltab == NULL)
+ return;
+
+ for (i = 0; i < L_TAB_SZ; ++i) {
+ if (ltab[i] == NULL)
+ continue;
+ pt = ltab[i];
+ ltab[i] = NULL;
+
+ /*
+ * free up each entry on this chain
+ */
+ while (pt != NULL) {
+ ppt = pt;
+ pt = ppt->fow;
+ (void)free((char *)ppt->name);
+ (void)free((char *)ppt);
+ }
+ }
+ return;
+}
+
+/*
+ * modification time table routines
+ *
+ * The modification time table keeps track of last modification times for all
+ * files stored in an archive during a write phase when -u is set. We only
+ * add a file to the archive if it is newer than a file with the same name
+ * already stored on the archive (if there is no other file with the same
+ * name on the archive it is added). This applies to writes and appends.
+ * An append with an -u must read the archive and store the modification time
+ * for every file on that archive before starting the write phase. It is clear
+ * that this is one HUGE database. To save memory space, the actual file names
+ * are stored in a scratch file and indexed by an in-memory hash table. The
+ * hash table is indexed by hashing the file path. The nodes in the table store
+ * the length of the filename and the lseek offset within the scratch file
+ * where the actual name is stored. Since there are never any deletions from
+ * this table, fragmentation of the scratch file is never a issue. Lookups
+ * seem to not exhibit any locality at all (files in the database are rarely
+ * looked up more than once...), so caching is just a waste of memory. The
+ * only limitation is the amount of scratch file space available to store the
+ * path names.
+ */
+
+/*
+ * ftime_start()
+ * create the file time hash table and open for read/write the scratch
+ * file. (after created it is unlinked, so when we exit we leave
+ * no witnesses).
+ * Return:
+ * 0 if the table and file was created ok, -1 otherwise
+ */
+
+int
+ftime_start(void)
+{
+
+ if (ftab != NULL)
+ return(0);
+ if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) {
+ paxwarn(1, "Cannot allocate memory for file time table");
+ return(-1);
+ }
+
+ /*
+ * get random name and create temporary scratch file, unlink name
+ * so it will get removed on exit
+ */
+ memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE));
+ if ((ffd = mkstemp(tempfile)) < 0) {
+ syswarn(1, errno, "Unable to create temporary file: %s",
+ tempfile);
+ return(-1);
+ }
+ (void)unlink(tempfile);
+
+ return(0);
+}
+
+/*
+ * chk_ftime()
+ * looks up entry in file time hash table. If not found, the file is
+ * added to the hash table and the file named stored in the scratch file.
+ * If a file with the same name is found, the file times are compared and
+ * the most recent file time is retained. If the new file was younger (or
+ * was not in the database) the new file is selected for storage.
+ * Return:
+ * 0 if file should be added to the archive, 1 if it should be skipped,
+ * -1 on error
+ */
+
+int
+chk_ftime(ARCHD *arcn)
+{
+ FTM *pt;
+ int namelen;
+ u_int indx;
+ char ckname[PAXPATHLEN+1];
+
+ /*
+ * no info, go ahead and add to archive
+ */
+ if (ftab == NULL)
+ return(0);
+
+ /*
+ * hash the pathname and look up in table
+ */
+ namelen = arcn->nlen;
+ indx = st_hash(arcn->name, namelen, F_TAB_SZ);
+ if ((pt = ftab[indx]) != NULL) {
+ /*
+ * the hash chain is not empty, walk down looking for match
+ * only read up the path names if the lengths match, speeds
+ * up the search a lot
+ */
+ while (pt != NULL) {
+ if (pt->namelen == namelen) {
+ /*
+ * potential match, have to read the name
+ * from the scratch file.
+ */
+ if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) {
+ syswarn(1, errno,
+ "Failed ftime table seek");
+ return(-1);
+ }
+ if (read(ffd, ckname, namelen) != namelen) {
+ syswarn(1, errno,
+ "Failed ftime table read");
+ return(-1);
+ }
+
+ /*
+ * if the names match, we are done
+ */
+ if (!strncmp(ckname, arcn->name, namelen))
+ break;
+ }
+
+ /*
+ * try the next entry on the chain
+ */
+ pt = pt->fow;
+ }
+
+ if (pt != NULL) {
+ /*
+ * found the file, compare the times, save the newer
+ */
+ if (arcn->sb.st_mtime > pt->mtime) {
+ /*
+ * file is newer
+ */
+ pt->mtime = arcn->sb.st_mtime;
+ return(0);
+ }
+ /*
+ * file is older
+ */
+ return(1);
+ }
+ }
+
+ /*
+ * not in table, add it
+ */
+ if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) {
+ /*
+ * add the name at the end of the scratch file, saving the
+ * offset. add the file to the head of the hash chain
+ */
+ if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) {
+ if (write(ffd, arcn->name, namelen) == namelen) {
+ pt->mtime = arcn->sb.st_mtime;
+ pt->namelen = namelen;
+ pt->fow = ftab[indx];
+ ftab[indx] = pt;
+ return(0);
+ }
+ syswarn(1, errno, "Failed write to file time table");
+ } else
+ syswarn(1, errno, "Failed seek on file time table");
+ } else
+ paxwarn(1, "File time table ran out of memory");
+
+ if (pt != NULL)
+ (void)free((char *)pt);
+ return(-1);
+}
+
+/*
+ * Interactive rename table routines
+ *
+ * The interactive rename table keeps track of the new names that the user
+ * assigns to files from tty input. Since this map is unique for each file
+ * we must store it in case there is a reference to the file later in archive
+ * (a link). Otherwise we will be unable to find the file we know was
+ * extracted. The remapping of these files is stored in a memory based hash
+ * table (it is assumed since input must come from /dev/tty, it is unlikely to
+ * be a very large table).
+ */
+
+/*
+ * name_start()
+ * create the interactive rename table
+ * Return:
+ * 0 if successful, -1 otherwise
+ */
+
+int
+name_start(void)
+{
+ if (ntab != NULL)
+ return(0);
+ if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) {
+ paxwarn(1, "Cannot allocate memory for interactive rename table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * add_name()
+ * add the new name to old name mapping just created by the user.
+ * If an old name mapping is found (there may be duplicate names on an
+ * archive) only the most recent is kept.
+ * Return:
+ * 0 if added, -1 otherwise
+ */
+
+int
+add_name(char *oname, int onamelen, char *nname)
+{
+ NAMT *pt;
+ u_int indx;
+
+ if (ntab == NULL) {
+ /*
+ * should never happen
+ */
+ paxwarn(0, "No interactive rename table, links may fail");
+ return(0);
+ }
+
+ /*
+ * look to see if we have already mapped this file, if so we
+ * will update it
+ */
+ indx = st_hash(oname, onamelen, N_TAB_SZ);
+ if ((pt = ntab[indx]) != NULL) {
+ /*
+ * look down the has chain for the file
+ */
+ while ((pt != NULL) && (strcmp(oname, pt->oname) != 0))
+ pt = pt->fow;
+
+ if (pt != NULL) {
+ /*
+ * found an old mapping, replace it with the new one
+ * the user just input (if it is different)
+ */
+ if (strcmp(nname, pt->nname) == 0)
+ return(0);
+
+ (void)free((char *)pt->nname);
+ if ((pt->nname = strdup(nname)) == NULL) {
+ paxwarn(1, "Cannot update rename table");
+ return(-1);
+ }
+ return(0);
+ }
+ }
+
+ /*
+ * this is a new mapping, add it to the table
+ */
+ if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) {
+ if ((pt->oname = strdup(oname)) != NULL) {
+ if ((pt->nname = strdup(nname)) != NULL) {
+ pt->fow = ntab[indx];
+ ntab[indx] = pt;
+ return(0);
+ }
+ (void)free((char *)pt->oname);
+ }
+ (void)free((char *)pt);
+ }
+ paxwarn(1, "Interactive rename table out of memory");
+ return(-1);
+}
+
+/*
+ * sub_name()
+ * look up a link name to see if it points at a file that has been
+ * remapped by the user. If found, the link is adjusted to contain the
+ * new name (oname is the link to name)
+ */
+
+void
+sub_name(char *oname, int *onamelen, size_t onamesize)
+{
+ NAMT *pt;
+ u_int indx;
+
+ if (ntab == NULL)
+ return;
+ /*
+ * look the name up in the hash table
+ */
+ indx = st_hash(oname, *onamelen, N_TAB_SZ);
+ if ((pt = ntab[indx]) == NULL)
+ return;
+
+ while (pt != NULL) {
+ /*
+ * walk down the hash chain looking for a match
+ */
+ if (strcmp(oname, pt->oname) == 0) {
+ /*
+ * found it, replace it with the new name
+ * and return (we know that oname has enough space)
+ */
+ *onamelen = strlcpy(oname, pt->nname, onamesize);
+ if (*onamelen >= onamesize)
+ *onamelen = onamesize - 1; /* XXX truncate? */
+ return;
+ }
+ pt = pt->fow;
+ }
+
+ /*
+ * no match, just return
+ */
+ return;
+}
+
+/*
+ * device/inode mapping table routines
+ * (used with formats that store device and inodes fields)
+ *
+ * device/inode mapping tables remap the device field in a archive header. The
+ * device/inode fields are used to determine when files are hard links to each
+ * other. However these values have very little meaning outside of that. This
+ * database is used to solve one of two different problems.
+ *
+ * 1) when files are appended to an archive, while the new files may have hard
+ * links to each other, you cannot determine if they have hard links to any
+ * file already stored on the archive from a prior run of pax. We must assume
+ * that these inode/device pairs are unique only within a SINGLE run of pax
+ * (which adds a set of files to an archive). So we have to make sure the
+ * inode/dev pairs we add each time are always unique. We do this by observing
+ * while the inode field is very dense, the use of the dev field is fairly
+ * sparse. Within each run of pax, we remap any device number of a new archive
+ * member that has a device number used in a prior run and already stored in a
+ * file on the archive. During the read phase of the append, we store the
+ * device numbers used and mark them to not be used by any file during the
+ * write phase. If during write we go to use one of those old device numbers,
+ * we remap it to a new value.
+ *
+ * 2) Often the fields in the archive header used to store these values are
+ * too small to store the entire value. The result is an inode or device value
+ * which can be truncated. This really can foul up an archive. With truncation
+ * we end up creating links between files that are really not links (after
+ * truncation the inodes are the same value). We address that by detecting
+ * truncation and forcing a remap of the device field to split truncated
+ * inodes away from each other. Each truncation creates a pattern of bits that
+ * are removed. We use this pattern of truncated bits to partition the inodes
+ * on a single device to many different devices (each one represented by the
+ * truncated bit pattern). All inodes on the same device that have the same
+ * truncation pattern are mapped to the same new device. Two inodes that
+ * truncate to the same value clearly will always have different truncation
+ * bit patterns, so they will be split from away each other. When we spot
+ * device truncation we remap the device number to a non truncated value.
+ * (for more info see table.h for the data structures involved).
+ */
+
+/*
+ * dev_start()
+ * create the device mapping table
+ * Return:
+ * 0 if successful, -1 otherwise
+ */
+
+int
+dev_start(void)
+{
+ if (dtab != NULL)
+ return(0);
+ if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) {
+ paxwarn(1, "Cannot allocate memory for device mapping table");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * add_dev()
+ * add a device number to the table. this will force the device to be
+ * remapped to a new value if it be used during a write phase. This
+ * function is called during the read phase of an append to prohibit the
+ * use of any device number already in the archive.
+ * Return:
+ * 0 if added ok, -1 otherwise
+ */
+
+int
+add_dev(ARCHD *arcn)
+{
+ if (chk_dev(arcn->sb.st_dev, 1) == NULL)
+ return(-1);
+ return(0);
+}
+
+/*
+ * chk_dev()
+ * check for a device value in the device table. If not found and the add
+ * flag is set, it is added. This does NOT assign any mapping values, just
+ * adds the device number as one that need to be remapped. If this device
+ * is already mapped, just return with a pointer to that entry.
+ * Return:
+ * pointer to the entry for this device in the device map table. Null
+ * if the add flag is not set and the device is not in the table (it is
+ * not been seen yet). If add is set and the device cannot be added, null
+ * is returned (indicates an error).
+ */
+
+static DEVT *
+chk_dev(dev_t dev, int add)
+{
+ DEVT *pt;
+ u_int indx;
+
+ if (dtab == NULL)
+ return(NULL);
+ /*
+ * look to see if this device is already in the table
+ */
+ indx = ((unsigned)dev) % D_TAB_SZ;
+ if ((pt = dtab[indx]) != NULL) {
+ while ((pt != NULL) && (pt->dev != dev))
+ pt = pt->fow;
+
+ /*
+ * found it, return a pointer to it
+ */
+ if (pt != NULL)
+ return(pt);
+ }
+
+ /*
+ * not in table, we add it only if told to as this may just be a check
+ * to see if a device number is being used.
+ */
+ if (add == 0)
+ return(NULL);
+
+ /*
+ * allocate a node for this device and add it to the front of the hash
+ * chain. Note we do not assign remaps values here, so the pt->list
+ * list must be NULL.
+ */
+ if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) {
+ paxwarn(1, "Device map table out of memory");
+ return(NULL);
+ }
+ pt->dev = dev;
+ pt->list = NULL;
+ pt->fow = dtab[indx];
+ dtab[indx] = pt;
+ return(pt);
+}
+/*
+ * map_dev()
+ * given an inode and device storage mask (the mask has a 1 for each bit
+ * the archive format is able to store in a header), we check for inode
+ * and device truncation and remap the device as required. Device mapping
+ * can also occur when during the read phase of append a device number was
+ * seen (and was marked as do not use during the write phase). WE ASSUME
+ * that unsigned longs are the same size or bigger than the fields used
+ * for ino_t and dev_t. If not the types will have to be changed.
+ * Return:
+ * 0 if all ok, -1 otherwise.
+ */
+
+int
+map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask)
+{
+ DEVT *pt;
+ DLIST *dpt;
+ static dev_t lastdev = 0; /* next device number to try */
+ int trc_ino = 0;
+ int trc_dev = 0;
+ ino_t trunc_bits = 0;
+ ino_t nino;
+
+ if (dtab == NULL)
+ return(0);
+ /*
+ * check for device and inode truncation, and extract the truncated
+ * bit pattern.
+ */
+ if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev)
+ ++trc_dev;
+ if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) {
+ ++trc_ino;
+ trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask);
+ }
+
+ /*
+ * see if this device is already being mapped, look up the device
+ * then find the truncation bit pattern which applies
+ */
+ if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) {
+ /*
+ * this device is already marked to be remapped
+ */
+ for (dpt = pt->list; dpt != NULL; dpt = dpt->fow)
+ if (dpt->trunc_bits == trunc_bits)
+ break;
+
+ if (dpt != NULL) {
+ /*
+ * we are being remapped for this device and pattern
+ * change the device number to be stored and return
+ */
+ arcn->sb.st_dev = dpt->dev;
+ arcn->sb.st_ino = nino;
+ return(0);
+ }
+ } else {
+ /*
+ * this device is not being remapped YET. if we do not have any
+ * form of truncation, we do not need a remap
+ */
+ if (!trc_ino && !trc_dev)
+ return(0);
+
+ /*
+ * we have truncation, have to add this as a device to remap
+ */
+ if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL)
+ goto bad;
+
+ /*
+ * if we just have a truncated inode, we have to make sure that
+ * all future inodes that do not truncate (they have the
+ * truncation pattern of all 0's) continue to map to the same
+ * device number. We probably have already written inodes with
+ * this device number to the archive with the truncation
+ * pattern of all 0's. So we add the mapping for all 0's to the
+ * same device number.
+ */
+ if (!trc_dev && (trunc_bits != 0)) {
+ if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)
+ goto bad;
+ dpt->trunc_bits = 0;
+ dpt->dev = arcn->sb.st_dev;
+ dpt->fow = pt->list;
+ pt->list = dpt;
+ }
+ }
+
+ /*
+ * look for a device number not being used. We must watch for wrap
+ * around on lastdev (so we do not get stuck looking forever!)
+ */
+ while (++lastdev > 0) {
+ if (chk_dev(lastdev, 0) != NULL)
+ continue;
+ /*
+ * found an unused value. If we have reached truncation point
+ * for this format we are hosed, so we give up. Otherwise we
+ * mark it as being used.
+ */
+ if (((lastdev & ((dev_t)dev_mask)) != lastdev) ||
+ (chk_dev(lastdev, 1) == NULL))
+ goto bad;
+ break;
+ }
+
+ if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL))
+ goto bad;
+
+ /*
+ * got a new device number, store it under this truncation pattern.
+ * change the device number this file is being stored with.
+ */
+ dpt->trunc_bits = trunc_bits;
+ dpt->dev = lastdev;
+ dpt->fow = pt->list;
+ pt->list = dpt;
+ arcn->sb.st_dev = lastdev;
+ arcn->sb.st_ino = nino;
+ return(0);
+
+ bad:
+ paxwarn(1, "Unable to fix truncated inode/device field when storing %s",
+ arcn->name);
+ paxwarn(0, "Archive may create improper hard links when extracted");
+ return(0);
+}
+
+/*
+ * directory access/mod time reset table routines (for directories READ by pax)
+ *
+ * The pax -t flag requires that access times of archive files be the same
+ * before being read by pax. For regular files, access time is restored after
+ * the file has been copied. This database provides the same functionality for
+ * directories read during file tree traversal. Restoring directory access time
+ * is more complex than files since directories may be read several times until
+ * all the descendants in their subtree are visited by fts. Directory access
+ * and modification times are stored during the fts pre-order visit (done
+ * before any descendants in the subtree are visited) and restored after the
+ * fts post-order visit (after all the descendants have been visited). In the
+ * case of premature exit from a subtree (like from the effects of -n), any
+ * directory entries left in this database are reset during final cleanup
+ * operations of pax. Entries are hashed by inode number for fast lookup.
+ */
+
+/*
+ * atdir_start()
+ * create the directory access time database for directories READ by pax.
+ * Return:
+ * 0 is created ok, -1 otherwise.
+ */
+
+int
+atdir_start(void)
+{
+ if (atab != NULL)
+ return(0);
+ if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) {
+ paxwarn(1,"Cannot allocate space for directory access time table");
+ return(-1);
+ }
+ return(0);
+}
+
+
+/*
+ * atdir_end()
+ * walk through the directory access time table and reset the access time
+ * of any directory who still has an entry left in the database. These
+ * entries are for directories READ by pax
+ */
+
+void
+atdir_end(void)
+{
+ ATDIR *pt;
+ int i;
+
+ if (atab == NULL)
+ return;
+ /*
+ * for each non-empty hash table entry reset all the directories
+ * chained there.
+ */
+ for (i = 0; i < A_TAB_SZ; ++i) {
+ if ((pt = atab[i]) == NULL)
+ continue;
+ /*
+ * remember to force the times, set_ftime() looks at pmtime
+ * and patime, which only applies to things CREATED by pax,
+ * not read by pax. Read time reset is controlled by -t.
+ */
+ for (; pt != NULL; pt = pt->fow)
+ set_ftime(pt->name, pt->mtime, pt->atime, 1);
+ }
+}
+
+/*
+ * add_atdir()
+ * add a directory to the directory access time table. Table is hashed
+ * and chained by inode number. This is for directories READ by pax
+ */
+
+void
+add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime)
+{
+ ATDIR *pt;
+ u_int indx;
+
+ if (atab == NULL)
+ return;
+
+ /*
+ * make sure this directory is not already in the table, if so just
+ * return (the older entry always has the correct time). The only
+ * way this will happen is when the same subtree can be traversed by
+ * different args to pax and the -n option is aborting fts out of a
+ * subtree before all the post-order visits have been made.
+ */
+ indx = ((unsigned)ino) % A_TAB_SZ;
+ if ((pt = atab[indx]) != NULL) {
+ while (pt != NULL) {
+ if ((pt->ino == ino) && (pt->dev == dev))
+ break;
+ pt = pt->fow;
+ }
+
+ /*
+ * oops, already there. Leave it alone.
+ */
+ if (pt != NULL)
+ return;
+ }
+
+ /*
+ * add it to the front of the hash chain
+ */
+ if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) {
+ if ((pt->name = strdup(fname)) != NULL) {
+ pt->dev = dev;
+ pt->ino = ino;
+ pt->mtime = mtime;
+ pt->atime = atime;
+ pt->fow = atab[indx];
+ atab[indx] = pt;
+ return;
+ }
+ (void)free((char *)pt);
+ }
+
+ paxwarn(1, "Directory access time reset table ran out of memory");
+ return;
+}
+
+/*
+ * get_atdir()
+ * look up a directory by inode and device number to obtain the access
+ * and modification time you want to set to. If found, the modification
+ * and access time parameters are set and the entry is removed from the
+ * table (as it is no longer needed). These are for directories READ by
+ * pax
+ * Return:
+ * 0 if found, -1 if not found.
+ */
+
+int
+get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime)
+{
+ ATDIR *pt;
+ ATDIR **ppt;
+ u_int indx;
+
+ if (atab == NULL)
+ return(-1);
+ /*
+ * hash by inode and search the chain for an inode and device match
+ */
+ indx = ((unsigned)ino) % A_TAB_SZ;
+ if ((pt = atab[indx]) == NULL)
+ return(-1);
+
+ ppt = &(atab[indx]);
+ while (pt != NULL) {
+ if ((pt->ino == ino) && (pt->dev == dev))
+ break;
+ /*
+ * no match, go to next one
+ */
+ ppt = &(pt->fow);
+ pt = pt->fow;
+ }
+
+ /*
+ * return if we did not find it.
+ */
+ if (pt == NULL)
+ return(-1);
+
+ /*
+ * found it. return the times and remove the entry from the table.
+ */
+ *ppt = pt->fow;
+ *mtime = pt->mtime;
+ *atime = pt->atime;
+ (void)free((char *)pt->name);
+ (void)free((char *)pt);
+ return(0);
+}
+
+/*
+ * directory access mode and time storage routines (for directories CREATED
+ * by pax).
+ *
+ * Pax requires that extracted directories, by default, have their access/mod
+ * times and permissions set to the values specified in the archive. During the
+ * actions of extracting (and creating the destination subtree during -rw copy)
+ * directories extracted may be modified after being created. Even worse is
+ * that these directories may have been created with file permissions which
+ * prohibits any descendants of these directories from being extracted. When
+ * directories are created by pax, access rights may be added to permit the
+ * creation of files in their subtree. Every time pax creates a directory, the
+ * times and file permissions specified by the archive are stored. After all
+ * files have been extracted (or copied), these directories have their times
+ * and file modes reset to the stored values. The directory info is restored in
+ * reverse order as entries were added to the data file from root to leaf. To
+ * restore atime properly, we must go backwards. The data file consists of
+ * records with two parts, the file name followed by a DIRDATA trailer. The
+ * fixed sized trailer contains the size of the name plus the off_t location in
+ * the file. To restore we work backwards through the file reading the trailer
+ * then the file name.
+ */
+
+/*
+ * dir_start()
+ * set up the directory time and file mode storage for directories CREATED
+ * by pax.
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+dir_start(void)
+{
+ if (dirp != NULL)
+ return(0);
+
+ dirsize = DIRP_SIZE;
+ if ((dirp = calloc(dirsize, sizeof(DIRDATA))) == NULL) {
+ paxwarn(1, "Unable to allocate memory for directory times");
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * add_dir()
+ * add the mode and times for a newly CREATED directory
+ * name is name of the directory, psb the stat buffer with the data in it,
+ * frc_mode is a flag that says whether to force the setting of the mode
+ * (ignoring the user set values for preserving file mode). Frc_mode is
+ * for the case where we created a file and found that the resulting
+ * directory was not writeable and the user asked for file modes to NOT
+ * be preserved. (we have to preserve what was created by default, so we
+ * have to force the setting at the end. this is stated explicitly in the
+ * pax spec)
+ */
+
+void
+add_dir(char *name, size_t nlen, struct stat *psb, int frc_mode)
+{
+ DIRDATA *dblk;
+ char realname[MAXPATHLEN], *rp;
+
+ if (dirp == NULL)
+ return;
+
+ if (havechd && *name != '/') {
+ if ((rp = realpath(name, realname)) == NULL) {
+ paxwarn(1, "Cannot canonicalize %s", name);
+ return;
+ }
+ name = rp;
+ }
+ if (dircnt == dirsize) {
+ dblk = realloc(dirp, 2 * dirsize * sizeof(DIRDATA));
+ if (dblk == NULL) {
+ paxwarn(1, "Unable to store mode and times for created"
+ " directory: %s", name);
+ return;
+ }
+ dirp = dblk;
+ dirsize *= 2;
+ }
+ dblk = &dirp[dircnt];
+ if ((dblk->name = strdup(name)) == NULL) {
+ paxwarn(1, "Unable to store mode and times for created"
+ " directory: %s", name);
+ return;
+ }
+ dblk->mode = psb->st_mode & 0xffff;
+ dblk->mtime = psb->st_mtime;
+ dblk->atime = psb->st_atime;
+ dblk->frc_mode = frc_mode;
+ ++dircnt;
+}
+
+/*
+ * proc_dir()
+ * process all file modes and times stored for directories CREATED
+ * by pax
+ */
+
+void
+proc_dir(void)
+{
+ DIRDATA *dblk;
+ long cnt;
+
+ if (dirp == NULL)
+ return;
+ /*
+ * read backwards through the file and process each directory
+ */
+ cnt = dircnt;
+ while (--cnt >= 0) {
+ /*
+ * frc_mode set, make sure we set the file modes even if
+ * the user didn't ask for it (see file_subs.c for more info)
+ */
+ dblk = &dirp[cnt];
+ if (pmode || dblk->frc_mode)
+ set_pmode(dblk->name, dblk->mode);
+ if (patime || pmtime)
+ set_ftime(dblk->name, dblk->mtime, dblk->atime, 0);
+ free(dblk->name);
+ }
+
+ free(dirp);
+ dirp = NULL;
+ dircnt = 0;
+}
+
+/*
+ * database independent routines
+ */
+
+/*
+ * st_hash()
+ * hashes filenames to a u_int for hashing into a table. Looks at the tail
+ * end of file, as this provides far better distribution than any other
+ * part of the name. For performance reasons we only care about the last
+ * MAXKEYLEN chars (should be at LEAST large enough to pick off the file
+ * name). Was tested on 500,000 name file tree traversal from the root
+ * and gave almost a perfectly uniform distribution of keys when used with
+ * prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int)
+ * chars at a time and pads with 0 for last addition.
+ * Return:
+ * the hash value of the string MOD (%) the table size.
+ */
+
+u_int
+st_hash(char *name, int len, int tabsz)
+{
+ char *pt;
+ char *dest;
+ char *end;
+ int i;
+ u_int key = 0;
+ int steps;
+ int res;
+ u_int val = 0;
+
+ /*
+ * only look at the tail up to MAXKEYLEN, we do not need to waste
+ * time here (remember these are pathnames, the tail is what will
+ * spread out the keys)
+ */
+ if (len > MAXKEYLEN) {
+ pt = &(name[len - MAXKEYLEN]);
+ len = MAXKEYLEN;
+ } else
+ pt = name;
+
+ /*
+ * calculate the number of u_int size steps in the string and if
+ * there is a runt to deal with
+ */
+ steps = len/sizeof(u_int);
+ res = len % sizeof(u_int);
+
+ /*
+ * add up the value of the string in unsigned integer sized pieces
+ * too bad we cannot have unsigned int aligned strings, then we
+ * could avoid the expensive copy.
+ */
+ for (i = 0; i < steps; ++i) {
+ end = pt + sizeof(u_int);
+ dest = (char *)&val;
+ while (pt < end)
+ *dest++ = *pt++;
+ key += val;
+ }
+
+ /*
+ * add in the runt padded with zero to the right
+ */
+ if (res) {
+ val = 0;
+ end = pt + res;
+ dest = (char *)&val;
+ while (pt < end)
+ *dest++ = *pt++;
+ key += val;
+ }
+
+ /*
+ * return the result mod the table size
+ */
+ return(key % tabsz);
+}
diff --git a/file_cmds/pax/tables.h b/file_cmds/pax/tables.h
new file mode 100644
index 0000000..3b1970f
--- /dev/null
+++ b/file_cmds/pax/tables.h
@@ -0,0 +1,175 @@
+/* $OpenBSD: tables.h,v 1.8 2006/08/05 23:05:13 ray Exp $ */
+/* $NetBSD: tables.h,v 1.3 1995/03/21 09:07:47 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tables.h 8.1 (Berkeley) 5/31/93
+ */
+
+#ifndef _TABLES_H_
+#define _TABLES_H_
+
+/*
+ * data structures and constants used by the different databases kept by pax
+ */
+
+/*
+ * Hash Table Sizes MUST BE PRIME, if set too small performance suffers.
+ * Probably safe to expect 500000 inodes per tape. Assuming good key
+ * distribution (inodes) chains of under 50 long (worst case) is ok.
+ */
+#define L_TAB_SZ 2503 /* hard link hash table size */
+#define F_TAB_SZ 50503 /* file time hash table size */
+#define N_TAB_SZ 541 /* interactive rename hash table */
+#define D_TAB_SZ 317 /* unique device mapping table */
+#define A_TAB_SZ 317 /* ftree dir access time reset table */
+#define MAXKEYLEN 64 /* max number of chars for hash */
+#define DIRP_SIZE 64 /* initial size of created dir table */
+
+/*
+ * file hard link structure (hashed by dev/ino and chained) used to find the
+ * hard links in a file system or with some archive formats (cpio)
+ */
+typedef struct hrdlnk {
+ char *name; /* name of first file seen with this ino/dev */
+ dev_t dev; /* files device number */
+ ino_t ino; /* files inode number */
+ u_long nlink; /* expected link count */
+ struct hrdlnk *fow;
+} HRDLNK;
+
+/*
+ * Archive write update file time table (the -u, -C flag), hashed by filename.
+ * Filenames are stored in a scratch file at seek offset into the file. The
+ * file time (mod time) and the file name length (for a quick check) are
+ * stored in a hash table node. We were forced to use a scratch file because
+ * with -u, the mtime for every node in the archive must always be available
+ * to compare against (and this data can get REALLY large with big archives).
+ * By being careful to read only when we have a good chance of a match, the
+ * performance loss is not measurable (and the size of the archive we can
+ * handle is greatly increased).
+ */
+typedef struct ftm {
+ int namelen; /* file name length */
+ time_t mtime; /* files last modification time */
+ off_t seek; /* location in scratch file */
+ struct ftm *fow;
+} FTM;
+
+/*
+ * Interactive rename table (-i flag), hashed by orig filename.
+ * We assume this will not be a large table as this mapping data can only be
+ * obtained through interactive input by the user. Nobody is going to type in
+ * changes for 500000 files? We use chaining to resolve collisions.
+ */
+
+typedef struct namt {
+ char *oname; /* old name */
+ char *nname; /* new name typed in by the user */
+ struct namt *fow;
+} NAMT;
+
+/*
+ * Unique device mapping tables. Some protocols (e.g. cpio) require that the
+ * <c_dev,c_ino> pair will uniquely identify a file in an archive unless they
+ * are links to the same file. Appending to archives can break this. For those
+ * protocols that have this requirement we map c_dev to a unique value not seen
+ * in the archive when we append. We also try to handle inode truncation with
+ * this table. (When the inode field in the archive header are too small, we
+ * remap the dev on writes to remove accidental collisions).
+ *
+ * The list is hashed by device number using chain collision resolution. Off of
+ * each DEVT are linked the various remaps for this device based on those bits
+ * in the inode which were truncated. For example if we are just remapping to
+ * avoid a device number during an update append, off the DEVT we would have
+ * only a single DLIST that has a truncation id of 0 (no inode bits were
+ * stripped for this device so far). When we spot inode truncation we create
+ * a new mapping based on the set of bits in the inode which were stripped off.
+ * so if the top four bits of the inode are stripped and they have a pattern of
+ * 0110...... (where . are those bits not truncated) we would have a mapping
+ * assigned for all inodes that has the same 0110.... pattern (with this dev
+ * number of course). This keeps the mapping sparse and should be able to store
+ * close to the limit of files which can be represented by the optimal
+ * combination of dev and inode bits, and without creating a fouled up archive.
+ * Note we also remap truncated devs in the same way (an exercise for the
+ * dedicated reader; always wanted to say that...:)
+ */
+
+typedef struct devt {
+ dev_t dev; /* the orig device number we now have to map */
+ struct devt *fow; /* new device map list */
+ struct dlist *list; /* map list based on inode truncation bits */
+} DEVT;
+
+typedef struct dlist {
+ ino_t trunc_bits; /* truncation pattern for a specific map */
+ dev_t dev; /* the new device id we use */
+ struct dlist *fow;
+} DLIST;
+
+/*
+ * ftree directory access time reset table. When we are done with a
+ * subtree we reset the access and mod time of the directory when the tflag is
+ * set. Not really explicitly specified in the pax spec, but easy and fast to
+ * do (and this may have even been intended in the spec, it is not clear).
+ * table is hashed by inode with chaining.
+ */
+
+typedef struct atdir {
+ char *name; /* name of directory to reset */
+ dev_t dev; /* dev and inode for fast lookup */
+ ino_t ino;
+ time_t mtime; /* access and mod time to reset to */
+ time_t atime;
+ struct atdir *fow;
+} ATDIR;
+
+/*
+ * created directory time and mode storage entry. After pax is finished during
+ * extraction or copy, we must reset directory access modes and times that
+ * may have been modified after creation (they no longer have the specified
+ * times and/or modes). We must reset time in the reverse order of creation,
+ * because entries are added from the top of the file tree to the bottom.
+ * We MUST reset times from leaf to root (it will not work the other
+ * direction).
+ */
+
+typedef struct dirdata {
+ char *name; /* file name */
+ time_t mtime; /* mtime to set */
+ time_t atime; /* atime to set */
+ u_int16_t mode; /* file mode to restore */
+ u_int16_t frc_mode; /* do we force mode settings? */
+} DIRDATA;
+
+#endif /* _TABLES_H_ */
diff --git a/file_cmds/pax/tar.c b/file_cmds/pax/tar.c
new file mode 100644
index 0000000..360541f
--- /dev/null
+++ b/file_cmds/pax/tar.c
@@ -0,0 +1,1206 @@
+/* $OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $ */
+/* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "extern.h"
+#include "tar.h"
+
+/*
+ * Routines for reading, writing and header identify of various versions of tar
+ */
+
+static size_t expandname(char *, size_t, char **, const char *, size_t);
+static u_long tar_chksm(char *, int);
+static char *name_split(char *, int);
+static int ul_oct(u_long, char *, int, int);
+#ifndef LONG_OFF_T
+static int uqd_oct(u_quad_t, char *, int, int);
+#endif
+
+static uid_t uid_nobody;
+static uid_t uid_warn;
+static gid_t gid_nobody;
+static gid_t gid_warn;
+
+/*
+ * Routines common to all versions of tar
+ */
+
+static int tar_nodir; /* do not write dirs under old tar */
+char *gnu_name_string; /* GNU ././@LongLink hackery name */
+char *gnu_link_string; /* GNU ././@LongLink hackery link */
+
+/*
+ * tar_endwr()
+ * add the tar trailer of two null blocks
+ * Return:
+ * 0 if ok, -1 otherwise (what wr_skip returns)
+ */
+
+int
+tar_endwr(void)
+{
+ return(wr_skip((off_t)(NULLCNT*BLKMULT)));
+}
+
+/*
+ * tar_endrd()
+ * no cleanup needed here, just return size of trailer (for append)
+ * Return:
+ * size of trailer (2 * BLKMULT)
+ */
+
+off_t
+tar_endrd(void)
+{
+ return((off_t)(NULLCNT*BLKMULT));
+}
+
+/*
+ * tar_trail()
+ * Called to determine if a header block is a valid trailer. We are passed
+ * the block, the in_sync flag (which tells us we are in resync mode;
+ * looking for a valid header), and cnt (which starts at zero) which is
+ * used to count the number of empty blocks we have seen so far.
+ * Return:
+ * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block
+ * could never contain a header.
+ */
+
+int
+tar_trail(ARCHD *ignore, char *buf, int in_resync, int *cnt)
+{
+ int i;
+
+ /*
+ * look for all zero, trailer is two consecutive blocks of zero
+ */
+ for (i = 0; i < BLKMULT; ++i) {
+ if (buf[i] != '\0')
+ break;
+ }
+
+ /*
+ * if not all zero it is not a trailer, but MIGHT be a header.
+ */
+ if (i != BLKMULT)
+ return(-1);
+
+ /*
+ * When given a zero block, we must be careful!
+ * If we are not in resync mode, check for the trailer. Have to watch
+ * out that we do not mis-identify file data as the trailer, so we do
+ * NOT try to id a trailer during resync mode. During resync mode we
+ * might as well throw this block out since a valid header can NEVER be
+ * a block of all 0 (we must have a valid file name).
+ */
+ if (!in_resync && (++*cnt >= NULLCNT))
+ return(0);
+ return(1);
+}
+
+/*
+ * ul_oct()
+ * convert an unsigned long to an octal string. many oddball field
+ * termination characters are used by the various versions of tar in the
+ * different fields. term selects which kind to use. str is '0' padded
+ * at the front to len. we are unable to use only one format as many old
+ * tar readers are very cranky about this.
+ * Return:
+ * 0 if the number fit into the string, -1 otherwise
+ */
+
+static int
+ul_oct(u_long val, char *str, int len, int term)
+{
+ char *pt;
+
+ /*
+ * term selects the appropriate character(s) for the end of the string
+ */
+ pt = str + len - 1;
+ switch (term) {
+ case 3:
+ *pt-- = '\0';
+ break;
+ case 2:
+ *pt-- = ' ';
+ *pt-- = '\0';
+ break;
+ case 1:
+ *pt-- = ' ';
+ break;
+ case 0:
+ default:
+ *pt-- = '\0';
+ *pt-- = ' ';
+ break;
+ }
+
+ /*
+ * convert and blank pad if there is space
+ */
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = val >> 3) == (u_long)0)
+ break;
+ }
+
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_long)0)
+ return(-1);
+ return(0);
+}
+
+#ifndef LONG_OFF_T
+/*
+ * uqd_oct()
+ * convert an u_quad_t to an octal string. one of many oddball field
+ * termination characters are used by the various versions of tar in the
+ * different fields. term selects which kind to use. str is '0' padded
+ * at the front to len. we are unable to use only one format as many old
+ * tar readers are very cranky about this.
+ * Return:
+ * 0 if the number fit into the string, -1 otherwise
+ */
+
+static int
+uqd_oct(u_quad_t val, char *str, int len, int term)
+{
+ char *pt;
+
+ /*
+ * term selects the appropriate character(s) for the end of the string
+ */
+ pt = str + len - 1;
+ switch (term) {
+ case 3:
+ *pt-- = '\0';
+ break;
+ case 2:
+ *pt-- = ' ';
+ *pt-- = '\0';
+ break;
+ case 1:
+ *pt-- = ' ';
+ break;
+ case 0:
+ default:
+ *pt-- = '\0';
+ *pt-- = ' ';
+ break;
+ }
+
+ /*
+ * convert and blank pad if there is space
+ */
+ while (pt >= str) {
+ *pt-- = '0' + (char)(val & 0x7);
+ if ((val = val >> 3) == 0)
+ break;
+ }
+
+ while (pt >= str)
+ *pt-- = '0';
+ if (val != (u_quad_t)0)
+ return(-1);
+ return(0);
+}
+#endif
+
+/*
+ * tar_chksm()
+ * calculate the checksum for a tar block counting the checksum field as
+ * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks).
+ * NOTE: we use len to short circuit summing 0's on write since we ALWAYS
+ * pad headers with 0.
+ * Return:
+ * unsigned long checksum
+ */
+
+static u_long
+tar_chksm(char *blk, int len)
+{
+ char *stop;
+ char *pt;
+ u_long chksm = BLNKSUM; /* initial value is checksum field sum */
+
+ /*
+ * add the part of the block before the checksum field
+ */
+ pt = blk;
+ stop = blk + CHK_OFFSET;
+ while (pt < stop)
+ chksm += (u_long)(*pt++ & 0xff);
+ /*
+ * move past the checksum field and keep going, spec counts the
+ * checksum field as the sum of 8 blanks (which is pre-computed as
+ * BLNKSUM).
+ * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding
+ * starts, no point in summing zero's)
+ */
+ pt += CHK_LEN;
+ stop = blk + len;
+ while (pt < stop)
+ chksm += (u_long)(*pt++ & 0xff);
+ return(chksm);
+}
+
+/*
+ * Routines for old BSD style tar (also made portable to sysV tar)
+ */
+
+/*
+ * tar_id()
+ * determine if a block given to us is a valid tar header (and not a USTAR
+ * header). We have to be on the lookout for those pesky blocks of all
+ * zero's.
+ * Return:
+ * 0 if a tar header, -1 otherwise
+ */
+
+int
+tar_id(char *blk, int size)
+{
+ HD_TAR *hd;
+ HD_USTAR *uhd;
+
+ if (size < BLKMULT)
+ return(-1);
+ hd = (HD_TAR *)blk;
+ uhd = (HD_USTAR *)blk;
+
+ /*
+ * check for block of zero's first, a simple and fast test, then make
+ * sure this is not a ustar header by looking for the ustar magic
+ * cookie. We should use TMAGLEN, but some USTAR archive programs are
+ * wrong and create archives missing the \0. Last we check the
+ * checksum. If this is ok we have to assume it is a valid header.
+ */
+ if (hd->name[0] == '\0')
+ return(-1);
+ if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0)
+ return(-1);
+ if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
+ return(-1);
+ force_one_volume = 1;
+ return(0);
+}
+
+/*
+ * tar_opt()
+ * handle tar format specific -o options
+ * Return:
+ * 0 if ok -1 otherwise
+ */
+
+int
+tar_opt(void)
+{
+ OPLIST *opt;
+
+ while ((opt = opt_next()) != NULL) {
+ if (strcmp(opt->name, TAR_OPTION) ||
+ strcmp(opt->value, TAR_NODIR)) {
+ paxwarn(1, "Unknown tar format -o option/value pair %s=%s",
+ opt->name, opt->value);
+ paxwarn(1,"%s=%s is the only supported tar format option",
+ TAR_OPTION, TAR_NODIR);
+ return(-1);
+ }
+
+ /*
+ * we only support one option, and only when writing
+ */
+ if ((act != APPND) && (act != ARCHIVE)) {
+ paxwarn(1, "%s=%s is only supported when writing.",
+ opt->name, opt->value);
+ return(-1);
+ }
+ tar_nodir = 1;
+ }
+ return(0);
+}
+
+
+/*
+ * tar_rd()
+ * extract the values out of block already determined to be a tar header.
+ * store the values in the ARCHD parameter.
+ * Return:
+ * 0
+ */
+
+int
+tar_rd(ARCHD *arcn, char *buf)
+{
+ HD_TAR *hd;
+ char *pt;
+
+ /*
+ * we only get proper sized buffers passed to us
+ */
+ if (tar_id(buf, BLKMULT) < 0)
+ return(-1);
+ memset(arcn, 0, sizeof(*arcn));
+ arcn->org_name = arcn->name;
+ arcn->sb.st_nlink = 1;
+
+ /*
+ * copy out the name and values in the stat buffer
+ */
+ hd = (HD_TAR *)buf;
+ if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) {
+ arcn->nlen = expandname(arcn->name, sizeof(arcn->name),
+ &gnu_name_string, hd->name, sizeof(hd->name));
+ arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
+ &gnu_link_string, hd->linkname, sizeof(hd->linkname));
+ }
+ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) &
+ 0xfff);
+ arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
+ arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
+#ifdef LONG_OFF_T
+ arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#else
+ arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
+#endif
+ arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
+ arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+
+ /*
+ * have to look at the last character, it may be a '/' and that is used
+ * to encode this as a directory
+ */
+ pt = &(arcn->name[arcn->nlen - 1]);
+ arcn->pad = 0;
+ arcn->skip = 0;
+ switch (hd->linkflag) {
+ case SYMTYPE:
+ /*
+ * symbolic link, need to get the link name and set the type in
+ * the st_mode so -v printing will look correct.
+ */
+ arcn->type = PAX_SLK;
+ arcn->sb.st_mode |= S_IFLNK;
+ arcn->ln_nlen = strlcpy(arcn->ln_name, hd->linkname, sizeof(arcn->ln_name));
+ break;
+ case LNKTYPE:
+ /*
+ * hard link, need to get the link name, set the type in the
+ * st_mode and st_nlink so -v printing will look better.
+ */
+ arcn->type = PAX_HLK;
+ arcn->sb.st_nlink = 2;
+ arcn->ln_nlen = strlcpy(arcn->ln_name, hd->linkname, sizeof(arcn->ln_name));
+
+ /*
+ * no idea of what type this thing really points at, but
+ * we set something for printing only.
+ */
+ arcn->sb.st_mode |= S_IFREG;
+ break;
+ case LONGLINKTYPE:
+ case LONGNAMETYPE:
+ /*
+ * GNU long link/file; we tag these here and let the
+ * pax internals deal with it -- too ugly otherwise.
+ */
+ arcn->type =
+ hd->linkflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ break;
+ case DIRTYPE:
+ /*
+ * It is a directory, set the mode for -v printing
+ */
+ arcn->type = PAX_DIR;
+ arcn->sb.st_mode |= S_IFDIR;
+ arcn->sb.st_nlink = 2;
+ break;
+ case AREGTYPE:
+ case REGTYPE:
+ default:
+ /*
+ * If we have a trailing / this is a directory and NOT a file.
+ */
+ arcn->ln_name[0] = '\0';
+ arcn->ln_nlen = 0;
+ if (*pt == '/') {
+ /*
+ * it is a directory, set the mode for -v printing
+ */
+ arcn->type = PAX_DIR;
+ arcn->sb.st_mode |= S_IFDIR;
+ arcn->sb.st_nlink = 2;
+ } else {
+ /*
+ * have a file that will be followed by data. Set the
+ * skip value to the size field and calculate the size
+ * of the padding.
+ */
+ arcn->type = PAX_REG;
+ arcn->sb.st_mode |= S_IFREG;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ }
+ break;
+ }
+
+ /*
+ * strip off any trailing slash.
+ */
+ if (*pt == '/') {
+ *pt = '\0';
+ --arcn->nlen;
+ }
+ return(0);
+}
+
+/*
+ * tar_wr()
+ * write a tar header for the file specified in the ARCHD to the archive.
+ * Have to check for file types that cannot be stored and file names that
+ * are too long. Be careful of the term (last arg) to ul_oct, each field
+ * of tar has it own spec for the termination character(s).
+ * ASSUMED: space after header in header block is zero filled
+ * Return:
+ * 0 if file has data to be written after the header, 1 if file has NO
+ * data to write after the header, -1 if archive write failed
+ */
+
+int
+tar_wr(ARCHD *arcn)
+{
+ HD_TAR *hd;
+ int len;
+ HD_TAR hdblk;
+
+ /*
+ * check for those file system types which tar cannot store
+ */
+ switch (arcn->type) {
+ case PAX_DIR:
+ /*
+ * user asked that dirs not be written to the archive
+ */
+ if (tar_nodir)
+ return(1);
+ break;
+ case PAX_CHR:
+ paxwarn(1, "Tar cannot archive a character device %s",
+ arcn->org_name);
+ return(1);
+ case PAX_BLK:
+ paxwarn(1, "Tar cannot archive a block device %s", arcn->org_name);
+ return(1);
+ case PAX_SCK:
+ paxwarn(1, "Tar cannot archive a socket %s", arcn->org_name);
+ return(1);
+ case PAX_FIF:
+ paxwarn(1, "Tar cannot archive a fifo %s", arcn->org_name);
+ return(1);
+ case PAX_SLK:
+ case PAX_HLK:
+ case PAX_HRG:
+ if (arcn->ln_nlen >= sizeof(hd->linkname)) {
+ paxwarn(1, "Link name too long for tar %s",
+ arcn->ln_name);
+ return(1);
+ }
+ break;
+ case PAX_REG:
+ case PAX_CTG:
+ default:
+ break;
+ }
+
+ /*
+ * check file name len, remember extra char for dirs (the / at the end)
+ */
+ len = arcn->nlen;
+ if (arcn->type == PAX_DIR)
+ ++len;
+ if (len >= sizeof(hd->name)) {
+ paxwarn(1, "File name too long for tar %s", arcn->name);
+ return(1);
+ }
+
+ /*
+ * Copy the data out of the ARCHD into the tar header based on the type
+ * of the file. Remember, many tar readers want all fields to be
+ * padded with zero so we zero the header first. We then set the
+ * linkflag field (type), the linkname, the size, and set the padding
+ * (if any) to be added after the file data (0 for all other types,
+ * as they only have a header).
+ */
+ memset(&hdblk, 0, sizeof(hdblk));
+ hd = (HD_TAR *)&hdblk;
+ strlcpy(hd->name, arcn->name, sizeof(hd->name));
+ arcn->pad = 0;
+
+ if (arcn->type == PAX_DIR) {
+ /*
+ * directories are the same as files, except have a filename
+ * that ends with a /, we add the slash here. No data follows
+ * dirs, so no pad.
+ */
+ hd->linkflag = AREGTYPE;
+ hd->name[len-1] = '/';
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
+ goto out;
+ } else if (arcn->type == PAX_SLK) {
+ /*
+ * no data follows this file, so no pad
+ */
+ hd->linkflag = SYMTYPE;
+ strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
+ goto out;
+ } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
+ /*
+ * no data follows this file, so no pad
+ */
+ hd->linkflag = LNKTYPE;
+ strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
+ goto out;
+ } else {
+ /*
+ * data follows this file, so set the pad
+ */
+ hd->linkflag = AREGTYPE;
+# ifdef LONG_OFF_T
+ if (ul_oct((u_long)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), 1)) {
+# else
+ if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), 1)) {
+# endif
+ paxwarn(1,"File is too large for tar %s", arcn->org_name);
+ return(1);
+ }
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ }
+
+ /*
+ * copy those fields that are independent of the type
+ */
+ if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
+ ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
+ ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) ||
+ ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1))
+ goto out;
+
+ /*
+ * calculate and add the checksum, then write the header. A return of
+ * 0 tells the caller to now write the file data, 1 says no data needs
+ * to be written
+ */
+ if (ul_oct(tar_chksm((char *)&hdblk, sizeof(HD_TAR)), hd->chksum,
+ sizeof(hd->chksum), 3))
+ goto out;
+ if (wr_rdbuf((char *)&hdblk, sizeof(HD_TAR)) < 0)
+ return(-1);
+ if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0)
+ return(-1);
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
+ return(0);
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1, "Tar header field is too small for %s", arcn->org_name);
+ return(1);
+}
+
+/*
+ * Routines for POSIX ustar
+ */
+
+/*
+ * ustar_strd()
+ * initialization for ustar read
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+ustar_strd(void)
+{
+ if ((usrtb_start() < 0) || (grptb_start() < 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * ustar_stwr()
+ * initialization for ustar write
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+
+int
+ustar_stwr(void)
+{
+ if ((uidtb_start() < 0) || (gidtb_start() < 0))
+ return(-1);
+ return(0);
+}
+
+/*
+ * ustar_id()
+ * determine if a block given to us is a valid ustar header. We have to
+ * be on the lookout for those pesky blocks of all zero's
+ * Return:
+ * 0 if a ustar header, -1 otherwise
+ */
+
+int
+ustar_id(char *blk, int size)
+{
+ HD_USTAR *hd;
+
+ if (size < BLKMULT)
+ return(-1);
+ hd = (HD_USTAR *)blk;
+
+ /*
+ * check for block of zero's first, a simple and fast test then check
+ * ustar magic cookie. We should use TMAGLEN, but some USTAR archive
+ * programs are fouled up and create archives missing the \0. Last we
+ * check the checksum. If ok we have to assume it is a valid header.
+ */
+ if (hd->name[0] == '\0')
+ return(-1);
+ if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
+ return(-1);
+ if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
+ return(-1);
+ return(0);
+}
+
+/*
+ * ustar_rd()
+ * extract the values out of block already determined to be a ustar header.
+ * store the values in the ARCHD parameter.
+ * Return:
+ * 0
+ */
+
+int
+ustar_rd(ARCHD *arcn, char *buf)
+{
+ HD_USTAR *hd;
+ char *dest;
+ int cnt = 0;
+ dev_t devmajor;
+ dev_t devminor;
+
+ /*
+ * we only get proper sized buffers
+ */
+ if (ustar_id(buf, BLKMULT) < 0)
+ return(-1);
+ memset(arcn, 0, sizeof(*arcn));
+ arcn->org_name = arcn->name;
+ arcn->sb.st_nlink = 1;
+ hd = (HD_USTAR *)buf;
+
+ /*
+ * see if the filename is split into two parts. if, so joint the parts.
+ * we copy the prefix first and add a / between the prefix and name.
+ */
+ dest = arcn->name;
+ if (*(hd->prefix) != '\0') {
+ cnt = strlcpy(dest, hd->prefix, sizeof(arcn->name) - 1);
+ dest += cnt;
+ *dest++ = '/';
+ cnt++;
+ } else {
+ cnt = 0;
+ }
+
+ if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
+ arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt,
+ &gnu_name_string, hd->name, sizeof(hd->name));
+ arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
+ &gnu_link_string, hd->linkname, sizeof(hd->linkname));
+ }
+
+ /*
+ * follow the spec to the letter. we should only have mode bits, strip
+ * off all other crud we may be passed.
+ */
+ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) &
+ 0xfff);
+#ifdef LONG_OFF_T
+ arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#else
+ arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
+#endif
+ arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
+ arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+
+ /*
+ * If we can find the ascii names for gname and uname in the password
+ * and group files we will use the uid's and gid they bind. Otherwise
+ * we use the uid and gid values stored in the header. (This is what
+ * the POSIX spec wants).
+ */
+ hd->gname[sizeof(hd->gname) - 1] = '\0';
+ if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0)
+ arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
+ hd->uname[sizeof(hd->uname) - 1] = '\0';
+ if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0)
+ arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
+
+ /*
+ * set the defaults, these may be changed depending on the file type
+ */
+ arcn->pad = 0;
+ arcn->skip = 0;
+ arcn->sb.st_rdev = (dev_t)0;
+
+ /*
+ * set the mode and PAX type according to the typeflag in the header
+ */
+ switch (hd->typeflag) {
+ case FIFOTYPE:
+ arcn->type = PAX_FIF;
+ arcn->sb.st_mode |= S_IFIFO;
+ break;
+ case DIRTYPE:
+ arcn->type = PAX_DIR;
+ arcn->sb.st_mode |= S_IFDIR;
+ arcn->sb.st_nlink = 2;
+
+ /*
+ * Some programs that create ustar archives append a '/'
+ * to the pathname for directories. This clearly violates
+ * ustar specs, but we will silently strip it off anyway.
+ */
+ if (arcn->name[arcn->nlen - 1] == '/')
+ arcn->name[--arcn->nlen] = '\0';
+ break;
+ case BLKTYPE:
+ case CHRTYPE:
+ /*
+ * this type requires the rdev field to be set.
+ */
+ if (hd->typeflag == BLKTYPE) {
+ arcn->type = PAX_BLK;
+ arcn->sb.st_mode |= S_IFBLK;
+ } else {
+ arcn->type = PAX_CHR;
+ arcn->sb.st_mode |= S_IFCHR;
+ }
+ devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT);
+ devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT);
+ arcn->sb.st_rdev = TODEV(devmajor, devminor);
+ break;
+ case SYMTYPE:
+ case LNKTYPE:
+ if (hd->typeflag == SYMTYPE) {
+ arcn->type = PAX_SLK;
+ arcn->sb.st_mode |= S_IFLNK;
+ } else {
+ arcn->type = PAX_HLK;
+ /*
+ * so printing looks better
+ */
+ arcn->sb.st_mode |= S_IFREG;
+ arcn->sb.st_nlink = 2;
+ }
+ break;
+ case LONGLINKTYPE:
+ case LONGNAMETYPE:
+ /*
+ * GNU long link/file; we tag these here and let the
+ * pax internals deal with it -- too ugly otherwise.
+ */
+ arcn->type =
+ hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ break;
+ case CONTTYPE:
+ case AREGTYPE:
+ case REGTYPE:
+ default:
+ /*
+ * these types have file data that follows. Set the skip and
+ * pad fields.
+ */
+ arcn->type = PAX_REG;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+ arcn->skip = arcn->sb.st_size;
+ arcn->sb.st_mode |= S_IFREG;
+ break;
+ }
+ return(0);
+}
+
+/*
+ * ustar_wr()
+ * write a ustar header for the file specified in the ARCHD to the archive
+ * Have to check for file types that cannot be stored and file names that
+ * are too long. Be careful of the term (last arg) to ul_oct, we only use
+ * '\0' for the termination character (this is different than picky tar)
+ * ASSUMED: space after header in header block is zero filled
+ * Return:
+ * 0 if file has data to be written after the header, 1 if file has NO
+ * data to write after the header, -1 if archive write failed
+ */
+
+int
+ustar_wr(ARCHD *arcn)
+{
+ HD_USTAR *hd;
+ char *pt;
+ char hdblk[sizeof(HD_USTAR)];
+ mode_t mode12only;
+ int term_char=3; /* orignal setting */
+ term_char=1; /* To pass conformance tests 274, 301 */
+
+ /*
+ * check for those file system types ustar cannot store
+ */
+ if (arcn->type == PAX_SCK) {
+ paxwarn(1, "Ustar cannot archive a socket %s", arcn->org_name);
+ return(1);
+ }
+
+ /*
+ * check the length of the linkname
+ */
+ if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
+ (arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){
+ paxwarn(1, "Link name too long for ustar %s", arcn->ln_name);
+ /*
+ * Conformance: test pax:285 wants error code to be non-zero, and
+ * test tar:12 wants error code from pax to be 0
+ */
+ return(1);
+ }
+
+ /*
+ * split the path name into prefix and name fields (if needed). if
+ * pt != arcn->name, the name has to be split
+ */
+ if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
+ paxwarn(1, "File name too long for ustar %s", arcn->name);
+ return(1);
+ }
+
+ /*
+ * zero out the header so we don't have to worry about zero fill below
+ */
+ memset(hdblk, 0, sizeof(hdblk));
+ hd = (HD_USTAR *)hdblk;
+ arcn->pad = 0L;
+
+ /* To pass conformance tests 274/301, always set these fields to "zero" */
+ ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char);
+ ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char);
+
+ /*
+ * split the name, or zero out the prefix
+ */
+ if (pt != arcn->name) {
+ /*
+ * name was split, pt points at the / where the split is to
+ * occur, we remove the / and copy the first part to the prefix
+ */
+ *pt = '\0';
+ strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix));
+ *pt++ = '/';
+ }
+
+ /*
+ * copy the name part. this may be the whole path or the part after
+ * the prefix. both the name and prefix may fill the entire field.
+ */
+ if (strlen(pt) == sizeof(hd->name)) { /* must account for name just fits in buffer */
+ strncpy(hd->name, pt, sizeof(hd->name));
+ } else {
+ strlcpy(hd->name, pt, sizeof(hd->name));
+ }
+
+ /*
+ * set the fields in the header that are type dependent
+ */
+ switch (arcn->type) {
+ case PAX_DIR:
+ hd->typeflag = DIRTYPE;
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_CHR:
+ case PAX_BLK:
+ if (arcn->type == PAX_CHR)
+ hd->typeflag = CHRTYPE;
+ else
+ hd->typeflag = BLKTYPE;
+ if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
+ sizeof(hd->devmajor), term_char) ||
+ ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
+ sizeof(hd->devminor), term_char) ||
+ ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_FIF:
+ hd->typeflag = FIFOTYPE;
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_SLK:
+ case PAX_HLK:
+ case PAX_HRG:
+ if (arcn->type == PAX_SLK)
+ hd->typeflag = SYMTYPE;
+ else
+ hd->typeflag = LNKTYPE;
+ if (strlen(arcn->ln_name) == sizeof(hd->linkname)) { /* must account for name just fits in buffer */
+ strncpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ } else {
+ strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
+ }
+ if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char))
+ goto out;
+ break;
+ case PAX_REG:
+ case PAX_CTG:
+ default:
+ /*
+ * file data with this type, set the padding
+ */
+ if (arcn->type == PAX_CTG)
+ hd->typeflag = CONTTYPE;
+ else
+ hd->typeflag = REGTYPE;
+ arcn->pad = TAR_PAD(arcn->sb.st_size);
+# ifdef LONG_OFF_T
+ if (ul_oct((u_long)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), term_char)) {
+# else
+ if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size,
+ sizeof(hd->size), term_char)) {
+# endif
+ paxwarn(1,"File is too long for ustar %s",arcn->org_name);
+ return(1);
+ }
+ break;
+ }
+
+ strncpy(hd->magic, TMAGIC, TMAGLEN);
+ strncpy(hd->version, TVERSION, TVERSLEN);
+
+ /*
+ * set the remaining fields. Some versions want all 16 bits of mode
+ * we better humor them (they really do not meet spec though)....
+ */
+ if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), term_char)) {
+ if (uid_nobody == 0) {
+ if (uid_name("nobody", &uid_nobody) == -1)
+ goto out;
+ }
+ if (uid_warn != arcn->sb.st_uid) {
+ uid_warn = arcn->sb.st_uid;
+ paxwarn(1,
+ "Ustar header field is too small for uid %lu, "
+ "using nobody", (u_long)arcn->sb.st_uid);
+ }
+ if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), term_char))
+ goto out;
+ }
+ if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), term_char)) {
+ if (gid_nobody == 0) {
+ if (gid_name("nobody", &gid_nobody) == -1)
+ goto out;
+ }
+ if (gid_warn != arcn->sb.st_gid) {
+ gid_warn = arcn->sb.st_gid;
+ paxwarn(1,
+ "Ustar header field is too small for gid %lu, "
+ "using nobody", (u_long)arcn->sb.st_gid);
+ }
+ if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), term_char))
+ goto out;
+ }
+ /* However, Unix conformance tests do not like MORE than 12 mode bits:
+ remove all beyond (see definition of stat.st_mode structure) */
+ mode12only = ((u_long)arcn->sb.st_mode) & 0x00000fff;
+ if (ul_oct((u_long)mode12only, hd->mode, sizeof(hd->mode), term_char) ||
+ ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char))
+ goto out;
+ strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname));
+ strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname));
+
+ /*
+ * calculate and store the checksum write the header to the archive
+ * return 0 tells the caller to now write the file data, 1 says no data
+ * needs to be written
+ */
+ if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
+ sizeof(hd->chksum), term_char))
+ goto out;
+ if (wr_rdbuf((char *)&hdblk, sizeof(HD_USTAR)) < 0)
+ return(-1);
+ if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
+ return(-1);
+ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
+ return(0);
+ return(1);
+
+ out:
+ /*
+ * header field is out of range
+ */
+ paxwarn(1, "Ustar header field is too small for %s", arcn->org_name);
+ return(1);
+}
+
+/*
+ * name_split()
+ * see if the name has to be split for storage in a ustar header. We try
+ * to fit the entire name in the name field without splitting if we can.
+ * The split point is always at a /
+ * Return
+ * character pointer to split point (always the / that is to be removed
+ * if the split is not needed, the points is set to the start of the file
+ * name (it would violate the spec to split there). A NULL is returned if
+ * the file name is too long
+ */
+
+static char *
+name_split(char *name, int len)
+{
+ char *start;
+
+ /*
+ * check to see if the file name is small enough to fit in the name
+ * field. if so just return a pointer to the name.
+ * The strings can fill the complete name and prefix fields
+ * without a NUL terminator.
+ */
+ if (len <= TNMSZ)
+ return(name);
+ if (len > (TPFSZ + TNMSZ + 1))
+ return(NULL);
+
+ /*
+ * we start looking at the biggest sized piece that fits in the name
+ * field. We walk forward looking for a slash to split at. The idea is
+ * to find the biggest piece to fit in the name field (or the smallest
+ * prefix we can find) (the -1 is correct the biggest piece would
+ * include the slash between the two parts that gets thrown away)
+ */
+ start = name + len - TNMSZ - 1;
+ if ((*start == '/') && (start == name))
+ ++start; /* 101 byte paths with leading '/' are dinged otherwise */
+ while ((*start != '\0') && (*start != '/'))
+ ++start;
+
+ /*
+ * if we hit the end of the string, this name cannot be split, so we
+ * cannot store this file.
+ */
+ if (*start == '\0')
+ return(NULL);
+ len = start - name;
+
+ /*
+ * NOTE: /str where the length of str == TNMSZ can not be stored under
+ * the p1003.1-1990 spec for ustar. We could force a prefix of / and
+ * the file would then expand on extract to //str. The len == 0 below
+ * makes this special case follow the spec to the letter.
+ */
+ if ((len > TPFSZ) || (len == 0))
+ return(NULL);
+
+ /*
+ * ok have a split point, return it to the caller
+ */
+ return(start);
+}
+
+static size_t
+expandname(char *buf, size_t len, char **gnu_name, const char *name,
+ size_t name_len)
+{
+ size_t nlen;
+
+ if (*gnu_name) {
+ /* *gnu_name is NUL terminated */
+ if ((nlen = strlcpy(buf, *gnu_name, len)) >= len)
+ nlen = len - 1;
+ free(*gnu_name);
+ *gnu_name = NULL;
+ } else {
+ if (name_len < len) {
+ /* name may not be null terminated: it might be as big as the
+ field, so copy is limited to the max size of the header field */
+ if ((nlen = strlcpy(buf, name, name_len+1)) >= name_len+1)
+ nlen = name_len;
+ } else {
+ if ((nlen = strlcpy(buf, name, len)) >= len)
+ nlen = len - 1;
+ }
+ }
+ return(nlen);
+}
diff --git a/file_cmds/pax/tar.h b/file_cmds/pax/tar.h
new file mode 100644
index 0000000..a78813c
--- /dev/null
+++ b/file_cmds/pax/tar.h
@@ -0,0 +1,161 @@
+/* $OpenBSD: tar.h,v 1.7 2003/06/02 23:32:09 millert Exp $ */
+/* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tar.h 8.2 (Berkeley) 4/18/94
+ */
+
+#ifndef _TAR_H_
+#define _TAR_H_
+
+/*
+ * defines and data structures common to all tar formats
+ */
+#define CHK_LEN 8 /* length of checksum field */
+#define TNMSZ 100 /* size of name field */
+#ifdef _PAX_
+#define NULLCNT 2 /* number of null blocks in trailer */
+#define CHK_OFFSET 148 /* start of chksum field */
+#define BLNKSUM 256L /* sum of checksum field using ' ' */
+#endif /* _PAX_ */
+
+/*
+ * Values used in typeflag field in all tar formats
+ * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers)
+ */
+#define REGTYPE '0' /* Regular File */
+#define AREGTYPE '\0' /* Regular File */
+#define LNKTYPE '1' /* Link */
+#define SYMTYPE '2' /* Symlink */
+#define CHRTYPE '3' /* Character Special File */
+#define BLKTYPE '4' /* Block Special File */
+#define DIRTYPE '5' /* Directory */
+#define FIFOTYPE '6' /* FIFO */
+#define CONTTYPE '7' /* high perf file */
+
+#define PAXXTYPE 'x' /* pax format extended header */
+#define PAXGTYPE 'g' /* pax format global extended header */
+
+/*
+ * GNU tar compatibility;
+ */
+#define LONGLINKTYPE 'K' /* Long Symlink */
+#define LONGNAMETYPE 'L' /* Long File */
+
+/*
+ * Mode field encoding of the different file types - values in octal
+ */
+#define TSUID 04000 /* Set UID on execution */
+#define TSGID 02000 /* Set GID on execution */
+#define TSVTX 01000 /* Reserved */
+#define TUREAD 00400 /* Read by owner */
+#define TUWRITE 00200 /* Write by owner */
+#define TUEXEC 00100 /* Execute/Search by owner */
+#define TGREAD 00040 /* Read by group */
+#define TGWRITE 00020 /* Write by group */
+#define TGEXEC 00010 /* Execute/Search by group */
+#define TOREAD 00004 /* Read by other */
+#define TOWRITE 00002 /* Write by other */
+#define TOEXEC 00001 /* Execute/Search by other */
+
+#ifdef _PAX_
+/*
+ * Pad with a bit mask, much faster than doing a mod but only works on powers
+ * of 2. Macro below is for block of 512 bytes.
+ */
+#define TAR_PAD(x) ((512 - ((x) & 511)) & 511)
+#endif /* _PAX_ */
+
+/*
+ * structure of an old tar header as it appeared in BSD releases
+ */
+typedef struct {
+ char name[TNMSZ]; /* name of entry */
+ char mode[8]; /* mode */
+ char uid[8]; /* uid */
+ char gid[8]; /* gid */
+ char size[12]; /* size */
+ char mtime[12]; /* modification time */
+ char chksum[CHK_LEN]; /* checksum */
+ char linkflag; /* norm, hard, or sym. */
+ char linkname[TNMSZ]; /* linked to name */
+} HD_TAR;
+
+#ifdef _PAX_
+/*
+ * -o options for BSD tar to not write directories to the archive
+ */
+#define TAR_NODIR "nodir"
+#define TAR_OPTION "write_opt"
+
+/*
+ * default device names
+ */
+#define DEV_0 "/dev/rst0"
+#define DEV_1 "/dev/rst1"
+#define DEV_4 "/dev/rst4"
+#define DEV_5 "/dev/rst5"
+#define DEV_7 "/dev/rst7"
+#define DEV_8 "/dev/rst8"
+#endif /* _PAX_ */
+
+/*
+ * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990
+ */
+#define TPFSZ 155
+#define TMAGIC "ustar" /* ustar and a null */
+#define TMAGLEN 6
+#define TVERSION "00" /* 00 and no null */
+#define TVERSLEN 2
+
+typedef struct {
+ char name[TNMSZ]; /* name of entry */
+ char mode[8]; /* mode */
+ char uid[8]; /* uid */
+ char gid[8]; /* gid */
+ char size[12]; /* size */
+ char mtime[12]; /* modification time */
+ char chksum[CHK_LEN]; /* checksum */
+ char typeflag; /* type of file. */
+ char linkname[TNMSZ]; /* linked to name */
+ char magic[TMAGLEN]; /* magic cookie */
+ char version[TVERSLEN]; /* version */
+ char uname[32]; /* ascii owner name */
+ char gname[32]; /* ascii group name */
+ char devmajor[8]; /* major device number */
+ char devminor[8]; /* minor device number */
+ char prefix[TPFSZ]; /* linked to name */
+} HD_USTAR;
+
+#endif /* _TAR_H_ */
diff --git a/file_cmds/pax/tty_subs.c b/file_cmds/pax/tty_subs.c
new file mode 100644
index 0000000..74065c6
--- /dev/null
+++ b/file_cmds/pax/tty_subs.c
@@ -0,0 +1,200 @@
+/* $OpenBSD: tty_subs.c,v 1.12 2003/06/02 23:32:09 millert Exp $ */
+/* $NetBSD: tty_subs.c,v 1.5 1995/03/21 09:07:52 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] = "$OpenBSD: tty_subs.c,v 1.12 2003/06/02 23:32:09 millert Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pax.h"
+#include "extern.h"
+#include <stdarg.h>
+
+/*
+ * routines that deal with I/O to and from the user
+ */
+
+#define DEVTTY "/dev/tty" /* device for interactive i/o */
+static FILE *ttyoutf = NULL; /* output pointing at control tty */
+static FILE *ttyinf = NULL; /* input pointing at control tty */
+
+/*
+ * tty_init()
+ * try to open the controlling terminal (if any) for this process. if the
+ * open fails, future ops that require user input will get an EOF
+ */
+
+int
+tty_init(void)
+{
+ int ttyfd;
+
+ if ((ttyfd = open(DEVTTY, O_RDWR)) >= 0) {
+ if ((ttyoutf = fdopen(ttyfd, "w")) != NULL) {
+ if ((ttyinf = fdopen(ttyfd, "r")) != NULL)
+ return(0);
+ (void)fclose(ttyoutf);
+ }
+ (void)close(ttyfd);
+ }
+
+ if (iflag) {
+ paxwarn(1, "Fatal error, cannot open %s", DEVTTY);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * tty_prnt()
+ * print a message using the specified format to the controlling tty
+ * if there is no controlling terminal, just return.
+ */
+
+void
+tty_prnt(const char *fmt, ...)
+{
+ va_list ap;
+ if (ttyoutf == NULL)
+ return;
+ va_start(ap, fmt);
+ (void)vfprintf(ttyoutf, fmt, ap);
+ va_end(ap);
+ (void)fflush(ttyoutf);
+}
+
+/*
+ * tty_read()
+ * read a string from the controlling terminal if it is open into the
+ * supplied buffer
+ * Return:
+ * 0 if data was read, -1 otherwise.
+ */
+
+int
+tty_read(char *str, int len)
+{
+ char *pt;
+
+ if ((--len <= 0) || (ttyinf == NULL) || (fgets(str,len,ttyinf) == NULL))
+ return(-1);
+ *(str + len) = '\0';
+
+ /*
+ * strip off that trailing newline
+ */
+ if ((pt = strchr(str, '\n')) != NULL)
+ *pt = '\0';
+ return(0);
+}
+
+/*
+ * paxwarn()
+ * write a warning message to stderr. if "set" the exit value of pax
+ * will be non-zero.
+ */
+
+void
+paxwarn(int set, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (set && (pax_invalid_action==0))
+ exit_val = 1;
+ /*
+ * when vflag we better ship out an extra \n to get this message on a
+ * line by itself
+ */
+ if (vflag && vfpart) {
+ (void)fflush(listf);
+ (void)fputc('\n', stderr);
+ vfpart = 0;
+ }
+ (void)fprintf(stderr, "%s: ", argv0);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fputc('\n', stderr);
+}
+
+/*
+ * syswarn()
+ * write a warning message to stderr. if "set" the exit value of pax
+ * will be non-zero.
+ */
+
+void
+syswarn(int set, int errnum, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (set)
+ exit_val = 1;
+ /*
+ * when vflag we better ship out an extra \n to get this message on a
+ * line by itself
+ */
+ if (vflag && vfpart) {
+ (void)fflush(listf);
+ (void)fputc('\n', stderr);
+ vfpart = 0;
+ }
+ (void)fprintf(stderr, "%s: ", argv0);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ /*
+ * format and print the errno
+ */
+ if (errnum > 0)
+ (void)fprintf(stderr, " <%s>", strerror(errnum));
+ (void)fputc('\n', stderr);
+}
diff --git a/file_cmds/rm/rm.1 b/file_cmds/rm/rm.1
new file mode 100644
index 0000000..c40f8a7
--- /dev/null
+++ b/file_cmds/rm/rm.1
@@ -0,0 +1,216 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rm.1 8.5 (Berkeley) 12/5/94
+.\" $FreeBSD: src/bin/rm/rm.1,v 1.23 2001/07/15 07:49:05 dd Exp $
+.\"
+.Dd January 28, 1999
+.Dt RM 1
+.Os
+.Sh NAME
+.Nm rm ,
+.Nm unlink
+.Nd remove directory entries
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfiPRrvW
+.Ar
+.Nm unlink
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility attempts to remove the non-directory type files specified on the
+command line.
+If the permissions of the file do not permit writing, and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d
+Attempt to remove directories as well as other types of files.
+.It Fl f
+Attempt to remove the files without prompting for confirmation,
+regardless of the file's permissions.
+If the file does not exist, do not display a diagnostic message or modify
+the exit status to reflect an error.
+The
+.Fl f
+option overrides any previous
+.Fl i
+options.
+.It Fl i
+Request confirmation before attempting to remove each file, regardless of
+the file's permissions, or whether or not the standard input device is a
+terminal.
+The
+.Fl i
+option overrides any previous
+.Fl f
+options.
+.It Fl P
+Overwrite regular files before deleting them.
+Files are overwritten three times, first with the byte pattern 0xff,
+then 0x00, and then 0xff again, before they are deleted.
+.It Fl R
+Attempt to remove the file hierarchy rooted in each file argument.
+The
+.Fl R
+option implies the
+.Fl d
+option.
+If the
+.Fl i
+option is specified, the user is prompted for confirmation before
+each directory's contents are processed (as well as before the attempt
+is made to remove the directory).
+If the user does not respond affirmatively, the file hierarchy rooted in
+that directory is skipped.
+.Pp
+.It Fl r
+Equivalent to
+.Fl R .
+.It Fl v
+Be verbose when deleting files, showing them as they are removed.
+.It Fl W
+Attempt to undelete the named files.
+Currently, this option can only be used to recover
+files covered by whiteouts.
+.El
+.Pp
+The
+.Nm
+utility removes symbolic links, not the files referenced by the links.
+.Pp
+It is an error to attempt to remove the files
+.Dq .\&
+or
+.Dq .. .
+.Pp
+When the utility is called as
+.Nm unlink ,
+only one argument,
+which must not be a directory,
+may be supplied.
+No options may be supplied in this simple mode of operation,
+which performs an
+.Xr unlink 2
+operation on the passed argument.
+.Pp
+The
+.Nm
+utility exits 0 if all of the named files or file hierarchies were removed,
+or if the
+.Fl f
+option was specified and all of the existing files or file hierarchies were
+removed.
+If an error occurs,
+.Nm
+exits with a value >0.
+.Sh NOTE
+The
+.Nm
+command uses
+.Xr getopt 3
+to parse its arguments, which allows it to accept
+the
+.Sq Li --
+option which will cause it to stop processing flag options at that
+point. This will allow the removal of file names that begin
+with a dash
+.Pq Sq - .
+For example:
+.Dl rm -- -filename
+The same behavior can be obtained by using an absolute or relative
+path reference. For example:
+.Dl rm /home/user/-filename
+.Dl rm ./-filename
+.Sh SEE ALSO
+.Xr rmdir 1 ,
+.Xr undelete 2 ,
+.Xr unlink 2 ,
+.Xr fts 3 ,
+.Xr getopt 3 ,
+.Xr symlink 7
+.Sh BUGS
+The
+.Fl P
+option assumes that the underlying file system is a fixed-block file
+system. In addition, only regular files are overwritten, other types of files
+are not.
+.Sh COMPATIBILITY
+The
+.Nm
+utility differs from historical implementations in that the
+.Fl f
+option only masks attempts to remove non-existent files instead of
+masking a large variety of errors.
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Pp
+Also, historical
+.Bx
+implementations prompted on the standard output,
+not the standard error output.
+.Sh STANDARDS
+The
+.Nm
+command is almost
+.St -p1003.2
+compatible, except that
+.Tn POSIX
+requires
+.Nm
+to act like
+.Xr rmdir 1
+when the
+.Ar file
+specified is a directory. This implementation requires the
+.Fl d
+option if such behavior is desired. This follows the historical
+behavior of
+.Nm
+with respect to directories.
+.Pp
+The simplified
+.Nm unlink
+command conforms to
+.St -susv2 .
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/file_cmds/rm/rm.c b/file_cmds/rm/rm.c
new file mode 100644
index 0000000..ef457c5
--- /dev/null
+++ b/file_cmds/rm/rm.c
@@ -0,0 +1,601 @@
+/*-
+ * Copyright (c) 1990, 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 <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94";
+#else
+__used static const char rcsid[] =
+ "$FreeBSD: src/bin/rm/rm.c,v 1.33 2001/06/13 15:01:25 ru Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <locale.h>
+
+#ifdef __APPLE__
+#include <removefile.h>
+#include <pwd.h>
+#include <grp.h>
+#include "get_compat.h"
+
+#ifndef AT_REMOVEDIR_DATALESS
+#define AT_REMOVEDIR_DATALESS 0x0100 /* Remove a dataless directory without materializing first */
+#endif
+#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 *));
+void rm_tree __P((char **));
+void usage __P((void));
+
+/*
+ * rm --
+ * This rm is different from historic rm's, but is expected to match
+ * POSIX 1003.2 behavior. The most visible difference is that -f
+ * has two specific effects now, ignore non-existent files and force
+ * file removal.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, rflag;
+ char *p;
+
+ if (argc < 1)
+ usage();
+
+ /*
+ * Test for the special case where the utility is called as
+ * "unlink", for which the functionality provided is greatly
+ * simplified.
+ */
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ uid = geteuid();
+ if (strcmp(p, "unlink") == 0) {
+ if (argc == 2) {
+ rm_file(&argv[1]);
+ exit(eval);
+ } else
+ usage();
+ }
+
+ Pflag = rflag = 0;
+ while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
+ switch(ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ iflag = 0;
+ break;
+ case 'i':
+ fflag = 0;
+ iflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ break;
+ case 'R':
+ case 'r': /* Compatibility. */
+ rflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'W':
+ Wflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ if (fflag)
+ return 0;
+ usage();
+ }
+
+ checkdot(argv);
+
+ if (*argv) {
+ stdin_ok = isatty(STDIN_FILENO);
+
+ if (rflag)
+ rm_tree(argv);
+ else
+ rm_file(argv);
+ }
+
+ exit (eval);
+}
+
+void
+rm_tree(argv)
+ char **argv;
+{
+ FTS *fts;
+ FTSENT *p;
+ 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.
+ */
+ needstat = !uid || (!fflag && !iflag && stdin_ok);
+
+ /*
+ * If the -i option is specified, the user can skip on the pre-order
+ * visit. The fts_number field flags skipped directories.
+ */
+#define SKIPPED 1
+
+ flags = FTS_PHYSICAL;
+ if (!needstat)
+ flags |= FTS_NOSTAT;
+ if (Wflag)
+ flags |= FTS_WHITEOUT;
+ 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:
+ if (!fflag || p->fts_errno != ENOENT) {
+ warnx("%s: %s",
+ p->fts_path, strerror(p->fts_errno));
+ eval = 1;
+ }
+ continue;
+ case FTS_ERR:
+ errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
+ case FTS_NS:
+ /*
+ * FTS_NS: assume that if can't stat the file, it
+ * can't be unlinked.
+ */
+ if (!needstat)
+ break;
+ if (!fflag || p->fts_errno != ENOENT) {
+ warnx("%s: %s",
+ p->fts_path, strerror(p->fts_errno));
+ eval = 1;
+ }
+ continue;
+ case FTS_D:
+ /* Pre-order: give user chance to skip. */
+ /* 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;
+ }
+ else if (!uid &&
+ (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+ !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+ chflags(p->fts_accpath,
+ p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
+ goto err;
+ continue;
+ case FTS_DP:
+ /* Post-order: see if user 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 &&
+ !check(p->fts_path, p->fts_accpath, p->fts_statp))
+ continue;
+ }
+
+ rval = 0;
+ if (!uid &&
+ (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+ !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
+ rval = chflags(p->fts_accpath,
+ p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
+ if (rval == 0) {
+ /*
+ * If we can't read or search the directory, may still be
+ * able to remove it. Don't print out the un{read,search}able
+ * message unless the remove fails.
+ */
+ switch (p->fts_info) {
+ case FTS_DP:
+ case FTS_DNR:
+#if __APPLE__
+ rval = unlinkat(AT_FDCWD, p->fts_accpath, AT_REMOVEDIR_DATALESS);
+ if (rval == -1 && errno == EINVAL) {
+ /*
+ * Kernel rejected AT_REMOVEDIR_DATALESS?
+ * I guess we fall back on the painful
+ * route (but it's better than failing).
+ */
+ rval = rmdir(p->fts_accpath);
+ }
+#else
+ rval = rmdir(p->fts_accpath);
+#endif
+ if (rval == 0 || (fflag && errno == ENOENT)) {
+ if (rval == 0 && vflag)
+ (void)printf("%s\n",
+ p->fts_path);
+ continue;
+ }
+ break;
+
+ case FTS_W:
+ rval = undelete(p->fts_accpath);
+ if (rval == 0 && (fflag && errno == ENOENT)) {
+ if (vflag)
+ (void)printf("%s\n",
+ p->fts_path);
+ continue;
+ }
+ break;
+
+ default:
+#ifdef __APPLE__
+ if (Pflag) {
+ if (removefile(p->fts_accpath, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */
+ eval = rval = 1;
+ } else
+ rval = unlink(p->fts_accpath);
+#else /* !__APPLE_ */
+ if (Pflag)
+ rm_overwrite(p->fts_accpath, NULL);
+ rval = unlink(p->fts_accpath);
+#endif /* __APPLE__ */
+ if (rval == 0 || (fflag && errno == ENOENT)) {
+ if (rval == 0 && vflag)
+ (void)printf("%s\n",
+ p->fts_path);
+ continue;
+ }
+ }
+ }
+err:
+ warn("%s", p->fts_path);
+ eval = 1;
+ }
+ if (errno)
+ err(1, "fts_read");
+ fts_close(fts);
+}
+
+void
+rm_file(argv)
+ char **argv;
+{
+ struct stat sb;
+ int rval;
+ char *f;
+
+ /*
+ * Remove a file. POSIX 1003.2 states that, by default, attempting
+ * to remove a directory is an error, so must always stat the file.
+ */
+ while ((f = *argv++) != NULL) {
+ /* Assume if can't stat the file, can't unlink it. */
+ if (lstat(f, &sb)) {
+ if (Wflag) {
+ sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
+ } else {
+ if (!fflag || errno != ENOENT) {
+ warn("%s", f);
+ eval = 1;
+ }
+ continue;
+ }
+ } else if (Wflag) {
+ warnx("%s: %s", f, strerror(EEXIST));
+ eval = 1;
+ continue;
+ }
+
+ if (S_ISDIR(sb.st_mode) && !dflag) {
+ warnx("%s: is a directory", f);
+ eval = 1;
+ continue;
+ }
+ if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
+ continue;
+ rval = 0;
+ if (!uid &&
+ (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+ !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
+ rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
+ if (rval == 0) {
+ if (S_ISWHT(sb.st_mode))
+ rval = undelete(f);
+ else if (S_ISDIR(sb.st_mode))
+ rval = rmdir(f);
+ else {
+#ifdef __APPLE__
+ if (Pflag) {
+ if (removefile(f, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */
+ eval = rval = 1;
+ } else
+ rval = unlink(f);
+#else /* !__APPLE__ */
+ if (Pflag)
+ rm_overwrite(f, &sb);
+ rval = unlink(f);
+#endif /* __APPLE__ */
+ }
+ }
+ if (rval && (!fflag || errno != ENOENT)) {
+ warn("%s", f);
+ eval = 1;
+ }
+ if (vflag && rval == 0)
+ (void)printf("%s\n", f);
+ }
+}
+
+/*
+ * rm_overwrite --
+ * Overwrite the file 3 times with varying bit patterns.
+ *
+ * XXX
+ * This is a cheap way to *really* delete files. Note that only regular
+ * files are deleted, directories (and therefore names) will remain.
+ * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
+ * System V file system). In a logging file system, you'll have to have
+ * kernel support.
+ */
+void
+rm_overwrite(file, sbp)
+ char *file;
+ struct stat *sbp;
+{
+ struct stat sb;
+ struct statfs fsb;
+ off_t len;
+ int bsize, fd, wlen;
+ char *buf = NULL;
+
+ if (sbp == NULL) {
+ if (lstat(file, &sb))
+ goto err;
+ sbp = &sb;
+ }
+ if (!S_ISREG(sbp->st_mode))
+ return;
+ if ((fd = open(file, O_WRONLY, 0)) == -1)
+ goto err;
+ if (fstatfs(fd, &fsb) == -1)
+ goto err;
+ bsize = MAX(fsb.f_iosize, 1024);
+ if ((buf = malloc(bsize)) == NULL)
+ err(1, "malloc");
+
+#define PASS(byte) { \
+ memset(buf, byte, bsize); \
+ for (len = sbp->st_size; len > 0; len -= wlen) { \
+ wlen = len < bsize ? (int)len : bsize; \
+ if (write(fd, buf, wlen) != wlen) \
+ goto err; \
+ } \
+}
+ PASS(0xff);
+ if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
+ goto err;
+ PASS(0x00);
+ if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
+ goto err;
+ PASS(0xff);
+ if (!fsync(fd) && !close(fd)) {
+ free(buf);
+ return;
+ }
+
+err: eval = 1;
+ if (buf)
+ free(buf);
+ warn("%s", file);
+}
+
+int
+yes_or_no()
+{
+ int ch, first;
+ char resp[] = {'\0', '\0'};
+
+ (void)fflush(stderr);
+
+ /* Load user specified locale */
+ setlocale(LC_MESSAGES, "");
+
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+
+ /* only care about the first character */
+ resp[0] = first;
+
+ return (rpmatch(resp) == 1);
+}
+
+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;
+{
+ char modep[15], *flagsp;
+
+ /* Check -i first. */
+ if (iflag)
+ (void)fprintf(stderr, "remove %s? ", path);
+ else {
+ /*
+ * If it's not a symbolic link and it's unwritable and we're
+ * talking to a terminal, ask. Symbolic links are excluded
+ * because their permissions are meaningless. Check stdin_ok
+ * first because we may not have stat'ed the file.
+ */
+ if (!stdin_ok || S_ISLNK(sp->st_mode) ||
+ (!access(name, W_OK) &&
+ !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+ (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid)))
+ return (1);
+ strmode(sp->st_mode, modep);
+ if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
+ err(1, NULL);
+ (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
+ modep + 1, modep[9] == ' ' ? "" : " ",
+ user_from_uid(sp->st_uid, 0),
+ group_from_gid(sp->st_gid, 0),
+ *flagsp ? flagsp : "", *flagsp ? " " : "",
+ path);
+ free(flagsp);
+ }
+ return yes_or_no();
+}
+
+
+#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
+void
+checkdot(argv)
+ char **argv;
+{
+ char *p, **save, **t;
+ int complained;
+
+ complained = 0;
+ for (t = argv; *t;) {
+ size_t len = strlen(*t);
+ char truncated[len];
+
+ if ((p = strrchr(*t, '/')) != NULL) {
+ if (p[1] == '\0') { // one or more trailing / -- treat as if not present
+ for (; (p > *t) && (p[-1] == '/');) {
+ len--;
+ p--;
+ }
+ strlcpy(truncated, *t, len);
+ p = strrchr(truncated, '/');
+ if (p) {
+ ++p;
+ } else {
+ p = truncated;
+ }
+ } else {
+ ++p;
+ }
+ } else {
+ p = *t;
+ }
+ if (ISDOT(p)) {
+ if (!complained++)
+ warnx("\".\" and \"..\" may not be removed");
+ eval = 1;
+ for (save = t; (t[0] = t[1]) != NULL; ++t)
+ continue;
+ t = save;
+ } else
+ ++t;
+ }
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: rm [-f | -i] [-dPRrvW] file ...",
+ " unlink file");
+ exit(EX_USAGE);
+}
diff --git a/file_cmds/rm/unlink.1 b/file_cmds/rm/unlink.1
new file mode 100644
index 0000000..6764e6d
--- /dev/null
+++ b/file_cmds/rm/unlink.1
@@ -0,0 +1 @@
+.so man1/rm.1
diff --git a/file_cmds/rmdir/rmdir.1 b/file_cmds/rmdir/rmdir.1
new file mode 100644
index 0000000..577637a
--- /dev/null
+++ b/file_cmds/rmdir/rmdir.1
@@ -0,0 +1,100 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rmdir.1 8.1 (Berkeley) 5/31/93
+.\" $FreeBSD: src/bin/rmdir/rmdir.1,v 1.9 2000/11/20 11:39:40 ru Exp $
+.\"
+.Dd May 31, 1993
+.Dt RMDIR 1
+.Os
+.Sh NAME
+.Nm rmdir
+.Nd remove directories
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Ar directory ...
+.Sh DESCRIPTION
+The
+.Nm
+utility removes the directory entry specified by
+each
+.Ar directory
+argument, provided it is empty.
+.Pp
+Arguments are processed in the order given.
+In order to remove both a parent directory and a subdirectory
+of that parent, the subdirectory
+must be specified first so the parent directory
+is empty when
+.Nm
+tries to remove it.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl p
+Each
+.Ar directory
+argument is treated as a pathname of which all
+components will be removed, if they are empty,
+starting with the last most component.
+(See
+.Xr rm 1
+for fully non-discriminant recursive removal.)
+.El
+.Pp
+The
+.Nm
+utility exits with one of the following values:
+.Bl -tag -width Ds
+.It Li \&0
+Each directory entry specified by a dir operand
+referred to an empty directory and was removed
+successfully.
+.It Li \&>\&0
+An error occurred.
+.El
+.Sh SEE ALSO
+.Xr rm 1
+.Sh STANDARDS
+The
+.Nm
+command is expected to be
+.St -p1003.2
+compatible.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/file_cmds/rmdir/rmdir.c b/file_cmds/rmdir/rmdir.c
new file mode 100644
index 0000000..32969bc
--- /dev/null
+++ b/file_cmds/rmdir/rmdir.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1992, 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 <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/rmdir/rmdir.c,v 1.13 2002/06/30 05:15:03 obrien Exp $");
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int rm_path(char *);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, errors;
+ int pflag;
+
+ pflag = 0;
+ while ((ch = getopt(argc, argv, "p")) != -1)
+ switch(ch) {
+ case 'p':
+ pflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ for (errors = 0; *argv; argv++) {
+ if (rmdir(*argv) < 0) {
+ warn("%s", *argv);
+ errors = 1;
+ } else if (pflag)
+ errors |= rm_path(*argv);
+ }
+
+ exit(errors);
+}
+
+int
+rm_path(char *path)
+{
+ char *p;
+
+ p = path + strlen(path);
+ while (--p > path && *p == '/')
+ ;
+ *++p = '\0';
+ while ((p = strrchr(path, '/')) != NULL) {
+ /* Delete trailing slashes. */
+ while (--p > path && *p == '/')
+ ;
+ *++p = '\0';
+
+ if (rmdir(path) < 0) {
+ warn("%s", path);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: rmdir [-p] directory ...\n");
+ exit(1);
+}
diff --git a/file_cmds/rmt/rmt.8 b/file_cmds/rmt/rmt.8
new file mode 100644
index 0000000..77a9074
--- /dev/null
+++ b/file_cmds/rmt/rmt.8
@@ -0,0 +1,220 @@
+.\" $NetBSD: rmt.8,v 1.6 1997/10/17 13:03:15 lukem Exp $
+.\"
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+is a program used by the remote dump and restore programs
+in manipulating a magnetic tape drive through an interprocess
+communication connection.
+.Nm
+is normally started up with an
+.Xr rexec 3
+or
+.Xr rcmd 3
+call.
+.Pp
+The
+.Nm
+program accepts requests specific to the manipulation of
+magnetic tapes, performs the commands, then responds with
+a status indication. All responses are in
+.Tn ASCII
+and in
+one of two forms.
+Successful commands have responses of:
+.Bd -filled -offset indent
+.Sm off
+.Sy A Ar number No \en
+.Sm on
+.Ed
+.Pp
+.Ar Number
+is an
+.Tn ASCII
+representation of a decimal number.
+Unsuccessful commands are responded to with:
+.Bd -filled -offset indent
+.Sm off
+.Xo Sy E Ar error-number
+.No \en Ar error-message
+.No \en
+.Xc
+.Sm on
+.Ed
+.Pp
+.Ar Error-number
+is one of the possible error
+numbers described in
+.Xr intro 2
+and
+.Ar error-message
+is the corresponding error string as printed
+from a call to
+.Xr perror 3 .
+The protocol is comprised of the
+following commands, which are sent as indicated - no spaces are supplied
+between the command and its arguments, or between its arguments, and
+.Ql \en
+indicates that a newline should be supplied:
+.Bl -tag -width Ds
+.Sm off
+.It Xo Sy \&O Ar device
+.No \en Ar mode No \en
+.Xc
+Open the specified
+.Ar device
+using the indicated
+.Ar mode .
+.Ar Device
+is a full pathname and
+.Ar mode
+is an
+.Tn ASCII
+representation of a decimal
+number suitable for passing to
+.Xr open 2 .
+If a device had already been opened, it is
+closed before a new open is performed.
+.It Xo Sy C Ar device No \en
+.Xc
+Close the currently open device. The
+.Ar device
+specified is ignored.
+.It Xo Sy L
+.Ar offset No \en
+.Ar whence No \en
+.Xc
+.Sm on
+Perform an
+.Xr lseek 2
+operation using the specified parameters.
+The response value is that returned from the
+.Xr lseek
+call.
+.Sm off
+.It Sy W Ar count No \en
+.Sm on
+Write data onto the open device.
+.Nm
+reads
+.Ar count
+bytes from the connection, aborting if
+a premature end-of-file is encountered.
+The response value is that returned from
+the
+.Xr write 2
+call.
+.Sm off
+.It Sy R Ar count No \en
+.Sm on
+Read
+.Ar count
+bytes of data from the open device.
+If
+.Ar count
+exceeds the size of the data buffer (10 kilobytes), it is
+truncated to the data buffer size.
+.Nm
+then performs the requested
+.Xr read 2
+and responds with
+.Sm off
+.Sy A Ar count-read No \en
+.Sm on
+if the read was
+successful; otherwise an error in the
+standard format is returned. If the read
+was successful, the data read is then sent.
+.Sm off
+.It Xo Sy I Ar operation
+.No \en Ar count No \en
+.Xc
+.Sm on
+Perform a
+.Dv MTIOCOP
+.Xr ioctl 2
+command using the specified parameters.
+The parameters are interpreted as the
+.Tn ASCII
+representations of the decimal values
+to place in the
+.Ar mt_op
+and
+.Ar mt_count
+fields of the structure used in the
+.Xr ioctl
+call. The return value is the
+.Ar count
+parameter when the operation is successful.
+.ne 1i
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl
+call. If the operation was successful,
+an ``ack'' is sent with the size of the
+status buffer, then the status buffer is
+sent (in binary).
+.El
+.Sm on
+.Pp
+Any other command causes
+.Nm
+to exit.
+.Sh DIAGNOSTICS
+All responses are of the form described above.
+.Sh SEE ALSO
+.Xr rcmd 3 ,
+.Xr rexec 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/file_cmds/rmt/rmt.c b/file_cmds/rmt/rmt.c
new file mode 100644
index 0000000..bfb4d37
--- /dev/null
+++ b/file_cmds/rmt/rmt.c
@@ -0,0 +1,259 @@
+/* $NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $ */
+
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int tape = -1;
+
+char *record;
+int maxrecsize = -1;
+
+#define SSIZE 64
+char device[SSIZE];
+char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+char resp[BUFSIZ];
+
+FILE *debug;
+#define DEBUG(f) if (debug) fprintf(debug, f)
+#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
+#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+char *checkbuf __P((char *, int));
+void error __P((int));
+int main __P((int, char **));
+void getstring __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int rval;
+ char c;
+ int n, i, cc;
+
+ argc--, argv++;
+ if (argc > 0) {
+ debug = fopen(*argv, "w");
+ if (debug == 0)
+ exit(1);
+ (void)setbuf(debug, (char *)0);
+ }
+top:
+ errno = 0;
+ rval = 0;
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ exit(0);
+ switch (c) {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close(tape);
+ getstring(device);
+ getstring(mode);
+ DEBUG2("rmtd: O %s %s\n", device, mode);
+ tape = open(device, atoi(mode),
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG("rmtd: C\n");
+ getstring(device); /* discard */
+ if (close(tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring(count);
+ getstring(pos);
+ DEBUG2("rmtd: L %s %s\n", count, pos);
+ rval = lseek(tape, (off_t)strtoq(count, NULL, 10), atoi(pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring(count);
+ n = atoi(count);
+ DEBUG1("rmtd: W %s\n", count);
+ record = checkbuf(record, n);
+ for (i = 0; i < n; i += cc) {
+ cc = read(STDIN_FILENO, &record[i], n - i);
+ if (cc <= 0) {
+ DEBUG("rmtd: premature eof\n");
+ exit(2);
+ }
+ }
+ rval = write(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring(count);
+ DEBUG1("rmtd: R %s\n", count);
+ n = atoi(count);
+ record = checkbuf(record, n);
+ rval = read(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, record, rval);
+ goto top;
+
+ case 'I':
+ getstring(op);
+ getstring(count);
+ DEBUG2("rmtd: I %s %s\n", op, count);
+ {
+ struct mtop mtop;
+
+ mtop.mt_op = atoi(op);
+ mtop.mt_count = atoi(count);
+ if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+ goto respond;
+
+ case 'S': /* status */
+ DEBUG("rmtd: S\n");
+ {
+ struct mtget mtget;
+
+ if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, (char *)&mtget,
+ sizeof (mtget));
+ goto top;
+ }
+
+ default:
+ DEBUG1("rmtd: garbage command %c\n", c);
+ exit(3);
+ }
+respond:
+ DEBUG1("rmtd: A %d\n", rval);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ goto top;
+ioerror:
+ error(errno);
+ goto top;
+}
+
+void
+getstring(bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE - 1; i++) {
+ if (read(STDIN_FILENO, cp+i, 1) != 1)
+ exit(0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf(record, size)
+ char *record;
+ int size;
+{
+
+ if (size <= maxrecsize)
+ return (record);
+ if (record != 0)
+ free(record);
+ record = malloc(size);
+ if (record == 0) {
+ DEBUG("rmtd: cannot allocate buffer space\n");
+ exit(4);
+ }
+ maxrecsize = size;
+ while (size > 1024 &&
+ setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
+ size -= 1024;
+ return (record);
+}
+
+void
+error(num)
+ int num;
+{
+
+ DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
+ (void)sprintf(resp, "E%d\n%s\n", num, strerror(num));
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+}
diff --git a/file_cmds/shar/shar.1 b/file_cmds/shar/shar.1
new file mode 100644
index 0000000..3f9dac9
--- /dev/null
+++ b/file_cmds/shar/shar.1
@@ -0,0 +1,105 @@
+.\" $NetBSD: shar.1,v 1.6 1998/06/08 12:41:44 lukem Exp $
+.\"
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)shar.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt SHAR 1
+.Os BSD 4.4
+.Sh NAME
+.Nm shar
+.Nd create a shell archive of files
+.Sh SYNOPSIS
+.Nm
+.Ar
+.Sh DESCRIPTION
+.Nm
+writes an
+.Xr sh 1
+shell script to the standard output which will recreate the file
+hierarchy specified by the command line operands.
+Directories will be recreated and must be specified before the
+files they contain (the
+.Xr find 1
+utility does this correctly).
+.Pp
+.Nm
+is normally used for distributing files by
+.Xr ftp 1
+or
+.Xr mail 1 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr tar 1 ,
+.Xr uuencode 1
+.Sh BUGS
+.Nm
+makes no provisions for special types of files or files containing
+magic characters.
+.Sh EXAMPLES
+To create a shell archive of the program
+.Xr ls 1
+and mail it to Rick:
+.Bd -literal -offset indent
+cd ls
+shar `find . -print` \&| mail -s "ls source" rick
+.Ed
+.Pp
+To recreate the program directory:
+.Bd -literal -offset indent
+mkdir ls
+cd ls
+\&...
+<delete header lines and examine mailed archive>
+\&...
+sh archive
+.Ed
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
+.Sh SECURITY CONSIDERATIONS
+It is easy to insert trojan horses into
+.Nm
+files.
+It is strongly recommended that all shell archive files be examined
+before running them through
+.Xr sh 1 .
+Archives produced using this implementation of
+.Nm
+may be easily examined with the command:
+.Bd -literal -offset indent
+egrep -v '^[X#]' shar.file
+.Ed
diff --git a/file_cmds/shar/shar.sh b/file_cmds/shar/shar.sh
new file mode 100644
index 0000000..7048b44
--- /dev/null
+++ b/file_cmds/shar/shar.sh
@@ -0,0 +1,76 @@
+#!/bin/sh -
+#
+# $NetBSD: shar.sh,v 1.2 1994/12/21 08:42:04 jtc Exp $
+#
+# Copyright (c) 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)shar.sh 8.1 (Berkeley) 6/6/93
+#
+
+if [ $# -eq 0 ]; then
+ echo 'usage: shar file ...'
+ exit 1
+fi
+
+cat << EOF
+# This is a shell archive. Save it in a file, remove anything before
+# this line, and then unpack it by entering "sh file". Note, it may
+# create directories; files and directories will be owned by you and
+# have default permissions.
+#
+# This archive contains:
+#
+EOF
+
+for i
+do
+ echo "# $i"
+done
+
+echo "#"
+
+for i
+do
+ if [ -d $i ]; then
+ echo "echo c - $i"
+ echo "mkdir -p $i > /dev/null 2>&1"
+ else
+ echo "echo x - $i"
+ echo "sed 's/^X//' >$i << 'END-of-$i'"
+ sed 's/^/X/' $i
+ echo "END-of-$i"
+ fi
+done
+echo exit
+echo ""
+
+exit 0
diff --git a/file_cmds/stat/readlink.1 b/file_cmds/stat/readlink.1
new file mode 100644
index 0000000..968704b
--- /dev/null
+++ b/file_cmds/stat/readlink.1
@@ -0,0 +1 @@
+.so man1/stat.1
diff --git a/file_cmds/stat/stat.1 b/file_cmds/stat/stat.1
new file mode 100644
index 0000000..79c774f
--- /dev/null
+++ b/file_cmds/stat/stat.1
@@ -0,0 +1,534 @@
+.\" $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.8 2005/06/14 11:50:53 ru Exp $
+.\"
+.Dd May 8, 2003
+.Dt STAT 1
+.Os
+.Sh NAME
+.Nm readlink ,
+.Nm stat
+.Nd display file status
+.Sh SYNOPSIS
+.Nm stat
+.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 stat
+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 stat
+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 f Ar format
+Display information using the specified format.
+See the
+.Sx FORMATS
+section for a description of valid formats.
+.\" ==========
+.It Fl L
+Use
+.Xr stat 2
+instead of
+.Xr lstat 2 .
+The information reported by
+.Nm stat
+will refer to the target of
+.Ar file ,
+if file is a symbolic link, and not to
+.Ar file
+itself.
+.\" ==========
+.It Fl l
+Display output in
+.Nm ls Fl lT
+format.
+.\" ==========
+.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 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 t Ar timefmt
+Display timestamps using the specified format.
+This format is
+passed directly to
+.Xr strftime 3 .
+.\" ==========
+.It Fl x
+Display information in a more verbose way as known from some
+.Tn Linux
+distributions.
+.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 stat
+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 stat
+utility appeared in
+.Nx 1.6
+and
+.Fx 4.10 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm stat
+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/file_cmds/stat/stat.c b/file_cmds/stat/stat.c
new file mode 100644
index 0000000..610eff9
--- /dev/null
+++ b/file_cmds/stat/stat.c
@@ -0,0 +1,1004 @@
+/*
+ * 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
+
+__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 1
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
+#define HAVE_STRUCT_STAT_ST_ATIM 0
+#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: (%04OA/%.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_mode2 'A'
+#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_mode2);
+ 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 = {0,0};
+ 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:
+ case SHOW_st_mode2:
+ 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 (what == SHOW_st_mode2)
+ data &= 07777;
+ 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;
+ tm = localtime(&ts.tv_sec);
+ if (tm == NULL) {
+ ts.tv_sec = 0;
+ 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 |
+ FMTF_STRING;
+ if (ofmt == FMTF_STRING) {
+ small = 0;
+ data = 0;
+ snprintf(path, sizeof(path), "%s", fflagstostr(st->st_flags));
+ sdata = path;
+ }
+ 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));
+}
diff --git a/file_cmds/tests/chgrp.sh b/file_cmds/tests/chgrp.sh
new file mode 100644
index 0000000..503fbf1
--- /dev/null
+++ b/file_cmds/tests/chgrp.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+GROUPID=31337
+
+dscl /Local/Default -read /Groups/$GROUPID > /dev/null 2>&1
+if [ $? != "0" ]; then
+ dscl /Local/Default -create /Groups/$GROUPID
+ dscl /Local/Default -create /Groups/$GROUPID PrimaryGroupID 9999
+fi
+
+mkdir /tmp/$$
+chgrp $GROUPID /tmp/$$
+gid=`/usr/bin/stat -f '%g' /tmp/$$`
+if [ "$gid" != "9999" ]; then
+ echo "chgrp $GROUPID, expected group 9999, is $gid"
+ exit 1
+fi
+
+chgrp -n $GROUPID /tmp/$$
+gid=`/usr/bin/stat -f '%g' /tmp/$$`
+if [ "$gid" != "$GROUPID" ]; then
+ echo "chgrp -n $GROUPID, expected group $GROUPID, is $gid"
+ exit 1
+fi
+
+exit 0
diff --git a/file_cmds/tests/file_cmds.plist b/file_cmds/tests/file_cmds.plist
new file mode 100644
index 0000000..4e4d135
--- /dev/null
+++ b/file_cmds/tests/file_cmds.plist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Project</key>
+ <string>file_cmds</string>
+ <key>IgnoreOutput</key>
+ <true/>
+ <key>Tests</key>
+ <array>
+ <dict>
+ <key>Command</key>
+ <array>
+ <string>/bin/sh</string>
+ <string>chgrp.sh</string>
+ </array>
+ <key>AsRoot</key>
+ <true/>
+ <key>TestName</key>
+ <string>chgrp</string>
+ <key>WhenToRun</key>
+ <array>
+ <string>PRESUBMISSION</string>
+ <string>NIGHTLY</string>
+ </array>
+ <key>WorkingDirectory</key>
+ <string>/AppleInternal/Tests/file_cmds</string>
+ </dict>
+ </array>
+ <key>Timeout</key>
+ <integer>30</integer>
+</dict>
+</plist>
diff --git a/file_cmds/touch/touch.1 b/file_cmds/touch/touch.1
new file mode 100644
index 0000000..6e6678b
--- /dev/null
+++ b/file_cmds/touch/touch.1
@@ -0,0 +1,221 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)touch.1 8.3 (Berkeley) 4/28/95
+.\" $FreeBSD: src/usr.bin/touch/touch.1,v 1.16 2007/04/10 07:24:47 grog Exp $
+.\"
+.Dd April 28, 1995
+.Dt TOUCH 1
+.Os
+.Sh NAME
+.Nm touch
+.Nd change file access and modification times
+.Sh SYNOPSIS
+.Nm
+.Op Fl A Ar [-][[hh]mm]SS
+.Op Fl acfhm
+.Op Fl r Ar file
+.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS]
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility sets the modification and access times of files.
+If any file does not exist, it is created with default permissions.
+.Pp
+By default,
+.Nm
+changes both modification and access times. The
+.Fl a
+and
+.Fl m
+flags may be used to select the access time or the modification time
+individually.
+Selecting both is equivalent to the default.
+By default, the timestamps are set to the current time.
+The
+.Fl t
+flag explicitly specifies a different time, and the
+.Fl r
+flag specifies to set the times those of the specified file.
+The
+.Fl A
+flag adjusts the values by a specified amount.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl A
+Adjust the access and modification time stamps for the file by the
+specified value.
+This flag is intended for use in modifying files with incorrectly set
+time stamps.
+.Pp
+The argument is of the form
+.Dq [-][[hh]mm]SS
+where each pair of letters represents the following:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar -
+Make the adjustment negative: the new time stamp is set to be before
+the old one.
+.It Ar hh
+The number of hours, from 00 to 99.
+.It Ar mm
+The number of minutes, from 00 to 59.
+.It Ar SS
+The number of seconds, from 00 to 59.
+.El
+.Pp
+The
+.Fl A
+flag implies the
+.Fl c
+flag: if any file specified does not exist, it will be silently ignored.
+.It Fl a
+Change the access time of the file.
+The modification time of the file is not changed unless the
+.Fl m
+flag is also specified.
+.It Fl c
+Do not create the file if it does not exist.
+The
+.Nm
+utility does not treat this as an error.
+No error messages are displayed and the exit value is not affected.
+.It Fl f
+Attempt to force the update, even if the file permissions do not
+currently permit it.
+.It Fl h
+If the file is a symbolic link, change the times of the link
+itself rather than the file that the link points to.
+Note that
+.Fl h
+implies
+.Fl c
+and thus will not create any new files.
+.It Fl m
+Change the modification time of the file.
+The access time of the file is not changed unless the
+.Fl a
+flag is also specified.
+.It Fl r
+Use the access and modifications times from the specified file
+instead of the current time of day.
+.It Fl t
+Change the access and modification times to the specified time instead
+of the current time of day.
+The argument is of the form
+.Dq [[CC]YY]MMDDhhmm[.SS]
+where each pair of letters represents the following:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar YY
+The second two digits of the year.
+If
+.Dq YY
+is specified, but
+.Dq CC
+is not, a value for
+.Dq YY
+between 69 and 99 results in a
+.Dq CC
+value of 19.
+Otherwise, a
+.Dq CC
+value of 20 is used.
+.It Ar MM
+The month of the year, from 01 to 12.
+.It Ar DD
+the day of the month, from 01 to 31.
+.It Ar hh
+The hour of the day, from 00 to 23.
+.It Ar mm
+The minute of the hour, from 00 to 59.
+.It Ar SS
+The second of the minute, from 00 to 61.
+.El
+.Pp
+If the
+.Dq CC
+and
+.Dq YY
+letter pairs are not specified, the values default to the current
+year.
+If the
+.Dq SS
+letter pair is not specified, the value defaults to 0.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+The obsolescent form of
+.Nm ,
+where a time format is specified as the first argument, is supported.
+When no
+.Fl r
+or
+.Fl t
+option is specified, there are at least two arguments, and the first
+argument is a string of digits either eight or ten characters in length,
+the first argument is interpreted as a time specification of the form
+.Dq MMDDhhmm[YY] .
+.Pp
+The
+.Dq MM ,
+.Dq DD ,
+.Dq hh
+and
+.Dq mm
+letter pairs are treated as their counterparts specified to the
+.Fl t
+option.
+If the
+.Dq YY
+letter pair is in the range 39 to 99, the year is set to 1939 to 1999,
+otherwise, the year is set in the 21st century.
+.Sh SEE ALSO
+.Xr utimes 2
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2
+specification.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v7 .
diff --git a/file_cmds/touch/touch.c b/file_cmds/touch/touch.c
new file mode 100644
index 0000000..aca5b90
--- /dev/null
+++ b/file_cmds/touch/touch.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+__FBSDID("$FreeBSD: src/usr.bin/touch/touch.c,v 1.25 2010/03/28 13:16:08 ed Exp $");
+
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#ifndef lint
+__used static const char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+int rw(char *, struct stat *, int);
+void stime_arg1(char *, struct timeval *);
+void stime_arg2(char *, int, struct timeval *);
+void stime_file(char *, struct timeval *);
+int timeoffset(char *);
+void usage(char *);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ struct timeval tv[2];
+ int (*stat_f)(const char *, struct stat *);
+ int (*utimes_f)(const char *, const struct timeval *);
+ int Aflag, aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
+ char *p;
+ char *myname;
+
+ myname = basename(argv[0]);
+ Aflag = aflag = cflag = fflag = mflag = timeset = 0;
+ stat_f = stat;
+ utimes_f = utimes;
+ if (gettimeofday(&tv[0], NULL))
+ err(1, "gettimeofday");
+
+ while ((ch = getopt(argc, argv, "A:acfhmr:t:")) != -1)
+ switch(ch) {
+ case 'A':
+ Aflag = timeoffset(optarg);
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ cflag = 1;
+ stat_f = lstat;
+ utimes_f = lutimes;
+ break;
+ case 'm':
+ mflag = 1;
+ break;
+ case 'r':
+ timeset = 1;
+ stime_file(optarg, tv);
+ break;
+ case 't':
+ timeset = 1;
+ stime_arg1(optarg, tv);
+ break;
+ case '?':
+ default:
+ usage(myname);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag == 0 && mflag == 0)
+ aflag = mflag = 1;
+
+ if (timeset) {
+ if (Aflag) {
+ /*
+ * We're setting the time to an offset from a specified
+ * time. God knows why, but it means that we can set
+ * that time once and for all here.
+ */
+ if (aflag)
+ tv[0].tv_sec += Aflag;
+ if (mflag)
+ tv[1].tv_sec += Aflag;
+ Aflag = 0; /* done our job */
+ }
+ } else {
+ /*
+ * If no -r or -t flag, at least two operands, the first of
+ * which is an 8 or 10 digit number, use the obsolete time
+ * specification, otherwise use the current time.
+ */
+ if (argc > 1) {
+ strtol(argv[0], &p, 10);
+ len = p - argv[0];
+ if (*p == '\0' && (len == 8 || len == 10)) {
+ timeset = 1;
+ stime_arg2(*argv++, len == 10, tv);
+ }
+ }
+ /* Both times default to the same. */
+ tv[1] = tv[0];
+ }
+
+ if (*argv == NULL)
+ usage(myname);
+
+ if (Aflag)
+ cflag = 1;
+
+ for (rval = 0; *argv; ++argv) {
+ /* See if the file exists. */
+ if (stat_f(*argv, &sb) != 0) {
+ if (errno != ENOENT) {
+ rval = 1;
+ warn("%s", *argv);
+ continue;
+ }
+ if (!cflag) {
+ /* Create the file. */
+ fd = open(*argv,
+ O_WRONLY | O_CREAT, DEFFILEMODE);
+ if (fd == -1 || fstat(fd, &sb) || close(fd)) {
+ rval = 1;
+ warn("%s", *argv);
+ continue;
+ }
+
+ /* If using the current time, we're done. */
+ if (!timeset)
+ continue;
+ } else
+ continue;
+ }
+
+ if (!aflag)
+ TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
+ if (!mflag)
+ TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
+
+ /*
+ * We're adjusting the times based on the file times, not a
+ * specified time (that gets handled above).
+ */
+ if (Aflag) {
+ if (aflag) {
+ TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
+ tv[0].tv_sec += Aflag;
+ }
+ if (mflag) {
+ TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
+ tv[1].tv_sec += Aflag;
+ }
+ }
+
+ /* Try utimes(2). */
+ if (!utimes_f(*argv, tv))
+ continue;
+
+ /* If the user specified a time, nothing else we can do. */
+ if (timeset || Aflag) {
+ rval = 1;
+ warn("%s", *argv);
+ continue;
+ }
+
+ /*
+ * System V and POSIX 1003.1 require that a NULL argument
+ * set the access/modification times to the current time.
+ * The permission checks are different, too, in that the
+ * ability to write the file is sufficient. Take a shot.
+ */
+ if (!utimes_f(*argv, NULL))
+ continue;
+
+ /* Try reading/writing. */
+ if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode)) {
+ if (rw(*argv, &sb, fflag))
+ rval = 1;
+ } else {
+ rval = 1;
+ warn("%s", *argv);
+ }
+ }
+ exit(rval);
+}
+
+#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
+
+void
+stime_arg1(char *arg, struct timeval *tvp)
+{
+ time_t now;
+ struct tm *t;
+ int yearset;
+ char *p;
+ /* Start with the current time. */
+ now = tvp[0].tv_sec;
+ if ((t = localtime(&now)) == NULL)
+ err(1, "localtime");
+ /* [[CC]YY]MMDDhhmm[.SS] */
+ if ((p = strchr(arg, '.')) == NULL)
+ t->tm_sec = 0; /* Seconds defaults to 0. */
+ else {
+ if (strlen(p + 1) != 2)
+ goto terr;
+ *p++ = '\0';
+ t->tm_sec = ATOI2(p);
+ }
+
+ yearset = 0;
+ switch(strlen(arg)) {
+ case 12: /* CCYYMMDDhhmm */
+ t->tm_year = ATOI2(arg);
+ t->tm_year *= 100;
+ yearset = 1;
+ /* FALLTHROUGH */
+ case 10: /* YYMMDDhhmm */
+ if (yearset) {
+ yearset = ATOI2(arg);
+ t->tm_year += yearset;
+ } else {
+ yearset = ATOI2(arg);
+ if (yearset < 69)
+ t->tm_year = yearset + 2000;
+ else
+ t->tm_year = yearset + 1900;
+ }
+ t->tm_year -= 1900; /* Convert to UNIX time. */
+ /* FALLTHROUGH */
+ case 8: /* MMDDhhmm */
+ t->tm_mon = ATOI2(arg);
+ --t->tm_mon; /* Convert from 01-12 to 00-11 */
+ t->tm_mday = ATOI2(arg);
+ t->tm_hour = ATOI2(arg);
+ t->tm_min = ATOI2(arg);
+ break;
+ default:
+ goto terr;
+ }
+
+ t->tm_isdst = -1; /* Figure out DST. */
+ tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
+ if (tvp[0].tv_sec == -1)
+terr: errx(1,
+ "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
+
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+}
+
+void
+stime_arg2(char *arg, int year, struct timeval *tvp)
+{
+ time_t now;
+ struct tm *t;
+ /* Start with the current time. */
+ now = tvp[0].tv_sec;
+ if ((t = localtime(&now)) == NULL)
+ err(1, "localtime");
+
+ t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
+ --t->tm_mon; /* Convert from 01-12 to 00-11 */
+ t->tm_mday = ATOI2(arg);
+ t->tm_hour = ATOI2(arg);
+ t->tm_min = ATOI2(arg);
+ if (year) {
+ t->tm_year = ATOI2(arg);
+ if (t->tm_year < 39) /* support 2000-2038 not 1902-1969 */
+ t->tm_year += 100;
+ }
+
+ t->tm_isdst = -1; /* Figure out DST. */
+ tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
+ if (tvp[0].tv_sec == -1)
+ errx(1,
+ "out of range or illegal time specification: MMDDhhmm[yy]");
+
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+}
+
+/* Calculate a time offset in seconds, given an arg of the format [-]HHMMSS. */
+int
+timeoffset(char *arg)
+{
+ int offset;
+ int isneg;
+
+ offset = 0;
+ isneg = *arg == '-';
+ if (isneg)
+ arg++;
+ switch (strlen(arg)) {
+ default: /* invalid */
+ errx(1, "Invalid offset spec, must be [-][[HH]MM]SS");
+
+ case 6: /* HHMMSS */
+ offset = ATOI2(arg);
+ /* FALLTHROUGH */
+ case 4: /* MMSS */
+ offset = offset * 60 + ATOI2(arg);
+ /* FALLTHROUGH */
+ case 2: /* SS */
+ offset = offset * 60 + ATOI2(arg);
+ }
+ if (isneg)
+ return (-offset);
+ else
+ return (offset);
+}
+
+void
+stime_file(char *fname, struct timeval *tvp)
+{
+ struct stat sb;
+
+ if (stat(fname, &sb))
+ err(1, "%s", fname);
+ TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
+ TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
+}
+
+int
+rw(char *fname, struct stat *sbp, int force)
+{
+ int fd, needed_chmod, rval;
+ u_char byte;
+
+ /* Try regular files. */
+ if (!S_ISREG(sbp->st_mode)) {
+ warnx("%s: %s", fname, strerror(EFTYPE));
+ return (1);
+ }
+
+ needed_chmod = rval = 0;
+ if ((fd = open(fname, O_RDWR, 0)) == -1) {
+ if (!force || chmod(fname, DEFFILEMODE))
+ goto err;
+ if ((fd = open(fname, O_RDWR, 0)) == -1)
+ goto err;
+ needed_chmod = 1;
+ }
+
+ if (sbp->st_size != 0) {
+ if (read(fd, &byte, sizeof(byte)) != sizeof(byte))
+ goto err;
+ if (lseek(fd, (off_t)0, SEEK_SET) == -1)
+ goto err;
+ if (write(fd, &byte, sizeof(byte)) != sizeof(byte))
+ goto err;
+ } else {
+ if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
+err: rval = 1;
+ warn("%s", fname);
+ } else if (ftruncate(fd, (off_t)0)) {
+ rval = 1;
+ warn("%s: file modified", fname);
+ }
+ }
+
+ if (close(fd) && rval != 1) {
+ rval = 1;
+ warn("%s", fname);
+ }
+ if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) {
+ rval = 1;
+ warn("%s: permissions modified", fname);
+ }
+ return (rval);
+}
+
+void
+usage(char *myname)
+{
+ fprintf(stderr, "usage:\n" "%s [-A [-][[hh]mm]SS] [-acfhm] [-r file] "
+ "[-t [[CC]YY]MMDDhhmm[.SS]] file ...\n", myname);
+ exit(1);
+}
diff --git a/file_cmds/xcodescripts/hardlink.sh b/file_cmds/xcodescripts/hardlink.sh
new file mode 100644
index 0000000..8659623
--- /dev/null
+++ b/file_cmds/xcodescripts/hardlink.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Link input files to output files (in order).
+
+set -e
+
+if [ "$SCRIPT_INPUT_FILE_COUNT" -ne "$SCRIPT_OUTPUT_FILE_COUNT" ]; then
+ echo input and output file counts differ
+ exit 1
+fi
+
+X=0
+
+while [ "$X" -lt "$SCRIPT_INPUT_FILE_COUNT" ]; do
+ eval ln -fhv \"\$SCRIPT_INPUT_FILE_$X\" \"\$SCRIPT_OUTPUT_FILE_$X\"
+ X=$((X+1))
+done