]> git.saurik.com Git - apple/file_cmds.git/commitdiff
file_cmds-45.tar.gz mac-os-x-100 mac-os-x-1004 v45
authorApple <opensource@apple.com>
Tue, 10 Apr 2001 06:35:09 +0000 (06:35 +0000)
committerApple <opensource@apple.com>
Tue, 10 Apr 2001 06:35:09 +0000 (06:35 +0000)
201 files changed:
Makefile [new file with mode: 0644]
Makefile.postamble [new file with mode: 0644]
Makefile.preamble [new file with mode: 0644]
PB.project [new file with mode: 0644]
PROJECT [new file with mode: 0644]
chflags/Makefile [new file with mode: 0644]
chflags/Makefile.postamble [new file with mode: 0644]
chflags/Makefile.preamble [new file with mode: 0644]
chflags/PB.project [new file with mode: 0644]
chflags/chflags.1 [new file with mode: 0644]
chflags/chflags.c [new file with mode: 0644]
chmod/Makefile [new file with mode: 0644]
chmod/Makefile.postamble [new file with mode: 0644]
chmod/Makefile.preamble [new file with mode: 0644]
chmod/PB.project [new file with mode: 0644]
chmod/chmod.1 [new file with mode: 0644]
chmod/chmod.c [new file with mode: 0644]
chown/Makefile [new file with mode: 0644]
chown/Makefile.postamble [new file with mode: 0644]
chown/Makefile.preamble [new file with mode: 0644]
chown/PB.project [new file with mode: 0644]
chown/chgrp.1 [new file with mode: 0644]
chown/chown.8 [new file with mode: 0644]
chown/chown.c [new file with mode: 0644]
cksum/crc.c [new file with mode: 0644]
compress/Makefile [new file with mode: 0644]
compress/Makefile.postamble [new file with mode: 0644]
compress/Makefile.preamble [new file with mode: 0644]
compress/PB.project [new file with mode: 0644]
compress/compress.1 [new file with mode: 0644]
compress/compress.c [new file with mode: 0644]
compress/uncompress.1 [new file with mode: 0644]
compress/zopen.3 [new file with mode: 0644]
compress/zopen.c [new file with mode: 0644]
cp/Makefile [new file with mode: 0644]
cp/Makefile.postamble [new file with mode: 0644]
cp/Makefile.preamble [new file with mode: 0644]
cp/PB.project [new file with mode: 0644]
cp/cp.1 [new file with mode: 0644]
cp/cp.c [new file with mode: 0644]
cp/extern.h [new file with mode: 0644]
cp/utils.c [new file with mode: 0644]
csh/strpct.c [new file with mode: 0644]
dd/Makefile [new file with mode: 0644]
dd/Makefile.postamble [new file with mode: 0644]
dd/Makefile.preamble [new file with mode: 0644]
dd/PB.project [new file with mode: 0644]
dd/args.c [new file with mode: 0644]
dd/conv.c [new file with mode: 0644]
dd/conv_tab.c [new file with mode: 0644]
dd/dd.1 [new file with mode: 0644]
dd/dd.c [new file with mode: 0644]
dd/dd.h [new file with mode: 0644]
dd/extern.h [new file with mode: 0644]
dd/misc.c [new file with mode: 0644]
dd/position.c [new file with mode: 0644]
df/Makefile [new file with mode: 0644]
df/Makefile.postamble [new file with mode: 0644]
df/Makefile.preamble [new file with mode: 0644]
df/PB.project [new file with mode: 0644]
df/df.1 [new file with mode: 0644]
df/df.c [new file with mode: 0644]
du/Makefile [new file with mode: 0644]
du/Makefile.postamble [new file with mode: 0644]
du/Makefile.preamble [new file with mode: 0644]
du/PB.project [new file with mode: 0644]
du/du.1 [new file with mode: 0644]
du/du.c [new file with mode: 0644]
install/Makefile [new file with mode: 0644]
install/Makefile.postamble [new file with mode: 0644]
install/Makefile.preamble [new file with mode: 0644]
install/PB.project [new file with mode: 0644]
install/install.1 [new file with mode: 0644]
install/pathnames.h [new file with mode: 0644]
install/xinstall.c [new file with mode: 0644]
ln/Makefile [new file with mode: 0644]
ln/Makefile.postamble [new file with mode: 0644]
ln/Makefile.preamble [new file with mode: 0644]
ln/PB.project [new file with mode: 0644]
ln/ln.1 [new file with mode: 0644]
ln/ln.c [new file with mode: 0644]
ln/symlink.7 [new file with mode: 0644]
ls/Makefile [new file with mode: 0644]
ls/Makefile.postamble [new file with mode: 0644]
ls/Makefile.preamble [new file with mode: 0644]
ls/PB.project [new file with mode: 0644]
ls/cmp.c [new file with mode: 0644]
ls/extern.h [new file with mode: 0644]
ls/ls.1 [new file with mode: 0644]
ls/ls.c [new file with mode: 0644]
ls/ls.h [new file with mode: 0644]
ls/print.c [new file with mode: 0644]
ls/stat_flags.c [new file with mode: 0644]
ls/util.c [new file with mode: 0644]
mkdir/Makefile [new file with mode: 0644]
mkdir/Makefile.postamble [new file with mode: 0644]
mkdir/Makefile.preamble [new file with mode: 0644]
mkdir/PB.project [new file with mode: 0644]
mkdir/mkdir.1 [new file with mode: 0644]
mkdir/mkdir.c [new file with mode: 0644]
mkfifo/Makefile [new file with mode: 0644]
mkfifo/Makefile.postamble [new file with mode: 0644]
mkfifo/Makefile.preamble [new file with mode: 0644]
mkfifo/PB.project [new file with mode: 0644]
mkfifo/mkfifo.1 [new file with mode: 0644]
mkfifo/mkfifo.c [new file with mode: 0644]
mknod/Makefile [new file with mode: 0644]
mknod/Makefile.postamble [new file with mode: 0644]
mknod/Makefile.preamble [new file with mode: 0644]
mknod/PB.project [new file with mode: 0644]
mknod/mknod.8 [new file with mode: 0644]
mknod/mknod.c [new file with mode: 0644]
mtree/Makefile [new file with mode: 0644]
mtree/Makefile.postamble [new file with mode: 0644]
mtree/Makefile.preamble [new file with mode: 0644]
mtree/PB.project [new file with mode: 0644]
mtree/compare.c [new file with mode: 0644]
mtree/create.c [new file with mode: 0644]
mtree/extern.h [new file with mode: 0644]
mtree/misc.c [new file with mode: 0644]
mtree/mtree.8 [new file with mode: 0644]
mtree/mtree.c [new file with mode: 0644]
mtree/mtree.h [new file with mode: 0644]
mtree/spec.c [new file with mode: 0644]
mtree/verify.c [new file with mode: 0644]
mv/Makefile [new file with mode: 0644]
mv/Makefile.postamble [new file with mode: 0644]
mv/Makefile.preamble [new file with mode: 0644]
mv/PB.project [new file with mode: 0644]
mv/mv.1 [new file with mode: 0644]
mv/mv.c [new file with mode: 0644]
mv/pathnames.h [new file with mode: 0644]
pax/Makefile [new file with mode: 0644]
pax/Makefile.postamble [new file with mode: 0644]
pax/Makefile.preamble [new file with mode: 0644]
pax/PB.project [new file with mode: 0644]
pax/ar_io.c [new file with mode: 0644]
pax/ar_subs.c [new file with mode: 0644]
pax/buf_subs.c [new file with mode: 0644]
pax/cache.c [new file with mode: 0644]
pax/cache.h [new file with mode: 0644]
pax/cpio.1 [new file with mode: 0644]
pax/cpio.c [new file with mode: 0644]
pax/cpio.h [new file with mode: 0644]
pax/extern.h [new file with mode: 0644]
pax/file_subs.c [new file with mode: 0644]
pax/ftree.c [new file with mode: 0644]
pax/ftree.h [new file with mode: 0644]
pax/gen_subs.c [new file with mode: 0644]
pax/getoldopt.c [new file with mode: 0644]
pax/options.c [new file with mode: 0644]
pax/options.h [new file with mode: 0644]
pax/pat_rep.c [new file with mode: 0644]
pax/pat_rep.h [new file with mode: 0644]
pax/pax.1 [new file with mode: 0644]
pax/pax.c [new file with mode: 0644]
pax/pax.h [new file with mode: 0644]
pax/sel_subs.c [new file with mode: 0644]
pax/sel_subs.h [new file with mode: 0644]
pax/tables.c [new file with mode: 0644]
pax/tables.h [new file with mode: 0644]
pax/tar.1 [new file with mode: 0644]
pax/tar.c [new file with mode: 0644]
pax/tar.h [new file with mode: 0644]
pax/tty_subs.c [new file with mode: 0644]
rm/Makefile [new file with mode: 0644]
rm/Makefile.postamble [new file with mode: 0644]
rm/Makefile.preamble [new file with mode: 0644]
rm/PB.project [new file with mode: 0644]
rm/rm.1 [new file with mode: 0644]
rm/rm.c [new file with mode: 0644]
rmdir/Makefile [new file with mode: 0644]
rmdir/Makefile.postamble [new file with mode: 0644]
rmdir/Makefile.preamble [new file with mode: 0644]
rmdir/PB.project [new file with mode: 0644]
rmdir/rmdir.1 [new file with mode: 0644]
rmdir/rmdir.c [new file with mode: 0644]
rmt/Makefile [new file with mode: 0644]
rmt/Makefile.postamble [new file with mode: 0644]
rmt/Makefile.preamble [new file with mode: 0644]
rmt/PB.project [new file with mode: 0644]
rmt/rmt.8 [new file with mode: 0644]
rmt/rmt.c [new file with mode: 0644]
shar/Makefile [new file with mode: 0644]
shar/Makefile.postamble [new file with mode: 0644]
shar/Makefile.preamble [new file with mode: 0644]
shar/PB.project [new file with mode: 0644]
shar/shar.1 [new file with mode: 0644]
shar/shar.sh [new file with mode: 0644]
tcopy/Makefile [new file with mode: 0644]
tcopy/Makefile.postamble [new file with mode: 0644]
tcopy/Makefile.preamble [new file with mode: 0644]
tcopy/PB.project [new file with mode: 0644]
tcopy/tcopy.1 [new file with mode: 0644]
tcopy/tcopy.c [new file with mode: 0644]
touch/Makefile [new file with mode: 0644]
touch/Makefile.postamble [new file with mode: 0644]
touch/Makefile.preamble [new file with mode: 0644]
touch/PB.project [new file with mode: 0644]
touch/touch.1 [new file with mode: 0644]
touch/touch.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..97ed83a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = file_cmds
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Aggregate
+
+TOOLS = chflags chmod chown compress cp dd df du install ln ls\
+        mkdir mkfifo mknod mtree mv pax rm rmdir rmt shar tcopy\
+        touch
+
+OTHERSRCS = PROJECT Makefile.preamble Makefile Makefile.postamble
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = aggregate.make
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+NEXTSTEP_PB_CFLAGS = -Wall -Werror
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/Makefile.postamble b/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/Makefile.preamble b/Makefile.preamble
new file mode 100644 (file)
index 0000000..8f8a3bd
--- /dev/null
@@ -0,0 +1 @@
+include $(MAKEFILEPATH)/CoreOS/ProjectBuilder/Makefile.Preamble.Common
diff --git a/PB.project b/PB.project
new file mode 100644 (file)
index 0000000..09da8c8
--- /dev/null
@@ -0,0 +1,49 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        OTHER_SOURCES = (PROJECT, Makefile.preamble, Makefile, Makefile.postamble); 
+        SUBPROJECTS = (
+            chflags, 
+            chmod, 
+            chown, 
+            compress, 
+            cp, 
+            dd, 
+            df, 
+            du, 
+            install, 
+            ln, 
+            ls, 
+            mkdir, 
+            mkfifo, 
+            mknod, 
+            mtree, 
+            mv, 
+            pax, 
+            rm, 
+            rmdir, 
+            rmt, 
+            shar, 
+            tcopy, 
+            touch
+        ); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_COMPILEROPTIONS = "-Wall -Werror"; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = file_cmds; 
+    PROJECTTYPE = Aggregate; 
+    PROJECTVERSION = 2.8; 
+    TARGETS = (); 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/PROJECT b/PROJECT
new file mode 100644 (file)
index 0000000..6c7fd2c
--- /dev/null
+++ b/PROJECT
@@ -0,0 +1,10 @@
+Project                : file_cmds
+Description    : File Commands
+Distribution   : NetBSD Tue Oct  6 14:50:43 PDT 1998
+Source         : NetBSD Foundation, Inc.
+Location       : cvs.netbsd.org:/cvsroot
+Owner          : wsanchez
+Releases       : Titan
+Dependancies   : 
+
+Some file commands in NetBSD implement the -h flag and can otherwise charge permissions on symlinks. But this requires lchmod(2) from NetBSD and we don't currently have that, so it is disabled with -D__APPLE__. 
diff --git a/chflags/Makefile b/chflags/Makefile
new file mode 100644 (file)
index 0000000..4f99118
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = chflags
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = chflags.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chflags.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/chflags/Makefile.postamble b/chflags/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/chflags/Makefile.preamble b/chflags/Makefile.preamble
new file mode 100644 (file)
index 0000000..7a86fc2
--- /dev/null
@@ -0,0 +1,5 @@
+vpath stat_flags.c ../ls
+
+CFILES += stat_flags.c
+
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/chflags/PB.project b/chflags/PB.project
new file mode 100644 (file)
index 0000000..bf7e967
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (chflags.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chflags.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = chflags; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/chflags/chflags.1 b/chflags/chflags.1
new file mode 100644 (file)
index 0000000..c7ab4bc
--- /dev/null
@@ -0,0 +1,126 @@
+.\"    $NetBSD: chflags.1,v 1.6 1997/10/18 12:39:50 lukem Exp $
+.\"
+.\" 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. 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.
+.\"
+.\"    @(#)chflags.1   8.4 (Berkeley) 5/2/95
+.\"
+.Dd May 2, 1995
+.Dt CHFLAGS 1
+.Os
+.Sh NAME
+.Nm chflags
+.Nd change file flags
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar flags
+.Ar file ...
+.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 Ds
+.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 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.
+.It Fl R
+Change the file flags for the file hierarchies rooted
+in the files instead of just the files themselves.
+.El
+.Pp
+Flags are a comma separated list of keywords.
+The following keywords are currently defined:
+.Bd -literal -offset indent compact
+arch   set the archived flag (super-user only)
+opaque set the opaque flag (owner or super-user only)
+nodump set the nodump flag (owner or super-user only)
+sappnd set the system append-only flag (super-user only)
+schg   set the system immutable flag (super-user only)
+uappnd set the user append-only flag (owner or super-user only)
+uchg   set the user immutable flag (owner or super-user only)
+.Ed
+.Pp
+Putting the letters
+.Dq no
+before an option causes the flag to be turned off.
+For example:
+.Bd -literal -offset indent compact
+nouchg the immutable bit should be cleared
+.Ed
+.Pp
+Symbolic links do not have flags, so unless the
+.Fl H
+or
+.Fl L
+option is set,
+.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
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr chflags 2 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
diff --git a/chflags/chflags.c b/chflags/chflags.c
new file mode 100644 (file)
index 0000000..54d1bc3
--- /dev/null
@@ -0,0 +1,187 @@
+/*     $NetBSD: chflags.c,v 1.5 1997/10/18 12:39:54 lukem Exp $        */
+
+/*
+ * 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
+__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[] = "from: @(#)chflags.c    8.5 (Berkeley) 4/1/94";
+#else
+__RCSID("$NetBSD: chflags.c,v 1.5 1997/10/18 12:39:54 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#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>
+
+int    main __P((int, char **));
+u_long string_to_flags __P((char **, u_long *, u_long *));
+void   usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       FTS *ftsp;
+       FTSENT *p;
+       u_long clear, set;
+       long val;
+       int Hflag, Lflag, Pflag, Rflag, ch, fts_options, oct, rval;
+       char *flags, *ep;
+
+       Hflag = Lflag = Pflag = Rflag = 0;
+       while ((ch = getopt(argc, argv, "HLPR")) != -1)
+               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 '?':
+               default:
+                       usage();
+               }
+       argv += optind;
+       argc -= optind;
+
+       if (argc < 2)
+               usage();
+
+       fts_options = FTS_PHYSICAL;
+       if (Rflag) {
+               if (Hflag)
+                       fts_options |= FTS_COMFOLLOW;
+               if (Lflag) {
+                       fts_options &= ~FTS_PHYSICAL;
+                       fts_options |= FTS_LOGICAL;
+               }
+       }
+
+       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 (string_to_flags(&flags, &set, &clear))
+                        errx(1, "invalid flag: %s", flags);
+               clear = ~clear;
+               oct = 0;
+       }
+
+       if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL)
+               err(1, "fts_open `%s'", argv[0]); 
+
+       for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+               switch (p->fts_info) {
+               case FTS_D:
+                       if (Rflag)              /* Change it at FTS_DP. */
+                               continue;
+                       fts_set(ftsp, p, FTS_SKIP);
+                       break;
+               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.
+                        */
+                       continue;
+               default:
+                       break;
+               }
+               if (oct) {
+                       if (!chflags(p->fts_accpath, set))
+                               continue;
+               } else {
+                       p->fts_statp->st_flags |= set;
+                       p->fts_statp->st_flags &= clear;
+                       if (!chflags(p->fts_accpath, p->fts_statp->st_flags))
+                               continue;
+               }
+               warn("%s", p->fts_path);
+               rval = 1;
+       }
+       if (errno)
+               err(1, "fts_read");
+       exit(rval);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr,
+           "usage: chflags [-R [-H | -L | -P]] flags file ...\n");
+       exit(1);
+}
diff --git a/chmod/Makefile b/chmod/Makefile
new file mode 100644 (file)
index 0000000..34a6a63
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = chmod
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = chmod.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chmod.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/chmod/Makefile.postamble b/chmod/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/chmod/Makefile.preamble b/chmod/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/chmod/PB.project b/chmod/PB.project
new file mode 100644 (file)
index 0000000..644371e
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (chmod.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chmod.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = chmod; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/chmod/chmod.1 b/chmod/chmod.1
new file mode 100644 (file)
index 0000000..6a52eb6
--- /dev/null
@@ -0,0 +1,306 @@
+.\"    $NetBSD: chmod.1,v 1.11 1997/10/20 08:51:10 enami Exp $
+.\"
+.\" 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. 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.
+.\"
+.\"    @(#)chmod.1     8.4 (Berkeley) 3/31/94
+.\"
+.Dd March 31, 1994
+.Dt CHMOD 1
+.Os
+.Sh NAME
+.Nm chmod
+.Nd change file modes
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl h
+.Ar mode
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility modifies the file mode bits of the listed files
+as specified by the
+.Ar mode
+operand.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.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 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.
+.It Fl R
+Change the modes of the file hierarchies rooted in the files
+instead of just the files themselves.
+.\" .It Fl h
+.\" If
+.\" .Ar file
+.\" is symbolic link, the mode of the link is changed.
+.El
+.Pp
+.\" If the option
+.\" .Fl h
+.\" is not given,
+Unless the
+.Fl H
+or
+.Fl L
+option is set,
+.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
+Only the owner of a file or the super-user is permitted to change
+the mode of a file.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh MODES
+Modes may be absolute or symbolic.
+An absolute mode is an octal number constructed by
+.Ar or-ing
+the following values:
+.Pp
+.Bl -tag -width 6n -compact -offset indent
+.It Li 4000
+set-user-ID-on-execution
+.It Li 2000
+set-group-ID-on-execution
+.It Li 1000
+sticky bit, see
+.Xr chmod 2
+.It Li 0400
+read by owner
+.It Li 0200
+write by owner
+.It Li 0100
+execute (or search for directories) by owner
+.It Li 0070
+read, write, execute/search by group
+.It Li 0007
+read, write, execute/search by others
+.El
+.Pp
+The read, write, and execute/search values for group and others
+are encoded as described for owner.
+.Pp
+The symbolic mode is described by the following grammar:
+.Bd -literal -offset indent
+mode         ::= clause [, clause ...]
+clause       ::= [who ...] [action ...] last_action
+action       ::= op [perm ...]
+last_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
+.ne 1i
+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 mode of the original file.
+.It g
+The group permission bits in the mode of the original file.
+.It o
+The other permission bits in the mode of the original 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
+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 clear, 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 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.
+.Sh EXAMPLES
+.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 BUGS
+There's no
+.Ar perm
+option for the naughty bits.
+.Sh SEE ALSO
+.Xr install 1 ,
+.Xr chmod 2 ,
+.Xr stat 2 ,
+.Xr umask 2 ,
+.Xr fts 3 ,
+.Xr setmode 3 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible with the exception of the
+.Ar perm
+symbols
+.Dq t
+and
+.Dq X
+which are not included in that standard.
diff --git a/chmod/chmod.c b/chmod/chmod.c
new file mode 100644 (file)
index 0000000..4dae785
--- /dev/null
@@ -0,0 +1,242 @@
+/*     $NetBSD: chmod.c,v 1.20 1998/07/28 05:31:22 mycroft Exp $       */
+
+/*
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: chmod.c,v 1.20 1998/07/28 05:31:22 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+int main __P((int, char *[]));
+void usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       FTS *ftsp;
+       FTSENT *p;
+       mode_t *set;
+       long val;
+       int oct, omode;
+       int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval;
+       char *ep, *mode;
+       int (*change_mode) __P((const char *, mode_t));
+
+       set = NULL;     /* XXX gcc -Wuninitialized */
+       omode = 0;      /* XXX gcc -Wuninitialized */
+
+       (void)setlocale(LC_ALL, "");
+
+       Hflag = Lflag = Rflag = fflag = hflag = 0;
+       while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -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':               /* XXX: undocumented. */
+                       fflag = 1;
+                       break;
+#ifndef __APPLE__
+               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 NetBSD 1.3,
+                        * lchmod(2) is introduced and this option does real
+                        * work.
+                        */
+                       hflag = 1;
+                       break;
+#endif
+               /*
+                * 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 '?':
+               default:
+                       usage();
+               }
+done:  argv += optind;
+       argc -= optind;
+
+       if (argc < 2)
+               usage();
+
+       fts_options = FTS_PHYSICAL;
+       if (Rflag) {
+               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;
+               }
+       }
+#ifndef __APPLE__
+       if (hflag)
+               change_mode = lchmod;
+       else
+            change_mode = chmod;
+#else
+        change_mode = chmod;
+#endif
+
+       mode = *argv;
+       if (*mode >= '0' && *mode <= '7') {
+               errno = 0;
+               val = strtol(mode, &ep, 8);
+               if (val > INT_MAX || val < 0)
+                       errno = ERANGE;
+               if (errno)
+                       err(1, "invalid file mode: %s", mode);
+               if (*ep)
+                       errx(1, "invalid file mode: %s", mode);
+               omode = val;
+               oct = 1;
+       } else {
+               if ((set = setmode(mode)) == NULL)
+                       errx(1, "invalid file mode: %s", mode);
+               oct = 0;
+       }
+
+       if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+               err(1, argv[0]);
+       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_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.
+                        */
+#ifndef __APPLE__
+                       if (!hflag)
+                               continue;
+                       /* else */
+                       /* FALLTHROUGH */
+#else
+                       continue;
+#endif
+               default:
+                       break;
+               }
+               if ((*change_mode)(p->fts_accpath, oct ? omode :
+                   getmode(set, p->fts_statp->st_mode)) && !fflag) {
+                       warn("%s", p->fts_path);
+                       rval = 1;
+               }
+       }
+       if (errno)
+               err(1, "fts_read");
+       exit(rval);
+       /* NOTREACHED */
+}
+
+void
+usage()
+{
+#ifndef __APPLE__
+       (void)fprintf(stderr,
+           "usage: chmod [-R [-H | -L | -P]] [-h] mode file ...\n");
+#else
+       (void)fprintf(stderr,
+           "usage: chmod [-R [-H | -L | -P]] mode file ...\n");
+#endif
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/chown/Makefile b/chown/Makefile
new file mode 100644 (file)
index 0000000..eb0029c
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = chown
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = chown.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chgrp.1\
+            chown.8
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+NEXTSTEP_PB_CFLAGS = -DSUPPORT_DOT
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/chown/Makefile.postamble b/chown/Makefile.postamble
new file mode 100644 (file)
index 0000000..4da9b62
--- /dev/null
@@ -0,0 +1,4 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
+
+after_install::
+       $(LINKPRODUCT) $(DSTROOT)/usr/bin/chgrp
diff --git a/chown/Makefile.preamble b/chown/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/chown/PB.project b/chown/PB.project
new file mode 100644 (file)
index 0000000..d716382
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (chown.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chgrp.1, chown.8); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_COMPILEROPTIONS = "-DSUPPORT_DOT"; 
+    NEXTSTEP_INSTALLDIR = /usr/sbin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = chown; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/chown/chgrp.1 b/chown/chgrp.1
new file mode 100644 (file)
index 0000000..379a94d
--- /dev/null
@@ -0,0 +1,146 @@
+.\" 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.
+.\"
+.\"     from: @(#)chgrp.1      8.3 (Berkeley) 3/31/94
+.\"    $NetBSD: chgrp.1,v 1.10 1997/12/21 18:11:19 kleink Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHGRP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl fh
+.Ar group
+.Ar files ...
+.Sh DESCRIPTION
+The
+.Nm
+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
+Options:
+.Bl -tag -width Ds
+.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 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.
+.It Fl R
+Change the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.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
+.Ar file
+is a symbolic link, the group of the link is changed.
+.El
+.Pp
+If
+.Fl h
+is not given, unless the 
+.Fl H
+or
+.Fl L
+option is set,
+.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
+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
+must belong to the specified group and be the owner of the file,
+or be the super-user.
+.Pp
+Unless invoked by the super-user,
+.Nm
+clears the set-user-id and set-group-id bits on a file to prevent
+accidental or mischievous creation of set-user-id or set-group-id
+programs.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+Group ID file
+.El
+.Sh SEE ALSO
+.Xr chown 2 ,
+.Xr lchown 2 ,
+.Xr fts 3 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be POSIX 1003.2 compatible.
diff --git a/chown/chown.8 b/chown/chown.8
new file mode 100644 (file)
index 0000000..6e8065e
--- /dev/null
@@ -0,0 +1,154 @@
+.\" 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.
+.\"
+.\"     from: @(#)chown.8      8.3 (Berkeley) 3/31/94
+.\"    $NetBSD: chown.8,v 1.11 1998/10/05 21:37:38 kim Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHOWN 8
+.Os BSD 4
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl fh
+.Ar owner Op Ar :group
+.Ar file ...
+.Nm ""
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl fh
+.Ar :group
+.Ar file ...
+.Sh DESCRIPTION
+.Nm
+sets the user ID and/or the group ID of the specified files.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.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 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.
+.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 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
+.Ar file
+is a symbolic link, the owner and/or group of the link is changed.
+.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
+.Fl L
+option cannot be used together with the
+.Fl h
+option.
+.Pp
+The
+.Ar owner
+and
+.Ar group
+operands are both optional, however, 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
+The ownership of a file may only be altered by a super-user for
+obvious security reasons.
+.Pp
+Unless invoked by the super-user,
+.Nm
+clears the set-user-id and set-group-id bits on a file to prevent
+accidental or mischievous creation of set-user-id and set-group-id
+programs.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+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.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr lchown 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+command is expected to be POSIX 1003.2 compliant.
diff --git a/chown/chown.c b/chown/chown.c
new file mode 100644 (file)
index 0000000..c2dcdad
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: chown.c,v 1.15 1998/10/05 21:37:39 kim Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void   a_gid __P((char *));
+void   a_uid __P((char *));
+void   chownerr __P((char *));
+u_long id __P((char *, char *));
+int    main __P((int, char **));
+void   usage __P((void));
+
+uid_t uid;
+gid_t gid;
+int Rflag, ischown, fflag;
+char *gname, *myname;
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       FTS *ftsp;
+       FTSENT *p;
+       int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
+       char *cp;
+       int (*change_owner) __P((const char *, uid_t, gid_t));
+       
+       setlocale(LC_ALL, "");
+
+       myname = (cp = strrchr(*argv, '/')) ? cp + 1 : *argv;
+       ischown = myname[2] == 'o';
+       
+       Hflag = Lflag = Pflag = hflag = 0;
+       while ((ch = getopt(argc, argv, "HLPRfh")) != -1)
+               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':
+                       /*
+                        * In System V the -h option causes chown/chgrp to
+                        * change the owner/group of the symbolic link.
+                        * 4.4BSD's symbolic links didn't have owners/groups,
+                        * so it was an undocumented noop.
+                        * In NetBSD 1.3, lchown(2) is introduced.
+                        */
+                       hflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argv += optind;
+       argc -= optind;
+
+       if (argc < 2)
+               usage();
+
+       fts_options = FTS_PHYSICAL;
+       if (Rflag) {
+               if (Hflag)
+                       fts_options |= FTS_COMFOLLOW;
+               if (Lflag) {
+                       if (hflag)
+                               errx(1, "the -L and -h options may not be specified together.");
+                       fts_options &= ~FTS_PHYSICAL;
+                       fts_options |= FTS_LOGICAL;
+               }
+       }
+#ifndef __APPLE__
+        if (hflag)
+               change_owner = lchown;
+       else
+            change_owner = chown;
+#else
+        change_owner = chown;
+#endif
+
+       uid = gid = -1;
+       if (ischown) {
+#ifdef SUPPORT_DOT
+               if ((cp = strchr(*argv, '.')) != NULL) {
+                       *cp++ = '\0';
+                       a_gid(cp);
+               } else
+#endif
+               if ((cp = strchr(*argv, ':')) != NULL) {
+                       *cp++ = '\0';
+                       a_gid(cp);
+               } 
+               a_uid(*argv);
+       } else 
+               a_gid(*argv);
+
+       if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+               err(1, "%s", "");
+
+       for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+               switch (p->fts_info) {
+               case FTS_D:
+                       if (!Rflag)             /* Change it at FTS_DP. */
+                               fts_set(ftsp, p, FTS_SKIP);
+                       continue;
+               case FTS_DNR:                   /* Warn, chown, 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.
+                        */
+#ifndef __APPLE__
+                    if (!hflag)
+                        continue;
+#else
+                    continue;
+#endif
+                        /* else */
+                       /* FALLTHROUGH */
+               default:
+                       break;
+               }
+
+               if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) {
+                       warn("%s", p->fts_path);
+                       rval = 1;
+               }
+       }
+       if (errno)
+               err(1, "fts_read");
+       exit(rval);
+}
+
+void
+a_gid(s)
+       char *s;
+{
+       struct group *gr;
+
+       if (*s == '\0')                 /* Argument was "uid[:.]". */
+               return;
+       gname = s;
+       gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
+}
+
+void
+a_uid(s)
+       char *s;
+{
+       struct passwd *pw;
+
+       if (*s == '\0')                 /* Argument was "[:.]gid". */
+               return;
+       uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
+}
+
+u_long
+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(1, "%s", name);
+       if (*ep != '\0')
+               errx(1, "%s: invalid %s name", name, type);
+       return (val);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr,
+           "usage: %s [-R [-H | -L | -P]] [-fh] %s file ...\n",
+           myname, ischown ? "[owner][:group]" : "group");
+       exit(1);
+}
diff --git a/cksum/crc.c b/cksum/crc.c
new file mode 100644 (file)
index 0000000..ac889d6
--- /dev/null
@@ -0,0 +1,149 @@
+/*     $NetBSD: crc.c,v 1.8 1997/10/17 11:37:03 lukem Exp $    */
+
+/*-
+ * 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
+#if 0
+static char sccsid[] = "@(#)crc.c      8.1 (Berkeley) 6/17/93";
+#else
+__RCSID("$NetBSD: crc.c,v 1.8 1997/10/17 11:37:03 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static const u_int32_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.
+ */
+u_int32_t crc_total = ~0;              /* The crc over a number of files. */
+
+int
+crc(fd, cval, clen)
+       register int fd;
+       u_int32_t *cval, *clen;
+{
+       register u_char *p;
+       register int nr;
+       register u_int32_t crc, len;
+       u_char buf[16 * 1024];
+
+#define        COMPUTE(var, ch)        (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+
+       crc = len = 0;
+       crc_total = ~crc_total;
+       while ((nr = read(fd, buf, sizeof(buf))) > 0)
+               for (len += nr, p = buf; nr--; ++p) {
+                       COMPUTE(crc, *p);
+                       COMPUTE(crc_total, *p);
+               }
+       if (nr < 0)
+               return (1);
+
+       *clen = len;
+
+       /* Include the length of the file. */
+       for (; len != 0; len >>= 8) {
+               COMPUTE(crc, len & 0xff);
+               COMPUTE(crc_total, len & 0xff);
+       }
+
+       *cval = ~crc;
+       crc_total = ~crc_total;
+       return (0);
+}
diff --git a/compress/Makefile b/compress/Makefile
new file mode 100644 (file)
index 0000000..0c13072
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = compress
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = compress.c zopen.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble compress.1\
+            zopen.3 uncompress.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/compress/Makefile.postamble b/compress/Makefile.postamble
new file mode 100644 (file)
index 0000000..cb8937b
--- /dev/null
@@ -0,0 +1,5 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
+
+after_install::
+       $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/uncompress
+#      $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/zcat
diff --git a/compress/Makefile.preamble b/compress/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/compress/PB.project b/compress/PB.project
new file mode 100644 (file)
index 0000000..d9c8269
--- /dev/null
@@ -0,0 +1,32 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (compress.c, zopen.c); 
+        OTHER_SOURCES = (
+            Makefile, 
+            Makefile.preamble, 
+            Makefile.postamble, 
+            compress.1, 
+            zopen.3, 
+            uncompress.1
+        ); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = compress; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/compress/compress.1 b/compress/compress.1
new file mode 100644 (file)
index 0000000..2534855
--- /dev/null
@@ -0,0 +1,176 @@
+.\"    $NetBSD: compress.1,v 1.6 1997/09/15 10:58:37 lukem Exp $
+.\"
+.\" Copyright (c) 1986, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" 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.
+.\" 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.
+.\"
+.\"     @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt COMPRESS 1
+.Os BSD 4.3
+.Sh NAME
+.Nm compress ,
+.\".Nm uncompress ,
+.Nm uncompress
+.\".Nm zcat
+.Nd compress and expand data
+.Sh SYNOPSIS
+.Nm
+.Op Fl cfv
+.Op Fl b Ar bits
+.Op Ar
+.Nm uncompress
+.Op Fl cfv
+.Op Ar 
+.\".Nm zcat
+.\".Op Ar
+.Sh DESCRIPTION
+.Nm
+reduces the size of the named files using adaptive Lempel-Ziv coding.
+Each
+.Ar file
+is renamed to the same name plus the extension
+.Dq .Z .
+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.
+If compression would not reduce the size of a
+.Ar file ,
+the file is ignored.
+.Pp
+.Nm uncompress
+restores the compressed files to their original form, renaming the
+files by deleting the
+.Dq .Z
+extension.
+.\".Pp
+.\".Nm Zcat
+.\"is an alias for
+.\".Dq "uncompress -c" .
+.Pp
+If renaming the files would cause files to be overwritten and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+If prompting is not possible or confirmation is not received, the files
+are not overwritten.
+.Pp
+If no files are specified, the standard input is compressed or uncompressed
+to the standard output.
+If either the input and output files are not regular files, the checks for
+reduction in size and file overwriting are not performed, the input file is
+not removed, and the attributes of the input file are not retained.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Specify the
+.Ar bits
+code limit (see below).
+.It Fl c
+Compressed or uncompressed output is written to the standard output.
+No files are modified.
+.It Fl f
+Force compression of
+.Ar file ,
+even if it is not actually reduced in size.
+Additionally, files are overwritten without prompting for confirmation.
+.It Fl v
+Print the percentage reduction of each file.
+.El
+.Pp
+.Nm
+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
+flag is reached (the default is 16).
+.Ar Bits
+must be between 9 and 16.
+.Pp
+After the
+.Ar bits
+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
+flag is omitted for
+.Ar 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
+.ne 8
+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.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr zcat 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 HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/compress/compress.c b/compress/compress.c
new file mode 100644 (file)
index 0000000..2aa84ee
--- /dev/null
@@ -0,0 +1,491 @@
+/*     $NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $     */
+
+/*-
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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) 1992, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
+#else
+__RCSID("$NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void   compress __P((char *, char *, int));
+void   cwarn __P((const char *, ...));
+void   cwarnx __P((const char *, ...));
+void   decompress __P((char *, char *, int));
+int    permission __P((char *));
+void   setfile __P((char *, struct stat *));
+void   usage __P((int));
+
+int    main __P((int, char *[]));
+extern FILE *zopen __P((const char *fname, const char *mode, int bits));
+
+int eval, force, verbose;
+int isstdout, isstdin;
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+        enum {COMPRESS, DECOMPRESS} style = COMPRESS;
+       size_t len;
+       int bits, cat, ch;
+       char *p, newname[MAXPATHLEN];
+
+       if ((p = strrchr(argv[0], '/')) == NULL)
+               p = argv[0];
+       else
+               ++p;
+       if (!strcmp(p, "uncompress"))
+               style = DECOMPRESS;
+        else if (!strcmp(p, "compress"))
+                style = COMPRESS;
+        else if (!strcmp(p, "zcat")) {
+                style = DECOMPRESS;
+                cat = 1;
+        }
+       else
+               errx(1, "unknown program name");
+
+       bits = cat = 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) {
+               switch(style) {
+               case COMPRESS:
+                       isstdout = 1;
+                       isstdin = 1;
+                       (void)compress("/dev/stdin", "/dev/stdout", bits);
+                       break;
+               case DECOMPRESS:
+                       isstdout = 1;
+                       isstdin = 1;
+                       (void)decompress("/dev/stdin", "/dev/stdout", bits);
+                       break;
+               }
+               exit (eval);
+       }
+
+       if (cat == 1 && argc > 1)
+               errx(1, "the -c option permits only a single file argument");
+
+       for (; *argv; ++argv) {
+               isstdout = 0;
+               switch(style) {
+               case COMPRESS:
+                       if (cat) {
+                               isstdout = 1;
+                               compress(*argv, "/dev/stdout", bits);
+                               break;
+                       }
+                       if ((p = strrchr(*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:
+                       len = strlen(*argv);
+                       if ((p = strrchr(*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);
+                               if (cat)
+                                       isstdout = 1;
+                       } 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);
+                               if (cat)
+                                       isstdout = 1;
+                       }
+                       break;
+               }
+       }
+       exit (eval);
+}
+
+void
+compress(in, out, bits)
+       char *in, *out;
+       int bits;
+{
+       int nr;
+       struct stat isb, sb;
+       FILE *ifp, *ofp;
+       int exists, isreg, oreg;
+       u_char buf[BUFSIZ];
+
+       if (!isstdout) {
+               exists = !stat(out, &sb);
+               if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
+                       return;
+               oreg = !exists || S_ISREG(sb.st_mode);
+       } else
+               oreg = 0;
+
+       ifp = ofp = NULL;
+       if ((ifp = fopen(in, "r")) == NULL) {
+               cwarn("%s", in);
+               return;
+       }
+
+       if (!isstdin) {
+               if (stat(in, &isb)) {           /* DON'T FSTAT! */
+                       cwarn("%s", in);
+                       goto err;
+               }
+               if (!S_ISREG(isb.st_mode))
+                       isreg = 0;
+               else
+                       isreg = 1;
+       } else
+               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 (isreg && oreg) {
+               if (stat(out, &sb)) {
+                       cwarn("%s", out);
+                       goto err;
+               }
+
+               if (!force && sb.st_size >= isb.st_size) {
+                       if (verbose)
+               (void)printf("%s: file would grow; left unmodified\n", in);
+                       if (unlink(out))
+                               cwarn("%s", out);
+                       goto err;
+               }
+
+               setfile(out, &isb);
+
+               if (unlink(in))
+                       cwarn("%s", in);
+
+               if (verbose) {
+                       (void)printf("%s: ", out);
+                       if (isb.st_size > sb.st_size)
+                               (void)printf("%.0f%% compression\n",
+                                   ((double)sb.st_size / isb.st_size) * 100.0);
+                       else
+                               (void)printf("%.0f%% expansion\n",
+                                   ((double)isb.st_size / sb.st_size) * 100.0);
+               }
+       }
+       return;
+
+err:   if (ofp) {
+               if (oreg)
+                       (void)unlink(out);
+               (void)fclose(ofp);
+       }
+       if (ifp)
+               (void)fclose(ifp);
+}
+
+void
+decompress(in, out, bits)
+       char *in, *out;
+       int bits;
+{
+       int nr;
+       struct stat sb;
+       FILE *ifp, *ofp;
+       int exists, isreg, oreg;
+       u_char buf[BUFSIZ];
+
+       if (!isstdout) {
+               exists = !stat(out, &sb);
+               if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
+                       return;
+               oreg = !exists || S_ISREG(sb.st_mode);
+       } else
+               oreg = 0;
+
+       ifp = ofp = NULL;
+       if ((ofp = fopen(out, "w")) == NULL) {
+               cwarn("%s", out);
+               return;
+       }
+
+       if ((ifp = zopen(in, "r", bits)) == NULL) {
+               cwarn("%s", in);
+               goto err;
+       }
+       if (!isstdin) {
+               if (stat(in, &sb)) {
+                       cwarn("%s", in);
+                       goto err;
+               }
+               if (!S_ISREG(sb.st_mode))
+                       isreg = 0;
+               else
+                       isreg = 1;
+       } else
+               isreg = 0;
+
+       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 (isreg && oreg) {
+               setfile(out, &sb);
+
+               if (unlink(in))
+                       cwarn("%s", in);
+       }
+       return;
+
+err:   if (ofp) {
+               if (oreg)
+                       (void)unlink(out);
+               (void)fclose(ofp);
+       }
+       if (ifp)
+               (void)fclose(ifp);
+}
+
+void
+setfile(name, fs)
+       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))
+               cwarn("chown: %s", name);
+
+       /*
+        * Restore the file's flags.  However, do this only if the original
+        * file had any flags set; this avoids a warning on file-systems that
+        * do not support flags.
+        */
+       if (fs->st_flags != 0 && chflags(name, fs->st_flags))
+               cwarn("chflags: %s", name);
+}
+
+int
+permission(fname)
+       char *fname;
+{
+       int ch, first;
+
+       if (!isatty(fileno(stderr)))
+               return (0);
+       (void)fprintf(stderr, "overwrite %s? ", fname);
+       first = ch = getchar();
+       while (ch != '\n' && ch != EOF)
+               ch = getchar();
+       return (first == 'y');
+}
+
+void
+usage(iscompress)
+       int iscompress;
+{
+       if (iscompress)
+               (void)fprintf(stderr,
+                   "usage: compress [-cfv] [-b bits] [file ...]\n");
+       else
+               (void)fprintf(stderr,
+                   "usage: uncompress [-c] [-b bits] [file ...]\n");
+       exit(1);
+}
+
+void
+#if __STDC__
+cwarnx(const char *fmt, ...)
+#else
+cwarnx(fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarnx(fmt, ap);
+       va_end(ap);
+       eval = 1;
+}
+
+void
+#if __STDC__
+cwarn(const char *fmt, ...)
+#else
+cwarn(fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
+       eval = 1;
+}
diff --git a/compress/uncompress.1 b/compress/uncompress.1
new file mode 100644 (file)
index 0000000..208c3ce
--- /dev/null
@@ -0,0 +1 @@
+.so man1/compress.1
\ No newline at end of file
diff --git a/compress/zopen.3 b/compress/zopen.3
new file mode 100644 (file)
index 0000000..1e75687
--- /dev/null
@@ -0,0 +1,142 @@
+.\"    $NetBSD: zopen.3,v 1.4 1998/02/06 06:17:43 perry Exp $
+.\"
+.\" Copyright (c) 1992, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" 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.
+.\"
+.\"    @(#)zopen.3     8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt ZOPEN 3
+.Os
+.Sh NAME
+.Nm zopen
+.Nd compressed stream open function
+.Sh SYNOPSIS
+.Fd #include <stdio.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
+.Fn 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 zopen
+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/compress/zopen.c b/compress/zopen.c
new file mode 100644 (file)
index 0000000..c165117
--- /dev/null
@@ -0,0 +1,748 @@
+/*     $NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $  */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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 defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)zopen.c    8.1 (Berkeley) 6/27/93";
+#else
+static char rcsid[] = "$NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*-
+ * 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>
+
+#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 */
+       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 paramenters */
+               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 __P((struct s_zstate *));
+static void    cl_hash __P((struct s_zstate *, count_int));
+static code_int        getcode __P((struct s_zstate *));
+static int     output __P((struct s_zstate *, code_int));
+static int     zclose __P((void *));
+FILE          *zopen __P((const char *, const char *, int));
+static int     zread __P((void *, char *, int));
+static int     zwrite __P((void *, const char *, int));
+
+/*-
+ * 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(cookie, wbp, num)
+       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 = (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 (i = 0; 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(cookie)
+       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(zs, ocode)
+       struct s_zstate *zs;
+       code_int ocode;
+{
+       int bits, r_off;
+       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(cookie, rbp, num)
+       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) {
+               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 - 1;
+                       if ((code = getcode(zs)) == -1) /* O, untimely death! */
+                               break;
+               }
+               incode = code;
+
+               /* Special case for KwKwK string. */
+               if (code >= free_ent) {
+                       *stackp++ = finchar;
+                       code = oldcode;
+               }
+
+               /* 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) {
+                       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(zs)
+       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(zs)                   /* Table clear for block compress. */
+       struct s_zstate *zs;
+{
+       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(zs, cl_hsize)                  /* Reset code table. */
+       struct s_zstate *zs;
+       count_int cl_hsize;
+{
+       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(fname, mode, bits)
+       const char *fname, *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 = 1 << 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/cp/Makefile b/cp/Makefile
new file mode 100644 (file)
index 0000000..8d778e8
--- /dev/null
@@ -0,0 +1,51 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = cp
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = extern.h
+
+CFILES = cp.c utils.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble cp.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+NEXTSTEP_PB_CFLAGS = -DVM_AND_BUFFER_CACHE_SYNCHRONIZED
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/cp/Makefile.postamble b/cp/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/cp/Makefile.preamble b/cp/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/cp/PB.project b/cp/PB.project
new file mode 100644 (file)
index 0000000..e3be88f
--- /dev/null
@@ -0,0 +1,27 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (extern.h); 
+        OTHER_LINKED = (cp.c, utils.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, cp.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_COMPILEROPTIONS = "-DVM_AND_BUFFER_CACHE_SYNCHRONIZED"; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = cp; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/cp/cp.1 b/cp/cp.1
new file mode 100644 (file)
index 0000000..e5016c8
--- /dev/null
+++ b/cp/cp.1
@@ -0,0 +1,215 @@
+.\"    $NetBSD: cp.1,v 1.12 1997/10/11 02:14:42 enami Exp $
+.\"
+.\" 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. 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.
+.\"
+.\"    @(#)cp.1        8.3 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt CP 1
+.Os BSD 4
+.Sh NAME
+.Nm cp
+.Nd copy files
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f | i
+.Op Fl p
+.Ar source_file target_file
+.Nm cp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f | i
+.Op Fl p
+.Ar source_file ... target_directory
+.Sh DESCRIPTION
+In the first synopsis form, the
+.Nm
+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
+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 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 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.
+.It Fl R
+If
+.Ar source_file
+designates a directory,
+.Nm
+copies the directory and the entire subtree connected at that point.
+This option also causes symbolic links to be copied, rather than
+indirected through, and for
+.Nm
+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.
+.It Fl f
+Foreach existing destination pathname, attempt to overwrite it. If permissions
+do not allow copy to succeed, remove it and create a new file, without
+prompting for confirmation.
+(The
+.Fl i
+option is ignored if the
+.Fl f
+option is specified.)
+.It Fl i
+Causes
+.Nm
+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 ,
+the file copy is attempted.
+.It Fl p
+Causes
+.Nm
+to preserve in the copy as many of the modification time, access time,
+file flags, file mode, user ID, and group ID as allowed by permissions.
+.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 or set group ID bits are preserved in the copy's
+permissions.
+.El
+.Pp
+For each destination file that already exists, its contents are
+overwritten if permissions allow, but its mode, user ID, and group
+ID are unchanged.
+.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
+.Nm
+exits 0 on success, >0 if an error occurred.
+.Sh COMPATIBILITY
+Historic versions of the
+.Nm
+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.
+.Sh SEE ALSO
+.Xr mv 1 ,
+.Xr rcp 1 ,
+.Xr umask 2 , 
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/cp/cp.c b/cp/cp.c
new file mode 100644 (file)
index 0000000..55377bc
--- /dev/null
+++ b/cp/cp.c
@@ -0,0 +1,484 @@
+/*     $NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $  */
+
+/*
+ * 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.
+ * 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) 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[] = "@(#)cp.c       8.5 (Berkeley) 4/29/95";
+#else
+__RCSID("$NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * 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/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define        STRIP_TRAILING_SLASH(p) {                                       \
+        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')     \
+                *--(p).p_end = 0;                                      \
+}
+
+PATH_T to = { to.p_path, "" };
+
+uid_t myuid;
+int Rflag, iflag, pflag, rflag, fflag;
+mode_t myumask;
+
+enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
+
+int main __P((int, char *[]));
+int copy __P((char *[], enum op, int));
+int mastercmp __P((const FTSENT **, const FTSENT **));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat to_stat, tmp_stat;
+       enum op type;
+       int Hflag, Lflag, Pflag, ch, fts_options, r;
+       char *target;
+
+       Hflag = Lflag = Pflag = Rflag = 0;
+       while ((ch = getopt(argc, argv, "HLPRfipr")) != -1) 
+               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;
+                       iflag = 0;
+                       break;
+               case 'i':
+                       iflag = isatty(fileno(stdin));
+                       fflag = 0;
+                       break;
+               case 'p':
+                       pflag = 1;
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+                       break;
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 2)
+               usage();
+
+       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;
+       }
+
+       myuid = getuid();
+
+       /* Copy the umask for explicit mode setting. */
+       myumask = umask(0);
+       (void)umask(myumask);
+
+       /* Save the target base in "to". */
+       target = argv[--argc];
+       if (strlen(target) > MAXPATHLEN)
+               errx(1, "%s: name too long", target);
+       (void)strcpy(to.p_path, 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;
+       }
+        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)))
+                               r = stat(*argv, &tmp_stat);
+                       else
+                               r = lstat(*argv, &tmp_stat);
+                       if (r == -1)
+                               err(1, "%s", *argv);
+                       
+                       if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
+                               type = DIR_TO_DNE;
+                       else
+                               type = FILE_TO_FILE;
+               } else
+                       type = FILE_TO_FILE;
+       } else {
+               /*
+                * Case (2).  Target is a directory.
+                */
+               type = FILE_TO_DIR;
+       }
+
+       exit (copy(argv, type, fts_options));
+       /* NOTREACHED */
+}
+
+int
+copy(argv, type, fts_options)
+       char *argv[];
+       enum op type;
+       int fts_options;
+{
+       struct stat to_stat;
+       FTS *ftsp;
+       FTSENT *curr;
+       int base, dne, nlen, rval;
+       char *p, *tmp;
+
+       base = 0;       /* XXX gcc -Wuninitialized (see comment below) */
+
+       if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
+               err(1, argv[0]);
+       for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
+               switch (curr->fts_info) {
+               case FTS_NS:
+               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;
+               }
+
+               /*
+                * 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) {
+                       if ((curr->fts_namelen +
+                           to.target_end - to.p_path + 1) > MAXPATHLEN) {
+                               warnx("%s/%s: name too long (not copied)", 
+                                   to.p_path, curr->fts_name);
+                               rval = 1;
+                               continue;
+                       }
+
+                       /*
+                        * 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
+                        * concatentation 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;
+
+                       tmp = to.target_end;
+                       if (*p != '/' && *(tmp - 1) != '/')
+                               *tmp++ = '/';
+                       *tmp = 0;
+
+                       (void)strncat(tmp, p, nlen);
+                       to.p_end = tmp + nlen;
+                       *to.p_end = 0;
+                       STRIP_TRAILING_SLASH(to);
+               }
+
+               /* 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:
+                       if (copy_link(curr, !dne))
+                               rval = 1;
+                       break;
+               case S_IFDIR:
+                       if (!Rflag && !rflag) {
+                               if (curr->fts_info == FTS_DP)
+                                       warnx("%s is a directory (not copied).",
+                                           curr->fts_path);
+                               (void)fts_set(ftsp, curr, FTS_SKIP);
+                               rval = 1;
+                               break;
+                       }
+
+                        /*
+                         * Directories get noticed twice:
+                         *  In the first pass, create it if needed.
+                         *  In the second pass, after the children have been copied, set the permissions.
+                         */
+                       if (curr->fts_info == FTS_D) /* First pass */
+                       {
+                               /*
+                                * 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)
+                                               err(1, "%s", to.p_path);
+                               } else if (!S_ISDIR(to_stat.st_mode)) {
+                                       errno = ENOTDIR;
+                                       err(1, "%s", to.p_path);
+                               }
+                       }
+                       else if (curr->fts_info == FTS_DP) /* Second pass */
+                       {
+                               /*
+                                * If not -p and directory didn't exist, set it to be
+                                * the same as the from directory, umodified by the 
+                                * umask; arguably wrong, but it's been that way 
+                                * forever.
+                                */
+                               if (pflag && setfile(curr->fts_statp, 0))
+                                       rval = 1;
+                               else if (dne)
+                                       (void)chmod(to.p_path, 
+                                           curr->fts_statp->st_mode);
+                       }
+                       else
+                        {
+                               warnx("directory %s encountered when not expected.", curr->fts_path);
+                               rval = 1;
+                                break;
+                        }
+
+                       break;
+               case S_IFBLK:
+               case S_IFCHR:
+                       if (Rflag) {
+                               if (copy_special(curr->fts_statp, !dne))
+                                       rval = 1;
+                       } else
+                               if (copy_file(curr, dne))
+                                       rval = 1;
+                       break;
+               case S_IFIFO:
+                       if (Rflag) {
+                               if (copy_fifo(curr->fts_statp, !dne))
+                                       rval = 1;
+                       } else 
+                               if (copy_file(curr, dne))
+                                       rval = 1;
+                       break;
+               default:
+                       if (copy_file(curr, dne))
+                               rval = 1;
+                       break;
+               }
+       }
+       if (errno)
+               err(1, "fts_read");
+       return (rval);
+}
+
+/*
+ * mastercmp --
+ *     The comparison function for the copy order.  The order is to copy
+ *     non-directory files before directory files.  The reason for this
+ *     is because files tend to be in the same cylinder group as their
+ *     parent directory, whereas directories tend not to be.  Copying the
+ *     files first reduces seeking.
+ */
+int
+mastercmp(a, b)
+       const FTSENT **a, **b;
+{
+       int a_info, b_info;
+
+       a_info = (*a)->fts_info;
+       if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
+               return (0);
+       b_info = (*b)->fts_info;
+       if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
+               return (0);
+       if (a_info == FTS_D)
+               return (-1);
+       if (b_info == FTS_D)
+               return (1);
+       return (0);
+}
diff --git a/cp/extern.h b/cp/extern.h
new file mode 100644 (file)
index 0000000..7ae079e
--- /dev/null
@@ -0,0 +1,59 @@
+/*     $NetBSD: extern.h,v 1.4 1998/07/28 03:47:14 mycroft Exp $       */
+
+/*-
+ * 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.
+ * 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.2 (Berkeley) 4/1/94
+ */
+
+typedef struct {
+       char *p_end;                    /* pointer to NULL at end of path */
+       char *target_end;               /* pointer to end of target base */
+       char p_path[MAXPATHLEN + 1];    /* pointer to the start of a path */
+} PATH_T;
+
+extern PATH_T to;
+extern uid_t myuid;
+extern int iflag, pflag, fflag;
+extern mode_t myumask;
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int    copy_fifo __P((struct stat *, int));
+int    copy_file __P((FTSENT *, int));
+int    copy_link __P((FTSENT *, int));
+int    copy_special __P((struct stat *, int));
+int    set_utimes __P((const char *, struct stat *));
+int    setfile __P((struct stat *, int));
+void   usage __P((void));
+__END_DECLS
diff --git a/cp/utils.c b/cp/utils.c
new file mode 100644 (file)
index 0000000..5a61282
--- /dev/null
@@ -0,0 +1,356 @@
+/*     $NetBSD: utils.c,v 1.15 1998/08/19 01:29:11 thorpej Exp $       */
+
+/*-
+ * 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.
+ * 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[] = "@(#)utils.c    8.3 (Berkeley) 4/1/94";
+#else
+__RCSID("$NetBSD: utils.c,v 1.15 1998/08/19 01:29:11 thorpej Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int
+set_utimes(file, fs)
+       const char * file;
+       struct stat * fs;
+{
+    static struct timeval tv[2];
+
+    TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+    TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+
+    if (utimes(file, tv)) {
+       warn("utimes: %s", file);
+       return (1);
+    }
+    return (0);
+}
+
+int
+copy_file(entp, dne)
+       FTSENT *entp;
+       int dne;
+{
+       static char buf[MAXBSIZE];
+       struct stat to_stat, *fs;
+       int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
+#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
+       char *p;
+#endif
+       
+       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) {
+               if (iflag) {
+                       (void)fprintf(stderr, "overwrite %s? ", to.p_path);
+                       checkch = ch = getchar();
+                       while (ch != '\n' && ch != EOF)
+                               ch = getchar();
+                       if (checkch != 'y' && checkch != 'Y') {
+                               (void)close(from_fd);
+                               return (0);
+                       }
+               }
+               /* overwrite existing destination file name */
+               to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+       } else
+               to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+                   fs->st_mode & ~(S_ISUID | S_ISGID));
+
+       if (to_fd == -1 && fflag) {
+               /*
+                * attempt to remove existing destination file name and
+                * 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));
+       }
+
+       if (to_fd == -1) {
+               warn("%s", to.p_path);
+               (void)close(from_fd);
+               return (1);;
+       }
+
+       rval = 0;
+
+       /*
+        * There's no reason to do anything other than close the file
+        * now if it's empty, so let's not bother.
+        */
+       if (fs->st_size > 0) {
+               /*
+                * 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 (fs->st_size <= 8 * 1048576) {
+                       if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
+                           MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == (char *)-1) {
+                               warn("%s", entp->fts_path);
+                               rval = 1;
+                       } else {
+                               if (write(to_fd, p, fs->st_size) != fs->st_size) {
+                                       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
+               {
+                       while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+                               wcount = write(to_fd, buf, rcount);
+                               if (rcount != wcount || wcount == -1) {
+                                       warn("%s", to.p_path);
+                                       rval = 1;
+                                       break;
+                               }
+                       }
+                       if (rcount < 0) {
+                               warn("%s", entp->fts_path);
+                               rval = 1;
+                       }
+               }
+       }
+
+       if (rval == 1) {
+               (void)close(from_fd);
+               (void)close(to_fd);
+               return (1);
+       }
+
+       if (pflag && setfile(fs, to_fd))
+               rval = 1;
+       /*
+        * If the source was setuid or setgid, lose the bits unless the
+        * copy is owned by the same user and group.
+        */
+#define        RETAINBITS \
+       (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+       else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
+               if (fstat(to_fd, &to_stat)) {
+                       warn("%s", to.p_path);
+                       rval = 1;
+               } else if (fs->st_gid == to_stat.st_gid &&
+                   fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
+                       warn("%s", to.p_path);
+                       rval = 1;
+               }
+       }
+       (void)close(from_fd);
+       if (close(to_fd)) {
+               warn("%s", to.p_path);
+               rval = 1;
+       }
+       /* set the mod/access times now after close of the fd */
+       if (pflag && set_utimes(to.p_path, fs)) { 
+           rval = 1;
+       }
+       return (rval);
+}
+
+int
+copy_link(p, exists)
+       FTSENT *p;
+       int exists;
+{
+       int len;
+       char target[MAXPATHLEN];
+
+       if ((len = readlink(p->fts_path, target, sizeof(target))) == -1) {
+               warn("readlink: %s", p->fts_path);
+               return (1);
+       }
+       target[len] = '\0';
+       if (exists && unlink(to.p_path)) {
+               warn("unlink: %s", to.p_path);
+               return (1);
+       }
+       if (symlink(target, to.p_path)) {
+               warn("symlink: %s", target);
+               return (1);
+       }
+       return (pflag ? setfile(p->fts_statp, 0) : 0);
+}
+
+int
+copy_fifo(from_stat, exists)
+       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, 0) : 0);
+}
+
+int
+copy_special(from_stat, exists)
+       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, 0) : 0);
+}
+
+
+/*
+ * Function: setfile
+ *
+ * Purpose:
+ *   Set the owner/group/permissions for the "to" file to the information
+ *   in the stat structure.  If fd is zero, also call set_utimes() to set
+ *   the mod/access times.  If fd is non-zero, the caller must do a utimes
+ *   itself after close(fd).
+ */
+int
+setfile(fs, fd)
+       struct stat *fs;
+       int fd;
+{
+       int rval, islink;
+
+       rval = 0;
+       islink = S_ISLNK(fs->st_mode);
+       fs->st_mode &= S_ISUID | S_ISGID | 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 (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
+#ifdef __APPLE__
+           chown(to.p_path, fs->st_uid, fs->st_gid)) {
+#else
+           lchown(to.p_path, fs->st_uid, fs->st_gid)) {
+#endif
+               if (errno != EPERM) {
+                       warn("chown: %s", to.p_path);
+                       rval = 1;
+               }
+               fs->st_mode &= ~(S_ISUID | S_ISGID);
+       }
+#ifdef __APPLE__
+       if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
+#else
+       if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) {
+#endif
+               warn("chmod: %s", to.p_path);
+               rval = 1;
+       }
+
+       if (!islink) {
+               /*
+                * 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 (fd ? fchflags(fd, fs->st_flags) :
+                   chflags(to.p_path, fs->st_flags))
+                       if (errno != EOPNOTSUPP || fs->st_flags != 0) {
+                               warn("chflags: %s", to.p_path);
+                               rval = 1;
+                       }
+       }
+       /* if fd is non-zero, caller must call set_utimes() after close() */
+       if (fd == 0 && set_utimes(to.p_path, fs))
+           rval = 1;
+       return (rval);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr, "%s\n%s\n",
+           "usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target",
+           "       cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/csh/strpct.c b/csh/strpct.c
new file mode 100644 (file)
index 0000000..ac3b32d
--- /dev/null
@@ -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/dd/Makefile b/dd/Makefile
new file mode 100644 (file)
index 0000000..323723a
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = dd
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = dd.h extern.h
+
+CFILES = args.c conv.c conv_tab.c dd.c misc.c position.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble dd.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/dd/Makefile.postamble b/dd/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/dd/Makefile.preamble b/dd/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/dd/PB.project b/dd/PB.project
new file mode 100644 (file)
index 0000000..db89643
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (dd.h, extern.h); 
+        OTHER_LINKED = (args.c, conv.c, conv_tab.c, dd.c, misc.c, position.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, dd.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = dd; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/dd/args.c b/dd/args.c
new file mode 100644 (file)
index 0000000..733b147
--- /dev/null
+++ b/dd/args.c
@@ -0,0 +1,412 @@
+/*     $NetBSD: args.c,v 1.13 1998/07/28 03:47:15 mycroft Exp $        */
+
+/*-
+ * 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";
+#else
+__RCSID("$NetBSD: args.c,v 1.13 1998/07/28 03:47:15 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static int     c_arg __P((const void *, const void *));
+static int     c_conv __P((const void *, const void *));
+static void    f_bs __P((char *));
+static void    f_cbs __P((char *));
+static void    f_conv __P((char *));
+static void    f_count __P((char *));
+static void    f_files __P((char *));
+static void    f_ibs __P((char *));
+static void    f_if __P((char *));
+static void    f_obs __P((char *));
+static void    f_of __P((char *));
+static void    f_seek __P((char *));
+static void    f_skip __P((char *));
+static u_long  get_bsz __P((char *));
+
+static const struct arg {
+       char *name;
+       void (*f) __P((char *));
+       u_int set, noset;
+} args[] = {
+       { "bs",         f_bs,           C_BS,    C_BS|C_IBS|C_OBS|C_OSYNC },
+       { "cbs",        f_cbs,          C_CBS,   C_CBS },
+       { "conv",       f_conv,         0,       0 },
+       { "count",      f_count,        C_COUNT, C_COUNT },
+       { "files",      f_files,        C_FILES, C_FILES },
+       { "ibs",        f_ibs,          C_IBS,   C_BS|C_IBS },
+       { "if",         f_if,           C_IF,    C_IF },
+       { "obs",        f_obs,          C_OBS,   C_BS|C_OBS },
+       { "of",         f_of,           C_OF,    C_OF },
+       { "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(argv)
+       char **argv;
+{
+       struct arg *ap, tmp;
+       char *arg;
+
+       in.dbsz = out.dbsz = 512;
+
+       while ((oper = *++argv) != NULL) {
+               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");
+               if (cbsz == 0)
+                       errx(1, "cbs cannot be zero");
+       } else
+               cfunc = def;
+
+       if (in.dbsz == 0 || out.dbsz == 0)
+               errx(1, "buffer sizes cannot be zero");
+
+       /*
+        * Check to make sure that the buffers are not too large.
+        */
+       if (in.dbsz > INT_MAX || out.dbsz > INT_MAX)
+               errx(1, "buffer sizes cannot be greater than %d", INT_MAX);
+
+       /* Read, write and seek calls take off_t as arguments. 
+        *
+        * The following check is not done because an off_t is a quad
+        *  for current NetBSD implementations. 
+        *
+        * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
+        *      errx(1, "seek offsets cannot be larger than %d", INT_MAX);
+        */
+}
+
+static int
+c_arg(a, b)
+       const void *a, *b;
+{
+
+       return (strcmp(((const struct arg *)a)->name,
+           ((const struct arg *)b)->name));
+}
+
+static void
+f_bs(arg)
+       char *arg;
+{
+
+       in.dbsz = out.dbsz = (int)get_bsz(arg);
+}
+
+static void
+f_cbs(arg)
+       char *arg;
+{
+
+       cbsz = (int)get_bsz(arg);
+}
+
+static void
+f_count(arg)
+       char *arg;
+{
+
+       cpy_cnt = (u_int)get_bsz(arg);
+       if (!cpy_cnt)
+               terminate(0);
+}
+
+static void
+f_files(arg)
+       char *arg;
+{
+
+       files_cnt = (int)get_bsz(arg);
+}
+
+static void
+f_ibs(arg)
+       char *arg;
+{
+
+       if (!(ddflags & C_BS))
+               in.dbsz = (int)get_bsz(arg);
+}
+
+static void
+f_if(arg)
+       char *arg;
+{
+
+       in.name = arg;
+}
+
+static void
+f_obs(arg)
+       char *arg;
+{
+
+       if (!(ddflags & C_BS))
+               out.dbsz = (int)get_bsz(arg);
+}
+
+static void
+f_of(arg)
+       char *arg;
+{
+
+       out.name = arg;
+}
+
+static void
+f_seek(arg)
+       char *arg;
+{
+
+       out.offset = (u_int)get_bsz(arg);
+}
+
+static void
+f_skip(arg)
+       char *arg;
+{
+
+       in.offset = (u_int)get_bsz(arg);
+}
+
+#ifdef NO_CONV
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_conv(arg)
+       char *arg;
+{
+       errx(1, "conv option disabled");
+}
+#else  /* NO_CONV */
+
+static const struct conv {
+       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 },
+       { "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(arg)
+       char *arg;
+{
+       struct conv *cp, tmp;
+
+       while (arg != NULL) {
+               tmp.name = strsep(&arg, ",");
+               if (!(cp = (struct conv *)bsearch(&tmp, clist,
+                   sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
+                   c_conv)))
+                       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(a, b)
+       const void *a, *b;
+{
+
+       return (strcmp(((const struct conv *)a)->name,
+           ((const struct conv *)b)->name));
+}
+
+#endif /* NO_CONV */
+
+/*
+ * Convert an expression of the following forms to an unsigned long.
+ *     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).
+ *        seperated by x (also * for backwards compatibility), specifying
+ *        the product of the indicated values.
+ */
+static u_long
+get_bsz(val)
+       char *val;
+{
+       u_long num, t;
+       char *expr;
+
+       num = strtoul(val, &expr, 0);
+       if (num == ULONG_MAX)                   /* Overflow. */
+               err(1, "%s", oper);
+       if (expr == val)                        /* No 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 *= 1024;
+               if (t > num)
+                       goto erange;
+               ++expr;
+               break;
+       case 'm':
+               t = num;
+               num *= 1048576;
+               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_bsz(expr + 1);
+               if (t > num)
+erange:                        errx(1, "%s: %s", oper, strerror(ERANGE));
+               break;
+       default:
+               errx(1, "%s: illegal numeric value", oper);
+       }
+       return (num);
+}
diff --git a/dd/conv.c b/dd/conv.c
new file mode 100644 (file)
index 0000000..552e0d9
--- /dev/null
+++ b/dd/conv.c
@@ -0,0 +1,283 @@
+/*     $NetBSD: conv.c,v 1.8 1998/07/28 05:15:46 mycroft Exp $ */
+
+/*-
+ * 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";
+#else
+__RCSID("$NetBSD: conv.c,v 1.8 1998/07/28 05:15:46 mycroft Exp $");
+#endif
+#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()
+{
+       int cnt;
+       u_char *inp;
+       const u_char *t;
+
+       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()
+{
+       /* Just update the count, everything is already in the buffer. */
+       if (in.dbcnt)
+               out.dbcnt = in.dbcnt;
+}
+
+#ifdef NO_CONV
+/* Build a smaller version (i.e. for a miniroot) */
+/* These can not be called, but just in case...  */
+static char no_block[] = "unblock and -DNO_CONV?";
+void block()       { errx(1, no_block + 2); }
+void block_close() { errx(1, no_block + 2); }
+void unblock()       { errx(1, no_block); }
+void unblock_close() { errx(1, no_block); }
+#else  /* NO_CONV */
+
+/*
+ * 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()
+{
+       static int intrunc;
+       int ch = 0;     /* pacify gcc */
+       int cnt, maxlen;
+       u_char *inp, *outp;
+       const u_char *t;
+
+       /*
+        * 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.
+        */
+       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()
+{
+       /*
+        * 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()
+{
+       int cnt;
+       u_char *inp;
+       const u_char *t;
+
+       /* Translation and case conversion. */
+       if ((t = ctab) != NULL)
+               for (cnt = in.dbrcnt, inp = in.dbp; cnt--;)
+                       *--inp = t[*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.dbcnt;
+               *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()
+{
+       int cnt;
+       u_char *t;
+
+       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';
+       }
+}
+
+#endif /* NO_CONV */
diff --git a/dd/conv_tab.c b/dd/conv_tab.c
new file mode 100644 (file)
index 0000000..2f41d87
--- /dev/null
@@ -0,0 +1,291 @@
+/*     $NetBSD: conv_tab.c,v 1.8 1997/07/20 21:58:38 christos Exp $    */
+
+/*-
+ * 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";
+#else
+__RCSID("$NetBSD: conv_tab.c,v 1.8 1997/07/20 21:58:38 christos Exp $");
+#endif
+#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/dd/dd.1 b/dd/dd.1
new file mode 100644 (file)
index 0000000..5f98028
--- /dev/null
+++ b/dd/dd.1
@@ -0,0 +1,355 @@
+.\"    $NetBSD: dd.1,v 1.7 1998/02/06 05:39:31 perry 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
+.\" 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
+.\"
+.Dd January 13, 1994
+.Dt DD 1
+.Os
+.Sh NAME
+.Nm dd
+.Nd convert and copy a file
+.Sh SYNOPSIS
+.Nm
+.Op 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:
+.Bl -tag -width of=file
+.It Cm bs= Ns Ar n
+Set both input and output block size, 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 Ar n
+Set the conversion record size to
+.Va n
+bytes.
+The conversion record size is required by the record oriented conversion
+values.
+.It Cm count= Ns Ar n
+Copy only
+.Va n
+input blocks.
+.It Cm files= Ns Ar n
+Copy
+.Va n
+input files before terminating.
+This operand is only applicable when the input device is a tape.
+.It Cm ibs= Ns Ar n
+Set the input block size to
+.Va n
+bytes instead of the default 512.
+.It Cm if= Ns Ar file
+Read input from
+.Ar file
+instead of the standard input.
+.It Cm obs= Ns Ar n
+Set the output block size to
+.Va n
+bytes instead of the default 512.
+.It Cm of= 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 skipped (see the
+.Cm seek
+operand)
+the output file is truncated at that point.
+.It Cm seek= Ns Ar n
+Seek
+.Va n
+blocks from the beginning of the output before copying.
+On non-tape devices, a
+.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
+.Tn NUL
+bytes.
+.It Cm skip= Ns Ar n
+Skip
+.Va n
+blocks from the beginning of the input before copying.
+On input which supports seeks, a
+.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 Xo
+.Cm conv=
+.Ns Cm value Ns Op \&, Cm value \&...
+.Xc
+Where
+.Cm value
+is one of the symbols from the following list.
+.Bl -tag -width 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
+.Tn AT&T
+and pre-
+.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
+.Tn AT&T
+and pre
+.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
+.Tn 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 Ar n
+block size specification.
+.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
+.Tn 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 number of bytes is expected.
+If the number ends with a ``b'', ``k'', ``m'' or ``w'', the number
+is multiplied by 512, 1024 (1K), 1048576 (1M) or the number of bytes
+in an integer, respectively.
+Two or more numbers may be separated by an ``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 ``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.
+.Pp
+The
+.Nm
+utility exits 0 on success and >0 if an error occurred.
+.Sh SEE ALSO
+.Xr cp 1 ,
+.Xr mt 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
+.Tn POSIX
+standard.
diff --git a/dd/dd.c b/dd/dd.c
new file mode 100644 (file)
index 0000000..17f870f
--- /dev/null
+++ b/dd/dd.c
@@ -0,0 +1,412 @@
+/*     $NetBSD: dd.c,v 1.12 1998/08/19 01:32:44 thorpej Exp $  */
+
+/*-
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: dd.c,v 1.12 1998/08/19 01:32:44 thorpej Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static void dd_close __P((void));
+static void dd_in __P((void));
+static void getfdtype __P((IO *));
+static void setup __P((void));
+
+int main __P((int, char *[]));
+
+IO     in, out;                /* input/output state */
+STAT   st;                     /* statistics */
+void   (*cfunc) __P((void));   /* conversion function */
+u_long cpy_cnt;                /* # of blocks to copy */
+u_int  ddflags;                /* conversion options */
+u_int  cbsz;                   /* conversion block size */
+u_int  files_cnt = 1;          /* # of files to copy */
+const u_char   *ctab;          /* conversion table */
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       jcl(argv);
+       setup();
+
+       (void)signal(SIGINFO, summaryx);
+       (void)signal(SIGINT, terminate);
+
+       (void)atexit(summary);
+
+       while (files_cnt--)
+               dd_in();
+
+       dd_close();
+       exit(0);
+       /* NOTREACHED */
+}
+
+static void
+setup()
+{
+       u_int cnt;
+
+       if (in.name == NULL) {
+               in.name = "stdin";
+               in.fd = STDIN_FILENO;
+       } else {
+               in.fd = open(in.name, O_RDONLY, 0);
+               if (in.fd < 0)
+                       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 < 0) {
+                       out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
+                       out.flags |= NOREAD;
+               }
+               if (out.fd < 0)
+                       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, "%s", "");
+               out.db = in.db;
+       } else if ((in.db =
+           malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+           (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL)
+               err(1, "%s", "");
+       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; ignore errors because it fails on some
+        * kinds of output files, tapes, for example.
+        */
+       if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+               (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+
+       /*
+        * 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)) {
+#ifdef NO_CONV
+               /* Should not get here, but just in case... */
+               errx(1, "case conv and -DNO_CONV");
+#else  /* NO_CONV */
+               if (ddflags & C_ASCII || ddflags & 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(cnt);
+                       } else {
+                               for (cnt = 0; cnt < 0377; ++cnt)
+                                       casetab[cnt] = toupper(cnt);
+                       }
+               }
+
+               ctab = casetab;
+#endif /* NO_CONV */
+       }
+
+       (void)time(&st.start);                  /* Statistics timestamp. */
+}
+
+static void
+getfdtype(io)
+       IO *io;
+{
+       struct mtget mt;
+       struct stat sb;
+
+       if (fstat(io->fd, &sb))
+               err(1, "%s", io->name);
+       if (S_ISCHR(sb.st_mode))
+               io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE;
+       else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+               io->flags |= ISPIPE;            /* XXX fixed in 4.4BSD */
+}
+
+static void
+dd_in()
+{
+       int flags, n;
+
+       for (flags = ddflags;;) {
+               if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+                       return;
+
+               /*
+                * Clear the buffer first if doing "sync" on input.
+                * If doing block operations use spaces.  This will
+                * affect not only the C_NOERROR case, but also the
+                * last partial input block which should be padded
+                * with zero and not garbage.
+                */
+               if (flags & C_SYNC) {
+                       if (flags & (C_BLOCK|C_UNBLOCK))
+                               (void)memset(in.dbp, ' ', in.dbsz);
+                       else
+                               (void)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 < 0) {
+                       /*
+                        * If noerror not specified, die.  POSIX requires that
+                        * the warning message be followed by an I/O display.
+                        */
+                       if (!(flags & C_NOERROR))
+                               err(1, "%s", in.name);
+                       warn("%s", in.name);
+                       summary();
+
+                       /*
+                        * If it's not a tape drive or a pipe, 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 & (ISPIPE|ISTAPE)) &&
+                           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 (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.dbcnt) & 1) {
+                               ++st.swab;
+                               --n;
+                       }
+                       swab(in.dbp, in.dbp, n);
+               }
+
+               in.dbp += in.dbrcnt;
+               (*cfunc)();
+       }
+}
+
+/*
+ * Cleanup any remaining I/O and flush output.  If necesssary, output file
+ * is truncated.
+ */
+static void
+dd_close()
+{
+       if (cfunc == def)
+               def_close();
+       else if (cfunc == block)
+               block_close();
+       else if (cfunc == unblock)
+               unblock_close();
+       if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+               (void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
+               out.dbcnt = out.dbsz;
+       }
+       if (out.dbcnt)
+               dd_out(1);
+}
+
+void
+dd_out(force)
+       int force;
+{
+       static int warned;
+       int cnt, n, nw;
+       u_char *outp;
+
+       /*
+        * 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) {
+                       nw = write(out.fd, outp, cnt);
+                       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 (nw == n) {
+                               if (n != out.dbsz)
+                                       ++st.out_part;
+                               else
+                                       ++st.out_full;
+                               break;
+                       }
+                       ++st.out_part;
+                       if (nw == cnt)
+                               break;
+                       if (out.flags & ISCHR && !warned) {
+                               warned = 1;
+                               warnx("%s: short write on character device",
+                                   out.name);
+                       }
+                       if (out.flags & ISTAPE)
+                               errx(1, "%s: short write on tape 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/dd/dd.h b/dd/dd.h
new file mode 100644 (file)
index 0000000..6d001ed
--- /dev/null
+++ b/dd/dd.h
@@ -0,0 +1,98 @@
+/*     $NetBSD: dd.h,v 1.5 1998/02/04 06:42:31 enami Exp $     */
+
+/*-
+ * 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
+ */
+
+/* Input/output stream state. */
+typedef struct {
+       u_char  *db;                    /* buffer address */
+       u_char  *dbp;                   /* current buffer I/O address */
+       u_long  dbcnt;                  /* current buffer byte count */
+       int     dbrcnt;                 /* last read byte count */
+       u_long  dbsz;                   /* buffer size */
+
+#define        ISCHR           0x01            /* character device (warn on short) */
+#define        ISPIPE          0x02            /* pipe (not truncatable) */
+#define        ISTAPE          0x04            /* tape (not seekable) */
+#define        NOREAD          0x08            /* not readable */
+       u_int   flags;
+
+       char    *name;                  /* name */
+       int     fd;                     /* file descriptor */
+       u_long  offset;                 /* # of blocks to skip */
+
+       u_long  f_stats;                /* # of full blocks processed */
+       u_long  p_stats;                /* # of partial blocks processed */
+       u_long  s_stats;                /* # of odd swab blocks */
+       u_long  t_stats;                /* # of truncations */
+} IO;
+
+typedef struct {
+       u_long  in_full;                /* # of full input blocks */
+       u_long  in_part;                /* # of partial input blocks */
+       u_long  out_full;               /* # of full output blocks */
+       u_long  out_part;               /* # of partial output blocks */
+       u_long  trunc;                  /* # of truncated records */
+       u_long  swab;                   /* # of odd-length swab blocks */
+       u_quad_t bytes;                 /* # of bytes written */
+       time_t  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
diff --git a/dd/extern.h b/dd/extern.h
new file mode 100644 (file)
index 0000000..79e272b
--- /dev/null
@@ -0,0 +1,69 @@
+/*     $NetBSD: extern.h,v 1.8 1997/07/20 21:58:40 christos Exp $      */
+
+/*-
+ * 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
+ */
+
+#include <sys/cdefs.h>
+
+void block __P((void));
+void block_close __P((void));
+void dd_out __P((int));
+void def __P((void));
+void def_close __P((void));
+void jcl __P((char **));
+void pos_in __P((void));
+void pos_out __P((void));
+void summary __P((void));
+void summaryx __P((int));
+void terminate __P((int));
+void unblock __P((void));
+void unblock_close __P((void));
+
+extern IO in, out;
+extern STAT st;
+extern void (*cfunc) __P((void));
+extern u_long cpy_cnt;
+extern u_int cbsz;
+extern u_int ddflags;
+extern u_int 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[];
diff --git a/dd/misc.c b/dd/misc.c
new file mode 100644 (file)
index 0000000..8254392
--- /dev/null
+++ b/dd/misc.c
@@ -0,0 +1,108 @@
+/*     $NetBSD: misc.c,v 1.8 1998/07/28 05:31:23 mycroft Exp $ */
+
+/*-
+ * 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";
+#else
+__RCSID("$NetBSD: misc.c,v 1.8 1998/07/28 05:31:23 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+void
+summary()
+{
+       time_t secs;
+       char buf[100];
+
+       (void)time(&secs);
+       if ((secs -= st.start) == 0)
+               secs = 1;
+       /* Use snprintf(3) so that we don't reenter stdio(3). */
+       (void)snprintf(buf, sizeof(buf),
+           "%lu+%lu records in\n%lu+%lu 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), "%lu 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), "%lu truncated %s\n",
+                    st.trunc, (st.trunc == 1) ? "block" : "blocks");
+               (void)write(STDERR_FILENO, buf, strlen(buf));
+       }
+       (void)snprintf(buf, sizeof(buf),
+           "%qu bytes transferred in %lu secs (%qu bytes/sec)\n",
+           (long long) st.bytes, (long) secs, (long long) (st.bytes / secs));
+       (void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+/* ARGSUSED */
+void
+summaryx(notused)
+       int notused;
+{
+
+       summary();
+}
+
+/* ARGSUSED */
+void
+terminate(notused)
+       int notused;
+{
+
+       exit(0);
+       /* NOTREACHED */
+}
diff --git a/dd/position.c b/dd/position.c
new file mode 100644 (file)
index 0000000..e302477
--- /dev/null
@@ -0,0 +1,174 @@
+/*     $NetBSD: position.c,v 1.6 1997/07/25 06:46:24 phil Exp $        */
+
+/*-
+ * 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";
+#else
+__RCSID("$NetBSD: position.c,v 1.6 1997/07/25 06:46:24 phil Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.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()
+{
+       int bcnt, cnt, nr, warned;
+
+       /* If not a character, pipe or tape device, try to seek on it. */
+       if (!(in.flags & (ISCHR|ISPIPE|ISTAPE))) {
+               if (lseek(in.fd, (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR)
+                   == -1)
+                       err(1, "%s", in.name);
+               return;
+       }
+
+       /*
+        * 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()
+{
+       struct mtop t_op;
+       int cnt, 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 & ISTAPE)) {
+               if (lseek(out.fd,
+                   (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1)
+                       err(1, "%s", out.name);
+               return;
+       }
+
+       /* If no read access, try using mtio. */
+       if (out.flags & NOREAD) {
+               t_op.mt_op = MTFSR;
+               t_op.mt_count = out.offset;
+
+               if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)
+                       err(1, "%s", out.name);
+               return;
+       }
+
+       /* Read it. */
+       for (cnt = 0; cnt < out.offset; ++cnt) {
+               if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+                       continue;
+
+               if (n < 0)
+                       err(1, "%s", out.name);
+
+               /*
+                * If reach EOF, fill with NUL characters; first, back up over
+                * the EOF mark.  Note, cnt has not yet been incremented, so
+                * the EOF read does not count as a seek'd block.
+                */
+               t_op.mt_op = MTBSR;
+               t_op.mt_count = 1;
+               if (ioctl(out.fd, MTIOCTOP, &t_op) == -1)
+                       err(1, "%s", out.name);
+
+               while (cnt++ < out.offset)
+                       if ((n = write(out.fd, out.db, out.dbsz)) != out.dbsz)
+                               err(1, "%s", out.name);
+               break;
+       }
+}
diff --git a/df/Makefile b/df/Makefile
new file mode 100644 (file)
index 0000000..154ddff
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = df
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = df.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble df.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/df/Makefile.postamble b/df/Makefile.postamble
new file mode 100644 (file)
index 0000000..234330d
--- /dev/null
@@ -0,0 +1,4 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
+
+INSTALL_AS_GROUP    = operator
+INSTALL_PERMISSIONS = 2555
diff --git a/df/Makefile.preamble b/df/Makefile.preamble
new file mode 100644 (file)
index 0000000..7782dbf
--- /dev/null
@@ -0,0 +1,5 @@
+vpath strpct.c ../csh
+
+CFILES += strpct.c
+
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/df/PB.project b/df/PB.project
new file mode 100644 (file)
index 0000000..b036be4
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (df.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, df.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = df; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/df/df.1 b/df/df.1
new file mode 100644 (file)
index 0000000..ccedb98
--- /dev/null
+++ b/df/df.1
@@ -0,0 +1,122 @@
+.\"    $NetBSD: df.1,v 1.14 1997/10/20 08:51:31 enami Exp $
+.\"
+.\" 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.2 (Berkeley) 1/13/92
+.\"
+.Dd January 13, 1994
+.Dt DF 1
+.Os BSD 4
+.Sh NAME
+.Nm df
+.Nd display free disk space
+.Sh SYNOPSIS
+.Nm
+.Op Fl ikln
+.Op Fl t Ar type
+.Op Ar file | Ar file_system ...
+.Sh DESCRIPTION
+.Nm
+displays statistics about the amount of free disk space on the specified
+.Ar file_system
+or on the file system of which
+.Ar file
+is a part.
+Values are displayed in 512-byte per block block counts.
+If neither a file or a
+.Ar file_system
+operand is specified,
+statistics for all mounted file systems are displayed
+(subject to the
+.Fl l
+and
+.Fl t
+options below).
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl i
+Include statistics on the number of free inodes.
+.It Fl k
+By default, all sizes are reported in 512-byte block counts.
+The
+.Fl k
+option causes the numbers to be reported in kilobyte counts.
+.It Fl l
+Display statistics only about mounted file systems with the MNT_LOCAL
+flag set.  If a non-local file system is given as an argument, a
+warning is issued and no information is given on that file system.
+.It Fl n
+Print out the previously obtained statistics from the file systems.
+This option should be used if it is possible that one or more
+file systems are in a state such that they will not be able to provide
+statistics without a long delay.
+When this option is specified,
+.Nm
+will not request new statistics from the file systems, but will respond
+with the possibly stale statistics that were previously obtained.
+.It Fl t Ar type
+Is used to indicate the actions should only be taken on
+filesystems of the specified type.
+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.  If a file system is given on the command line that is not of
+the specified type, a warning is issued and no information is given on
+that file system.
+.El
+.Sh ENVIRONMENT VARIABLES
+.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.
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr statfs 2 ,
+.Xr fstatfs 2 ,
+.Xr getfsstat 2 ,
+.Xr getmntinfo 3 ,
+.Xr fstab 5 ,
+.Xr mount 8 ,
+.Xr quot 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/df/df.c b/df/df.c
new file mode 100644 (file)
index 0000000..0a1cadd
--- /dev/null
+++ b/df/df.c
@@ -0,0 +1,455 @@
+/*     $NetBSD: df.c,v 1.30 1998/07/28 05:31:24 mycroft Exp $  */
+
+/*
+ * 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
+__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.7 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: df.c,v 1.30 1998/07/28 05:31:24 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#ifdef __APPLE__
+#define MOUNT_FFS "ffs"
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern char * strpct __P((u_long num, u_long denom, u_int digits));
+
+int     main __P((int, char *[]));
+int     bread __P((off_t, void *, int));
+char   *getmntpt __P((char *));
+void    prtstat __P((struct statfs *, int));
+int     ufs_df __P((char *, struct statfs *));
+int     selected __P((const char *));
+void    maketypelist __P((char *));
+long    regetmntinfo __P((struct statfs **, long));
+void    usage __P((void));
+
+int    iflag, kflag, lflag, nflag;
+char   **typelist = NULL;
+struct ufs_args mdev;
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat stbuf;
+       struct statfs *mntbuf;
+       long mntsize;
+       int ch, i, maxwidth, width;
+       char *mntpt;
+
+       while ((ch = getopt(argc, argv, "iklnt:")) != -1)
+               switch (ch) {
+               case 'i':
+                       iflag = 1;
+                       break;
+               case 'k':
+                       kflag = 1;
+                       break;
+               case 'l':
+                       lflag = 1;
+                       break;
+               case 'n':
+                       nflag = 1;
+                       break;
+               case 't':
+                       if (typelist != NULL)
+                               errx(1, "only one -t option may be specified.");
+                       maketypelist(optarg);
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+       if (mntsize == 0)
+               err(1, "retrieving information on mounted file systems");
+
+       if (!*argv) {
+               mntsize = regetmntinfo(&mntbuf, mntsize);
+       } else {
+               mntbuf = malloc(argc * sizeof(struct statfs));
+               mntsize = 0;
+               for (; *argv; argv++) {
+                       if (stat(*argv, &stbuf) < 0) {
+                               if ((mntpt = getmntpt(*argv)) == 0) {
+                                       warn("%s", *argv);
+                                       continue;
+                               }
+                       } else if (S_ISCHR(stbuf.st_mode)) {
+                               if (!ufs_df(*argv, &mntbuf[mntsize]))
+                                       ++mntsize;
+                               continue;
+                       } else if (S_ISBLK(stbuf.st_mode)) {
+                               if ((mntpt = getmntpt(*argv)) == 0) {
+#ifdef __APPLE__ /* We lack mkdtemp() */
+                                       mntpt = mktemp(strdup("/tmp/df.XXXXXX"));
+                                       mdev.fspec = *argv;
+                                       if (mkdir(mntpt, DEFFILEMODE) != 0) {
+#else
+                                       mntpt = strdup("/tmp/df.XXXXXX");
+                                       if (!mkdtemp(mntpt)) {
+#endif
+                                               warn("%s", mntpt);
+                                               continue;
+                                       }
+                                       mdev.fspec = *argv;
+                                       if (mount(MOUNT_FFS, mntpt, MNT_RDONLY,
+                                           &mdev) != 0) {
+                                               (void)rmdir(mntpt);
+                                               if (!ufs_df(*argv,
+                                                   &mntbuf[mntsize]))
+                                                       ++mntsize;
+                                               continue;
+                                       } else if (!statfs(mntpt,
+                                           &mntbuf[mntsize])) {
+                                               mntbuf[mntsize].f_mntonname[0] =
+                                                   '\0';
+                                               ++mntsize;
+                                       } else
+                                               warn("%s", *argv);
+                                       (void)unmount(mntpt, 0);
+                                       (void)rmdir(mntpt);
+                                       continue;
+                               }
+                       } else
+                               mntpt = *argv;
+                       /*
+                        * Statfs does not take a `wait' flag, so we cannot
+                        * implement nflag here.
+                        */
+                       if (!statfs(mntpt, &mntbuf[mntsize]))
+                               if (lflag &&
+                                   (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0)
+                                       warnx("Warning: %s is not a local %s",
+                                           *argv, "file system");
+                               else if
+                                   (!selected(mntbuf[mntsize].f_fstypename))
+                                       warnx("Warning: %s mounted as a %s %s",
+                                           *argv,
+                                           mntbuf[mntsize].f_fstypename,
+                                           "file system");
+                               else
+                                       ++mntsize;
+                       else
+                               warn("%s", *argv);
+               }
+       }
+
+       maxwidth = 0;
+       for (i = 0; i < mntsize; i++) {
+               width = strlen(mntbuf[i].f_mntfromname);
+               if (width > maxwidth)
+                       maxwidth = width;
+       }
+       for (i = 0; i < mntsize; i++)
+               prtstat(&mntbuf[i], maxwidth);
+       exit(0);
+       /* NOTREACHED */
+}
+
+char *
+getmntpt(name)
+       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);
+}
+
+static enum { IN_LIST, NOT_IN_LIST } which;
+
+int
+selected(type)
+       const char *type;
+{
+       char **av;
+
+       /* If no type specified, it's always selected. */
+       if (typelist == NULL)
+               return (1);
+       for (av = typelist; *av != NULL; ++av)
+               if (!strncmp(type, *av, MFSNAMELEN))
+                       return (which == IN_LIST ? 1 : 0);
+       return (which == IN_LIST ? 0 : 1);
+}
+
+void
+maketypelist(fslist)
+       char *fslist;
+{
+       int i;
+       char *nextcp, **av;
+
+       if ((fslist == NULL) || (fslist[0] == '\0'))
+               errx(1, "empty type list");
+
+       /*
+        * XXX
+        * Note: the syntax is "noxxx,yyy" for no xxx's and
+        * no yyy's, not the more intuitive "noyyy,noyyy".
+        */
+       if (fslist[0] == 'n' && fslist[1] == 'o') {
+               fslist += 2;
+               which = NOT_IN_LIST;
+       } else
+               which = IN_LIST;
+
+       /* Count the number of types. */
+       for (i = 1, nextcp = fslist;
+           (nextcp = strchr(nextcp, ',')) != NULL; i++)
+               ++nextcp;
+
+       /* Build an array of that many types. */
+       if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL)
+               err(1, "can't allocate type array");
+       av[0] = fslist;
+       for (i = 1, nextcp = fslist;
+           (nextcp = strchr(nextcp, ',')) != NULL; i++) {
+               *nextcp = '\0';
+               av[i] = ++nextcp;
+       }
+       /* Terminate the array. */
+       av[i] = NULL;
+}
+
+/*
+ * Make a pass over the filesystem info in ``mntbuf'' filtering out
+ * filesystem types not in ``fsmask'' and possibly re-stating to get
+ * current (not cached) info.  Returns the new count of valid statfs bufs.
+ */
+long
+regetmntinfo(mntbufp, mntsize)
+       struct statfs **mntbufp;
+       long mntsize;
+{
+       int i, j;
+       struct statfs *mntbuf;
+
+       if (!lflag && typelist == NULL)
+               return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
+
+       mntbuf = *mntbufp;
+       j = 0;
+       for (i = 0; i < mntsize; i++) {
+               if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0)
+                       continue;
+               if (!selected(mntbuf[i].f_fstypename))
+                       continue;
+               if (nflag)
+                       mntbuf[j] = mntbuf[i];
+               else
+                       (void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]);
+               j++;
+       }
+       return (j);
+}
+
+/*
+ * Convert statfs returned filesystem size into BLOCKSIZE units.
+ * Attempts to avoid overflow for large filesystems.
+ */
+#define fsbtoblk(num, fsbs, bs) \
+        ((long int) ((((float) fsbs) != 0 && ((float) fsbs) < ((float) bs)) ? \
+                ((float) num) / (((float) bs) / ((float) fsbs)) : ((float) num) * (((float) fsbs) / ((float) bs))))
+
+/*
+ * Print out status about a filesystem.
+ */
+void
+prtstat(sfsp, maxwidth)
+       struct statfs *sfsp;
+       int maxwidth;
+{
+       static long blocksize;
+       static int headerlen, timesthrough;
+       static char *header;
+       long used, availblks, inodes;
+       static char     *full = "100%";
+
+       if (maxwidth < 11)
+               maxwidth = 11;
+       if (++timesthrough == 1) {
+               if (kflag) {
+                       blocksize = 1024;
+                       header = "1K-blocks";
+                       headerlen = strlen(header);
+               } else
+                       header = getbsize(&headerlen, &blocksize);
+               (void)printf("%-*.*s %s     Used    Avail Capacity",
+                   maxwidth, maxwidth, "Filesystem", header);
+               if (iflag)
+                       (void)printf(" iused   ifree  %%iused");
+               (void)printf("  Mounted on\n");
+       }
+       (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
+       used = sfsp->f_blocks - sfsp->f_bfree;
+       availblks = sfsp->f_bavail + used;
+       (void)printf(" %*ld %8ld %8ld", headerlen,
+           fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
+           fsbtoblk(used, sfsp->f_bsize, blocksize),
+           fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
+       (void)printf(" %6s",
+           availblks == 0 ? full : strpct((u_long)used, (u_long)availblks, 0));
+       if (iflag) {
+               inodes = sfsp->f_files;
+               used = inodes - sfsp->f_ffree;
+               (void)printf(" %7ld %7ld %6s ", used, sfsp->f_ffree,
+                  inodes == 0 ? full : strpct((u_long)used, (u_long)inodes, 0));
+       } else 
+               (void)printf("  ");
+       (void)printf("  %s\n", sfsp->f_mntonname);
+}
+
+/*
+ * This code constitutes the pre-system call Berkeley df code for extracting
+ * information from filesystem superblocks.
+ */
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <errno.h>
+#include <fstab.h>
+
+union {
+       struct fs iu_fs;
+       char dummy[SBSIZE];
+} sb;
+#define sblock sb.iu_fs
+
+int    rfd;
+
+int
+ufs_df(file, sfsp)
+       char *file;
+       struct statfs *sfsp;
+{
+       char *mntpt;
+       static int synced;
+
+       if (synced++ == 0)
+               sync();
+
+       if ((rfd = open(file, O_RDONLY)) < 0) {
+               warn("%s", file);
+               return (-1);
+       }
+       if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
+               (void)close(rfd);
+               return (-1);
+       }
+       sfsp->f_type = 0;
+       sfsp->f_flags = 0;
+       sfsp->f_bsize = sblock.fs_fsize;
+       sfsp->f_iosize = sblock.fs_bsize;
+       sfsp->f_blocks = sblock.fs_dsize;
+       sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
+               sblock.fs_cstotal.cs_nffree;
+       sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree);
+       sfsp->f_files =  sblock.fs_ncg * sblock.fs_ipg;
+       sfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
+       sfsp->f_fsid.val[0] = 0;
+       sfsp->f_fsid.val[1] = 0;
+       if ((mntpt = getmntpt(file)) == 0)
+               mntpt = "";
+       (void)memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN);
+       (void)memmove(&sfsp->f_mntfromname[0], file, MNAMELEN);
+       (void)strncpy(sfsp->f_fstypename, MOUNT_FFS, MFSNAMELEN);
+       (void)close(rfd);
+       return (0);
+}
+
+int
+bread(off, buf, cnt)
+       off_t off;
+       void *buf;
+       int cnt;
+{
+       int nr;
+
+       (void)lseek(rfd, off, SEEK_SET);
+       if ((nr = read(rfd, buf, cnt)) != cnt) {
+               /* Probably a dismounted disk if errno == EIO. */
+               if (errno != EIO)
+                       (void)fprintf(stderr, "\ndf: %qd: %s\n",
+                           (long long)off, strerror(nr > 0 ? EIO : errno));
+               return (0);
+       }
+       return (1);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr, "usage: df [-ikln] [-t type] [file | file_system ...]\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/du/Makefile b/du/Makefile
new file mode 100644 (file)
index 0000000..d4a2b75
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = du
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = du.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble du.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/du/Makefile.postamble b/du/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/du/Makefile.preamble b/du/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/du/PB.project b/du/PB.project
new file mode 100644 (file)
index 0000000..6334025
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (du.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, du.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = du; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/du/du.1 b/du/du.1
new file mode 100644 (file)
index 0000000..6c5ca9c
--- /dev/null
+++ b/du/du.1
@@ -0,0 +1,134 @@
+.\"    $NetBSD: du.1,v 1.8 1998/02/15 17:08:17 kleink 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.
+.\"
+.\"    @(#)du.1        8.2 (Berkeley) 4/1/94
+.\"
+.Dd October 4, 1996
+.Dt DU 1
+.Os
+.Sh NAME
+.Nm du
+.Nd display disk usage statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl H | Fl L | Fl P
+.Op Fl a | Fl s
+.Op Fl ckrx
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+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 Ds
+.It Fl H
+Symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl L
+All symbolic links are followed.
+.It Fl P
+No symbolic links are followed.
+.It Fl a
+Display an entry for each file in the file hierarchy.
+.It Fl k
+By default,
+.Nm
+displays the number of blocks as returned by the
+.Xr stat  2
+system call, i.e. 512-byte blocks.
+If the
+.Fl k
+flag is specified, the number displayed is the number of 1024-byte
+blocks.
+Partial numbers of blocks are rounded up.
+.It Fl c
+Display the grand total after all the arguments have been processed.
+.It Fl r
+Generate warning messages about directories that cannot be read.
+This is the default behaviour.
+.It Fl s
+Display only the grand total for the specified files.
+.It Fl x
+Filesystem mount points are not traversed.
+.El
+.Pp
+.Nm
+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.
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options override each other and the command's actions are determined
+by the last one specified.
+.Pp
+Files having multiple hard links are counted (and displayed) a single
+time per
+.Nm
+execution.
+.Sh ENVIRONMENT VARIABLES
+.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.
+.El
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr fts 3 ,
+.Xr symlink 7 ,
+.Xr quot 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/du/du.c b/du/du.c
new file mode 100644 (file)
index 0000000..3d6914f
--- /dev/null
+++ b/du/du.c
@@ -0,0 +1,255 @@
+/*     $NetBSD: du.c,v 1.14 1998/02/15 17:08:18 kleink Exp $   */
+
+/*
+ * 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
+__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[] = "@(#)du.c       8.5 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: du.c,v 1.14 1998/02/15 17:08:18 kleink Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int    linkchk __P((FTSENT *));
+int    main __P((int, char **));
+void   usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       FTS *fts;
+       FTSENT *p;
+       long blocksize, totalblocks;
+       int ftsoptions, listdirs, listfiles;
+       int Hflag, Lflag, Pflag, aflag, ch, cflag, kflag, notused, rval, sflag;
+       char **save;
+
+       save = argv;
+       Hflag = Lflag = Pflag = aflag = cflag = kflag = sflag = 0;
+       totalblocks = 0;
+       ftsoptions = FTS_PHYSICAL;
+       while ((ch = getopt(argc, argv, "HLPackrsx")) != -1)
+               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 'a':
+                       aflag = 1;
+                       break;
+               case 'c':
+                       cflag = 1;
+                       break;
+               case 'k':
+                       blocksize = 1024;
+                       kflag = 1;
+                       break;
+               case 'r':
+                       break;
+               case 's':
+                       sflag = 1;
+                       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)
+               ftsoptions |= FTS_COMFOLLOW;
+       if (Lflag) {
+               ftsoptions &= ~FTS_PHYSICAL;
+               ftsoptions |= FTS_LOGICAL;
+       }
+
+       if (aflag) {
+               if (sflag)
+                       usage();
+               listdirs = listfiles = 1;
+       } else if (sflag)
+               listdirs = listfiles = 0;
+       else {
+               listfiles = 0;
+               listdirs = 1;
+       }
+
+       if (!*argv) {
+               argv = save;
+               argv[0] = ".";
+               argv[1] = NULL;
+       }
+
+       if (!kflag)
+               (void)getbsize(&notused, &blocksize);
+       blocksize /= 512;
+
+       if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+               err(1, "fts_open `%s'", *argv);
+
+       for (rval = 0; (p = fts_read(fts)) != NULL;)
+               switch (p->fts_info) {
+               case FTS_D:                     /* Ignore. */
+                       break;
+               case FTS_DP:
+                       p->fts_parent->fts_number += 
+                           p->fts_number += p->fts_statp->st_blocks;
+                       if (cflag)
+                               totalblocks += p->fts_statp->st_blocks;
+                       /*
+                        * If listing each directory, or not listing files
+                        * or directories and this is post-order of the
+                        * root of a traversal, display the total.
+                        */
+                       if (listdirs || (!listfiles && !p->fts_level))
+                               (void)printf("%ld\t%s\n",
+                                   howmany(p->fts_number, blocksize),
+                                   p->fts_path);
+                       break;
+               case FTS_DC:                    /* Ignore. */
+                       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;
+               default:
+                       if (p->fts_statp->st_nlink > 1 && linkchk(p))
+                               break;
+                       /*
+                        * If listing each file, or a non-directory file was
+                        * the root of a traversal, display the total.
+                        */
+                       if (listfiles || !p->fts_level)
+                               (void)printf("%qd\t%s\n", (long long)
+                                   howmany(p->fts_statp->st_blocks, blocksize),
+                                   p->fts_path);
+                       p->fts_parent->fts_number += p->fts_statp->st_blocks;
+                       if (cflag)
+                               totalblocks += p->fts_statp->st_blocks;
+               }
+       if (errno)
+               err(1, "fts_read");
+       if (cflag)
+               (void)printf("%ld\ttotal\n",
+                   howmany(totalblocks, blocksize));
+       exit(0);
+}
+
+typedef struct _ID {
+       dev_t   dev;
+       ino_t   inode;
+} ID;
+
+int
+linkchk(p)
+       FTSENT *p;
+{
+       static ID *files;
+       static int maxfiles, nfiles;
+       ID *fp, *start;
+       ino_t ino;
+       dev_t dev;
+
+       ino = p->fts_statp->st_ino;
+       dev = p->fts_statp->st_dev;
+       if ((start = files) != NULL)
+               for (fp = start + nfiles - 1; fp >= start; --fp)
+                       if (ino == fp->inode && dev == fp->dev)
+                               return (1);
+
+       if (nfiles == maxfiles && (files = realloc((char *)files,
+           (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
+               err(1, "realloc");
+       files[nfiles].inode = ino;
+       files[nfiles].dev = dev;
+       ++nfiles;
+       return (0);
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr,
+               "usage: du [-H | -L | -P] [-a | -s] [-ckx] [file ...]\n");
+       exit(1);
+}
diff --git a/install/Makefile b/install/Makefile
new file mode 100644 (file)
index 0000000..d36083c
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = install
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = pathnames.h
+
+CFILES = xinstall.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble install.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/install/Makefile.postamble b/install/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/install/Makefile.preamble b/install/Makefile.preamble
new file mode 100644 (file)
index 0000000..7a86fc2
--- /dev/null
@@ -0,0 +1,5 @@
+vpath stat_flags.c ../ls
+
+CFILES += stat_flags.c
+
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/install/PB.project b/install/PB.project
new file mode 100644 (file)
index 0000000..519a9e7
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (pathnames.h); 
+        OTHER_LINKED = (xinstall.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, install.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = install; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/install/install.1 b/install/install.1
new file mode 100644 (file)
index 0000000..e9acb8c
--- /dev/null
@@ -0,0 +1,183 @@
+.\"    $NetBSD: install.1,v 1.11 1998/09/28 08:16:15 christos Exp $
+.\"
+.\" 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.
+.\"
+.\"     @(#)install.1  8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt INSTALL 1
+.Os BSD 4.2
+.Sh NAME
+.Nm install
+.Nd install binaries
+.Sh SYNOPSIS
+.Nm
+.Op Fl cps
+.Op Fl f Ar flags
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Op Fl g Ar group
+.Op Fl l Ar linkflags
+.Op Fl S Ar stripflag
+.Ar file1 file2
+.Nm ""
+.Op Fl cps
+.Op Fl f Ar flags
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Op Fl g Ar group
+.Op Fl l Ar linkflags
+.Op Fl S Ar stripflag
+.Ar file1
+\&...
+.Ar fileN directory
+.Nm ""
+.Fl pd
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Op Fl g Ar group
+.Ar directory
+\&...
+.Sh DESCRIPTION
+The file(s) are moved (copied if the
+.Fl c
+option is specified, or linked if the
+.Fl l
+option is specified) to the target file or directory.
+If the destination is a directory, then the
+.Ar file
+is moved into
+.Ar directory
+with its original filename.
+If the target file already exists, it is overwritten if permissions
+allow.
+.Pp
+.Bl -tag -width Ds
+.It Fl c
+Copy the file.
+This flag turns off the default behavior of
+.Nm
+where it deletes the original file after creating the target.
+.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.
+.It Fl m
+Specify an alternative 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 l Ar linkflags
+Instead of copying the file make a link to the source. The type of the
+link is determined by the 
+.Ar linkflags
+argument. Valid
+.Ar linkflags
+are:
+.Ar a
+(absolute),
+.Ar r
+(relative),
+.Ar h
+(hard),
+.Ar s
+(symbolic),
+.Ar m
+(mixed). Absolute and relative have effect only for symbolic links. Mixed links
+are hard links for files on the same filesystem, symbolic otherwise.
+.It Fl o
+Specify an owner.
+.It Fl p
+Preserve the source files access and modification times.
+.It Fl s
+.Nm
+exec's the command
+.Xr strip  1
+to strip binaries so that install can be portable over a large
+number of systems and binary types.  If the environment variable
+.Ev STRIP
+is set, it is used as the
+.Xr strip 1
+program.
+.It Fl S Ar stripflags
+.Nm
+passes
+.Ar stripflags
+as option arguments to
+.Xr strip  1 .
+When -S is used,
+.Xr strip  1
+is invoked via the
+.Xr sh  1
+shell, allowing a single -S argument be to specified to
+.Nm
+which the shell can then tokenize. Normally,
+.Nm
+invokes
+.Xr strip  1
+directly. This flag implies -s.
+.It Fl d
+Create directories. 
+Missing parent directories are created as required.
+.El
+.Pp
+By default,
+.Nm
+preserves all file flags, with the exception of the ``nodump'' flag.
+.Pp
+The
+.Nm
+utility attempts to prevent moving a file onto itself.
+.Pp
+Installing
+.Pa /dev/null
+creates an empty file.
+.Pp
+Upon successful completion a value of 0 is returned.
+Otherwise, a value of 1 is returned.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr mv 1 ,
+.Xr strip 1 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/install/pathnames.h b/install/pathnames.h
new file mode 100644 (file)
index 0000000..7af9165
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $NetBSD: pathnames.h,v 1.4 1997/12/30 22:31:17 thorpej Exp $    */
+
+/*
+ * 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
+ */
+
+#define        _PATH_STRIP     "/usr/bin/strip"
diff --git a/install/xinstall.c b/install/xinstall.c
new file mode 100644 (file)
index 0000000..fa51bab
--- /dev/null
@@ -0,0 +1,563 @@
+/*     $NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $        */
+
+/*
+ * 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
+__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[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
+#else
+__RCSID("$NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.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 <err.h>
+
+#include "pathnames.h"
+
+#define STRIP_ARGS_MAX 32
+
+struct passwd *pp;
+struct group *gp;
+int docopy=0, dodir=0, dostrip=0, dolink=0, dopreserve=0;
+int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+char pathbuf[MAXPATHLEN];
+uid_t uid;
+gid_t gid;
+char *stripArgs=NULL;
+
+#define LN_ABSOLUTE    0x01
+#define LN_RELATIVE    0x02
+#define LN_HARD                0x04
+#define LN_SYMBOLIC    0x08
+#define LN_MIXED       0x10
+
+#define        DIRECTORY       0x01            /* Tell install it's a directory. */
+#define        SETFLAGS        0x02            /* Tell install to set flags. */
+
+void   copy __P((int, char *, int, char *, off_t));
+void   makelink __P((char *, char *));
+void   install __P((char *, char *, u_long, u_int));
+void   install_dir __P((char *));
+u_long string_to_flags __P((char **, u_long *, u_long *));
+void   strip __P((char *));
+void   usage __P((void));
+int    main __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat from_sb, to_sb;
+       mode_t *set;
+       u_long fset;
+       u_int iflags;
+       int ch, no_target;
+       char *p;
+       char *flags = NULL, *to_name, *group = NULL, *owner = NULL;
+
+       iflags = 0;
+       while ((ch = getopt(argc, argv, "cdf:g:l:m:o:psS:")) != -1)
+               switch((char)ch) {
+               case 'c':
+                       docopy = 1;
+                       break;
+               case 'd':
+                       dodir = 1;
+                       break;
+               case 'f':
+                       flags = optarg;
+                       if (string_to_flags(&flags, &fset, NULL))
+                               errx(1, "%s: invalid flag", flags);
+                       iflags |= SETFLAGS;
+                       break;
+               case 'g':
+                       group = optarg;
+                       break;
+               case 'l':
+                       for (p = optarg; *p; p++)
+                               switch (*p) {
+                               case 's':
+                                       dolink &= ~(LN_HARD|LN_MIXED);
+                                       dolink |= LN_SYMBOLIC;
+                                       break;
+                               case 'h':
+                                       dolink &= ~(LN_SYMBOLIC|LN_MIXED);
+                                       dolink |= LN_HARD;
+                                       break;
+                               case 'm':
+                                       dolink &= ~(LN_SYMBOLIC|LN_HARD);
+                                       dolink |= LN_MIXED;
+                                       break;
+                               case 'a':
+                                       dolink &= ~LN_RELATIVE;
+                                       dolink |= LN_ABSOLUTE;
+                                       break;
+                               case 'r':
+                                       dolink &= ~LN_ABSOLUTE;
+                                       dolink |= LN_RELATIVE;
+                                       break;
+                               default:
+                                       errx(1, "%c: invalid link type", *p);
+                                       break;
+                               }
+                       break;
+               case 'm':
+                       if (!(set = setmode(optarg)))
+                               errx(1, "%s: invalid file mode", optarg);
+                       mode = getmode(set, 0);
+                       break;
+               case 'o':
+                       owner = optarg;
+                       break;
+               case 'p':
+                       dopreserve = 1;
+                       break;
+               case 'S':
+                       stripArgs = (char*)malloc(sizeof(char)*(strlen(optarg)+1));
+                       strcpy(stripArgs,optarg);
+                       /* fall through; -S implies -s */
+               case 's':
+                       dostrip = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       /* strip and link options make no sense when creating directories */
+       if ((dostrip || dolink) && dodir)
+               usage();
+
+       /* strip and flags make no sense with links */
+       if ((dostrip || flags) && dolink)
+               usage();
+
+       /* must have at least two arguments, except when creating directories */
+       if (argc < 2 && !dodir)
+               usage();
+
+       /* get group and owner id's */
+       if (group && !(gp = getgrnam(group)) && !isdigit(*group))
+               errx(1, "unknown group %s", group);
+       gid = (group) ? ((gp) ? gp->gr_gid : atoi(group)) : -1;
+       if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner))
+               errx(1, "unknown user %s", owner);
+       uid = (owner) ? ((pp) ? pp->pw_uid : atoi(owner)) : -1;
+
+       if (dodir) {
+               for (; *argv != NULL; ++argv)
+                       install_dir(*argv);
+               exit (0);
+       }
+
+       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(0);
+       }
+
+       /* can't do file1 file2 directory/file */
+       if (argc != 2)
+               usage();
+
+       if (!no_target) {
+               if (stat(*argv, &from_sb))
+                       err(1, "%s", *argv);
+               if (!S_ISREG(to_sb.st_mode))
+                       errx(1, "%s: %s", to_name, strerror(EFTYPE));
+               if (!dolink && to_sb.st_dev == from_sb.st_dev &&
+                   to_sb.st_ino == from_sb.st_ino)
+                       errx(1, "%s and %s are the same file", *argv, to_name);
+               /*
+                * Unlink now... avoid ETXTBSY errors later.  Try and turn
+                * off the append/immutable bits -- if we fail, go ahead,
+                * it might work.
+                */
+#define        NOCHANGEBITS    (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
+               if (to_sb.st_flags & NOCHANGEBITS)
+                       (void)chflags(to_name,
+                           to_sb.st_flags & ~(NOCHANGEBITS));
+               (void)unlink(to_name);
+       }
+       install(*argv, to_name, fset, iflags);
+       exit(0);
+}
+
+/*
+ * makelink --
+ *     make a link from source to destination
+ */
+void
+makelink(from_name, to_name)
+       char *from_name;
+       char *to_name;
+{
+       char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
+
+       /* Try hard links first */
+       if (dolink & (LN_HARD|LN_MIXED)) {
+               if (link(from_name, to_name) == -1) {
+                       if ((dolink & LN_HARD) || errno != EXDEV)
+                               err(1, "link %s -> %s", from_name, to_name);
+               }
+               else
+                       return;
+       }
+
+       /* Symbolic links */
+       if (dolink & LN_ABSOLUTE) {
+               /* Convert source path to absolute */
+               if (realpath(from_name, src) == NULL)
+                       err(1, "%s", src);
+               if (symlink(src, to_name) == -1)
+                       err(1, "symlink %s -> %s", src, to_name);
+               return;
+       }
+
+       if (dolink & LN_RELATIVE) {
+               char *s, *d;
+
+               /* Resolve pathnames */
+               if (realpath(from_name, src) == NULL)
+                       err(1, "%s", src);
+               if (realpath(to_name, dst) == NULL)
+                       err(1, "%s", dst);
+
+               /* trim common path components */
+               for (s = src, d = dst; *s == *d; s++, d++)
+                       continue;
+               while (*s != '/')
+                       s--, d--;
+
+               /* count the number of directories we need to backtrack */
+               for (++d, lnk[0] = '\0'; *d; d++)
+                       if (*d == '/')
+                               (void) strcat(lnk, "../");
+
+               (void) strcat(lnk, ++s);
+
+               if (symlink(lnk, dst) == -1)
+                       err(1, "symlink %s -> %s", lnk, dst);
+               return;
+       }
+
+       /*
+        * If absolute or relative was not specified, 
+        * try the names the user provided
+        */
+       if (symlink(from_name, to_name) == -1)
+               err(1, "symlink %s -> %s", from_name, to_name);
+
+}
+
+/*
+ * 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, to_sb;
+       int devnull, from_fd, to_fd, serrno;
+       char *p;
+
+       /* 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(1, "%s", from_name);
+               if (!S_ISREG(from_sb.st_mode))
+                       errx(1, "%s: %s", from_name, strerror(EFTYPE));
+               /* 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 {
+               from_sb.st_flags = 0;   /* XXX */
+               devnull = 1;
+       }
+
+       /*
+        * Unlink now... avoid ETXTBSY errors later.  Try and turn
+        * off the append/immutable bits -- if we fail, go ahead,
+        * it might work.
+        */
+       if (stat(to_name, &to_sb) == 0 &&
+           to_sb.st_flags & (NOCHANGEBITS))
+               (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
+       (void)unlink(to_name);
+
+       if (dolink) {
+               makelink(from_name, to_name);
+               return;
+       }
+
+       /* Create target. */
+       if ((to_fd = open(to_name,
+           O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
+               err(1, "%s", to_name);
+       if (!devnull) {
+               if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
+                       (void)unlink(to_name);
+                       err(1, "%s", from_name);
+               }
+               copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
+               (void)close(from_fd);
+       }
+
+       if (dostrip) {
+               strip(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);
+               if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
+                 err(1, "stripping %s", to_name);
+       }
+
+       /*
+        * Set owner, group, mode for target; do the chown first,
+        * chown may lose the setuid bits.
+        */
+       if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) {
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
+       }
+       if (fchmod(to_fd, mode)) {
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "%s: chmod: %s", to_name, strerror(serrno));
+       }
+
+       /*
+        * If provided a set of flags, set them, otherwise, preserve the
+        * flags, except for the dump flag.
+        */
+       if (fchflags(to_fd,
+           flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
+               if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
+                       warn("%s: chflags", to_name);
+       }
+
+       /*
+        * Preserve the date of the source file.
+        */
+       if (dopreserve) {
+               struct timeval tv[2];
+
+               TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
+               TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
+#ifndef __APPLE__
+               if (futimes(to_fd, tv) == -1)
+                       warn("%s: futimes", to_name);
+#else
+               if (utimes(to_name, tv) == -1)
+                       warn("%s: utimes", to_name);
+#endif
+       }
+
+       (void)close(to_fd);
+       if (!docopy && !devnull && unlink(from_name))
+               err(1, "%s", from_name);
+}
+
+/*
+ * copy --
+ *     copy from one file to another
+ */
+void
+copy(from_fd, from_name, to_fd, to_name, size)
+       int from_fd, to_fd;
+       char *from_name, *to_name;
+       off_t size;
+{
+       int nr, nw;
+       int serrno;
+       char *p;
+       char buf[MAXBSIZE];
+
+       /*
+        * There's no reason to do anything other than close the file
+        * now if it's empty, so let's not bother.
+        */
+       if (size > 0) {
+               /*
+                * 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.
+                */
+               if (size <= 8 * 1048576) {
+                       if ((p = mmap(NULL, (size_t)size, PROT_READ,
+                           MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == (char *)-1)
+                               err(1, "%s", from_name);
+                       if (write(to_fd, p, size) != size)
+                               err(1, "%s", to_name);
+               } else {
+                       while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
+                               if ((nw = write(to_fd, buf, nr)) != nr) {
+                                       serrno = errno;
+                                       (void)unlink(to_name);
+                                       errx(1, "%s: %s",
+                                           to_name, strerror(nw > 0 ? EIO : serrno));
+                               }
+                       if (nr != 0) {
+                               serrno = errno;
+                               (void)unlink(to_name);
+                               errx(1, "%s: %s", from_name, strerror(serrno));
+                       }
+               }
+       }
+}
+
+/*
+ * strip --
+ *     use strip(1) to strip the target file
+ */
+void
+strip(to_name)
+       char *to_name;
+{
+       int serrno, status;
+       char *stripprog;
+
+       switch (vfork()) {
+       case -1:
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "vfork: %s", strerror(serrno));
+       case 0:
+               stripprog = getenv("STRIP");
+               if (stripprog == NULL)
+                       stripprog = _PATH_STRIP;
+
+               if (stripArgs) {
+                       /* build up a command line and let /bin/sh parse the arguments */
+                       char* cmd = (char*)malloc(sizeof(char)*
+                                                 (3+strlen(stripprog)+
+                                                    strlen(stripArgs)+
+                                                    strlen(to_name)));
+
+                       sprintf(cmd, "%s %s %s", stripprog, stripArgs, to_name);
+
+                       execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+               } else
+                       execl(stripprog, "strip", to_name, NULL);
+
+               warn("%s", stripprog);
+               _exit(1);
+       default:
+               if (wait(&status) == -1 || status)
+                       (void)unlink(to_name);
+       }
+}
+
+/*
+ * install_dir --
+ *     build directory heirarchy
+ */
+void
+install_dir(path)
+        char *path;
+{
+        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, 0777) < 0) {
+                                       err(1, "%s", path);
+                                       /* NOTREACHED */
+                                }
+                        }
+                        if (!(*p = ch))
+                               break;
+                }
+
+       if (((gid != -1 || uid != -1) && chown(path, uid, gid)) ||
+            chmod(path, mode)) {
+                warn("%s", path);
+        }
+}
+
+/*
+ * usage --
+ *     print a usage message and die
+ */
+void
+usage()
+{
+       (void)fprintf(stderr, "\
+usage: install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\
+       install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 ... fileN directory\n\
+       install -pd [-m mode] [-o owner] [-g group] directory ...\n");
+       exit(1);
+}
diff --git a/ln/Makefile b/ln/Makefile
new file mode 100644 (file)
index 0000000..15d04bd
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = ln
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = ln.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ln.1\
+            symlink.7
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/ln/Makefile.postamble b/ln/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/ln/Makefile.preamble b/ln/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/ln/PB.project b/ln/PB.project
new file mode 100644 (file)
index 0000000..fe1d0a7
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (ln.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ln.1, symlink.7); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = ln; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/ln/ln.1 b/ln/ln.1
new file mode 100644 (file)
index 0000000..0d1a2b9
--- /dev/null
+++ b/ln/ln.1
@@ -0,0 +1,147 @@
+.\"    $NetBSD: ln.1,v 1.13 1997/10/20 08:52:19 enami Exp $
+.\"
+.\" 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.
+.\" 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.
+.\"
+.\"    @(#)ln.1        8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt LN 1
+.Os BSD 4
+.Sh NAME
+.Nm ln
+.Nd make links
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhns
+.Ar source_file
+.Op target_file
+.Nm ""
+.Op Fl fhns
+.Ar source_file ...
+.Op target_dir
+.Sh DESCRIPTION
+The
+.Nm
+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 or symbolic link.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl f
+Unlink any already existing file, permitting the link to occur.
+.It Fl h
+If the
+.Ar target_file
+or
+.Ar target_dir
+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 n
+Same as
+.Fl h ,
+for compatibility with other
+.Nm
+implementations.
+.It Fl s
+Create a symbolic link.
+.El
+.Pp
+By default
+.Nm
+makes
+.Em hard
+links.
+A hard link to a file is indistinguishable from the original directory entry;
+any changes to a file are effective 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
+creates a link to an existing file
+.Ar source_file  .
+If
+.Ar target_file
+is given, the link has that name;
+.Ar target_file
+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
+makes links in
+.Ar target_dir
+to all the named source files.
+The links made will have the same name as the files being linked to.
+.Sh SEE ALSO
+.Xr link 2 ,
+.Xr lstat 2 ,
+.Xr readlink 2 ,
+.Xr stat 2 ,
+.Xr symlink 2 ,
+.Xr symlink 7
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/ln/ln.c b/ln/ln.c
new file mode 100644 (file)
index 0000000..f8bbce3
--- /dev/null
+++ b/ln/ln.c
@@ -0,0 +1,182 @@
+/*     $NetBSD: ln.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $  */
+
+/*
+ * 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.
+ * 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) 1987, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ln.c       8.2 (Berkeley) 3/31/94";
+#else
+__RCSID("$NetBSD: ln.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int    fflag;                          /* Unlink existing files. */
+int    hflag;                          /* Check new name for symlink first. */
+int    sflag;                          /* Symbolic, not hard, link. */
+                                       /* System link call. */
+int (*linkf) __P((const char *, const char *));
+
+int    linkit __P((char *, char *, int));
+void   usage __P((void));
+int    main __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat sb;
+       int ch, exitval;
+       char *sourcedir;
+
+       while ((ch = getopt(argc, argv, "fhns")) != -1)
+               switch (ch) {
+               case 'f':
+                       fflag = 1;
+                       break;
+               case 'h':
+               case 'n':
+                       hflag = 1;
+                       break;
+               case 's':
+                       sflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+
+       argv += optind;
+       argc -= optind;
+
+       linkf = sflag ? symlink : link;
+
+       switch(argc) {
+       case 0:
+               usage();
+               /* NOTREACHED */
+       case 1:                         /* ln target */
+               exit(linkit(argv[0], ".", 1));
+               /* NOTREACHED */
+       case 2:                         /* ln target source */
+               exit(linkit(argv[0], argv[1], 0));
+               /* NOTREACHED */
+       }
+                                       /* 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);
+       /* NOTREACHED */
+}
+
+int
+linkit(target, source, isdir)
+       char *target, *source;
+       int isdir;
+{
+       struct stat sb;
+       char *p, path[MAXPATHLEN];
+
+       if (!sflag) {
+               /* If target doesn't exist, quit now. */
+               if (stat(target, &sb)) {
+                       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) && S_ISDIR(sb.st_mode)) ||
+           (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) {
+               if ((p = strrchr(target, '/')) == NULL)
+                       p = target;
+               else
+                       ++p;
+               (void)snprintf(path, sizeof(path), "%s/%s", source, p);
+               source = path;
+       }
+
+       /*
+        * If the file exists, and -f was specified, unlink it.
+        * Attempt the link.
+        */
+       if ((fflag && unlink(source) < 0 && errno != ENOENT) ||
+           (*linkf)(target, source)) {
+               warn("%s", source);
+               return (1);
+       }
+
+       return (0);
+}
+
+void
+usage()
+{
+
+       extern char *__progname;
+       (void)fprintf(stderr,
+           "Usage:\t%s [-fhns] file1 file2\n\t%s [-fhns] file ... directory\n",
+           __progname, __progname);
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/ln/symlink.7 b/ln/symlink.7
new file mode 100644 (file)
index 0000000..7e6e68a
--- /dev/null
@@ -0,0 +1,427 @@
+.\"    $NetBSD: symlink.7,v 1.7 1998/02/06 05:39:47 perry Exp $
+.\"
+.\" 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.
+.\"
+.\"    @(#)symlink.7   8.3 (Berkeley) 3/31/94
+.\"
+.Dd March 31, 1994
+.Dt SYMLINK 7
+.Os BSD 4
+.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 filesystem
+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 doesn't 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:
+.sp
+.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 four system calls that do not follow links, and which operate
+on the symbolic link itself.
+They are:
+.Xr lstat 2 ,
+.Xr readlink 2 ,
+.Xr rename 2 ,
+and
+.Xr unlink 2 .
+Because
+.Xr remove 3
+is an alias for
+.Xr unlink 2 ,
+it also does not follow symbolic links.
+.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.
+.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 "file slink"
+display the type of 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 "file slink"
+would report the type of the file referenced by
+.Dq Li slink ,
+while
+.Dq Li "file -h slink"
+would report that
+.Dq Li slink
+was a symbolic link.
+.Pp
+There are three 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 L
+option is specified,
+or if the
+.Fl F ,
+.Fl d
+or
+.Fl l
+options are not specified.
+(If the
+.Fl L
+option is specified,
+.Nm ls
+always follows symbolic links.
+.Nm ls
+is the only command where the
+.Fl L
+option affects its behavior even though it is not doing a walk of
+a file tree.)
+.Pp
+The
+.Bx 4.4
+system differs from historical
+.Bx 4
+systems in that the
+.Nm chown ,
+.Nm chgrp
+and
+.Nm file
+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
+For example, the command
+.Dq Li "chown -R user slink directory"
+will ignore
+.Dq Li slink ,
+because symbolic links in this system do not have owners.
+Any symbolic links encountered during the tree traversal will also be
+ignored.
+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 either
+.Nm chown
+or
+.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 never follows symbolic links unless the
+.Fl L
+flag is specified.
+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.
+The
+.Nm ls
+command does not support the
+.Fl H
+or
+.Fl P
+options.
+.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 lstat 2 ,
+.Xr readlink 2 ,
+.Xr rename 2 ,
+.Xr symlink 2 ,
+.Xr unlink 2 ,
+.Xr fts 3 ,
+.Xr remove 3 ,
+.Xr chown 8
diff --git a/ls/Makefile b/ls/Makefile
new file mode 100644 (file)
index 0000000..f29efd4
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = ls
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = extern.h ls.h
+
+CFILES = cmp.c ls.c print.c stat_flags.c util.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ls.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/ls/Makefile.postamble b/ls/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/ls/Makefile.preamble b/ls/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/ls/PB.project b/ls/PB.project
new file mode 100644 (file)
index 0000000..32e2a56
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (extern.h, ls.h); 
+        OTHER_LINKED = (cmp.c, ls.c, print.c, stat_flags.c, util.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ls.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = ls; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/ls/cmp.c b/ls/cmp.c
new file mode 100644 (file)
index 0000000..31b7565
--- /dev/null
+++ b/ls/cmp.c
@@ -0,0 +1,203 @@
+/*     $NetBSD: cmp.c,v 1.14 1998/10/09 02:00:39 enami Exp $   */
+
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmp.c      8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: cmp.c,v 1.14 1998/10/09 02:00:39 enami Exp $");
+#endif
+#endif /* not lint */
+
+#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)
+#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)
+#endif
+
+int
+namecmp(a, b)
+       const FTSENT *a, *b;
+{
+       return (strcmp(a->fts_name, b->fts_name));
+}
+
+int
+revnamecmp(a, b)
+       const FTSENT *a, *b;
+{
+       return (strcmp(b->fts_name, a->fts_name));
+}
+
+int
+modcmp(a, b)
+       const FTSENT *a, *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(a, b)
+       const FTSENT *a, *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(a, b)
+       const FTSENT *a, *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(a, b)
+       const FTSENT *a, *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(a, b)
+       const FTSENT *a, *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(a, b)
+       const FTSENT *a, *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));
+}
diff --git a/ls/extern.h b/ls/extern.h
new file mode 100644 (file)
index 0000000..a42d48f
--- /dev/null
@@ -0,0 +1,56 @@
+/*     $NetBSD: extern.h,v 1.7 1998/01/18 13:30:03 lukem Exp $ */
+
+/*-
+ * 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) 5/31/93
+ */
+
+int     acccmp __P((const FTSENT *, const FTSENT *));
+int     revacccmp __P((const FTSENT *, const FTSENT *));
+int     modcmp __P((const FTSENT *, const FTSENT *));
+int     revmodcmp __P((const FTSENT *, const FTSENT *));
+int     namecmp __P((const FTSENT *, const FTSENT *));
+int     revnamecmp __P((const FTSENT *, const FTSENT *));
+int     statcmp __P((const FTSENT *, const FTSENT *));
+int     revstatcmp __P((const FTSENT *, const FTSENT *));
+int     sizecmp __P((const FTSENT *, const FTSENT *));
+int     revsizecmp __P((const FTSENT *, const FTSENT *));
+
+char   *flags_to_string __P((u_long, char *));
+int     string_to_flags __P((char **, u_long *, u_long *));
+void    prcopy __P((char *, char *, int));
+void    printacol __P((DISPLAY *));
+void    printcol __P((DISPLAY *));
+void    printlong __P((DISPLAY *));
+void    printscol __P((DISPLAY *));
+void    usage __P((void));
diff --git a/ls/ls.1 b/ls/ls.1
new file mode 100644 (file)
index 0000000..6181998
--- /dev/null
+++ b/ls/ls.1
@@ -0,0 +1,367 @@
+.\"    $NetBSD: ls.1,v 1.19 1998/06/01 21:11:28 hubertf Exp $
+.\"
+.\" 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 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.
+.\"
+.\"     @(#)ls.1       8.7 (Berkeley) 7/29/94
+.\"
+.Dd July 29, 1994
+.Dt LS 1
+.Os
+.Sh NAME
+.Nm ls
+.Nd list directory contents
+.Sh SYNOPSIS
+.Nm
+.Op Fl ACFLRSTWacdfgiklnoqrstux1
+.Op Ar file ...
+.Sh DESCRIPTION
+For each operand that names a
+.Ar file
+of a type other than
+directory,
+.Nm
+displays its name as well as any requested,
+associated information.
+For each operand that names a
+.Ar file
+of type directory,
+.Nm
+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 A
+List all entries except for
+.Ql \&.
+and
+.Ql \&.. .
+Always set for the super-user.
+.It Fl C
+Force multi-column output; this is the default when output is to a terminal.
+.It Fl F
+Display a slash (/) immediately after each pathname that is a directory,
+an asterisk (*) after each that is executable,
+an at sign (@) after each symbolic link,
+a percent sign (%) after each whiteout,
+an equal sign (=) after each socket,
+and a vertical bar (|) after each that is a
+.Tn FIFO . 
+.It Fl L
+If argument is a symbolic link, list the file or directory the link references
+rather than the link itself.
+.It Fl R
+Recursively list subdirectories encountered.
+.It Fl S
+Sort by size, largest file first.
+.It Fl T
+Display complete time information for the file, including
+month, day, hour, minute, second, and year.
+.It Fl W
+Display whiteouts when scanning directories.
+.It Fl a
+Include directory entries whose names begin with a
+dot (.).
+.It Fl c
+Use time when file status was last changed for sorting or printing.
+.It Fl d
+Directories are listed as plain files (not searched recursively) and
+symbolic links in the argument list are not indirected through.
+.It Fl f
+Output is not sorted.
+.It Fl g
+Does nothing; kept for compatibility with older versions of
+.Xr ls 1 .
+.It Fl i
+For each file, print the file's file serial number (inode number).
+.It Fl k
+Modifies the
+.Fl s
+option, causing the sizes to be reported in kilobytes.
+.It Fl l
+(The lowercase letter ``ell.'')  List in long format. (See below.)
+If the output is to a terminal, a total sum for all the file
+sizes is output on a line before the long listing.
+.It Fl n
+Display the user and group IDs numerically rather than converting
+to a user or group name in a long
+.Pq Fl l
+output.
+.It Fl o
+Include the file flags in a long
+.Pq Fl l
+output.
+.It Fl q
+Force printing of non-graphic characters in file names as
+the character `?'; this is the default when output is to a terminal.
+.It Fl r
+Reverse the order of the sort to get reverse
+lexicographical order or the smallest or oldest entries first.
+.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.
+.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 printing
+.Pq Fl l .
+.It Fl x
+Multi-column output sorted across the page rather than down the page.
+.It Fl v
+Force unedited printing of non-graphic characters; this is the default when
+output is not to a terminal.
+.It Fl \&1
+(The numeric digit ``one.'')  Force output to be
+one entry per line.
+This is the default when
+output is not to a terminal.
+.El
+.Pp
+The
+.Fl 1 ,
+.Fl C ,
+.Fl l ,
+and
+.Fl x
+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
+By default,
+.Nm
+lists one entry per line to standard
+output; the exceptions are to terminals or when the
+.Fl C
+option is specified.
+.Pp
+File information is displayed with one or more
+<blank>s separating the information associated with the
+.Fl i ,
+.Fl 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.
+.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 \-> .
+.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 w
+Whiteout.
+.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
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh ENVIRONMENT VARIABLES
+The following environment variables affect the execution of
+.Nm "" :
+.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
+(see
+.Fl s )
+will be displayed in units of that size block.
+.It 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
+utility calculates how
+many pathname text columns to display
+based on the width provided.
+(See
+.Fl C . )
+.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 SEE ALSO
+.Xr chmod 1 ,
+.Xr symlink 7 ,
+.Xr sticky 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2
+specification.
+.Sh HISTORY
+An
+.Nm
+utility appeared in
+.At v5 .
diff --git a/ls/ls.c b/ls/ls.c
new file mode 100644 (file)
index 0000000..c943b9c
--- /dev/null
+++ b/ls/ls.c
@@ -0,0 +1,616 @@
+/*     $NetBSD: ls.c,v 1.31 1998/08/19 01:44:19 thorpej Exp $  */
+
+/*
+ * 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
+__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[] = "@(#)ls.c       8.7 (Berkeley) 8/5/94";
+#else
+__RCSID("$NetBSD: ls.c,v 1.31 1998/08/19 01:44:19 thorpej Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int    main __P((int, char *[]));
+
+static void     display __P((FTSENT *, FTSENT *));
+static int      mastercmp __P((const FTSENT **, const FTSENT **));
+static void     traverse __P((int, char **, int));
+
+static void (*printfcn) __P((DISPLAY *));
+static int (*sortfcn) __P((const FTSENT *, const FTSENT *));
+
+#define        BY_NAME 0
+#define        BY_SIZE 1
+#define        BY_TIME 2
+
+long blocksize;                        /* block size units */
+int termwidth = 80;            /* default terminal width */
+int sortkey = BY_NAME;
+
+/* flags */
+int f_accesstime;              /* use time of last access */
+int f_column;                  /* columnated format */
+int f_columnacross;            /* columnated format, sorted across */
+int f_flags;                   /* show flags associated with a file */
+int f_inode;                   /* print inode */
+int f_listdir;                 /* list actual directory, not contents */
+int f_listdot;                 /* list files beginning with . */
+int f_longform;                        /* long listing format */
+int f_nonprint;                        /* show unprintables as ? */
+int f_nosort;                  /* don't sort output */
+int f_numericonly;             /* don't convert uid/gid to name */
+int f_recursive;               /* ls subdirectories also */
+int f_reversesort;             /* reverse whatever sort is used */
+int f_sectime;                 /* print the real time for all files */
+int f_singlecol;               /* use single column output */
+int f_size;                    /* list size in short listing */
+int f_statustime;              /* use time of last mode change */
+int f_type;                    /* add type character for non-regular files */
+int f_whiteout;                        /* show whiteout entries */
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       static char dot[] = ".", *dotav[] = { dot, NULL };
+       struct winsize win;
+       int ch, fts_options, notused;
+       int kflag = 0;
+       const char *p;
+
+       /* Terminal defaults to -Cq, non-terminal defaults to -1. */
+       if (isatty(STDOUT_FILENO)) {
+               if ((p = getenv("COLUMNS")) != NULL)
+                       termwidth = atoi(p);
+               else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
+                   win.ws_col > 0)
+                       termwidth = win.ws_col;
+               f_column = f_nonprint = 1;
+       } else
+               f_singlecol = 1;
+
+       /* Root is -A automatically. */
+       if (!getuid())
+               f_listdot = 1;
+
+       fts_options = FTS_PHYSICAL;
+       while ((ch = getopt(argc, argv, "1ACFLRSTWacdfgiklnoqrstuxv")) != -1) {
+               switch (ch) {
+               /*
+                * The -1, -C, -l and -x options all override each other so
+                * shell aliasing works correctly.
+                */
+               case '1':
+                       f_singlecol = 1;
+                       f_column = f_columnacross = f_longform = 0;
+                       break;
+               case 'C':
+                       f_column = 1;
+                       f_columnacross = f_longform = f_singlecol = 0;
+                       break;
+               case 'l':
+                       f_longform = 1;
+                       f_column = f_columnacross = f_singlecol = 0;
+                       break;
+               case 'x':
+                       f_columnacross = 1;
+                       f_column = f_longform = f_singlecol = 0;
+                       break;
+               /* The -c and -u options override each other. */
+               case 'c':
+                       f_statustime = 1;
+                       f_accesstime = 0;
+                       break;
+               case 'u':
+                       f_accesstime = 1;
+                       f_statustime = 0;
+                       break;
+               case 'F':
+                       f_type = 1;
+                       break;
+               case 'L':
+                       fts_options &= ~FTS_PHYSICAL;
+                       fts_options |= FTS_LOGICAL;
+                       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;
+                       break;
+               case 'g':               /* Compatibility with 4.3BSD. */
+                       break;
+               case 'i':
+                       f_inode = 1;
+                       break;
+               case 'k':
+                       blocksize = 1024;
+                       kflag = 1;
+                       break;
+               case 'n':
+                       f_numericonly = 1;
+                       break;
+               case 'o':
+                       f_flags = 1;
+                       break;
+               case 'q':
+                       f_nonprint = 1;
+                       break;
+               case 'r':
+                       f_reversesort = 1;
+                       break;
+               case 'S':
+                       sortkey = BY_SIZE;
+                       break;
+               case 's':
+                       f_size = 1;
+                       break;
+               case 'T':
+                       f_sectime = 1;
+                       break;
+               case 't':
+                       sortkey = BY_TIME;
+                       break;
+               case 'W':
+                       f_whiteout = 1;
+                       break;
+               case 'v':
+                       f_nonprint = 0;
+                       break;
+               default:
+               case '?':
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * If not -F, -i, -l, -S, -s or -t options, don't require stat
+        * information.
+        */
+       if (!f_inode && !f_longform && !f_size && !f_type &&
+           sortkey == BY_NAME)
+               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)
+               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 (!kflag)
+                       (void)getbsize(&notused, &blocksize);
+               blocksize /= 512;
+       }
+
+       /* Select a sort function. */
+       if (f_reversesort) {
+               switch (sortkey) {
+               case BY_NAME:
+                       sortfcn = revnamecmp;
+                       break;
+               case BY_SIZE:
+                       sortfcn = revsizecmp;
+                       break;
+               case BY_TIME:
+                       if (f_accesstime)
+                               sortfcn = revacccmp;
+                       else if (f_statustime)
+                               sortfcn = revstatcmp;
+                       else /* Use modification time. */
+                               sortfcn = revmodcmp;
+                       break;
+               }
+       } else {
+               switch (sortkey) {
+               case BY_NAME:
+                       sortfcn = namecmp;
+                       break;
+               case BY_SIZE:
+                       sortfcn = sizecmp;
+                       break;
+               case BY_TIME:
+                       if (f_accesstime)
+                               sortfcn = acccmp;
+                       else if (f_statustime)
+                               sortfcn = statcmp;
+                       else /* Use modification time. */
+                               sortfcn = modcmp;
+                       break;
+               }
+       }
+
+       /* Select a print function. */
+       if (f_singlecol)
+               printfcn = printscol;
+       else if (f_columnacross)
+               printfcn = printacol;
+       else if (f_longform)
+               printfcn = printlong;
+       else
+               printfcn = printcol;
+
+       if (argc)
+               traverse(argc, argv, fts_options);
+       else
+               traverse(1, dotav, fts_options);
+       exit(0);
+       /* NOTREACHED */
+}
+
+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(argc, argv, options)
+       int argc, options;
+       char *argv[];
+{
+       FTS *ftsp;
+       FTSENT *p, *chp;
+       int ch_options;
+       char pbuf[MAXPATHLEN + 1], *path;
+
+       if ((ftsp =
+           fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
+               err(1, "%s", "");
+
+       display(NULL, fts_children(ftsp, 0));
+       if (f_listdir)
+               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);
+                       break;
+               case FTS_DNR:
+               case FTS_ERR:
+                       warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
+                       break;
+               case FTS_D:
+                       if (p->fts_level != FTS_ROOTLEVEL &&
+                           p->fts_name[0] == '.' && !f_listdot)
+                               break;
+
+                       /*
+                        * If already output something, put out a newline as
+                        * a separator.  If multiple arguments, precede each
+                        * directory with its name.
+                        */
+                       if (f_nonprint) {
+                               prcopy(p->fts_path, pbuf, p->fts_pathlen+1);
+                               path = pbuf;
+                       } else
+                               path = p->fts_path;
+
+                       if (output)
+                               (void)printf("\n%s:\n", path);
+                       else if (argc > 1) {
+                               (void)printf("%s:\n", path);
+                               output = 1;
+                       }
+
+                       chp = fts_children(ftsp, ch_options);
+                       display(p, chp);
+
+                       if (!f_recursive && chp != NULL)
+                               (void)fts_set(ftsp, p, FTS_SKIP);
+                       break;
+               }
+       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(p, list)
+       FTSENT *p, *list;
+{
+       struct stat *sp;
+       DISPLAY d;
+       FTSENT *cur;
+       NAMES *np;
+       u_int64_t btotal, maxblock, maxsize;
+       int maxinode, maxnlink, maxmajor, maxminor;
+       int bcfile, entries, flen, glen, ulen, maxflags, maxgroup, maxlen;
+       int maxuser, needstats;
+       const char *user, *group;
+       char ubuf[21]="", gbuf[21]="", buf[21]; /* 64 bits == 20 digits, +1 for NUL */
+       char nuser[12], ngroup[12];
+       char *flags = NULL;
+
+#ifdef __GNUC__
+       /* This outrageous construct just to shut up a GCC warning. */
+       (void) &maxsize;
+#endif
+
+       /*
+        * 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 signalled in traverse().
+        */
+       if (list == NULL)
+               return;
+
+       needstats = f_inode || f_longform || f_size;
+       flen = 0;
+       maxinode = maxnlink = 0;
+       bcfile = 0;
+       maxuser = maxgroup = maxflags = maxlen = 0;
+       btotal = maxblock = maxsize = 0;
+       maxmajor = maxminor = 0;
+       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;
+                       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 (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;
+                       if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) {
+                               bcfile = 1;
+                               if (major(sp->st_rdev) > maxmajor)
+                                       maxmajor = major(sp->st_rdev);
+                               if (minor(sp->st_rdev) > maxminor)
+                                       maxminor = minor(sp->st_rdev);
+                       }
+
+                       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 {
+                                       if ((user = user_from_uid(sp->st_uid, 0)) == NULL) {
+                                               (void)snprintf(ubuf, sizeof(ubuf), "%d", (int)sp->st_uid);
+                                               user = ubuf;
+                                       }
+                                       if ((group = group_from_gid(sp->st_gid, 0)) == NULL) {
+                                               (void)snprintf(gbuf, sizeof(gbuf), "%d", (int)sp->st_gid);
+                                               group = gbuf;
+                                       }
+                               }
+                               if ((ulen = strlen(user)) > maxuser)
+                                       maxuser = ulen;
+                               if ((glen = strlen(group)) > maxgroup)
+                                       maxgroup = glen;
+                               if (f_flags) {
+                                       flags =
+                                           flags_to_string(sp->st_flags, "-");
+                                       if ((flen = strlen(flags)) > maxflags)
+                                               maxflags = flen;
+                               } else
+                                       flen = 0;
+
+                               if ((np = malloc(sizeof(NAMES) +
+                                   ulen + glen + flen + 3)) == NULL)
+                                       err(1, "%s", "");
+
+                               np->user = &np->data[0];
+                               (void)strcpy(np->user, user);
+                               np->group = &np->data[ulen + 1];
+                               (void)strcpy(np->group, group);
+
+                               if (f_flags) {
+                                       np->flags = &np->data[ulen + glen + 2];
+                                       (void)strcpy(np->flags, flags);
+                               }
+                               cur->fts_pointer = np;
+                       }
+               }
+               ++entries;
+       }
+
+       if (!entries)
+               return;
+
+       d.list = list;
+       d.entries = entries;
+       d.maxlen = maxlen;
+       if (needstats) {
+               d.btotal = btotal;
+               (void)snprintf(buf, sizeof(buf), "%qu", (long long)maxblock);
+               d.s_block = strlen(buf);
+               d.s_flags = maxflags;
+               d.s_group = maxgroup;
+               (void)snprintf(buf, sizeof(buf), "%u", maxinode);
+               d.s_inode = strlen(buf);
+               (void)snprintf(buf, sizeof(buf), "%u", maxnlink);
+               d.s_nlink = strlen(buf);
+               (void)snprintf(buf, sizeof(buf), "%qu", (long long)maxsize);
+               d.s_size = strlen(buf);
+               d.s_user = maxuser;
+               if (bcfile) {
+                       (void)snprintf(buf, sizeof(buf), "%u", maxmajor);
+                       d.s_major = strlen(buf);
+                       (void)snprintf(buf, sizeof(buf), "%u", maxminor);
+                       d.s_minor = strlen(buf);
+                       if (d.s_major + d.s_minor + 2 > d.s_size)
+                               d.s_size = d.s_major + d.s_minor + 2;
+                       else if (d.s_size - d.s_minor - 2 > d.s_major)
+                               d.s_major = d.s_size - d.s_minor - 2;
+               } else {
+                       d.s_major = 0;
+                       d.s_minor = 0;
+               }
+       }
+
+       printfcn(&d);
+       output = 1;
+
+       if (f_longform)
+               for (cur = list; cur; cur = cur->fts_link)
+                       free(cur->fts_pointer);
+}
+
+/*
+ * 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(a, b)
+       const FTSENT **a, **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) {
+               if (b_info != FTS_NS)
+                       return (1);
+               else if (a_info != FTS_NS)
+                       return (-1);
+               else
+                       return (namecmp(*a, *b));
+       }
+
+       if (a_info != b_info && !f_listdir &&
+           (*a)->fts_level == FTS_ROOTLEVEL) {
+               if (a_info == FTS_D)
+                       return (1);
+               else if (b_info == FTS_D)
+                       return (-1);
+       }
+       return (sortfcn(*a, *b));
+}
diff --git a/ls/ls.h b/ls/ls.h
new file mode 100644 (file)
index 0000000..733a7bb
--- /dev/null
+++ b/ls/ls.h
@@ -0,0 +1,76 @@
+/*     $NetBSD: ls.h,v 1.9 1998/05/16 15:12:26 lukem Exp $     */
+
+/*
+ * 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.
+ *
+ *     @(#)ls.h        8.1 (Berkeley) 5/31/93
+ */
+
+#define NO_PRINT       1
+
+extern long blocksize;         /* block size units */
+
+extern int f_accesstime;       /* use time of last access */
+extern int f_flags;            /* show flags associated with a file */
+extern int f_inode;            /* print inode */
+extern int f_longform;         /* long listing format */
+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_statustime;       /* use time of last mode change */
+extern int f_type;             /* add type character for non-regular files */
+
+typedef struct {
+       FTSENT *list;
+       u_int64_t btotal;
+       int entries;
+       int maxlen;
+       int s_block;
+       int s_flags;
+       int s_group;
+       int s_inode;
+       int s_nlink;
+       int s_size;
+       int s_user;
+       int s_major;
+       int s_minor;
+} DISPLAY;
+
+typedef struct {
+       char *user;
+       char *group;
+       char *flags;
+       char data[1];
+} NAMES;
diff --git a/ls/print.c b/ls/print.c
new file mode 100644 (file)
index 0000000..240f221
--- /dev/null
@@ -0,0 +1,363 @@
+/*     $NetBSD: print.c,v 1.22 1998/07/28 05:15:47 mycroft Exp $       */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)print.c    8.5 (Berkeley) 7/28/94";
+#else
+__RCSID("$NetBSD: print.c,v 1.22 1998/07/28 05:15:47 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#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 <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "ls.h"
+#include "extern.h"
+
+static int     printaname __P((FTSENT *, int, int));
+static void    printlink __P((FTSENT *));
+static void    printtime __P((time_t));
+static int     printtype __P((u_int));
+
+static time_t  now;
+
+#define        IS_NOPRINT(p)   ((p)->fts_number == NO_PRINT)
+
+void
+printscol(dp)
+       DISPLAY *dp;
+{
+       FTSENT *p;
+
+       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');
+       }
+}
+
+void
+printlong(dp)
+       DISPLAY *dp;
+{
+       struct stat *sp;
+       FTSENT *p;
+       NAMES *np;
+       char buf[20];
+       char nbuf[MAXPATHLEN + 1], *name;
+
+       now = time(NULL);
+
+       if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+               (void)printf("total %qu\n",
+                   (long long)(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)
+                       (void)printf("%*u ", dp->s_inode, sp->st_ino);
+               if (f_size)
+                       (void)printf("%*qu ", dp->s_block,
+                           (long long)howmany(sp->st_blocks, blocksize));
+               (void)strmode(sp->st_mode, buf);
+               np = p->fts_pointer;
+               (void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
+                   sp->st_nlink, dp->s_user, np->user, dp->s_group,
+                   np->group);
+               if (f_flags)
+                       (void)printf("%-*s ", dp->s_flags, np->flags);
+               if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
+                       (void)printf("%*u, %*u ",
+                           dp->s_major, major(sp->st_rdev), dp->s_minor,
+                           minor(sp->st_rdev));
+               else
+                       (void)printf("%*qu ", dp->s_size,
+                           (long long)sp->st_size);
+               if (f_accesstime)
+                       printtime(sp->st_atime);
+               else if (f_statustime)
+                       printtime(sp->st_ctime);
+               else
+                       printtime(sp->st_mtime);
+               if (f_nonprint) {
+                       prcopy(p->fts_name, nbuf, p->fts_namelen+1);
+                       name = nbuf;
+               } else
+                       name = p->fts_name;
+               (void)printf("%s", name);
+               if (f_type)
+                       (void)printtype(sp->st_mode);
+               if (S_ISLNK(sp->st_mode))
+                       printlink(p);
+               (void)putchar('\n');
+       }
+}
+
+void
+printcol(dp)
+       DISPLAY *dp;
+{
+       extern int termwidth;
+       static FTSENT **array;
+       static int lastentries = -1;
+       FTSENT *p;
+       int base, chcnt, col, colwidth, num;
+       int numcols, numrows, row;
+
+       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 += 1;
+
+       if (termwidth < 2 * colwidth) {
+               printscol(dp);
+               return;
+       }
+
+       /*
+        * Have to do random access in the linked list -- build a table
+        * of pointers.
+        */
+       if (dp->entries > lastentries) {
+               lastentries = dp->entries;
+               if ((array =
+                   realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
+                       warn("%s", "");
+                       printscol(dp);
+               }
+       }
+       for (p = dp->list, num = 0; p; p = p->fts_link)
+               if (p->fts_number != NO_PRINT)
+                       array[num++] = p;
+
+       numcols = termwidth / colwidth;
+       colwidth = termwidth / numcols;         /* spread out if possible */
+       numrows = num / numcols;
+       if (num % numcols)
+               ++numrows;
+
+       if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+               (void)printf("total %qu\n",
+                   (long long)(howmany(dp->btotal, blocksize)));
+       for (row = 0; row < numrows; ++row) {
+               for (base = row, chcnt = col = 0; col < numcols; ++col) {
+                       chcnt = printaname(array[base], dp->s_inode,
+                           dp->s_block);
+                       if ((base += numrows) >= num)
+                               break;
+                       while (chcnt++ < colwidth)
+                               (void)putchar(' ');
+               }
+               (void)putchar('\n');
+       }
+}
+
+void
+printacol(dp)
+       DISPLAY *dp;
+{
+       extern int termwidth;
+       FTSENT *p;
+       int chcnt, col, colwidth;
+       int numcols;
+
+       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 += 1;
+
+       if (termwidth < 2 * colwidth) {
+               printscol(dp);
+               return;
+       }
+
+       numcols = termwidth / colwidth;
+       colwidth = termwidth / numcols;         /* spread out if possible */
+
+       if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+               (void)printf("total %qu\n", 
+                   (long long)(howmany(dp->btotal, blocksize)));
+       chcnt = col = 0;
+       for (p = dp->list; p; p = p->fts_link) {
+               if (IS_NOPRINT(p))
+                       continue;
+               if (col >= numcols) {
+                       chcnt = col = 0;
+                       (void)putchar('\n');
+               }
+               chcnt = printaname(p, dp->s_inode, dp->s_block);
+               while (chcnt++ < colwidth)
+                       (void)putchar(' ');
+               col++;
+       }
+       (void)putchar('\n');
+}
+
+/*
+ * print [inode] [size] name
+ * return # of characters printed, no trailing characters.
+ */
+static int
+printaname(p, inodefield, sizefield)
+       FTSENT *p;
+       int sizefield, inodefield;
+{
+       struct stat *sp;
+       int chcnt;
+       char nbuf[MAXPATHLEN + 1], *name;
+
+       sp = p->fts_statp;
+       chcnt = 0;
+       if (f_inode)
+               chcnt += printf("%*u ", inodefield, sp->st_ino);
+       if (f_size)
+               chcnt += printf("%*qu ", sizefield,
+                   (long long)howmany(sp->st_blocks, blocksize));
+       if (f_nonprint) {
+               prcopy(p->fts_name, nbuf, p->fts_namelen+1);
+               name = nbuf;
+       } else
+               name = p->fts_name;
+       chcnt += printf("%s", name);
+       if (f_type)
+               chcnt += printtype(sp->st_mode);
+       return (chcnt);
+}
+
+static void
+printtime(ftime)
+       time_t ftime;
+{
+       int i;
+       char *longstring;
+
+       longstring = ctime(&ftime);
+       for (i = 4; i < 11; ++i)
+               (void)putchar(longstring[i]);
+
+#define        SIXMONTHS       ((DAYSPERNYEAR / 2) * SECSPERDAY)
+       if (f_sectime)
+               for (i = 11; i < 24; i++)
+                       (void)putchar(longstring[i]);
+       else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now)
+               for (i = 11; i < 16; ++i)
+                       (void)putchar(longstring[i]);
+       else {
+               (void)putchar(' ');
+               for (i = 20; i < 24; ++i)
+                       (void)putchar(longstring[i]);
+       }
+       (void)putchar(' ');
+}
+
+static int
+printtype(mode)
+       u_int mode;
+{
+       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);
+       }
+       if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+               (void)putchar('*');
+               return (1);
+       }
+       return (0);
+}
+
+static void
+printlink(p)
+       FTSENT *p;
+{
+       int lnklen;
+       char name[MAXPATHLEN + 1], 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);
+       prcopy(name, name, strlen(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(" -> %s", path);
+}
diff --git a/ls/stat_flags.c b/ls/stat_flags.c
new file mode 100644 (file)
index 0000000..e46a3e8
--- /dev/null
@@ -0,0 +1,164 @@
+/*     $NetBSD: stat_flags.c,v 1.6 1997/07/20 18:53:12 christos Exp $  */
+
+/*-
+ * 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>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)stat_flags.c       8.2 (Berkeley) 7/28/94";
+#else
+__RCSID("$NetBSD: stat_flags.c,v 1.6 1997/07/20 18:53:12 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stddef.h>
+#include <string.h>
+#include <fts.h>
+
+#include "ls.h"
+#include "extern.h"
+
+#define        SAPPEND(s) {                                                    \
+       if (prefix != NULL)                                             \
+               (void)strcat(string, prefix);                           \
+       (void)strcat(string, s);                                        \
+       prefix = ",";                                                   \
+}
+
+/*
+ * flags_to_string --
+ *     Convert stat flags to a comma-separated string.  If no flags
+ *     are set, return the default string.
+ */
+char *
+flags_to_string(flags, def)
+       u_long flags;
+       char *def;
+{
+       static char string[128];
+       char *prefix;
+
+       string[0] = '\0';
+       prefix = NULL;
+       if (flags & UF_APPEND)
+               SAPPEND("uappnd");
+       if (flags & UF_IMMUTABLE)
+               SAPPEND("uchg");
+       if (flags & UF_NODUMP)
+               SAPPEND("nodump");
+       if (flags & UF_OPAQUE)
+               SAPPEND("opaque");
+       if (flags & SF_APPEND)
+               SAPPEND("sappnd");
+       if (flags & SF_ARCHIVED)
+               SAPPEND("arch");
+       if (flags & SF_IMMUTABLE)
+               SAPPEND("schg");
+       return (prefix == NULL && def != NULL ? def : string);
+}
+
+#define        TEST(a, b, f) {                                                 \
+       if (!memcmp(a, b, sizeof(b))) {                                 \
+               if (clear) {                                            \
+                       if (clrp)                                       \
+                               *clrp |= (f);                           \
+               } else if (setp)                                        \
+                       *setp |= (f);                                   \
+               break;                                                  \
+       }                                                               \
+}
+
+/*
+ * string_to_flags --
+ *     Take string of arguments and return stat flags.  Return 0 on
+ *     success, 1 on failure.  On failure, stringp is set to point
+ *     to the offending token.
+ */
+int
+string_to_flags(stringp, setp, clrp)
+       char **stringp;
+       u_long *setp, *clrp;
+{
+       int clear;
+       char *string, *p;
+
+       clear = 0;
+       if (setp)
+               *setp = 0;
+       if (clrp)
+               *clrp = 0;
+       string = *stringp;
+       while ((p = strsep(&string, "\t ,")) != NULL) {
+               *stringp = p;
+               if (*p == '\0')
+                       continue;
+               if (p[0] == 'n' && p[1] == 'o') {
+                       clear = 1;
+                       p += 2;
+               }
+               switch (p[0]) {
+               case 'a':
+                       TEST(p, "arch", SF_ARCHIVED);
+                       TEST(p, "archived", SF_ARCHIVED);
+                       return (1);
+               case 'd':
+                       clear = !clear;
+                       TEST(p, "dump", UF_NODUMP);
+                       return (1);
+               case 'o':
+                       TEST(p, "opaque", UF_OPAQUE);
+                       return (1);
+               case 's':
+                       TEST(p, "sappnd", SF_APPEND);
+                       TEST(p, "sappend", SF_APPEND);
+                       TEST(p, "schg", SF_IMMUTABLE);
+                       TEST(p, "schange", SF_IMMUTABLE);
+                       TEST(p, "simmutable", SF_IMMUTABLE);
+                       return (1);
+               case 'u':
+                       TEST(p, "uappnd", UF_APPEND);
+                       TEST(p, "uappend", UF_APPEND);
+                       TEST(p, "uchg", UF_IMMUTABLE);
+                       TEST(p, "uchange", UF_IMMUTABLE);
+                       TEST(p, "uimmutable", UF_IMMUTABLE);
+                       /* FALLTHROUGH */
+               default:
+                       return (1);
+               }
+       }
+       return (0);
+}
diff --git a/ls/util.c b/ls/util.c
new file mode 100644 (file)
index 0000000..fee0027
--- /dev/null
+++ b/ls/util.c
@@ -0,0 +1,80 @@
+/*     $NetBSD: util.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $        */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)util.c     8.5 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: util.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ls.h"
+#include "extern.h"
+
+void
+prcopy(src, dest, len)
+       char *src, *dest;
+       int len;
+{
+       int ch;
+
+       while (len--) {
+               ch = *src++;
+               *dest++ = (isprint(ch) || ch == '\0') ? ch : '?';
+       }
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr, 
+           "usage: ls [-1ACFLRSTWacdfgiklnoqrstux] [file ...]\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/mkdir/Makefile b/mkdir/Makefile
new file mode 100644 (file)
index 0000000..d26e5ad
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = mkdir
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = mkdir.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mkdir.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/mkdir/Makefile.postamble b/mkdir/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/mkdir/Makefile.preamble b/mkdir/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/mkdir/PB.project b/mkdir/PB.project
new file mode 100644 (file)
index 0000000..0d6316a
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (mkdir.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mkdir.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = mkdir; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/mkdir/mkdir.1 b/mkdir/mkdir.1
new file mode 100644 (file)
index 0000000..83b87a4
--- /dev/null
@@ -0,0 +1,97 @@
+.\"    $NetBSD: mkdir.1,v 1.10 1997/10/20 08:52:35 enami Exp $
+.\"
+.\" 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
+.\"
+.Dd January 25, 1994
+.Dt MKDIR 1
+.Os
+.Sh NAME
+.Nm mkdir
+.Nd make directories
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Op Fl m Ar mode
+.Ar directory_name  ...
+.Sh DESCRIPTION
+.Nm
+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
+Set the file permission bits of the final created directory to
+the specified mode.
+The mode argument can be in any of the formats specified to the
+.Xr chmod 1
+utility.
+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.
+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.  Do not consider it an error if the
+argument directory already exists.
+.El
+.Pp
+The user must have write permission in the parent directory.
+.Pp
+.Nm
+exits 0 if successful, and >0 if an error occurred.
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr rmdir 1 ,
+.Xr umask 2
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/mkdir/mkdir.c b/mkdir/mkdir.c
new file mode 100644 (file)
index 0000000..2dcd1cf
--- /dev/null
@@ -0,0 +1,195 @@
+/*     $NetBSD: mkdir.c,v 1.19 1998/07/28 05:31:25 mycroft Exp $       */
+
+/*
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: mkdir.c,v 1.19 1998/07/28 05:31:25 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int    mkpath __P((char *, mode_t, mode_t));
+void   usage __P((void));
+int    main __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch, exitval, pflag;
+       mode_t *set;
+       mode_t mode, dir_mode;
+
+       (void)setlocale(LC_ALL, "");
+
+       /*
+        * The default file mode is a=rwx (0777) with selected permissions
+        * removed in accordance with the file mode creation mask.  For
+        * intermediate path name components, the mode is the default modified
+        * by u+wx so that the subdirectories can always be created.
+        */
+       mode = 0777 & ~umask(0);
+       dir_mode = mode | S_IWUSR | S_IXUSR;
+
+       pflag = 0;
+       while ((ch = getopt(argc, argv, "m:p")) != -1)
+               switch(ch) {
+               case 'p':
+                       pflag = 1;
+                       break;
+               case 'm':
+                       if ((set = setmode(optarg)) == NULL)
+                               errx(1, "invalid file mode: %s", optarg);
+                       mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (*argv == NULL)
+               usage();
+       
+       for (exitval = 0; *argv != NULL; ++argv) {
+               char *slash;
+
+               /* Remove trailing slashes, per POSIX. */
+               slash = strrchr(*argv, '\0');
+               while (--slash > *argv && *slash == '/')
+                       *slash = '\0';
+
+               if (pflag) {
+                       if (mkpath(*argv, mode, dir_mode) < 0)
+                               exitval = 1;
+               } else {
+                       if (mkdir(*argv, mode) < 0) {
+                               warn("%s", *argv);
+                               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. So chmod().
+                         */
+                       else if (chmod(*argv, mode) == -1) {
+                               warn("%s", *argv);
+                               exitval = 1;
+                        }
+               }
+       }
+       exit(exitval);
+       /* NOTREACHED */
+}
+
+/*
+ * mkpath -- create directories.  
+ *     path     - path
+ *     mode     - file mode of terminal directory
+ *     dir_mode - file mode of intermediate directories
+ */
+int
+mkpath(path, mode, dir_mode)
+       char *path;
+       mode_t mode;
+       mode_t dir_mode;
+{
+       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, done ? mode : dir_mode)) {
+                               warn("%s", path);
+                               return (-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. So chmod().
+                         */
+                       if (chmod(path, done ? mode : dir_mode) == -1) {
+                               warn("%s", path);
+                               return (-1);
+                        }
+               } else if (!S_ISDIR(sb.st_mode)) {
+                       warnx("%s: %s", path, strerror(ENOTDIR));
+                       return (-1);
+               }
+                   
+               *slash = '/';
+       }
+
+       return (0);
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr, "usage: mkdir [-p] [-m mode] dirname ...\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/mkfifo/Makefile b/mkfifo/Makefile
new file mode 100644 (file)
index 0000000..ed49dbe
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = mkfifo
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = mkfifo.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mkfifo.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/mkfifo/Makefile.postamble b/mkfifo/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/mkfifo/Makefile.preamble b/mkfifo/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/mkfifo/PB.project b/mkfifo/PB.project
new file mode 100644 (file)
index 0000000..b348276
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (mkfifo.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mkfifo.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = mkfifo; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/mkfifo/mkfifo.1 b/mkfifo/mkfifo.1
new file mode 100644 (file)
index 0000000..f8a0e16
--- /dev/null
@@ -0,0 +1,91 @@
+.\"    $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
+.Op Fl m Ar mode
+.Ar fifo_name  ...
+.Sh DESCRIPTION
+.Nm
+creates the fifos requested, in the order specified,
+using mode
+.Li \&0666 
+modified 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 directories to
+.Ar mode .
+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
+requires write permission in the parent directory.
+.Pp
+.Nm
+exits 0 if successful, and >0 if an error occurred.
+.Sh SEE ALSO
+.Xr mkdir 1 ,
+.Xr rm 1 ,
+.Xr mkfifo 2 ,
+.Xr mknod 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2-92
+compliant.
+.Sh HISTORY
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/mkfifo/mkfifo.c b/mkfifo/mkfifo.c
new file mode 100644 (file)
index 0000000..6bd45b0
--- /dev/null
@@ -0,0 +1,113 @@
+/*     $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>
+
+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;
+
+       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 */
+       mode = 0666 & ~umask(0);
+
+       while ((ch = getopt(argc, argv, "m:")) != -1)
+               switch(ch) {
+               case 'm':
+                       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();
+
+       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/mknod/Makefile b/mknod/Makefile
new file mode 100644 (file)
index 0000000..b91bcdc
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = mknod
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = mknod.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mknod.8
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /sbin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/mknod/Makefile.postamble b/mknod/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/mknod/Makefile.preamble b/mknod/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/mknod/PB.project b/mknod/PB.project
new file mode 100644 (file)
index 0000000..db5f15a
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (mknod.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mknod.8); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /sbin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = mknod; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/mknod/mknod.8 b/mknod/mknod.8
new file mode 100644 (file)
index 0000000..7a1517d
--- /dev/null
@@ -0,0 +1,149 @@
+.\"    $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
+.Sh DESCRIPTION
+The
+.Nm
+command creates device special files.
+Normally the shell script
+.Pa /dev/MAKEDEV
+is used to create special files for commonly known devices; it executes
+.Nm
+with the appropriate arguments and can make all the files required for the
+device.
+.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
+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 .
+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.  To learn what
+major device number to use for a particular device, check the file
+.Pa /dev/MAKEDEV
+to see if the device is known, or check
+the system dependent device configuration file:
+.Bd -filled -offset indent
+.Dq Pa /usr/src/sys/conf/device. Ns Em architecture
+.Ed
+.Pp
+(for example
+.Pa device.hp300 ) .
+.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 ,
+.Xr MAKEDEV 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
+The
+.Fl F
+option appeared in
+.Nx 1.4 .
diff --git a/mknod/mknod.c b/mknod/mknod.c
new file mode 100644 (file)
index 0000000..c45abd5
--- /dev/null
@@ -0,0 +1,396 @@
+/*     $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>
+
+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 < 3 || 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
+               errx(1, "node type must be 'b' or 'c'.");
+       argc--;
+       argv++;
+
+       for (n = 0; n < argc; n++) {
+               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 (argc == 1)
+               dev = 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", argv[0]);
+#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");
+       exit(1);
+}
diff --git a/mtree/Makefile b/mtree/Makefile
new file mode 100644 (file)
index 0000000..01d374a
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = mtree
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = extern.h mtree.h
+
+CFILES = compare.c create.c misc.c mtree.c spec.c verify.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mtree.8
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/mtree/Makefile.postamble b/mtree/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/mtree/Makefile.preamble b/mtree/Makefile.preamble
new file mode 100644 (file)
index 0000000..65868da
--- /dev/null
@@ -0,0 +1,5 @@
+vpath crc.c ../cksum
+
+CFILES += crc.c
+
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/mtree/PB.project b/mtree/PB.project
new file mode 100644 (file)
index 0000000..22cc839
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (extern.h, mtree.h); 
+        OTHER_LINKED = (compare.c, create.c, misc.c, mtree.c, spec.c, verify.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mtree.8); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/sbin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = mtree; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/mtree/compare.c b/mtree/compare.c
new file mode 100644 (file)
index 0000000..596f9de
--- /dev/null
@@ -0,0 +1,298 @@
+/*     $NetBSD: compare.c,v 1.15 1998/08/27 18:03:45 ross Exp $        */
+
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)compare.c  8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: compare.c,v 1.15 1998/08/27 18:03:45 ross Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int tflag, uflag;
+
+static char *ftype __P((u_int));
+
+#define        INDENTNAMELEN   8
+#define        LABEL \
+       if (!label++) { \
+               len = printf("%s: ", RP(p)); \
+               if (len > INDENTNAMELEN) { \
+                       tab = "\t"; \
+                       (void)printf("\n"); \
+               } else { \
+                       tab = ""; \
+                       (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
+               } \
+       }
+
+int
+compare(name, s, p)
+       char *name;
+       NODE *s;
+       FTSENT *p;
+{
+       u_int32_t len, val;
+       int fd, label;
+       char *cp, *tab;
+
+       tab = NULL;
+       label = 0;
+       switch(s->type) {
+       case F_BLOCK:
+               if (!S_ISBLK(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_CHAR:
+               if (!S_ISCHR(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_DIR:
+               if (!S_ISDIR(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_FIFO:
+               if (!S_ISFIFO(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_FILE:
+               if (!S_ISREG(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_LINK:
+               if (!S_ISLNK(p->fts_statp->st_mode))
+                       goto typeerr;
+               break;
+       case F_SOCK:
+               if (!S_ISSOCK(p->fts_statp->st_mode)) {
+typeerr:               LABEL;
+                       (void)printf("\ttype (%s, %s)\n",
+                           ftype(s->type), inotype(p->fts_statp->st_mode));
+               }
+               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 (%u, %u",
+                   tab, s->st_uid, p->fts_statp->st_uid);
+               if (uflag)
+                       if (chown(p->fts_accpath, s->st_uid, -1))
+                               (void)printf(", not modified: %s)\n",
+                                   strerror(errno));
+                       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 (%u, %u",
+                   tab, s->st_gid, p->fts_statp->st_gid);
+               if (uflag)
+                       if (chown(p->fts_accpath, -1, s->st_gid))
+                               (void)printf(", not modified: %s)\n",
+                                   strerror(errno));
+                       else
+                               (void)printf(", modified)\n");
+               else
+                       (void)printf(")\n");
+               tab = "\t";
+       }
+       if (s->flags & F_MODE &&
+           s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+               LABEL;
+               (void)printf("%spermissions (%#o, %#o",
+                   tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+               if (uflag)
+                       if (chmod(p->fts_accpath, s->st_mode))
+                               (void)printf(", not modified: %s)\n",
+                                   strerror(errno));
+                       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 (%u, %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) {
+               LABEL;
+               (void)printf("%ssize (%qd, %qd)\n",
+                   tab, (long long)s->st_size,
+                   (long long)p->fts_statp->st_size);
+               tab = "\t";
+       }
+       /*
+        * XXX
+        * Since utimes(2) only takes a timeval, there's no point in
+        * comparing the low bits of the timespec nanosecond field.  This
+        * will only result in mismatches that we can never fix.
+        *
+        * Doesn't display microsecond differences.
+        */
+       if (s->flags & F_TIME) {
+               struct timeval tv[2];
+
+               TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
+               TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec);
+               if (tv[0].tv_sec != tv[1].tv_sec ||
+                   tv[0].tv_usec != tv[1].tv_usec) {
+                       LABEL;
+                       (void)printf("%smodification time (%.24s, ",
+                           tab, ctime(&s->st_mtimespec.tv_sec));
+                       (void)printf("%.24s",
+                           ctime(&p->fts_statp->st_mtimespec.tv_sec));
+                       if (tflag) {
+                               tv[1] = tv[0];
+                               if (utimes(p->fts_accpath, tv))
+                                       (void)printf(", not modified: %s)\n",
+                                           strerror(errno));
+                               else
+                                       (void)printf(", modified)\n");
+                       } else
+                               (void)printf(")\n");
+                       tab = "\t";
+               }
+       }
+       if (s->flags & F_CKSUM) {
+               if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+                       LABEL;
+                       (void)printf("%scksum: %s: %s\n",
+                           tab, p->fts_accpath, strerror(errno));
+                       tab = "\t";
+               } else if (crc(fd, &val, &len)) {
+                       (void)close(fd);
+                       LABEL;
+                       (void)printf("%scksum: %s: %s\n",
+                           tab, p->fts_accpath, strerror(errno));
+                       tab = "\t";
+               } else {
+                       (void)close(fd);
+                       if (s->cksum != val) {
+                               LABEL;
+                               (void)printf("%scksum (%lu, %lu)\n", 
+                                   tab, s->cksum, (unsigned long)val);
+                       }
+                       tab = "\t";
+               }
+       }
+       if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
+               LABEL;
+               (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
+       }
+       return (label);
+}
+
+char *
+inotype(type)
+       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 */
+}
+
+static char *
+ftype(type)
+       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(name)
+       char *name;
+{
+       static char lbuf[MAXPATHLEN];
+       int len;
+
+       if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1)
+               mtree_err("%s: %s", name, strerror(errno));
+       lbuf[len] = '\0';
+       return (lbuf);
+}
diff --git a/mtree/create.c b/mtree/create.c
new file mode 100644 (file)
index 0000000..7487de1
--- /dev/null
@@ -0,0 +1,306 @@
+/*     $NetBSD: create.c,v 1.16 1998/08/30 03:20:09 nathanw Exp $      */
+
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)create.c   8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: create.c,v 1.16 1998/08/30 03:20:09 nathanw Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define        INDENTNAMELEN   15
+#define        MAXLINELEN      80
+
+extern int crc_total, ftsoptions;
+extern int dflag, sflag;
+extern u_short keys;
+extern char fullpath[MAXPATHLEN];
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+
+static int     dsort __P((const FTSENT **, const FTSENT **));
+static void    output __P((int *, const char *, ...));
+static int     statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *));
+static void    statf __P((FTSENT *));
+
+void
+cwalk()
+{
+       FTS *t;
+       FTSENT *p;
+       time_t clock;
+       char *argv[2], host[MAXHOSTNAMELEN + 1];
+
+       (void)time(&clock);
+       (void)gethostname(host, sizeof(host));
+       host[sizeof(host) - 1] = '\0';
+       (void)printf(
+           "#\t   user: %s\n#\tmachine: %s\n#\t   tree: %s\n#\t   date: %s",
+           getlogin(), host, fullpath, ctime(&clock));
+
+       argv[0] = ".";
+       argv[1] = NULL;
+       if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+               mtree_err("fts_open: %s", strerror(errno));
+       while ((p = fts_read(t)) != NULL)
+               switch(p->fts_info) {
+               case FTS_D:
+                       (void)printf("\n# %s\n", p->fts_path);
+                       statd(t, p, &uid, &gid, &mode);
+                       statf(p);
+                       break;
+               case FTS_DP:
+                       if (p->fts_level > 0)
+                               (void)printf("# %s\n..\n\n", p->fts_path);
+                       break;
+               case FTS_DNR:
+               case FTS_ERR:
+               case FTS_NS:
+                       (void)fprintf(stderr,
+                           "mtree: %s: %s\n", p->fts_path, strerror(errno));
+                       break;
+               default:
+                       if (!dflag)
+                               statf(p);
+                       break;
+                       
+               }
+       (void)fts_close(t);
+       if (sflag && keys & F_CKSUM)
+               (void)fprintf(stderr,
+                   "mtree: %s checksum: %u\n", fullpath, crc_total);
+}
+
+static void
+statf(p)
+       FTSENT *p;
+{
+       struct group *gr;
+       struct passwd *pw;
+       u_int32_t len, val;
+       int fd, indent;
+
+       if (S_ISDIR(p->fts_statp->st_mode))
+               indent = printf("%s", p->fts_name); 
+       else
+               indent = printf("    %s", p->fts_name);
+
+       if (indent > INDENTNAMELEN)
+               indent = MAXLINELEN;
+       else
+               indent += printf("%*s", INDENTNAMELEN - indent, "");
+
+       if (!S_ISREG(p->fts_statp->st_mode))
+               output(&indent, "type=%s", inotype(p->fts_statp->st_mode));
+       if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
+               if (keys & F_UNAME && (pw = getpwuid(p->fts_statp->st_uid)))
+                       output(&indent, "uname=%s", pw->pw_name);
+               else /* if (keys & F_UID) */
+                       output(&indent, "uid=%u", p->fts_statp->st_uid);
+       }
+       if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
+               if (keys & F_GNAME && (gr = getgrgid(p->fts_statp->st_gid)))
+                       output(&indent, "gname=%s", gr->gr_name);
+               else /* if (keys & F_GID) */
+                       output(&indent, "gid=%u", p->fts_statp->st_gid);
+       }
+       if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
+               output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS);
+       if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
+               output(&indent, "nlink=%u", p->fts_statp->st_nlink);
+       if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode))
+               output(&indent, "size=%qd", p->fts_statp->st_size);
+       if (keys & F_TIME)
+               output(&indent, "time=%ld.%ld",
+                   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))
+                       mtree_err("%s: %s", p->fts_accpath, strerror(errno));
+               (void)close(fd);
+               output(&indent, "cksum=%lu", val);
+       }
+       if (keys & F_SLINK &&
+           (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+               output(&indent, "link=%s", rlink(p->fts_accpath));
+       (void)putchar('\n');
+}
+
+#define        MAXGID  5000
+#define        MAXUID  5000
+#define        MAXMODE MBITS + 1
+
+static int
+statd(t, parent, puid, pgid, pmode)
+       FTS *t;
+       FTSENT *parent;
+       uid_t *puid;
+       gid_t *pgid;
+       mode_t *pmode;
+{
+       FTSENT *p;
+       gid_t sgid;
+       uid_t suid;
+       mode_t smode;
+       struct group *gr;
+       struct passwd *pw;
+       gid_t savegid;
+       uid_t saveuid;
+       mode_t savemode;
+       u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE];
+
+       savegid = 0;
+       saveuid = 0;
+       savemode = 0;
+       if ((p = fts_children(t, 0)) == NULL) {
+               if (errno)
+                       mtree_err("%s: %s", RP(parent), strerror(errno));
+               return (1);
+       }
+
+       memset(g, 0, sizeof(g));
+       memset(u, 0, sizeof(u));
+       memset(m, 0, sizeof(m));
+
+       maxuid = maxgid = maxmode = 0;
+       for (; p; p = p->fts_link) {
+               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];
+               }
+       }
+       (void)printf("/set type=file");
+       if (keys & F_GID)
+               (void)printf(" gid=%u", savegid);
+       if (keys & F_GNAME) {
+               if ((gr = getgrgid(savegid)) != NULL)
+                       (void)printf(" gname=%s", gr->gr_name);
+               else
+                       (void)printf(" gid=%u", savegid);
+       }
+       if (keys & F_UNAME) {
+               if ((pw = getpwuid(saveuid)) != NULL)
+                       (void)printf(" uname=%s", pw->pw_name);
+               else
+                       (void)printf(" uid=%u", saveuid);
+       }
+       if (keys & F_UID)
+               (void)printf(" uid=%u", saveuid);
+       if (keys & F_MODE)
+               (void)printf(" mode=%#o", savemode);
+       if (keys & F_NLINK)
+               (void)printf(" nlink=1");
+       (void)printf("\n");
+       *puid = saveuid;
+       *pgid = savegid;
+       *pmode = savemode;
+       return (0);
+}
+
+static int
+dsort(a, b)
+       const FTSENT **a, **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));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+output(int *offset, const char *fmt, ...)
+#else
+output(offset, fmt, va_alist)
+       int *offset;
+       char *fmt;
+        va_dcl
+#endif
+{
+       va_list ap;
+       char buf[1024];
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+
+       if (*offset + strlen(buf) > MAXLINELEN - 3) {
+               (void)printf(" \\\n%*s", INDENTNAMELEN, "");
+               *offset = INDENTNAMELEN;
+       }
+       *offset += printf(" %s", buf) + 1;
+}
diff --git a/mtree/extern.h b/mtree/extern.h
new file mode 100644 (file)
index 0000000..4b09f62
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $NetBSD: extern.h,v 1.3 1995/03/07 21:12:07 cgd Exp $   */
+
+/*-
+ * 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
+ */
+
+#include "mtree.h"
+
+#ifdef __APPLE__
+#include <fts.h>
+#endif
+
+int     compare __P((char *, NODE *, FTSENT *));
+int     crc __P((int, u_int32_t *, u_int32_t *));
+void    cwalk __P((void));
+void    mtree_err __P((const char *, ...));
+char   *inotype __P((u_int));
+u_int   parsekey __P((char *, int *));
+char   *rlink __P((char *));
+NODE   *spec __P((void));
+int     verify __P((void));
diff --git a/mtree/misc.c b/mtree/misc.c
new file mode 100644 (file)
index 0000000..4eeceef
--- /dev/null
@@ -0,0 +1,134 @@
+/*     $NetBSD: misc.c,v 1.5 1997/10/17 11:46:40 lukem Exp $   */
+
+/*-
+ * 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.
+ *
+ *     @(#)misc.c      8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: misc.c,v 1.5 1997/10/17 11:46:40 lukem Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int lineno;
+
+typedef struct _key {
+       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[] = {
+       {"cksum",       F_CKSUM,        NEEDVALUE},
+       {"gid",         F_GID,          NEEDVALUE},
+       {"gname",       F_GNAME,        NEEDVALUE},
+       {"ignore",      F_IGN,          0},
+       {"link",        F_SLINK,        NEEDVALUE},
+       {"mode",        F_MODE,         NEEDVALUE},
+       {"nlink",       F_NLINK,        NEEDVALUE},
+       {"optional",    F_OPT,          0},
+       {"size",        F_SIZE,         NEEDVALUE},
+       {"time",        F_TIME,         NEEDVALUE},
+       {"type",        F_TYPE,         NEEDVALUE},
+       {"uid",         F_UID,          NEEDVALUE},
+       {"uname",       F_UNAME,        NEEDVALUE}
+};
+
+int keycompare __P((const void *, const void *));
+
+u_int
+parsekey(name, needvaluep)
+       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)
+               mtree_err("unknown keyword %s", name);
+
+       if (needvaluep)
+               *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+       return (k->val);
+}
+
+int
+keycompare(a, b)
+       const void *a, *b;
+{
+       return (strcmp(((KEY *)a)->name, ((KEY *)b)->name));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+mtree_err(const char *fmt, ...)
+#else
+mtree_err(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)fprintf(stderr, "mtree: ");
+       (void)vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void)fprintf(stderr, "\n");
+       if (lineno)
+               (void)fprintf(stderr,
+                   "mtree: failed at line %d of the specification\n", lineno);
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/mtree/mtree.8 b/mtree/mtree.8
new file mode 100644 (file)
index 0000000..cb89812
--- /dev/null
@@ -0,0 +1,261 @@
+.\"    $NetBSD: mtree.8,v 1.7 1997/10/17 11:46:46 lukem Exp $
+.\"
+.\" 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.
+.\"
+.\"     @(#)mtree.8    8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl cderux
+.Op Fl f Ar spec
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl p Ar path
+.Op Fl s Ar seed
+.Sh DESCRIPTION
+The utility
+.Nm
+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 specification, 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
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl f
+Read the specification from
+.Ar file  ,
+instead of from the standard input.
+.It Fl K
+Add the specified (whitespace or comma separated) keywords to the current
+set of keywords.
+.It Fl k
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+keywords instead of the current set of keywords.
+.It Fl p
+Use the file hierarchy rooted in
+.Ar path  ,
+instead of the current directory.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl s
+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, and permissions of existing files to match
+the specification and create any missing directories.
+User, group, and permissions must all be specified for missing directories
+to be created.
+Exit with a status of 0 on success, 1 if any error occurred;
+a mismatch is not considered to be an error if it was corrected.
+.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 x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e. strings that
+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 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 link
+The file the symbolic link is expected to reference.
+.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 optional
+The file is optional; don't complain about the file if it's
+not in the file hierarchy.
+.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 time
+The last modification time of the file.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.sp
+.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 gid ,
+.Cm link ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.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 Nm \&..
+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
+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.
+.Sh EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm
+be run on the file systems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The seed for the
+.Fl s
+option should not be an obvious value and the final checksum should not be
+stored on-line under any circumstances!
+Then, periodically,
+.Nm
+should be run against the on-line specifications and the final checksum
+compared with the previous value.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it shouldn't be possible for them
+to make it produce the same final checksum value.
+If the final checksum value changes, the off-line copies of the specification
+can be used to detect which of the binaries have actually been modified.
+.Pp
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr chgrp 1 ,
+.Xr cksum 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
diff --git a/mtree/mtree.c b/mtree/mtree.c
new file mode 100644 (file)
index 0000000..f51b372
--- /dev/null
@@ -0,0 +1,162 @@
+/*     $NetBSD: mtree.c,v 1.9 1997/10/17 11:46:51 lukem Exp $  */
+
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mtree.c    8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: mtree.c,v 1.9 1997/10/17 11:46:51 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fts.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int crc_total;
+
+int ftsoptions = FTS_PHYSICAL;
+int cflag, dflag, eflag, rflag, sflag, tflag, uflag, Uflag;
+u_short keys;
+char fullpath[MAXPATHLEN];
+
+       int     main __P((int, char **));
+static void    usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch;
+       char *dir, *p;
+       int status;
+
+       dir = NULL;
+       keys = KEYDEFAULT;
+       while ((ch = getopt(argc, argv, "cdef:K:k:p:rs:tUux")) != -1)
+               switch((char)ch) {
+               case 'c':
+                       cflag = 1;
+                       break;
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'e':
+                       eflag = 1;
+                       break;
+               case 'f':
+                       if (!(freopen(optarg, "r", stdin)))
+                               mtree_err("%s: %s", optarg, strerror(errno));
+                       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 'p':
+                       dir = optarg;
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               case 's':
+                       sflag = 1;
+                       crc_total = ~strtol(optarg, &p, 0);
+                       if (*p)
+                               mtree_err("illegal seed value -- %s", optarg);
+                       break;
+               case 't':
+                       tflag = 1;
+                       break;
+               case 'U':
+                       Uflag = uflag = 1;
+                       break;
+               case 'u':
+                       uflag = 1;
+                       break;
+               case 'x':
+                       ftsoptions |= FTS_XDEV;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc)
+               usage();
+
+       if (dir && chdir(dir))
+               mtree_err("%s: %s", dir, strerror(errno));
+
+       if ((cflag || sflag) && !getwd(fullpath))
+               mtree_err("%s", fullpath);
+
+       if (cflag) {
+               cwalk();
+               exit(0);
+       }
+       status = verify();
+       if (Uflag & (status == MISMATCHEXIT))
+               status = 0;
+       exit(status);
+}
+
+static void
+usage()
+{
+       (void)fprintf(stderr,
+"usage: mtree [-cderUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n");
+       exit(1);
+}
diff --git a/mtree/mtree.h b/mtree/mtree.h
new file mode 100644 (file)
index 0000000..431fdd8
--- /dev/null
@@ -0,0 +1,97 @@
+/*     $NetBSD: mtree.h,v 1.7 1995/03/07 21:26:27 cgd 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.
+ *
+ *     @(#)mtree.h     8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _MTREE_H_
+#define _MTREE_H_
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#define        KEYDEFAULT \
+       (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID)
+
+#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    *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 */
+       nlink_t st_nlink;                       /* link count */
+
+#define        F_CKSUM 0x0001                          /* check sum */
+#define        F_DONE  0x0002                          /* directory done */
+#define        F_GID   0x0004                          /* gid */
+#define        F_GNAME 0x0008                          /* group name */
+#define        F_IGN   0x0010                          /* ignore */
+#define        F_MAGIC 0x0020                          /* name has magic chars */
+#define        F_MODE  0x0040                          /* mode */
+#define        F_NLINK 0x0080                          /* number of links */
+#define        F_OPT   0x0100                          /* existence optional */
+#define        F_SIZE  0x0200                          /* size */
+#define        F_SLINK 0x0400                          /* link count */
+#define        F_TIME  0x0800                          /* modification time */
+#define        F_TYPE  0x1000                          /* file type */
+#define        F_UID   0x2000                          /* uid */
+#define        F_UNAME 0x4000                          /* user name */
+#define        F_VISIT 0x8000                          /* file visited */
+       u_short 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/mtree/spec.c b/mtree/spec.c
new file mode 100644 (file)
index 0000000..88045ee
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $NetBSD: spec.c,v 1.12 1998/09/23 19:46:00 itohy Exp $  */
+
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)spec.c     8.2 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: spec.c,v 1.12 1998/09/23 19:46:00 itohy Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mtree.h"
+#include "extern.h"
+
+int lineno;                            /* Current spec line number. */
+
+static void     set __P((char *, NODE *));
+static void     unset __P((char *, NODE *));
+
+NODE *
+spec()
+{
+       NODE *centry, *last;
+       char *p;
+       NODE ginfo, *root;
+       int c_cur, c_next;
+       char buf[2048];
+
+       root = NULL;
+       centry = last = NULL;
+       memset(&ginfo, 0, sizeof(ginfo));
+       c_cur = c_next = 0;
+       for (lineno = 1; fgets(buf, sizeof(buf), stdin);
+           ++lineno, c_cur = c_next, c_next = 0) {
+               /* Skip empty lines. */
+               if (buf[0] == '\n')
+                       continue;
+
+               /* Find end of line. */
+               if ((p = strchr(buf, '\n')) == NULL)
+                       mtree_err("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)
+                       mtree_err("missing field");
+
+               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 (strchr(p, '/'))
+                       mtree_err("slash character in file name");
+
+               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:              mtree_err("no parent node");
+               }
+
+               if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
+                       mtree_err("%s", strerror(errno));
+               *centry = ginfo;
+               (void)strcpy(centry->name, p);
+#define        MAGIC   "?*["
+               if (strpbrk(p, MAGIC))
+                       centry->flags |= F_MAGIC;
+               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(t, ip)
+       char *t;
+       NODE *ip;
+{
+       int type;
+       char *kw, *val;
+       struct group *gr;
+       struct passwd *pw;
+       mode_t *m;
+       int value;
+       char *ep;
+
+       val = NULL;
+       for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) {
+               ip->flags |= type = parsekey(kw, &value);
+               if (value && (val = strtok(NULL, " \t\n")) == NULL)
+                       mtree_err("missing value");
+               switch(type) {
+               case F_CKSUM:
+                       ip->cksum = strtoul(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid checksum %s", val);
+                       break;
+               case F_GID:
+                       ip->st_gid = (gid_t)strtoul(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid gid %s", val);
+                       break;
+               case F_GNAME:
+                       if ((gr = getgrnam(val)) == NULL)
+                           mtree_err("unknown group %s", val);
+                       ip->st_gid = gr->gr_gid;
+                       break;
+               case F_IGN:
+                       /* just set flag bit */
+                       break;
+               case F_MODE:
+                       if ((m = setmode(val)) == NULL)
+                               mtree_err("invalid file mode %s", val);
+                       ip->st_mode = getmode(m, 0);
+                       free(m);
+                       break;
+               case F_NLINK:
+                       ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid link count %s", val);
+                       break;
+               case F_SIZE:
+                       ip->st_size = (off_t)strtoq(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid size %s", val);
+                       break;
+               case F_SLINK:
+                       if ((ip->slink = strdup(val)) == NULL)
+                               mtree_err("%s", strerror(errno));
+                       break;
+               case F_TIME:
+                       ip->st_mtimespec.tv_sec =
+                           (time_t)strtoul(val, &ep, 10);
+                       if (*ep != '.')
+                               mtree_err("invalid time %s", val);
+                       val = ep + 1;
+                       ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid time %s", 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:
+                               mtree_err("unknown file type %s", val);
+                       }
+                       break;
+               case F_UID:
+                       ip->st_uid = (uid_t)strtoul(val, &ep, 10);
+                       if (*ep)
+                               mtree_err("invalid uid %s", val);
+                       break;
+               case F_UNAME:
+                       if ((pw = getpwnam(val)) == NULL)
+                           mtree_err("unknown user %s", val);
+                       ip->st_uid = pw->pw_uid;
+                       break;
+               }
+       }
+}
+
+static void
+unset(t, ip)
+       char *t;
+       NODE *ip;
+{
+       char *p;
+
+       while ((p = strtok(t, "\n\t ")) != NULL)
+               ip->flags &= ~parsekey(p, NULL);
+}
diff --git a/mtree/verify.c b/mtree/verify.c
new file mode 100644 (file)
index 0000000..057f240
--- /dev/null
@@ -0,0 +1,211 @@
+/*     $NetBSD: verify.c,v 1.14 1998/08/27 18:03:45 ross 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
+#if 0
+static char sccsid[] = "@(#)verify.c   8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: verify.c,v 1.14 1998/08/27 18:03:45 ross Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fts.h>
+#include <fnmatch.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int crc_total, ftsoptions;
+extern int dflag, eflag, rflag, sflag, uflag;
+extern char fullpath[MAXPATHLEN];
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void    miss __P((NODE *, char *));
+static int     vwalk __P((void));
+
+int
+verify()
+{
+       int rval;
+
+       root = spec();
+       rval = vwalk();
+       miss(root, path);
+       return (rval);
+}
+
+static int
+vwalk()
+{
+       FTS *t;
+       FTSENT *p;
+       NODE *ep, *level;
+       int ftsdepth, specdepth, rval;
+       char *argv[2];
+
+       argv[0] = ".";
+       argv[1] = NULL;
+       if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+               mtree_err("fts_open: %s", strerror(errno));
+       level = root;
+       ftsdepth = specdepth = rval = 0;
+       while ((p = fts_read(t)) != NULL) {
+               switch(p->fts_info) {
+               case FTS_D:
+                       ++ftsdepth; 
+                       break;
+               case FTS_DP:
+                       --ftsdepth; 
+                       if (specdepth > ftsdepth) {
+                               for (level = level->parent; level->prev;
+                                     level = level->prev);  
+                               --specdepth;
+                       }
+                       continue;
+               case FTS_DNR:
+               case FTS_ERR:
+               case FTS_NS:
+                       (void)fprintf(stderr, "mtree: %s: %s\n",
+                           RP(p), strerror(errno));
+                       continue;
+               default:
+                       if (dflag)
+                               continue;
+               }
+
+               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 (compare(ep->name, ep, p))
+                                       rval = MISMATCHEXIT;
+                               if (!(ep->flags & F_IGN) &&
+                                   ep->child && ep->type == F_DIR &&
+                                   p->fts_info == FTS_D) {
+                                       level = ep->child;
+                                       ++specdepth;
+                               } else
+                                       (void)fts_set(t, p, FTS_SKIP);
+                               break;
+                       }
+
+               if (ep)
+                       continue;
+               if (!eflag) {
+                       (void)printf("extra: %s", RP(p));
+                       if (rflag) {
+                               if (unlink(p->fts_accpath)) {
+                                       (void)printf(", not removed: %s",
+                                           strerror(errno));
+                               } else
+                                       (void)printf(", removed");
+                       }
+                       (void)putchar('\n');
+               }
+               (void)fts_set(t, p, FTS_SKIP);
+       }
+       (void)fts_close(t);
+       if (sflag)
+               (void)fprintf(stderr,
+                   "mtree: %s checksum: %u\n", fullpath, crc_total);
+       return (rval);
+}
+
+static void
+miss(p, tail)
+       NODE *p;
+       char *tail;
+{
+       int create;
+       char *tp;
+
+       for (; p; p = p->next) {
+               if (p->flags & F_OPT && !(p->flags & F_VISIT))
+                       continue;
+               if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+                       continue;
+               (void)strcpy(tail, p->name);
+               if (!(p->flags & F_VISIT))
+                       (void)printf("missing: %s", path);
+               if (p->type != F_DIR) {
+                       putchar('\n');
+                       continue;
+               }
+
+               create = 0;
+               if (!(p->flags & F_VISIT) && uflag) {
+                       if (!(p->flags & (F_UID | F_UNAME)))
+                           (void)printf(" (not created: user not specified)");
+                       else if (!(p->flags & (F_GID | F_GNAME)))
+                           (void)printf(" (not created: group not specified)");
+                       else if (!(p->flags & F_MODE))
+                           (void)printf(" (not created: mode not specified)");
+                       else if (mkdir(path, S_IRWXU))
+                               (void)printf(" (not created: %s)",
+                                   strerror(errno));
+                       else {
+                               create = 1;
+                               (void)printf(" (created)");
+                       }
+               }
+               if (!(p->flags & F_VISIT))
+                       (void)putchar('\n');
+
+               for (tp = tail; *tp; ++tp);
+               *tp = '/';
+               miss(p->child, tp + 1);
+               *tp = '\0';
+
+               if (!create)
+                       continue;
+               if (chown(path, p->st_uid, p->st_gid)) {
+                       (void)printf("%s: user/group/mode not modified: %s\n",
+                           path, strerror(errno));
+                       continue;
+               }
+               if (chmod(path, p->st_mode))
+                       (void)printf("%s: permissions not set: %s\n",
+                           path, strerror(errno));
+       }
+}
diff --git a/mv/Makefile b/mv/Makefile
new file mode 100644 (file)
index 0000000..52e16a4
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = mv
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = pathnames.h
+
+CFILES = mv.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mv.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/mv/Makefile.postamble b/mv/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/mv/Makefile.preamble b/mv/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/mv/PB.project b/mv/PB.project
new file mode 100644 (file)
index 0000000..626c69c
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (pathnames.h); 
+        OTHER_LINKED = (mv.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mv.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = mv; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/mv/mv.1 b/mv/mv.1
new file mode 100644 (file)
index 0000000..f831399
--- /dev/null
+++ b/mv/mv.1
@@ -0,0 +1,133 @@
+.\"    $NetBSD: mv.1,v 1.11 1998/04/20 05:43:30 ross Exp $
+.\"
+.\" 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
+.\"
+.Dd May 31, 1993
+.Dt MV 1
+.Os
+.Sh NAME
+.Nm mv
+.Nd move files
+.Sh SYNOPSIS
+.Nm
+.Op Fl fi
+.Ar source target
+.Nm ""
+.Op Fl fi
+.Ar source ... directory
+.Sh DESCRIPTION
+.Pp
+In its first form, the
+.Nm
+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
+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 flag
+.It Fl f
+Do not prompt for confirmation before overwriting the destination
+path.
+.It Fl i
+Causes
+.Nm
+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 ``y'',
+the move is attempted.
+.El
+The last of any
+.Fl f
+or
+.Fl i
+options is the one which affects
+.Nm Ns 's
+behavior.
+.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
+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
+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
+\tcp -PRp source_file destination && \e
+\trm -rf source_file
+.Ed
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr cp 1 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/mv/mv.c b/mv/mv.c
new file mode 100644 (file)
index 0000000..a8c08d0
--- /dev/null
+++ b/mv/mv.c
@@ -0,0 +1,361 @@
+/*     $NetBSD: mv.c,v 1.19 1998/07/28 11:41:49 mycroft Exp $  */
+
+/*
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: mv.c,v 1.19 1998/07/28 11:41:49 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/stat.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 <pwd.h>
+#include <grp.h>
+
+#include "pathnames.h"
+
+int fflg, iflg;
+int stdin_ok;
+
+int    copy __P((char *, char *));
+int    do_move __P((char *, char *));
+int    fastcopy __P((char *, char *, struct stat *));
+void   usage __P((void));
+int    main __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int baselen, len, rval;
+       char *p, *endp;
+       struct stat sb;
+       int ch;
+       char path[MAXPATHLEN + 1];
+
+       (void)setlocale(LC_ALL, "");
+
+       while ((ch = getopt(argc, argv, "if")) != -1)
+               switch (ch) {
+               case 'i':
+                       fflg = 0;
+                       iflg = 1;
+                       break;
+               case 'f':
+                       iflg = 0;
+                       fflg = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 2)
+               usage();
+
+       stdin_ok = isatty(STDIN_FILENO);
+
+       /*
+        * 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]));
+       }
+
+       /* It's a directory, move each file into it. */
+       (void)strcpy(path, argv[argc - 1]);
+       baselen = strlen(path);
+       endp = &path[baselen];
+       *endp++ = '/';
+       ++baselen;
+       for (rval = 0; --argc; ++argv) {
+               p = *argv + strlen(*argv) - 1;
+               while (*p == '/' && p != *argv)
+                       *p-- = '\0';
+               if ((p = strrchr(*argv, '/')) == NULL)
+                       p = *argv;
+               else
+                       ++p;
+
+               if ((baselen + (len = strlen(p))) >= MAXPATHLEN) {
+                       warnx("%s: destination pathname too long", *argv);
+                       rval = 1;
+               } else {
+                       memmove(endp, p, len + 1);
+                       if (do_move(*argv, path))
+                               rval = 1;
+               }
+       }
+       exit(rval);
+       /* NOTREACHED */
+}
+
+int
+do_move(from, to)
+       char *from, *to;
+{
+       struct stat sb;
+       char modep[15];
+
+       /*
+        * (1)  If the destination path exists, the -f option is not specified
+        *      and either of the following conditions are true:
+        *
+        *      (a) The perimissions of the destination path do not permit
+        *          writing and the standard input is a terminal.
+        *      (b) The -i option is specified.
+        *
+        *      the mv utility shall write a prompt to standard error and
+        *      read a line from standard input.  If the response is not
+        *      affirmative, mv shall do nothing more with the current
+        *      source file...
+        */
+       if (!fflg && !access(to, F_OK)) {
+               int ask = 1;
+               int ch;
+
+               if (iflg) {
+                       (void)fprintf(stderr, "overwrite %s? ", to);
+               } else if (stdin_ok && access(to, W_OK) && !stat(to, &sb)) {
+                       strmode(sb.st_mode, modep);
+                       (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
+                           modep + 1, modep[9] == ' ' ? "" : " ",
+                           user_from_uid(sb.st_uid, 0),
+                           group_from_gid(sb.st_gid, 0), to);
+               } else
+                       ask = 0;
+               if (ask) {
+                       if ((ch = getchar()) != EOF && ch != '\n')
+                               while (getchar() != '\n');
+                       if (ch != 'y' && ch != 'Y')
+                               return (0);
+               }
+       }
+
+       /*
+        * (2)  If rename() succeeds, mv shall do nothing more with the
+        *      current source file.  If it fails for any other reason than
+        *      EXDEV, mv shall write a diagnostic message to the standard
+        *      error and do nothing more with the current source file.
+        *
+        * (3)  If the destination path exists, and it is a file of type
+        *      directory and source_file is not a file of type directory,
+        *      or it is a file not of type directory, and source file is
+        *      a file of type directory, mv shall write a diagnostic
+        *      message to standard error, and do nothing more with the
+        *      current source file...
+        */
+       if (!rename(from, to))
+               return (0);
+
+       if (errno != EXDEV) {
+               warn("rename %s to %s", from, to);
+               return (1);
+       }
+
+       /*
+        * (4)  If the destination path exists, mv shall attempt to remove it.
+        *      If this fails for any reason, mv shall write a diagnostic
+        *      message to the standard error and do nothing more with the
+        *      current source file...
+        */
+       if (!lstat(to, &sb)) {
+               if ((S_ISDIR(sb.st_mode)) ? rmdir(to) : unlink(to)) {
+                       warn("can't remove %s", to);
+                       return (1);
+               }
+       }
+
+       /*
+        * (5)  The file hierarchy rooted in source_file shall be duplicated
+        *      as a file hierarchy rooted in the destination path...
+        */
+       if (lstat(from, &sb)) {
+               warn("%s", from);
+               return (1);
+       }
+       return (S_ISREG(sb.st_mode) ?
+           fastcopy(from, to, &sb) : copy(from, to));
+}
+
+int
+fastcopy(from, to, sbp)
+       char *from, *to;
+       struct stat *sbp;
+{
+       struct timeval tval[2];
+       static u_int blen;
+       static char *bp;
+       int nread, from_fd, to_fd;
+
+       if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
+               warn("%s", from);
+               return (1);
+       }
+       if ((to_fd =
+           open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) {
+               warn("%s", to);
+               (void)close(from_fd);
+               return (1);
+       }
+       if (!blen && !(bp = malloc(blen = sbp->st_blksize))) {
+               warn("%s", "");
+               return (1);
+       }
+       while ((nread = read(from_fd, bp, blen)) > 0)
+               if (write(to_fd, bp, 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);
+       }
+       (void)close(from_fd);
+
+       TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec);
+       TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec);
+#ifndef __APPLE__
+       if (futimes(to_fd, tval))
+#else
+       if (utimes(to, tval))
+#endif
+               warn("%s: set times", to);
+       if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) {
+               if (errno != EPERM)
+                       warn("%s: set owner/group", to);
+               sbp->st_mode &= ~(S_ISUID | S_ISGID);
+       }
+       if (fchmod(to_fd, sbp->st_mode))
+               warn("%s: set mode", to);
+
+       if (close(to_fd)) {
+               warn("%s", to);
+               return (1);
+       }
+
+       if (unlink(from)) {
+               warn("%s: remove", from);
+               return (1);
+       }
+       return (0);
+}
+
+int
+copy(from, to)
+       char *from, *to;
+{
+       int pid, status;
+
+       if ((pid = vfork()) == 0) {
+               execl(_PATH_CP, "mv", "-PRp", from, to, NULL);
+               warn("%s", _PATH_CP);
+               _exit(1);
+       }
+       if (waitpid(pid, &status, 0) == -1) {
+               warn("%s: waitpid", _PATH_CP);
+               return (1);
+       }
+       if (!WIFEXITED(status)) {
+               warn("%s: did not terminate normally", _PATH_CP);
+               return (1);
+       }
+       if (WEXITSTATUS(status)) {
+               warn("%s: terminated with %d (non-zero) status",
+                   _PATH_CP, WEXITSTATUS(status));
+               return (1);
+       }
+       if (!(pid = vfork())) {
+               execl(_PATH_RM, "mv", "-rf", from, NULL);
+               warn("%s", _PATH_RM);
+               _exit(1);
+       }
+       if (waitpid(pid, &status, 0) == -1) {
+               warn("%s: waitpid", _PATH_RM);
+               return (1);
+       }
+       if (!WIFEXITED(status)) {
+               warn("%s: did not terminate normally", _PATH_RM);
+               return (1);
+       }
+       if (WEXITSTATUS(status)) {
+               warn("%s: terminated with %d (non-zero) status",
+                   _PATH_RM, WEXITSTATUS(status));
+               return (1);
+       }
+       return (0);
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr, "usage: mv [-fi] source target\n");
+       (void)fprintf(stderr, "       mv [-fi] source ... directory\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/mv/pathnames.h b/mv/pathnames.h
new file mode 100644 (file)
index 0000000..c612fd0
--- /dev/null
@@ -0,0 +1,39 @@
+/*     $NetBSD: pathnames.h,v 1.6 1995/03/21 09:06:55 cgd Exp $        */
+
+/*
+ * 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
+ */
+
+#define        _PATH_RM        "/bin/rm"
+#define        _PATH_CP        "/bin/cp"
diff --git a/pax/Makefile b/pax/Makefile
new file mode 100644 (file)
index 0000000..ebe65d9
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = pax
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+HFILES = cache.h cpio.h extern.h ftree.h options.h pat_rep.h pax.h\
+         sel_subs.h tables.h tar.h
+
+CFILES = ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c\
+         ftree.c gen_subs.c getoldopt.c options.c pat_rep.c pax.c\
+         sel_subs.c tables.c tar.c tty_subs.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble pax.1 cpio.1\
+            tar.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/pax/Makefile.postamble b/pax/Makefile.postamble
new file mode 100644 (file)
index 0000000..3879a87
--- /dev/null
@@ -0,0 +1,5 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
+
+after_install::
+       $(LINKPRODUCT) $(DSTROOT)$(USRBINDIR)/cpio
+       $(LINKPRODUCT) $(DSTROOT)$(USRBINDIR)/tar
diff --git a/pax/Makefile.preamble b/pax/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/pax/PB.project b/pax/PB.project
new file mode 100644 (file)
index 0000000..11107f6
--- /dev/null
@@ -0,0 +1,54 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        H_FILES = (
+            cache.h, 
+            cpio.h, 
+            extern.h, 
+            ftree.h, 
+            options.h, 
+            pat_rep.h, 
+            pax.h, 
+            sel_subs.h, 
+            tables.h, 
+            tar.h
+        ); 
+        OTHER_LINKED = (
+            ar_io.c, 
+            ar_subs.c, 
+            buf_subs.c, 
+            cache.c, 
+            cpio.c, 
+            file_subs.c, 
+            ftree.c, 
+            gen_subs.c, 
+            getoldopt.c, 
+            options.c, 
+            pat_rep.c, 
+            pax.c, 
+            sel_subs.c, 
+            tables.c, 
+            tar.c, 
+            tty_subs.c
+        ); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, pax.1, cpio.1, tar.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = pax; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/pax/ar_io.c b/pax/ar_io.c
new file mode 100644 (file)
index 0000000..9fd7346
--- /dev/null
@@ -0,0 +1,1372 @@
+/*     $OpenBSD: ar_io.c,v 1.17 1997/09/01 18:29:42 deraadt 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. 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[] = "@(#)ar_io.c    8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_io.c,v 1.17 1997/09/01 18:29:42 deraadt Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/param.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 "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>"      /* psuedo name for stdout */
+#define STDN           "<STDIN>"       /* psuedo name for stdin */
+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?  */
+char *arcname;                         /* printable name of archive */
+char *gzip_program;                    /* name of gzip program */
+
+static int get_phys __P((void));
+extern sigset_t s_mask;
+static void ar_start_gzip __P((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
+ */
+
+#ifdef __STDC__
+int
+ar_open(char *name)
+#else
+int
+ar_open(name)
+       char *name;
+#endif
+{
+       struct mtget mb;
+
+       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(0, errno, "Failed open to read on %s", name);
+               if (zflag)
+                       ar_start_gzip(arfd);
+               break;
+       case ARCHIVE:
+               if (name == NULL) {
+                       arfd = STDOUT_FILENO;
+                       arcname = STDO;
+               } else if ((arfd = open(name, AR_MODE, DMOD)) < 0)
+                       syswarn(0, errno, "Failed open to write on %s", name);
+               else
+                       can_unlnk = 1;
+               if (zflag)
+                       ar_start_gzip(arfd);
+               break;
+       case APPND:
+               if (zflag)
+                       err(1, "can not gzip while appending");
+               if (name == NULL) {
+                       arfd = STDOUT_FILENO;
+                       arcname = STDO;
+               } else if ((arfd = open(name, APP_MODE, DMOD)) < 0)
+                       syswarn(0, 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 (chdir(chdname) != 0)
+                       syswarn(1, errno, "Failed chdir to %s", chdname);
+       /*
+        * 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);
+       }
+
+       if (S_ISCHR(arsb.st_mode))
+               artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE;
+       else 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, otherwize we will continue to use rdblksz). We
+        * must to 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 cannont 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, worse case, slow... 
+                */
+               blksz = rdblksz = BLKMULT;
+               break;
+       }
+       lstrval = 1;
+       return(0);
+}
+
+/*
+ * ar_close()
+ *     closes archive device, increments volume number, and prints i/o summary
+ */
+#ifdef __STDC__
+void
+ar_close(void)
+#else
+void
+ar_close()
+#endif
+{
+       FILE *outf;
+
+       if (arfd < 0) {
+               did_io = io_ok = flcnt = 0;
+               return;
+       }
+
+       if (act == LIST)
+               outf = stdout;
+       else
+               outf = stderr;
+
+       /*
+        * 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', outf);
+               (void)fprintf(outf,
+                       "%s: Waiting for tape drive close to complete...",
+                       argv0);
+               (void)fflush(outf);
+       }
+
+       /*
+        * 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;
+       }
+
+       (void)close(arfd);
+
+       if (vflag && (artyp == ISTAPE)) {
+               (void)fputs("done.\n", outf);
+               vfpart = 0;
+               (void)fflush(outf);
+       }
+       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', outf);
+               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 NET2_STAT
+               (void)fprintf(outf, "%s: unknown format, %lu bytes skipped.\n",
+#      else
+               (void)fprintf(outf, "%s: unknown format, %qu bytes skipped.\n",
+#      endif
+                   argv0, rdcnt);
+               (void)fflush(outf);
+               flcnt = 0;
+               return;
+       }
+
+       if (strcmp(NM_CPIO, argv0) == 0)
+               (void)fprintf(outf, "%qu blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120);
+       else if (strcmp(NM_TAR, argv0) != 0)
+               (void)fprintf(outf,
+#      ifdef NET2_STAT
+                   "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n",
+#      else
+                   "%s: %s vol %d, %lu files, %qu bytes read, %qu bytes written.\n",
+#      endif
+                   argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt);
+       (void)fflush(outf);
+       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).
+ */
+#ifdef __STDC__
+void
+ar_drain(void)
+#else
+void
+ar_drain()
+#endif
+{
+       register 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
+ */
+
+#ifdef __STDC__
+int
+ar_set_wr(void)
+#else
+int
+ar_set_wr()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+ar_app_ok(void)
+#else
+int
+ar_app_ok()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+ar_read(register char *buf, register int cnt)
+#else
+int
+ar_read(buf, cnt)
+       register char *buf;
+       register int cnt;
+#endif
+{
+       register 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 is 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 occured.
+ */
+
+#ifdef __STDC__
+int
+ar_write(register char *buf, register int bsz)
+#else
+int
+ar_write(buf, bsz)
+       register char *buf;
+       register int bsz;
+#endif
+{
+       register 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);
+       }
+       /*
+        * 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.");
+                       res = 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 boundry. 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.
+ */
+
+#ifdef __STDC__
+int
+ar_rdsync(void)
+#else
+int
+ar_rdsync()
+#endif
+{
+       long fsbz;
+       off_t cpos;
+       off_t mpos;
+       struct mtop mb;
+
+       /*
+        * Fail resync attempts at user request (done) or 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) {
+       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 foward 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;
+       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 foward 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)
+ */
+
+#ifdef __STDC__
+int
+ar_fow(off_t sksz, off_t *skipped)
+#else
+int
+ar_fow(sksz, skipped)
+       off_t sksz;
+       off_t *skipped;
+#endif
+{
+       off_t cpos;
+       off_t mpos;
+
+       *skipped = 0;
+       if (sksz <= 0)
+               return(0);
+
+       /*
+        * we cannot move foward 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 foward 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, "Foward 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
+ */
+
+#ifdef __STDC__
+int
+ar_rev(off_t sksz)
+#else
+int
+ar_rev(sksz)
+       off_t sksz;
+#endif
+{
+       off_t cpos;
+       struct mtop mb;
+       register int phyblk;
+
+       /*
+        * 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 hapens 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;
+       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 handler 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;
+       }
+       lstrval = 1;
+       return(0);
+}
+
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+static int
+get_phys(void)
+#else
+static int
+get_phys()
+#endif
+{
+       register int padsz = 0;
+       register int res;
+       register 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 foward 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);
+}
+
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+int
+ar_next(void)
+#else
+int
+ar_next()
+#endif
+{
+       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 (done || !wr_trail || strcmp(NM_TAR, 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(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
+#ifdef __STDC__
+ar_start_gzip(int fd)
+#else
+ar_start_gzip(fd)
+       int fd;
+#endif
+{
+       pid_t pid;
+       int fds[2];
+       char *gzip_flags=NULL;
+
+       if (pipe(fds) < 0)
+               err(1, "could not pipe");
+       pid = fork();
+       if (pid < 0)
+               err(1, "could not fork");
+
+       /* parent */
+       if (pid) {
+               switch (act) {
+               case ARCHIVE:
+                       dup2(fds[1], fd);
+                       break;
+               case LIST:
+               case EXTRACT:
+                       dup2(fds[0], fd);
+                       break;
+               default:
+                       errx(1, "ar_start_gzip:  impossible");
+               }
+               close(fds[0]);
+               close(fds[1]);
+       } else {
+               switch (act) {
+               case ARCHIVE:
+                       dup2(fds[0], STDIN_FILENO);
+                       dup2(fd, STDOUT_FILENO);
+                       gzip_flags = "-c";
+                       break;
+               case LIST:
+               case EXTRACT:
+                       dup2(fds[1], STDOUT_FILENO);
+                       dup2(fd, STDIN_FILENO);
+                       gzip_flags = "-dc";
+                       break;
+               default:
+                       errx(1, "ar_start_gzip:  impossible");
+               }
+               close(fds[0]);
+               close(fds[1]);
+               if (execlp(gzip_program, gzip_program, gzip_flags, NULL) < 0)
+                       err(1, "could not exec");
+               /* NOTREACHED */
+       }
+}
diff --git a/pax/ar_subs.c b/pax/ar_subs.c
new file mode 100644 (file)
index 0000000..95bd7d5
--- /dev/null
@@ -0,0 +1,1288 @@
+/*     $OpenBSD: ar_subs.c,v 1.13 1997/09/16 21:20:35 niklas 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. 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[] = "@(#)ar_subs.c  8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_subs.c,v 1.13 1997/09/16 21:20:35 niklas 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>
+#include "pax.h"
+#include "extern.h"
+
+static void wr_archive __P((register ARCHD *, int is_app));
+static int get_arc __P((void));
+static int next_head __P((register ARCHD *));
+extern sigset_t s_mask;
+
+char *chdname;
+
+/*
+ * 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 */
+
+/*
+ * list()
+ *     list the contents of an archive which match user supplied pattern(s)
+ *     (no pattern matches all).
+ */
+
+#ifdef __STDC__
+void
+list(void)
+#else
+void
+list()
+#endif
+{
+       register ARCHD *arcn;
+       register 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) {
+               /*
+                * 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)
+ */
+
+#ifdef __STDC__
+void
+extract(void)
+#else
+void
+extract()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       off_t cnt;
+       ARCHD archd;
+       struct stat sb;
+       int fd;
+       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;
+        * 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) {
+
+               /*
+                * 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 (nos
+                * 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 exisiting 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, stderr);
+                       else {
+                               (void)fputs(arcn->name, stderr);
+                               vfpart = 1;
+                       }
+               }
+
+               /*
+                * if required, chdir around.
+                */
+               if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL))
+                       if (chdir(arcn->pat->chdname) != 0)
+                               syswarn(1, errno, "Cannot chdir to %s",
+                                   arcn->pat->chdname);
+
+               /*
+                * 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', stderr);
+                               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', stderr);
+                       vfpart = 0;
+               }
+               if (!res)
+                       (void)rd_skip(cnt + arcn->pad);
+
+               /*
+                * if required, chdir around.
+                */
+               if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL))
+                       if (fchdir(cwdfd) != 0)
+                               syswarn(1, errno,
+                                   "Can't fchdir to starting directory");
+       }
+
+       /*
+        * 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.
+ */
+
+#ifdef __STDC__
+static void
+wr_archive(register ARCHD *arcn, int is_app)
+#else
+static void
+wr_archive(arcn, is_app)
+       register ARCHD *arcn;
+       int is_app;
+#endif
+{
+       register int res;
+       register int hlk;
+       register int wr_one;
+       off_t cnt;
+       int (*wrf)();
+       int fd = -1;
+       time_t now;
+
+       /*
+        * if this format supports hard link storage, start up the database
+        * that detects them.
+        */
+       if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0))
+               return;
+
+       /*
+        * 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 not append, and there are no files, we do no 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)
+                       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;
+               }
+
+               /*
+                * 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))
+                       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).
+                        */
+                       if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+                               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, stderr);
+                       else {
+                               (void)fputs(arcn->name, stderr);
+                               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', stderr);
+                               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', stderr);
+                       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;
+       }
+
+       /*
+        * tell format to write trailer; pad to block boundry; 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.
+ */
+
+#ifdef __STDC__
+void
+append(void)
+#else
+void
+append()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       ARCHD archd;
+       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 foramt.
+        */
+       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(stderr,
+                       "%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 postion 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", stderr);
+               vfpart = 0;
+       }
+       
+       /*
+        * go to the writing phase to add the new members
+        */
+       wr_archive(arcn, 1);
+}
+
+/*
+ * archive()
+ *     write a new archive
+ */
+
+#ifdef __STDC__
+void
+archive(void)
+#else
+void
+archive()
+#endif
+{
+       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).
+ */
+
+#ifdef __STDC__
+void
+copy(void)
+#else
+void
+copy()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       register int fddest;
+       register char *dest_pt;
+       register int dlen;
+       register int drem;
+       int fdsrc = -1;
+       struct stat sb;
+       ARCHD archd;
+       char dirbuf[PAXPATHLEN+1];
+
+       arcn = &archd;
+       /*
+        * set up the destination dir path and make sure it is a directory. We
+        * make sure we have a trailing / on the destination
+        */
+       dlen = l_strncpy(dirbuf, dirptr, sizeof(dirbuf) - 1);
+       dest_pt = dirbuf + dlen;
+       if (*(dest_pt-1) != '/') {
+               *dest_pt++ = '/';
+               ++dlen;
+       }
+       *dest_pt = '\0';
+       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;
+
+               /*
+                * check if this file meets user specified options
+                */
+               if (sel_chk(arcn) != 0)
+                       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 (*(arcn->name) == '/')
+                               res = 1;
+                       else
+                               res = 0;
+                       if ((arcn->nlen - res) > drem) {
+                               paxwarn(1, "Destination pathname too long %s",
+                                       arcn->name);
+                               continue;
+                       }
+                       (void)strncpy(dest_pt, arcn->name + res, drem);
+                       dirbuf[PAXPATHLEN] = '\0';
+
+                       /*
+                        * 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 exisiting 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)fputs(arcn->name, stderr);
+                       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', stderr);
+                               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);
+                       if (vflag && vfpart) {
+                               (void)putc('\n', stderr);
+                               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);
+               file_close(arcn, fddest);
+               rdfile_close(arcn, &fdsrc);
+
+               if (vflag && vfpart) {
+                       (void)putc('\n', stderr);
+                       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)
+ */
+
+#ifdef __STDC__
+static int
+next_head(register ARCHD *arcn)
+#else
+static int
+next_head(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int ret;
+       register char *hdend;
+       register int res;
+       register int shftsz;
+       register int hsz;
+       register 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)(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) == 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
+ */
+
+#ifdef __STDC__
+static int
+get_arc(void)
+#else
+static int
+get_arc()
+#endif
+{
+       register int i;
+       register int hdsz = 0;
+       register int res;
+       register 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/pax/buf_subs.c b/pax/buf_subs.c
new file mode 100644 (file)
index 0000000..6e13e8f
--- /dev/null
@@ -0,0 +1,1094 @@
+/*     $OpenBSD: buf_subs.c,v 1.7 1997/09/01 18:29:46 deraadt 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. 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[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: buf_subs.c,v 1.7 1997/09/01 18:29:46 deraadt 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
+ */
+
+#ifdef __STDC__
+int
+wr_start(void)
+#else
+int
+wr_start()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+rd_start(void)
+#else
+int
+rd_start()
+#endif
+{
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+void
+cp_start(void)
+#else
+void
+cp_start()
+#endif
+{
+       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 postions 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 boundries.
+ *     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 trival. 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 writes,
+ *     so it may not be feasable to append archives stored on all types of
+ *     devices. 
+ * Return:
+ *     0 for success, -1 for failure
+ */
+
+#ifdef __STDC__
+int
+appnd_start(off_t skcnt)
+#else
+int
+appnd_start(skcnt)
+       off_t skcnt;
+#endif
+{
+       register 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
+ */
+
+#ifdef __STDC__
+int
+rd_sync(void)
+#else
+int
+rd_sync()
+#endif
+{
+       register int errcnt = 0;
+       register 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 boundry (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.
+ */
+
+#ifdef __STDC__
+void
+pback(char *pt, int cnt)
+#else
+void
+pback(pt, cnt)
+       char *pt;
+       int cnt;
+#endif
+{
+       bufpt -= cnt;
+       memcpy(bufpt, pt, cnt);
+       return;
+}
+
+/*
+ * rd_skip()
+ *     skip foward 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.
+ */
+
+#ifdef __STDC__
+int
+rd_skip(off_t skcnt)
+#else
+int
+rd_skip(skcnt)
+       off_t skcnt;
+#endif
+{
+       off_t res;
+       off_t cnt;
+       off_t skipped = 0;
+
+       /*
+        * consume what data we have in the buffer. If we have to move foward
+        * 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 paddding SHOULD
+ *     BE a requirement....
+ */
+
+#ifdef __STDC__
+void
+wr_fin(void)
+#else
+void
+wr_fin()
+#endif
+{
+       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)
+ */
+
+#ifdef __STDC__
+int
+wr_rdbuf(register char *out, register int outcnt)
+#else
+int
+wr_rdbuf(out, outcnt)
+       register char *out;
+       register int outcnt;
+#endif
+{
+       register 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
+ */
+
+#ifdef __STDC__
+int
+rd_wrbuf(register char *in, register int cpcnt)
+#else
+int
+rd_wrbuf(in, cpcnt)
+       register char *in;
+       register int cpcnt;
+#endif
+{
+       register int res;
+       register int cnt;
+       register 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 occured 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 foward 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
+ */
+
+#ifdef __STDC__
+int
+wr_skip(off_t skcnt)
+#else
+int
+wr_skip(skcnt)
+       off_t skcnt;
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+wr_rdfile(ARCHD *arcn, int ifd, off_t *left)
+#else
+int
+wr_rdfile(arcn, ifd, left)
+       ARCHD *arcn;
+       int ifd;
+       off_t *left;
+#endif
+{
+       register int cnt;
+       register int res = 0;
+       register 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
+ */
+
+#ifdef __STDC__
+int
+rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
+#else
+int
+rd_wrfile(arcn, ofd, left)
+       ARCHD *arcn;
+       int ofd;
+       off_t *left;
+#endif
+{
+       register int cnt = 0;
+       register off_t size = arcn->sb.st_size;
+       register int res = 0;
+       register char *fnm = arcn->name;
+       int isem = 1;
+       int rem;
+       int sz = MINFBSZ;
+       struct stat sb;
+       u_long crc = 0L;
+
+       /*
+        * pass the blocksize of the file being written to the write routine,
+        * if the size is zero, use the default MINFBSZ
+        */
+       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 foward 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.
+ */
+
+#ifdef __STDC__
+void
+cp_file(ARCHD *arcn, int fd1, int fd2)
+#else
+void
+cp_file(arcn, fd1, fd2)
+       ARCHD *arcn;
+       int fd1;
+       int fd2;
+#endif
+{
+       register int cnt;
+       register off_t cpcnt = 0L;
+       register int res = 0;
+       register char *fnm = arcn->name;
+       register 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 foward 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()).
+ */
+
+#ifdef __STDC__
+int
+buf_fill(void)
+#else
+int
+buf_fill()
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+buf_flush(register int bufcnt)
+#else
+int
+buf_flush(bufcnt)
+       register int bufcnt;
+#endif
+{
+       register int cnt;
+       register int push = 0;
+       register int totcnt = 0;
+
+       /*
+        * if we have reached the user specified byte count for each archive
+        * volume, prompt for the next volume. (The non-standrad -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/pax/cache.c b/pax/cache.c
new file mode 100644 (file)
index 0000000..df58ec4
--- /dev/null
@@ -0,0 +1,500 @@
+/*     $OpenBSD: cache.c,v 1.6 1997/07/25 18:58:27 mickey 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. 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[] = "@(#)cache.c    8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: cache.c,v 1.6 1997/07/25 18:58:27 mickey 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 an empty uidtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+uidtb_start(void)
+#else
+int
+uidtb_start()
+#endif
+{
+       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 an empty gidtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+gidtb_start(void)
+#else
+int
+gidtb_start()
+#endif
+{
+       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 an empty usrtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+usrtb_start(void)
+#else
+int
+usrtb_start()
+#endif
+{
+       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 an empty grptb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+grptb_start(void)
+#else
+int
+grptb_start()
+#endif
+{
+       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)
+ */
+
+#ifdef __STDC__
+char *
+name_uid(uid_t uid, int frc)
+#else
+char *
+name_uid(uid, frc)
+       uid_t uid;
+       int frc;
+#endif
+{
+       register struct passwd *pw;
+       register UIDC *ptr;
+       register int hash;
+       if ((uidtb == NULL) && (uidtb_start() < 0))
+               return("");
+
+       /*
+        * see if we have this uid cached
+        */
+       hash = uid % UID_SZ;
+       ptr = uidtb[hash];
+       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 = (UIDC *)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 numberic format
+                */
+               if (ptr == NULL)
+                       return("");
+               ptr->uid = uid;
+               ptr->valid = INVALID;
+#              ifdef NET2_STAT
+               (void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid);
+#              else
+               (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
+                              (unsigned long)uid);
+#              endif
+               uidtb[hash] = ptr;
+               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)strncpy(ptr->name, pw->pw_name, UNMLEN-1);
+               ptr->name[UNMLEN-1] = '\0';
+               ptr->valid = VALID;
+               uidtb[hash] = ptr;
+       }
+       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)
+ */
+
+#ifdef __STDC__
+char *
+name_gid(gid_t gid, int frc)
+#else
+char *
+name_gid(gid, frc)
+       gid_t gid;
+       int frc;
+#endif
+{
+       register struct group *gr;
+       register GIDC *ptr;
+       register int hash;
+
+       if ((gidtb == NULL) && (gidtb_start() < 0))
+               return("");
+
+       /*
+        * see if we have this gid cached
+        */
+       hash = gid % GID_SZ;
+       ptr = gidtb[hash];
+       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 = (GIDC *)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;
+#              ifdef NET2_STAT
+               (void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid);
+#              else
+               (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
+                              (unsigned long)gid);
+#              endif
+               gidtb[hash] = ptr;
+               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)strncpy(ptr->name, gr->gr_name, GNMLEN-1);
+               ptr->name[GNMLEN-1] = '\0';
+               ptr->valid = VALID;
+               gidtb[hash] = ptr;
+       }
+       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
+ */
+
+#ifdef __STDC__
+int
+uid_name(char *name, uid_t *uid)
+#else
+int
+uid_name(name, uid)
+       char *name;
+       uid_t *uid;
+#endif
+{
+       register struct passwd *pw;
+       register UIDC *ptr;
+       register int namelen;
+       register int hash;
+
+       /*
+        * 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
+        */
+       hash = st_hash(name, namelen, UNM_SZ);
+       ptr = usrtb[hash];
+       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 = (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);
+       }
+       usrtb[hash] = ptr;
+       (void)strncpy(ptr->name, name, UNMLEN-1);
+       ptr->name[UNMLEN-1] = '\0';
+       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
+ */
+
+#ifdef __STDC__
+int
+gid_name(char *name, gid_t *gid)
+#else
+int
+gid_name(name, gid)
+       char *name;
+       gid_t *gid;
+#endif
+{
+       register struct group *gr;
+       register GIDC *ptr;
+       register int namelen;
+       register int hash;
+
+       /*
+        * 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
+        */
+       hash = st_hash(name, namelen, GID_SZ);
+       ptr = grptb[hash];
+       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 = (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);
+       }
+
+       grptb[hash] = ptr;
+       (void)strncpy(ptr->name, name, GNMLEN-1);
+       ptr->name[GNMLEN-1] = '\0';
+       if ((gr = getgrnam(name)) == NULL) {
+               ptr->valid = INVALID;
+               return(-1);
+       }
+       ptr->valid = VALID;
+       *gid = ptr->gid = gr->gr_gid;
+       return(0);
+}
diff --git a/pax/cache.h b/pax/cache.h
new file mode 100644 (file)
index 0000000..01283ef
--- /dev/null
@@ -0,0 +1,77 @@
+/*     $OpenBSD: cache.h,v 1.2 1996/06/23 14:20:31 deraadt 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. 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.
+ *
+ *      @(#)cache.h    8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * 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 adress 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;
diff --git a/pax/cpio.1 b/pax/cpio.1
new file mode 100644 (file)
index 0000000..639bc07
--- /dev/null
@@ -0,0 +1,270 @@
+.\"
+.\" 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.
+.\" 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.
+.\" 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 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.3 1997/04/06 06:11:11 millert Exp $
+.\"
+.Dd February 16, 1997
+.Dt CPIO 1
+.Os
+.Sh NAME
+.Nm cpio
+.Nd copy file archives in and out
+.Sh SYNOPSIS
+.Nm
+.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
+.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
+.Fl p
+.Op Fl adlLmuv
+.Ar destination-directory
+.Ar "< name-list"
+.Sh DESCRIPTION
+The
+.Nm
+command copies files to and from a
+.Nm
+archive.
+.Pp
+The following options are supported:
+.Bl -tag -width Fl
+.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 Fl
+.It Fl a
+Reset the access times on files that has been copied to the
+archive.
+.It Fl A
+Append to the specified archive.
+.It Fl B
+Set block size of output to 5120 bytes.
+.It Fl c
+Use ASCII format for
+.Nm
+header for portability.
+.It Fl C Ar bytes
+Set the block size of output to
+.Ar bytes .
+.It Fl F Ar archive
+.It Fl O 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:
+.Bl -tag -width Ds
+.It Ar bcpio
+Old binary cpio format.
+.It Ar cpio
+Old octal character cpio format.
+.It Ar sv4cpio
+SVR4 hex cpio format.
+.It Ar tar
+Old tar format.
+.It Ar ustar
+POSIX ustar format.
+.El
+.It Fl L
+Follow symbolic links.
+.It Fl v
+Be verbose about operations.  List filenames as they are
+written to the archive.
+.It Fl z
+Compress archive using
+.Xr gzip 1
+format.
+.It Fl Z
+Compress archive using
+.Xr compress 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 Fl
+.It Fl b
+Do byte- and word swapping after reading in data from the
+archive, for restoring archives created on systems with
+different byte order.
+.It Fl B
+Set the block size of the archive being read to 5120 bytes.
+.It Fl c
+Expect the archive headers to be in ASCII format.
+.It Fl C Ar bytes
+Read archive written with a blocksize of
+.Ar bytes .
+.It Fl d
+Create any intermediate directories as needed during
+restore.
+.It Fl E Ar file
+Read list of file name patters to extract or list from
+.Ar file .
+.It Fl f
+Restore all files except those matching the
+.Ar patterns
+given on the command line.
+.It Fl F Ar archive
+.It Fl I Ar archive
+Use the specified file as the input for the archive.
+.It Fl H Ar format
+Read an archive of the specified format.  Recognized
+formats are:
+.Bl -tag -width Ds
+.It Ar bcpio
+Old binary cpio format.
+.It Ar cpio
+Old octal character cpio format.
+.It Ar sv4cpio
+SVR4 hex cpio format.
+.It Ar tar
+Old tar format.
+.It Ar ustar
+POSIX ustar format.
+.El
+.It Fl m
+Restore modification times on files.
+.It Fl r
+Rename restored files interactively.
+.It Fl s
+Swap bytes after reading data from the archive.
+.It Fl S
+Swap words 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 gzip 1
+format.
+.It Fl Z
+Uncompress archive using
+.Xr compress 1
+format.
+.It Fl 6
+Process old-style \*Qcpio\*U 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 in and
+written out to a directory relative to the specified
+.Ar directory
+argument.
+.Bl -tag -width Fl
+.It Fl a
+Reset the access times on files that has been copied.
+.It Fl d
+Create any intermediate directories as needed to write
+the files at the new location.
+.It Fl l
+When possible, link files rather than creating an
+extra copy.
+.It Fl L
+Follow symbolic links.
+.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 ERRORS
+.Nm
+will exit with one of the following values:
+.Bl -tag -width 2n
+.It 0
+All files were processed successfully.
+.It 1
+An error occured.
+.El
+.Pp
+Whenever
+.Nm
+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
+options 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
+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 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
+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 BUGS
+The 
+.Fl s
+and
+.Fl S
+options are currently not implemented.
+.Sh AUTHOR
+Keith Muller at the University of California, San Diego
diff --git a/pax/cpio.c b/pax/cpio.c
new file mode 100644 (file)
index 0000000..43feb64
--- /dev/null
@@ -0,0 +1,1284 @@
+/*     $OpenBSD: cpio.c,v 1.5 1997/07/25 18:58:28 mickey 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. 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[] = "@(#)cpio.c     8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: cpio.c,v 1.5 1997/07/25 18:58:28 mickey 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 __P((register ARCHD *, int));
+static int rd_ln_nm __P((register ARCHD *));
+static int com_rd __P((register 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())
+ */
+
+#ifdef __STDC__
+int
+cpio_strd(void)
+#else
+int
+cpio_strd()
+#endif
+{
+       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, 
+ */
+
+#ifdef __STDC__
+int
+cpio_trail(register ARCHD *arcn)
+#else
+int
+cpio_trail(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+static int
+com_rd(register ARCHD *arcn)
+#else
+static int
+com_rd(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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_end_wr()
+ *     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
+ */
+
+#ifdef __STDC__
+int
+cpio_endwr(void)
+#else
+int
+cpio_endwr()
+#endif
+{
+       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)strcpy(last.name, TRAILER);
+       return((*frmt->wr)(&last));
+}
+
+/*
+ * rd_nam()
+ *     read in the file name which follows the cpio header
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+static int
+rd_nm(register ARCHD *arcn, int nsz)
+#else
+static int
+rd_nm(arcn, nsz)
+       register ARCHD *arcn;
+       int nsz;
+#endif
+{
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+static int
+rd_ln_nm(register ARCHD *arcn)
+#else
+static int
+rd_ln_nm(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * check the length specified for bogus values
+        */
+       if ((arcn->sb.st_size == 0) ||
+           (arcn->sb.st_size >= sizeof(arcn->ln_name))) {
+#              ifdef NET2_STAT
+               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 = 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
+ */
+
+#ifdef __STDC__
+int
+cpio_id(char *blk, int size)
+#else
+int
+cpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+cpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+cpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register int nsz;
+       register 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 NET2_STAT
+       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
+ */
+
+#ifdef __STDC__
+off_t
+cpio_endrd(void)
+#else
+off_t
+cpio_endrd()
+#endif
+{
+       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)
+ */
+
+#ifdef __STDC__
+int
+cpio_stwr(void)
+#else
+int
+cpio_stwr()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+cpio_wr(register ARCHD *arcn)
+#else
+int
+cpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_CPIO *hd;
+       register 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 NET2_STAT
+               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
+ */
+
+#ifdef __STDC__
+int
+vcpio_id(char *blk, int size)
+#else
+int
+vcpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+crc_id(char *blk, int size)
+#else
+int
+crc_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       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())
+ */
+
+#ifdef __STDC__
+int
+crc_strd(void)
+#else
+int
+crc_strd()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+vcpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+vcpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_VCPIO *hd;
+       dev_t devminor;
+       dev_t devmajor;
+       register 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 NET2_STAT
+       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 boundries
+        */
+       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
+ */
+
+#ifdef __STDC__
+off_t
+vcpio_endrd(void)
+#else
+off_t
+vcpio_endrd()
+#endif
+{
+       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)
+ */
+
+#ifdef __STDC__
+int
+crc_stwr(void)
+#else
+int
+crc_stwr()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+vcpio_wr(register ARCHD *arcn)
+#else
+int
+vcpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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 NET2_STAT
+               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
+ */
+
+#ifdef __STDC__
+int
+bcpio_id(char *blk, int size)
+#else
+int
+bcpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+bcpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+bcpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_BCPIO *hd;
+       register 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 boundries
+                */
+               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 boundries, 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
+ */
+
+#ifdef __STDC__
+off_t
+bcpio_endrd(void)
+#else
+off_t
+bcpio_endrd()
+#endif
+{
+       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 his their right mind
+ *     should write an achive 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
+ */
+
+#ifdef __STDC__
+int
+bcpio_wr(register ARCHD *arcn)
+#else
+int
+bcpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_BCPIO *hd;
+       register 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/pax/cpio.h b/pax/cpio.h
new file mode 100644 (file)
index 0000000..811f8f3
--- /dev/null
@@ -0,0 +1,154 @@
+/*     $OpenBSD: cpio.h,v 1.2 1996/06/23 14:20:32 deraadt 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. 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.
+ *
+ *     @(#)cpio.h      8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * 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_ */
diff --git a/pax/extern.h b/pax/extern.h
new file mode 100644 (file)
index 0000000..478152c
--- /dev/null
@@ -0,0 +1,299 @@
+/*     $OpenBSD: extern.h,v 1.14 1997/07/24 23:19:18 millert 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. 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.2 (Berkeley) 4/18/94
+ */
+
+/*
+ * External references from each source file
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * ar_io.c
+ */
+extern char *arcname;
+extern char *gzip_program;
+int ar_open __P((char *));
+void ar_close __P((void));
+void ar_drain __P((void));
+int ar_set_wr __P((void));
+int ar_app_ok __P((void));
+int ar_read __P((register char *, register int));
+int ar_write __P((register char *, register int));
+int ar_rdsync __P((void));
+int ar_fow __P((off_t, off_t *));
+int ar_rev __P((off_t ));
+int ar_next __P((void));
+
+/*
+ * ar_subs.c
+ */
+extern u_long flcnt;
+void list __P((void));
+void extract __P((void));
+void append __P((void));
+void archive __P((void));
+void copy __P((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 __P((void));
+int rd_start __P((void));
+void cp_start __P((void));
+int appnd_start __P((off_t));
+int rd_sync __P((void));
+void pback __P((char *, int));
+int rd_skip __P((off_t));
+void wr_fin __P((void));
+int wr_rdbuf __P((register char *, register int));
+int rd_wrbuf __P((register char *, register int));
+int wr_skip __P((off_t));
+int wr_rdfile __P((ARCHD *, int, off_t *));
+int rd_wrfile __P((ARCHD *, int, off_t *));
+void cp_file __P((ARCHD *, int, int));
+int buf_fill __P((void));
+int buf_flush __P((register int));
+
+/*
+ * cache.c
+ */
+int uidtb_start __P((void));
+int gidtb_start __P((void));
+int usrtb_start __P((void));
+int grptb_start __P((void));
+char * name_uid __P((uid_t, int));
+char * name_gid __P((gid_t, int));
+int uid_name __P((char *, uid_t *));
+int gid_name __P((char *, gid_t *));
+
+/*
+ * cpio.c
+ */
+int cpio_strd __P((void));
+int cpio_trail __P((register ARCHD *));
+int cpio_endwr __P((void));
+int cpio_id __P((char *, int));
+int cpio_rd __P((register ARCHD *, register char *));
+off_t cpio_endrd __P((void));
+int cpio_stwr __P((void));
+int cpio_wr __P((register ARCHD *));
+int vcpio_id __P((char *, int));
+int crc_id __P((char *, int));
+int crc_strd __P((void));
+int vcpio_rd __P((register ARCHD *, register char *));
+off_t vcpio_endrd __P((void));
+int crc_stwr __P((void));
+int vcpio_wr __P((register ARCHD *));
+int bcpio_id __P((char *, int));
+int bcpio_rd __P((register ARCHD *, register char *));
+off_t bcpio_endrd __P((void));
+int bcpio_wr __P((register ARCHD *));
+
+/*
+ * file_subs.c
+ */
+int file_creat __P((register ARCHD *));
+void file_close __P((register ARCHD *, int));
+int lnk_creat __P((register ARCHD *));
+int cross_lnk __P((register ARCHD *));
+int chk_same __P((register ARCHD *));
+int node_creat __P((register ARCHD *));
+int unlnk_exist __P((register char *, register int));
+int chk_path __P((register char *, uid_t, gid_t));
+void set_ftime __P((char *fnm, time_t mtime, time_t atime, int frc));
+int set_ids __P((char *, uid_t, gid_t));
+int set_lids __P((char *, uid_t, gid_t));
+void set_pmode __P((char *, mode_t));
+int file_write __P((int, char *, register int, int *, int *, int, char *));
+void file_flush __P((int, char *, int));
+void rdfile_close __P((register ARCHD *, register int *));
+int set_crc __P((register ARCHD *, register int));
+
+/*
+ * ftree.c
+ */
+int ftree_start __P((void));
+int ftree_add __P((register char *, int));
+void ftree_sel __P((register ARCHD *));
+void ftree_chk __P((void));
+int next_file __P((register ARCHD *));
+
+/*
+ * gen_subs.c
+ */
+void ls_list __P((register ARCHD *, time_t, FILE *));
+void ls_tty __P((register ARCHD *));
+int l_strncpy __P((register char *, register char *, int));
+u_long asc_ul __P((register char *, int, register int));
+int ul_asc __P((u_long, register char *, register int, register int));
+#ifndef NET2_STAT
+u_quad_t asc_uqd __P((register char *, int, register int));
+int uqd_asc __P((u_quad_t, register char *, register int, register int));
+#endif
+
+/* 
+ * getoldopt.c
+ */
+int getoldopt __P((int, char **, char *));
+
+/*
+ * options.c
+ */
+extern FSUB fsub[];
+extern int ford[];
+void options __P((register int, register char **));
+OPLIST * opt_next __P((void));
+int opt_add __P((register char *));
+int bad_opt __P((void));
+extern char *chdname;
+
+/*
+ * pat_rep.c
+ */
+int rep_add __P((register char *));
+int pat_add __P((char *, char *));
+void pat_chk __P((void));
+int pat_sel __P((register ARCHD *));
+int pat_match __P((register ARCHD *));
+int mod_name __P((register ARCHD *));
+int set_dest __P((register ARCHD *, char *, int));
+
+/*
+ * pax.c
+ */
+extern int act;
+extern 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 zflag;
+extern int Dflag;
+extern int Hflag;
+extern int Lflag;
+extern int Xflag;
+extern int Yflag;
+extern int Zflag;
+extern int vfpart;
+extern int patime;
+extern int pmtime;
+extern int nodirs;
+extern int pmode;
+extern int pids;
+extern int rmleadslash;
+extern int exit_val;
+extern int docrc;
+extern char *dirptr;
+extern char *ltmfrmt;
+extern char *argv0;
+int main __P((int, char **));
+void sig_cleanup __P((int));
+
+/*
+ * sel_subs.c
+ */
+int sel_chk __P((register ARCHD *));
+int grp_add __P((register char *));
+int usr_add __P((register char *));
+int trng_add __P((register char *));
+
+/*
+ * tables.c
+ */
+int lnk_start __P((void));
+int chk_lnk __P((register ARCHD *));
+void purg_lnk __P((register ARCHD *));
+void lnk_end __P((void));
+int ftime_start __P((void));
+int chk_ftime __P((register ARCHD *));
+int name_start __P((void));
+int add_name __P((register char *, int, char *));
+void sub_name __P((register char *, int *, size_t));
+int dev_start __P((void));
+int add_dev __P((register ARCHD *));
+int map_dev __P((register ARCHD *, u_long, u_long));
+int atdir_start __P((void));
+void atdir_end __P((void));
+void add_atdir __P((char *, dev_t, ino_t, time_t, time_t));
+int get_atdir __P((dev_t, ino_t, time_t *, time_t *));
+int dir_start __P((void));
+void add_dir __P((char *, int, struct stat *, int));
+void proc_dir __P((void));
+u_int st_hash __P((char *, int, int));
+
+/*
+ * tar.c
+ */
+int tar_endwr __P((void));
+off_t tar_endrd __P((void));
+int tar_trail __P((register char *, register int, register int *));
+int tar_id __P((register char *, int));
+int tar_opt __P((void));
+int tar_rd __P((register ARCHD *, register char *));
+int tar_wr __P((register ARCHD *));
+int ustar_strd __P((void));
+int ustar_stwr __P((void));
+int ustar_id __P((char *, int));
+int ustar_rd __P((register ARCHD *, register char *));
+int ustar_wr __P((register ARCHD *));
+
+/*
+ * tty_subs.c
+ */
+int tty_init __P((void));
+void tty_prnt __P((char *, ...));
+int tty_read __P((char *, int));
+void paxwarn __P((int, char *, ...));
+void syswarn __P((int, int, char *, ...));
diff --git a/pax/file_subs.c b/pax/file_subs.c
new file mode 100644 (file)
index 0000000..2aba0fb
--- /dev/null
@@ -0,0 +1,1112 @@
+/*     $OpenBSD: file_subs.c,v 1.13 1997/09/01 18:29:48 deraadt 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. 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[] = "@(#)file_subs.c        8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: file_subs.c,v 1.13 1997/09/01 18:29:48 deraadt Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "options.h"
+#include "extern.h"
+
+static int
+mk_link __P((register char *,register struct stat *,register 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
+ */
+
+#ifdef __STDC__
+int
+file_creat(register ARCHD *arcn)
+#else
+int
+file_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       int fd = -1;
+       mode_t file_mode;
+       int oerrno;
+
+       /*
+        * 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 figure spend the effore 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);
+
+       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(arcn->name, O_WRONLY | O_CREAT | O_TRUNC,
+                   file_mode)) >= 0)
+                       break;
+               oerrno = errno;
+               if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
+                       syswarn(1, oerrno, "Unable to create %s", arcn->name);
+                       return(-1);
+               }
+       }
+       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
+ */
+
+#ifdef __STDC__
+void
+file_close(register ARCHD *arcn, int fd)
+#else
+void
+file_close(arcn, fd)
+       register ARCHD *arcn;
+       int fd;
+#endif
+{
+       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);
+
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+int
+lnk_creat(register ARCHD *arcn)
+#else
+int
+lnk_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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);
+       }
+
+       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).
+ */
+
+#ifdef __STDC__
+int
+cross_lnk(register ARCHD *arcn)
+#else
+int
+cross_lnk(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * try to make a link to orginal 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);
+       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
+ */
+
+#ifdef __STDC__
+int
+chk_same(register ARCHD *arcn)
+#else
+int
+chk_same(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+static int
+mk_link(register char *to, register struct stat *to_sb, register char *from,
+       int ign)
+#else
+static int
+mk_link(to, to_sb, from, ign)
+       register char *to;
+       register struct stat *to_sb;
+       register char *from;
+       int ign;
+#endif
+{
+       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) == 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
+ */
+
+#ifdef __STDC__
+int
+node_creat(register ARCHD *arcn)
+#else
+int
+node_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int res;
+       register int ign = 0;
+       register int oerrno;
+       register int pass = 0;
+       mode_t file_mode;
+       struct stat sb;
+
+       /*
+        * 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:
+                       res = mkdir(arcn->name, file_mode);
+                       if (ign)
+                               res = 0;
+                       break;
+               case PAX_CHR:
+                       file_mode |= S_IFCHR;
+                       res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
+                       break;
+               case PAX_BLK:
+                       file_mode |= S_IFBLK;
+                       res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
+                       break;
+               case PAX_FIF:
+                       res = mkfifo(arcn->name, file_mode);
+                       break;
+               case PAX_SCK:
+                       /*
+                        * Skip sockets, operation has no meaning under BSD
+                        */
+                       paxwarn(0,
+                           "%s skipped. Sockets cannot be copied or extracted",
+                           arcn->name);
+                       return(-1);
+               case PAX_SLK:
+                       res = symlink(arcn->ln_name, arcn->name);
+                       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",
+                               arcn->name);
+                       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(arcn->name, arcn->type)) < 0)
+                       return(-1);
+
+               if (++pass <= 1)
+                       continue;
+
+               if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
+                       syswarn(1, oerrno, "Could not create: %s", arcn->name);
+                       return(-1);
+               }
+       }
+
+       /*
+        * we were able to create the node. set uid/gid, modes and times
+        */
+       if (pids)
+               res = ((arcn->type == PAX_SLK) ?
+                   set_lids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid) :
+                   set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid));
+       else
+               res = 0;
+
+       /*
+        * 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(arcn->name, 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(arcn->name, R_OK | W_OK | X_OK) < 0) {
+                       if (lstat(arcn->name, &sb) < 0) {
+                               syswarn(0, errno,"Could not access %s (stat)",
+                                   arcn->name);
+                               set_pmode(arcn->name,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(arcn->name,
+                                   ((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(arcn->name, arcn->nlen, &(arcn->sb), 1);
+               } else if (pmode || patime || pmtime)
+                       add_dir(arcn->name, arcn->nlen, &(arcn->sb), 0);
+       }
+
+       if (patime || pmtime)
+               set_ftime(arcn->name, 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.
+ */
+
+#ifdef __STDC__
+int
+unlnk_exist(register char *name, register int type)
+#else
+int
+unlnk_exist(name, type)
+       register char *name;
+       register int type;
+#endif
+{
+       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 (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
+ */
+
+#ifdef __STDC__
+int
+chk_path( register char *name, uid_t st_uid, gid_t st_gid)
+#else
+int
+chk_path(name, st_uid, st_gid)
+       register char *name;
+       uid_t st_uid;
+       gid_t st_gid;
+#endif
+{
+       register char *spt = name;
+       struct stat sb;
+       int retval = -1;
+
+       /*
+        * watch out for paths with nodes stored directly in / (e.g. /bozo)
+        */
+       if (*spt == '/')
+               ++spt;
+
+       for(;;) {
+               /*
+                * work foward 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++) = '/';
+                       continue;
+               }
+
+               /*
+                * 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 doen'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, spt - name, &sb, 1);
+               }
+               *(spt++) = '/';
+               continue;
+       }
+       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 ign 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.
+ */
+
+#ifdef __STDC__
+void
+set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
+#else
+void
+set_ftime(fnm, mtime, atime, frc)
+       char *fnm;
+       time_t mtime;
+       time_t atime;
+       int frc;
+#endif
+{
+       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 (utimes(fnm, 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
+ */
+
+#ifdef __STDC__
+int
+set_ids(char *fnm, uid_t uid, gid_t gid)
+#else
+int
+set_ids(fnm, uid, gid)
+       char *fnm;
+       uid_t uid;
+       gid_t gid;
+#endif
+{
+       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);
+}
+
+/*
+ * set_lids()
+ *     set the uid and gid of a file system node
+ * Return:
+ *     0 when set, -1 on failure
+ */
+
+#ifdef __STDC__
+int
+set_lids(char *fnm, uid_t uid, gid_t gid)
+#else
+int
+set_lids(fnm, uid, gid)
+       char *fnm;
+       uid_t uid;
+       gid_t gid;
+#endif
+{
+#ifdef __APPLE__
+       if (chown(fnm, uid, gid) < 0) {
+#else
+       if (lchown(fnm, uid, gid) < 0) {
+#endif
+               /*
+                * 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);
+}
+
+/*
+ * set_pmode()
+ *     Set file access mode
+ */
+
+#ifdef __STDC__
+void
+set_pmode(char *fnm, mode_t mode)
+#else
+void
+set_pmode(fnm, mode)
+       char *fnm;
+       mode_t mode;
+#endif
+{
+       mode &= ABITS;
+       if (chmod(fnm, 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 boundries. 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 boundries 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.
+ */
+
+#ifdef __STDC__
+int
+file_write(int fd, char *str, register int cnt, int *rem, int *isempt, int sz,
+       char *name)
+#else
+int
+file_write(fd, str, cnt, rem, isempt, sz, name)
+       int fd;
+       char *str;
+       register int cnt;
+       int *rem;
+       int *isempt;
+       int sz;
+       char *name;
+#endif
+{
+       register char *pt;
+       register char *end;
+       register int wcnt;
+       register char *st = str;
+
+       /*
+        * 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 (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
+                */
+               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).
+ */
+
+#ifdef __STDC__
+void
+file_flush(int fd, char *fname, int isempt)
+#else
+void
+file_flush(fd, fname, isempt)
+       int fd;
+       char *fname;
+       int isempt;
+#endif
+{
+       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).
+ */
+
+#ifdef __STDC__
+void
+rdfile_close(register ARCHD *arcn, register int *fd)
+#else
+void
+rdfile_close(arcn, fd)
+       register ARCHD *arcn;
+       register int *fd;
+#endif
+{
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+int
+set_crc(register ARCHD *arcn, register int fd)
+#else
+int
+set_crc(arcn, fd)
+       register ARCHD *arcn;
+       register int fd;
+#endif
+{
+       register int i;
+       register int res;
+       off_t cpcnt = 0L;
+       u_long size;
+       unsigned long crc = 0L;
+       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 inconsistant 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/pax/ftree.c b/pax/ftree.c
new file mode 100644 (file)
index 0000000..c5fcc44
--- /dev/null
@@ -0,0 +1,565 @@
+/*     $OpenBSD: ftree.c,v 1.8 1997/09/01 18:29:49 deraadt 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. 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[] = "@(#)ftree.c    8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ftree.c,v 1.8 1997/09/01 18:29:49 deraadt 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;               /* curent 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 __P((void));
+
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+int
+ftree_start(void)
+#else
+int
+ftree_start()
+#endif
+{
+       /*
+        * 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)
+#      ifdef NET2_FTS
+               paxwarn(0, "The -H flag is not supported on this version");
+#      else
+               ftsopts |= FTS_COMFOLLOW;
+#      endif
+       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
+ */
+
+#ifdef __STDC__
+int
+ftree_add(register char *str, int chflg)
+#else
+int
+ftree_add(str, chflg)
+       register char *str;
+       int chflg;
+#endif
+{
+       register FTREE *ft;
+       register int len;
+
+       /*
+        * simple check for bad args
+        */
+       if ((str == NULL) || (*str == '\0')) {
+               paxwarn(0, "Invalid file name arguement");
+               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->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.
+ */
+
+#ifdef __STDC__
+void
+ftree_sel(register ARCHD *arcn)
+#else
+void
+ftree_sel(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * 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_chk()
+ *     called at end on pax execution. Prints all those file args that did not
+ *     have a selected member (reference count still 0)
+ */
+
+#ifdef __STDC__
+void
+ftree_chk(void)
+#else
+void
+ftree_chk()
+#endif
+{
+       register FTREE *ft;
+       register 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->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).
+ */
+
+#ifdef __STDC__
+static int
+ftree_arg(void)
+#else
+static int
+ftree_arg()
+#endif
+{
+       register char *pt;
+
+       /*
+        * 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 (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
+                               return(-1);
+                       if ((pt = strchr(farray[0], '\n')) != NULL)
+                               *pt = '\0';
+               } else {
+                       /*
+                        * the user supplied the file args as arguements to pax
+                        */
+                       if (ftcur == NULL)
+                               ftcur = fthead;
+                       else if ((ftcur = ftcur->fow) == NULL)
+                               return(-1);
+                       if (ftcur->chflg) {
+                               /* First fchdir() back... */
+                               if (fchdir(cwdfd) < 0) {
+                                       syswarn(1, errno,
+                                         "Can't fchdir to starting directory");
+                                       return(-1);
+                               }
+                               if (chdir(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 cannnot 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.
+ */
+
+#ifdef __STDC__
+int
+next_file(register ARCHD *arcn)
+#else
+int
+next_file(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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) {
+                       /*
+                        * 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:
+               case FTS_SLNONE:
+                       /*
+                        * these are all ok
+                        */
+                       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).
+                        */
+#                      ifdef NET2_FTS
+                       if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
+                           ftent->fts_statb.st_ino, &mtime, &atime) < 0))
+#                      else
+                       if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
+                           ftent->fts_statp->st_ino, &mtime, &atime) < 0))
+#                      endif
+                               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:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "Unable to read directory %s", ftent->fts_path);
+                       continue;
+               case FTS_ERR:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "File system traversal error");
+                       continue;
+               case FTS_NS:
+               case FTS_NSOK:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "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';
+#              ifdef NET2_FTS
+               arcn->sb = ftent->fts_statb;
+#              else
+               arcn->sb = *(ftent->fts_statp);
+#              endif
+
+               /*
+                * 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
+                        * allways null 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 = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1);
+       arcn->name[arcn->nlen] = '\0';
+       arcn->org_name = ftent->fts_path;
+       return(0);
+}
diff --git a/pax/ftree.h b/pax/ftree.h
new file mode 100644 (file)
index 0000000..5ac8ff9
--- /dev/null
@@ -0,0 +1,54 @@
+/*     $OpenBSD: ftree.h,v 1.3 1996/10/27 06:45:11 downsj 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. 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.
+ *
+ *     @(#)ftree.h     8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * 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             chflg;          /* change directory flag */
+       struct ftree    *fow;           /* pointer to next entry on list */
+} FTREE;
diff --git a/pax/gen_subs.c b/pax/gen_subs.c
new file mode 100644 (file)
index 0000000..4f5f898
--- /dev/null
@@ -0,0 +1,467 @@
+/*     $OpenBSD: gen_subs.c,v 1.8 1997/09/01 18:29:51 deraadt 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. 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[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: gen_subs.c,v 1.8 1997/09/01 18:29:51 deraadt 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 <utmp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.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 CURFRMT                "%b %e %H:%M"
+#define OLDFRMT                "%b %e  %Y"
+#ifndef UT_NAMESIZE
+#define UT_NAMESIZE    8
+#endif
+#define UT_GRPSIZE     6
+
+/*
+ * ls_list()
+ *     list the members of an archive in ls format
+ */
+
+#ifdef __STDC__
+void
+ls_list(register ARCHD *arcn, time_t now, FILE *fp)
+#else
+void
+ls_list(arcn, now, fp)
+       register ARCHD *arcn;
+       time_t now;
+       FILE *fp;
+#endif
+{
+       register struct stat *sbp;
+       char f_mode[MODELEN];
+       char f_date[DATELEN];
+       char *timefrmt;
+
+       /*
+        * if not verbose, just print the file name
+        */
+       if (!vflag) {
+               (void)fprintf(fp, "%s\n", arcn->name);
+               (void)fflush(fp);
+               return;
+       }
+
+       /*
+        * user wants long mode
+        */
+       sbp = &(arcn->sb);
+       strmode(sbp->st_mode, f_mode);
+
+       if (ltmfrmt == NULL) {
+               /*
+                * no locale specified format. time format based on age
+                * compared to the time pax was started.
+                */
+               if ((sbp->st_mtime + SIXMONTHS) <= now)
+                       timefrmt = OLDFRMT;
+               else
+                       timefrmt = CURFRMT;
+       } else
+               timefrmt = ltmfrmt;
+
+       /*
+        * print file mode, link count, uid, gid and time
+        */
+       if (strftime(f_date,DATELEN,timefrmt,localtime((const time_t *)&(sbp->st_mtime))) == 0)
+               f_date[0] = '\0';
+       (void)fprintf(fp, "%s%2u %-*s %-*s ", f_mode, sbp->st_nlink,
+               UT_NAMESIZE, name_uid(sbp->st_uid, 1), UT_GRPSIZE,
+               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 NET2_STAT
+               (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 {
+#              ifdef NET2_STAT
+               (void)fprintf(fp, "%9lu ", sbp->st_size);
+#              else
+               (void)fprintf(fp, "%9qu ", sbp->st_size);
+#              endif
+       }
+
+       /*
+        * print name and link info for hard and soft links
+        */
+       (void)fprintf(fp, "%s %s", f_date, arcn->name);
+       if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+               (void)fprintf(fp, " == %s\n", arcn->ln_name);
+       else if (arcn->type == PAX_SLK)
+               (void)fprintf(fp, " => %s\n", arcn->ln_name);
+       else
+               (void)putc('\n', fp);
+       (void)fflush(fp);
+       return;
+}
+
+/*
+ * tty_ls()
+ *     print a short summary of file to tty.
+ */
+
+#ifdef __STDC__
+void
+ls_tty(register ARCHD *arcn)
+#else
+void
+ls_tty(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       char f_date[DATELEN];
+       char f_mode[MODELEN];
+       char *timefrmt;
+
+       if (ltmfrmt == NULL) {
+               /*
+                * no locale specified format
+                */
+               if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL))
+                       timefrmt = OLDFRMT;
+               else
+                       timefrmt = CURFRMT;
+       } else
+               timefrmt = ltmfrmt;
+
+       /*
+        * convert time to string, and print
+        */
+       if (strftime(f_date, DATELEN, timefrmt,
+           localtime((const time_t *)&(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;
+}
+
+/*
+ * l_strncpy()
+ *     copy src to dest up to len chars (stopping at first '\0').
+ *     when src is shorter than len, pads to len with '\0'. 
+ * Return:
+ *     number of chars copied. (Note this is a real performance win over
+ *     doing a strncpy(), a strlen(), and then a possible memset())
+ */
+
+#ifdef __STDC__
+int
+l_strncpy(register char *dest, register char *src, int len)
+#else
+int
+l_strncpy(dest, src, len)
+       register char *dest;
+       register char *src;
+       int len;
+#endif
+{
+       register char *stop;
+       register char *start;
+
+       stop = dest + len;
+       start = dest;
+       while ((dest < stop) && (*src != '\0'))
+               *dest++ = *src++;
+       len = dest - start;
+       while (dest < stop)
+               *dest++ = '\0';
+       return(len);
+}
+
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+u_long
+asc_ul(register char *str, int len, register int base)
+#else
+u_long
+asc_ul(str, len, base)
+       register char *str;
+       int len;
+       register int base;
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+ul_asc(u_long val, register char *str, register int len, register int base)
+#else
+int
+ul_asc(val, str, len, base)
+       u_long val;
+       register char *str;
+       register int len;
+       register int base;
+#endif
+{
+       register 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 NET2_STAT
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+u_quad_t
+asc_uqd(register char *str, int len, register int base)
+#else
+u_quad_t
+asc_uqd(str, len, base)
+       register char *str;
+       int len;
+       register int base;
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+uqd_asc(u_quad_t val, register char *str, register int len, register int base)
+#else
+int
+uqd_asc(val, str, len, base)
+       u_quad_t val;
+       register char *str;
+       register int len;
+       register int base;
+#endif
+{
+       register 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
diff --git a/pax/getoldopt.c b/pax/getoldopt.c
new file mode 100644 (file)
index 0000000..8a15a2f
--- /dev/null
@@ -0,0 +1,73 @@
+/*     $OpenBSD: getoldopt.c,v 1.3 1997/09/01 18:29:52 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 Pubic Domain for your edification and enjoyment.
+ */
+
+#ifndef lint
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: getoldopt.c,v 1.3 1997/09/01 18:29:52 deraadt Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+getoldopt(argc, argv, optstring)
+       int     argc;
+       char    **argv;
+       char    *optstring;
+{
+       extern char     *optarg;        /* Points to next arg */
+       extern int      optind;         /* Global argv index */
+       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 EOF;
+               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 EOF;
+       }
+       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/pax/options.c b/pax/options.c
new file mode 100644 (file)
index 0000000..9a64e1f
--- /dev/null
@@ -0,0 +1,1515 @@
+/*     $OpenBSD: options.c,v 1.31 1998/01/22 06:21:29 millert 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. 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[] = "@(#)options.c  8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: options.c,v 1.31 1998/01/22 06:21:29 millert Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mtio.h>
+#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 "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 __P((void));
+static void printflg __P((unsigned int));
+static int c_frmt __P((const void *, const void *));
+static off_t str_offt __P((char *));
+static void pax_options __P((register int, register char **));
+static void pax_usage __P((void));
+static void tar_options __P((register int, register char **));
+static void tar_usage __P((void));
+static void cpio_options __P((register int, register char **));
+static void cpio_usage __P((void));
+
+#define GZIP_CMD       "gzip"          /* command to run as gzip */
+#define COMPRESS_CMD   "compress"      /* command to run as compress */
+
+/*
+ *     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
+ */
+
+FSUB fsub[] = {
+/* 0: 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},
+
+/* 1: 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},
+
+/* 2: 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},
+
+/* 3: 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},
+
+/* 4: 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},
+
+/* 5: 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_CPIO  3       /* format when called as cpio */
+#define F_OTAR 4       /* format when called as tar -o */
+#define F_TAR  5       /* format when called as tar */
+#define DEFLT  5       /* 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[] = {5, 4, 3, 2, 1, 0, -1 };
+
+/*
+ * options()
+ *     figure out if we are pax, tar or cpio. Call the appropriate options
+ *     parser
+ */
+
+#ifdef __STDC__
+void
+options(register int argc, register char **argv)
+#else
+void
+options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+
+       /*
+        * 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)
+               return(tar_options(argc, argv));
+       else if (strcmp(NM_CPIO, argv0) == 0)
+               return(cpio_options(argc, argv));
+       /*
+        * assume pax as the default
+        */
+       argv0 = NM_PAX;
+       return(pax_options(argc, argv));
+}
+
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+static void
+pax_options(register int argc, register char **argv)
+#else
+static void
+pax_options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+       register int c;
+       register int i;
+       unsigned int flg = 0;
+       unsigned int bflg = 0;
+       register char *pt;
+       FSUB tmp;
+       extern char *optarg;
+       extern int optind;
+
+       /*
+        * process option flags
+        */
+       while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:zB:DE:G:HLPT:U:XYZ"))
+           != EOF) {
+               switch (c) {
+               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
+                        */
+                       arcname = optarg;
+                       flg |= FF;
+                       break;
+               case 'i':
+                       /*
+                        * interactive file rename
+                        */
+                       iflag = 1;
+                       flg |= IF;
+                       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 (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':
+                                       /*
+                                        * preserver 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
+                        */
+                       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;
+                       if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
+                           sizeof(fsub)/sizeof(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.
+                        */
+                       zflag = 1;
+                       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;
+                       break;
+               case 'L':
+                       /*
+                        * follow symlinks
+                        */
+                       Lflag = 1;
+                       flg |= CLF;
+                       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;
+               default:
+                       pax_usage();
+                       break;
+               }
+       }
+
+       /*
+        * 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;
+               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]);
+
+       /*
+        * 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
+ */
+
+#ifdef __STDC__
+static void
+tar_options(register int argc, register char **argv)
+#else
+static void
+tar_options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+       register int c;
+       int fstdin = 0;
+       int Oflag = 0;
+
+       /*
+        * Set default values.
+        */
+       rmleadslash = 1;
+
+       /*
+        * process option flags
+        */
+       while ((c = getoldopt(argc, argv,
+           "b:cef:hmopruts:vwxzBC:HLOPXZ014578"))
+           != EOF) {
+               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 'm':
+                       /*
+                        * do not preserve modification time
+                        */
+                       pmtime = 0;
+                       break;
+               case 'o':
+                       if (opt_add("write_opt=nodir") < 0)
+                               tar_usage();
+               case 'O':
+                       Oflag = 1;
+                       break;
+               case 'p':
+                       /*
+                        * preserve user id, group id, file
+                        * mode, access/modification times
+                        */
+                       pids = 1;
+                       pmode = 1;
+                       patime = 1;
+                       pmtime = 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':
+                       /*
+                        * write an archive, preserve ids if root
+                        */
+                       act = EXTRACT;
+                       if (geteuid() == 0)
+                               pids = 1;
+                       break;
+               case 'z':
+                       /*
+                        * use gzip.  Non standard option.
+                        */
+                       zflag = 1;
+                       gzip_program = GZIP_CMD;
+                       break;
+               case 'B':
+                       /*
+                        * Nothing to do here, this is pax default
+                        */
+                       break;
+               case 'C':
+                       chdname = optarg;
+                       break;
+               case 'H':
+                       /*
+                        * follow command line symlinks only
+                        */
+                       Hflag = 1;
+                       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.
+                        */
+                       zflag = 1;
+                       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 wants to read filelist from stdin) */
+       if ((act == ARCHIVE || act == APPND) && argc == 0)
+               exit(0);
+
+       /*
+        * if we are writing (ARCHIVE) specify tar, otherwise run like pax
+        * (unless -o specified)
+        */
+       if (act == ARCHIVE || act == APPND)
+               frmt = &(fsub[Oflag ? F_OTAR : F_TAR]);
+       else if (Oflag) {
+               paxwarn(1, "The -O/-o options are only valid when writing an archive");
+               tar_usage();            /* only valid when writing */
+       }
+
+       /*
+        * process the args as they are interpreted by the operation mode
+        */
+       switch (act) {
+       case LIST:
+       case EXTRACT:
+       default:
+               {
+                       int sawpat = 0;
+
+                       while (*argv != NULL) {
+                               if (strcmp(*argv, "-C") == 0) {
+                                       if(*++argv == NULL)
+                                               break;
+                                       chdname = *argv++;
+
+                                       continue;
+                               }
+                               if (pat_add(*argv++, chdname) < 0)
+                                       tar_usage();
+                               sawpat++;
+                       }
+                       /*
+                        * 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:
+               if (chdname != NULL) {  /* initial chdir() */
+                       if (ftree_add(chdname, 1) < 0)
+                               tar_usage();
+               }
+
+               while (*argv != NULL) {
+                       if (!strcmp(*argv, "-C")) {
+                               if (*++argv == NULL)
+                                       break;
+                               if (ftree_add(*argv++, 1) < 0)
+                                       tar_usage();
+                       } 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(path)
+       char *path;
+{
+       struct stat sb;
+       register 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
+ */
+
+#ifdef __STDC__
+static void
+cpio_options(register int argc, register char **argv)
+#else
+static void
+cpio_options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+       register int c, i;
+       size_t len;
+       char *str;
+       FSUB tmp;
+       FILE *fp;
+
+       kflag = 1;
+       pids = 1;
+       pmode = 1;
+       pmtime = 0;
+       arcname = NULL;
+       dflag = 1;
+       act = -1;
+       nodirs = 1;
+       while ((c=getopt(argc,argv,"abcdfiklmoprstuvzABC:E:F:H:I:LO:SZ6")) != EOF)
+               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 '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;
+                               break;
+                       case 'u':
+                               /*
+                                * replace newer files
+                                */
+                               kflag = 0;
+                               break;
+                       case 'v':
+                               /*
+                                * verbose operation mode
+                                */
+                               vflag = 1;
+                               break;
+                       case 'z':
+                               /*
+                                * use gzip.  Non standard option.
+                                */
+                               zflag = 1;
+                               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 = fgetln(fp, &len)) != NULL) {
+                                       str[len - 1] = '\0';
+                                       pat_add(str, NULL);
+                               }
+                               fclose(fp);
+                               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;
+                               if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub,
+                                   sizeof(fsub)/sizeof(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.
+                                */
+                               zflag = 1;
+                               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 = fgetln(stdin, &len)) != NULL) {
+                               str[len - 1] = '\0';
+                               ftree_add(strdup(str), NULL);
+                       }
+                       break;
+               default:
+                       cpio_usage();
+                       break;
+       }
+}
+
+/*
+ * printflg()
+ *     print out those invalid flag sets found to the user
+ */
+
+#ifdef __STDC__
+static void
+printflg(unsigned int flg)
+#else
+static void
+printflg(flg)
+       unsigned int flg;
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+static int
+c_frmt(const void *a, const void *b)
+#else
+static int
+c_frmt(a, b)
+       void *a;
+       void *b;
+#endif
+{
+       return(strcmp(((FSUB *)a)->name, ((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).
+ */
+
+#ifdef __STDC__
+OPLIST *
+opt_next(void)
+#else
+OPLIST *
+opt_next()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+bad_opt(void)
+#else
+int
+bad_opt()
+#endif
+{
+       register 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)
+               (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
+       pax_usage();
+       return(0);
+}
+
+/*
+ * 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
+ *     mulltiple -o may be specified.
+ * Return:
+ *     0 if format in name=value format, -1 if -o is passed junk
+ */
+
+#ifdef __STDC__
+int
+opt_add(register char *str)
+#else
+int
+opt_add(str)
+       register char *str;
+#endif
+{
+       register OPLIST *opt;
+       register char *frpt;
+       register char *pt;
+       register char *endpt;
+
+       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 = endpt = 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 = strchr(frpt, '=')) == NULL) {
+                       paxwarn(0, "Invalid options format");
+                       free(str);
+                       return(-1);
+               }
+               if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) {
+                       paxwarn(0, "Unable to allocate space for option list");
+                       free(str);
+                       return(-1);
+               }
+               *pt++ = '\0';
+               opt->name = frpt;
+               opt->value = pt;
+               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).
+ *        seperated by x (also * for backwards compatibility), specifying
+ *        the product of the indicated values.
+ * Return:
+ *     0 for an error, a positive value o.w.
+ */
+
+#ifdef __STDC__
+static off_t
+str_offt(char *val)
+#else
+static off_t
+str_offt(val)
+       char *val;
+#endif
+{
+       char *expr;
+       off_t num, t;
+
+#      ifdef NET2_STAT
+       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);
+}
+
+/*
+ * no_op()
+ *     for those option functions where the archive format has nothing to do.
+ * Return:
+ *     0
+ */
+
+#ifdef __STDC__
+static int
+no_op(void)
+#else
+static int
+no_op()
+#endif
+{
+       return(0);
+}
+
+/*
+ * pax_usage()
+ *     print the usage summary to the user
+ */
+
+#ifdef __STDC__
+void
+pax_usage(void)
+#else
+void
+pax_usage()
+#endif
+{
+       (void)fputs("usage: pax [-cdnv] [-E limit] [-f archive] ", stderr);
+       (void)fputs("[-s replstr] ... [-U user] ...", stderr);
+       (void)fputs("\n           [-G group] ... ", stderr);
+       (void)fputs("[-T [from_date][,to_date]] ... ", stderr);
+       (void)fputs("[pattern ...]\n", stderr);
+       (void)fputs("       pax -r [-cdiknuvDYZ] [-E limit] ", stderr);
+       (void)fputs("[-f archive] [-o options] ... \n", stderr);
+       (void)fputs("           [-p string] ... [-s replstr] ... ", stderr);
+       (void)fputs("[-U user] ... [-G group] ...\n           ", stderr);
+       (void)fputs("[-T [from_date][,to_date]] ... ", stderr);
+       (void)fputs(" [pattern ...]\n", stderr);
+       (void)fputs("       pax -w [-dituvHLPX] [-b blocksize] ", stderr);
+       (void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr);
+       (void)fputs("           [-B bytes] [-s replstr] ... ", stderr);
+       (void)fputs("[-o options] ... [-U user] ...", stderr);
+       (void)fputs("\n           [-G group] ... ", stderr);
+       (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
+       (void)fputs("[file ...]\n", stderr);
+       (void)fputs("       pax -r -w [-diklntuvDHLPXYZ] ", stderr);
+       (void)fputs("[-p string] ... [-s replstr] ...", stderr);
+       (void)fputs("\n           [-U user] ... [-G group] ... ", stderr);
+       (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr);
+       (void)fputs("\n           [file ...] directory\n", stderr);
+       exit(1);
+}
+
+/*
+ * tar_usage()
+ *     print the usage summary to the user
+ */
+
+#ifdef __STDC__
+void
+tar_usage(void)
+#else
+void
+tar_usage()
+#endif
+{
+       (void)fputs("usage: tar -{txru}[cevfbmopswzBHLPXZ014578] [tapefile] ",
+                stderr);
+       (void)fputs("[blocksize] [replstr] [-C directory] file1 file2...\n",
+           stderr);
+       exit(1);
+}
+
+/*
+ * cpio_usage()
+ *     print the usage summary to the user
+ */
+
+#ifdef __STDC__
+void
+cpio_usage(void)
+#else
+void
+cpio_usage()
+#endif
+{
+       (void)fputs("usage: cpio -o [-aABcLvVzZ] [-C bytes] [-H format] [-O archive]\n", stderr);
+       (void)fputs("               [-F archive] < name-list [> archive]\n", stderr);
+       (void)fputs("       cpio -i [-bBcdfmnrsStuvVzZ6] [-C bytes] [-E file] [-H format]\n", stderr);
+       (void)fputs("               [-I archive] [-F archive] [pattern...] [< archive]\n", stderr);
+       (void)fputs("       cpio -p [-adlLmuvV] destination-directory < name-list\n", stderr);
+       exit(1);
+}
diff --git a/pax/options.h b/pax/options.h
new file mode 100644 (file)
index 0000000..d45f54a
--- /dev/null
@@ -0,0 +1,116 @@
+/*     $OpenBSD: options.h,v 1.2 1996/06/23 14:20:37 deraadt 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. 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.
+ *
+ *     @(#)options.h   8.2 (Berkeley) 4/18/94
+ */
+
+/*
+ * 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 */
+
+/*
+ * 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  "abcdfiklnoprstuvwxBDEGHLPTUXYZ"
+
+/*
+ * 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|OF|XF|CBF|CEF)
+#define        BDLIST (AF|BF|IF|KF|LF|OF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF)
diff --git a/pax/pat_rep.c b/pax/pat_rep.c
new file mode 100644 (file)
index 0000000..9ff1dca
--- /dev/null
@@ -0,0 +1,1240 @@
+/*     $OpenBSD: pat_rep.c,v 1.11 1997/09/01 18:29:56 deraadt 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. 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[] = "@(#)pat_rep.c  8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: pat_rep.c,v 1.11 1997/09/01 18:29:56 deraadt 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>
+#ifdef NET2_REGEX
+#include <regexp.h>
+#else
+#include <regex.h>
+#endif
+#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 __P((char *, int *, int));
+static int tty_rename __P((register ARCHD *));
+static int fix_path __P((char *, int *, char *, int));
+static int fn_match __P((register char *, register char *, char **));
+static char * range_match __P((register char *, register int));
+#ifdef NET2_REGEX
+static int resub __P((regexp *, char *, char *, register char *));
+#else
+static int resub __P((regex_t *, regmatch_t *, char *, char *, char *));
+#endif
+
+/*
+ * 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.
+ */
+
+#ifdef __STDC__
+int
+rep_add(register char *str)
+#else
+int
+rep_add(str)
+       register char *str;
+#endif
+{
+       register char *pt1;
+       register char *pt2;
+       register REPLACE *rep;
+#      ifndef NET2_REGEX
+       register int res;
+       char rebuf[BUFSIZ];
+#      endif
+
+       /*
+        * 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
+        */
+       if ((pt1 = strchr(str+1, *str)) == NULL) {
+               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';
+#      ifdef NET2_REGEX
+       if ((rep->rcmp = regcomp(str+1)) == NULL) {
+#      else
+       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);
+#      endif
+               (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;
+       if ((pt2 = strchr(pt1, *str)) == NULL) {
+#              ifdef NET2_REGEX
+               (void)free((char *)rep->rcmp);
+#              else
+               regfree(&(rep->rcmp));
+#              endif
+               (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:
+#                      ifdef NET2_REGEX
+                       (void)free((char *)rep->rcmp);
+#                      else
+                       regfree(&(rep->rcmp));
+#                      endif
+                       (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
+ */
+
+#ifdef __STDC__
+int
+pat_add(char *str, char *chdname)
+#else
+int
+pat_add(str, chdname)
+       char *str;
+       char *chdname;
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+void
+pat_chk(void)
+#else
+void
+pat_chk()
+#endif
+{
+       register PATTERN *pt;
+       register 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 a 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.
+ */
+
+#ifdef __STDC__
+int
+pat_sel(register ARCHD *arcn)
+#else
+int
+pat_sel(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register PATTERN *pt;
+       register PATTERN **ppt;
+       register 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 inconsistant");
+               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)
+ */
+
+#ifdef __STDC__
+int
+pat_match(register ARCHD *arcn)
+#else
+int
+pat_match(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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 a 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.
+ */
+
+#ifdef __STDC__
+static int
+fn_match(register char *pattern, register char *string, char **pend)
+#else
+static int
+fn_match(pattern, string, pend)
+       register char *pattern;
+       register char *string;
+       char **pend;
+#endif
+{
+       register char c;
+       char test;
+
+       *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 ((test = *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 ((test = *string) != '\0') {
+                               if (!fn_match(pattern, string, pend))
+                                       return (0);
+                               ++string;
+                       }
+                       return (-1);
+               case '[':
+                       /*
+                        * range match
+                        */
+                       if (((test = *string++) == '\0') ||
+                           ((pattern = range_match(pattern, test)) == NULL))
+                               return (-1);
+                       break;
+               case '\\':
+               default:
+                       if (c != *string++)
+                               return (-1);
+                       break;
+               }
+       }
+       /* NOTREACHED */
+}
+
+#ifdef __STDC__
+static char *
+range_match(register char *pattern, register int test)
+#else
+static char *
+range_match(pattern, test)
+       register char *pattern;
+       register int test;
+#endif
+{
+       register char c;
+       register 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 
+ */
+
+#ifdef __STDC__
+int
+mod_name(register ARCHD *arcn)
+#else
+int
+mod_name(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int res = 0;
+
+       /*
+        * Strip off leading '/' if appropriate.
+        * Currently, this option is only set for the tar format.
+        */
+       if (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");
+               }
+       }
+       if (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, &(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, &(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
+ */
+
+#ifdef __STDC__
+static int
+tty_rename(register ARCHD *arcn)
+#else
+static int
+tty_rename(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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 = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1);
+       arcn->name[arcn->nlen] = '\0';
+       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)
+ */
+
+#ifdef __STDC__
+int
+set_dest(register ARCHD *arcn, char *dest_dir, int dir_len)
+#else
+int
+set_dest(arcn, dest_dir, dir_len)
+       register ARCHD *arcn;
+       char *dest_dir;
+       int dir_len;
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+static int
+fix_path( char *or_name, int *or_len, char *dir_name, int dir_len)
+#else
+static int
+fix_path(or_name, or_len, dir_name, dir_len)
+       char *or_name;
+       int *or_len;
+       char *dir_name;
+       int dir_len;
+#endif
+{
+       register char *src;
+       register char *dest;
+       register 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)
+ *     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)
+ */
+
+#ifdef __STDC__
+static int
+rep_name(char *name, int *nlen, int prnt)
+#else
+static int
+rep_name(name, nlen, prnt)
+       char *name;
+       int *nlen;
+       int prnt;
+#endif
+{
+       register REPLACE *pt;
+       register char *inpt;
+       register char *outpt;
+       register char *endpt;
+       register char *rpt;
+       register int found = 0;
+       register int res;
+#      ifndef NET2_REGEX
+       regmatch_t pm[MAXSUBEXP];
+#      endif
+       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)strcpy(buf1, name);
+       inpt = buf1;
+       outpt = nname;
+       endpt = outpt + PAXPATHLEN;
+
+       /*
+        * try each replacement string in order
+        */
+       while (pt != NULL) {
+               do {
+                       /*
+                        * check for a successful substitution, if not go to
+                        * the next pattern, or cleanup if we were global
+                        */
+#                      ifdef NET2_REGEX
+                       if (regexec(pt->rcmp, inpt) == 0)
+#                      else
+                       if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0)
+#                      endif
+                               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;
+#                      ifdef NET2_REGEX
+                       rpt = pt->rcmp->startp[0];
+#                      else
+                       rpt = inpt + pm[0].rm_so;
+#                      endif
+
+                       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.
+                        */
+#                      ifdef NET2_REGEX
+                       if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) {
+#                      else
+                       if ((res = resub(&(pt->rcmp),pm,pt->nstr,outpt,endpt))
+                           < 0) {
+#                      endif
+                               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
+                        */
+#                      ifdef NET2_REGEX
+                       inpt = pt->rcmp->endp[0];
+#                      else
+                       inpt += pm[0].rm_eo - pm[0].rm_so;
+#                      endif
+
+                       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 = l_strncpy(name, nname, PAXPATHLEN + 1);
+               name[PAXPATHLEN] = '\0';
+       }
+       return(0);
+}
+
+#ifdef NET2_REGEX
+/*
+ * 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.
+ */
+
+#ifdef __STDC__
+static int
+resub(regexp *prog, char *src, char *dest, register char *destend)
+#else
+static int
+resub(prog, src, dest, destend)
+       regexp *prog;
+       char *src;
+       char *dest;
+       register char *destend;
+#endif
+{
+       register char *spt;
+       register char *dpt;
+       register char c;
+       register int no;
+       register int len;
+
+       spt = src;
+       dpt = dest;
+       while ((dpt < destend) && ((c = *spt++) != '\0')) {
+               if (c == '&')
+                       no = 0;
+               else if ((c == '\\') && (*spt >= '0') && (*spt <= '9'))
+                       no = *spt++ - '0';
+               else {
+                       if ((c == '\\') && ((*spt == '\\') || (*spt == '&')))
+                               c = *spt++;
+                       *dpt++ = c;
+                       continue;
+               }
+               if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) ||
+                   ((len = prog->endp[no] - prog->startp[no]) <= 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;
+               if (l_strncpy(dpt, prog->startp[no], len) != len)
+                       return(-1);
+               dpt += len;
+       }
+       return(dpt - dest);
+}
+
+#else
+
+/*
+ * 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.
+ */
+
+#ifdef __STDC__
+static int
+resub(regex_t *rp, register regmatch_t *pm, char *src, char *dest,
+       register char *destend)
+#else
+static int
+resub(rp, pm, src, dest, destend)
+       regex_t *rp;
+       register regmatch_t *pm;
+       char *src;
+       char *dest;
+       register char *destend;
+#endif
+{
+       register char *spt;
+       register char *dpt;
+       register char c;
+       register regmatch_t *pmpt;
+       register 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 == '\\') || (*spt == '&')))
+                               c = *spt++;
+                       *dpt++ = c;
+                       continue;
+               }
+
+               /*
+                * continue if the subexpression is bogus
+                */
+               if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) ||
+                   ((len = 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;
+               if (l_strncpy(dpt, src + pmpt->rm_so, len) != len)
+                       return(-1);
+               dpt += len;
+       }
+       return(dpt - dest);
+}
+#endif
diff --git a/pax/pat_rep.h b/pax/pat_rep.h
new file mode 100644 (file)
index 0000000..b41f1e1
--- /dev/null
@@ -0,0 +1,57 @@
+/*     $OpenBSD: pat_rep.h,v 1.2 1996/06/23 14:20:38 deraadt 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. 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.
+ *
+ *     @(#)pat_rep.h   8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * data structure for storing user supplied replacement strings (-s)
+ */
+typedef struct replace {
+       char            *nstr;  /* the new string we will substitute with */
+#      ifdef NET2_REGEX
+       regexp          *rcmp;  /* compiled regular expression used to match */
+#      else
+       regex_t         rcmp;   /* compiled regular expression used to match */
+#      endif
+       int             flgs;   /* print conversions? global in operation?  */
+#define        PRNT            0x1
+#define        GLOB            0x2
+       struct replace  *fow;   /* pointer to next pattern */
+} REPLACE;
diff --git a/pax/pax.1 b/pax/pax.1
new file mode 100644 (file)
index 0000000..590c9df
--- /dev/null
+++ b/pax/pax.1
@@ -0,0 +1,1178 @@
+.\"    $OpenBSD: pax.1,v 1.5 1997/04/06 06:11:13 millert Exp $
+.\"    $NetBSD: pax.1,v 1.3 1995/03/21 09:07:37 cgd Exp $
+.\"
+.\" Copyright (c) 1992 Keith Muller.
+.\" Copyright (c) 1992, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" 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.
+.\"
+.\"    @(#)pax.1       8.4 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt PAX 1
+.Os BSD 4.4
+.Sh NAME
+.Nm pax
+.Nd read and write file archives and copy directory hierarchies
+.Sh SYNOPSIS
+.Nm pax
+.Op Fl cdnv
+.Bk -words
+.Op Fl f Ar archive
+.Ek
+.Bk -words
+.Op Fl s Ar replstr
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl U Ar user
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl G Ar group
+.Ar ...
+.Ek
+.Bk -words
+.Oo
+.Fl T
+.Op Ar from_date
+.Op Ar ,to_date
+.Oc
+.Ar ...
+.Ek
+.Op Ar pattern ...
+.Nm pax
+.Fl r
+.Op Fl cdiknuvDYZ
+.Bk -words
+.Op Fl f Ar archive
+.Ek
+.Bk -words
+.Op Fl o Ar options
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl p Ar string
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl s Ar replstr
+.Ar ...
+.Ek
+.Op Fl E Ar limit
+.Bk -words
+.Op Fl U Ar user
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl G Ar group
+.Ar ...
+.Ek
+.Bk -words
+.Oo
+.Fl T
+.Op Ar from_date
+.Op Ar ,to_date
+.Oc
+.Ar ...
+.Ek
+.Op Ar pattern ...
+.Nm pax
+.Fl w
+.Op Fl dituvHLPX
+.Bk -words
+.Op Fl b Ar blocksize
+.Ek
+.Oo
+.Op Fl a
+.Op Fl f Ar archive
+.Oc
+.Bk -words
+.Op Fl x Ar format
+.Ek
+.Bk -words
+.Op Fl s Ar replstr
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl o Ar options
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl U Ar user
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl G Ar group
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl B Ar bytes
+.Ek
+.Bk -words
+.Oo
+.Fl T
+.Op Ar from_date
+.Op Ar ,to_date
+.Op Ar /[c][m]
+.Oc
+.Ar ...
+.Ek
+.Op Ar file ...
+.Nm pax
+.Fl r
+.Fl w
+.Op Fl diklntuvDHLPXYZ
+.Bk -words
+.Op Fl p Ar string
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl s Ar replstr
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl U Ar user
+.Ar ...
+.Ek
+.Bk -words
+.Op Fl G Ar group
+.Ar ...
+.Ek
+.Bk -words
+.Oo
+.Fl T
+.Op Ar from_date
+.Op Ar ,to_date
+.Op Ar /[c][m]
+.Oc
+.Ar ...
+.Ek
+.Op Ar file ...
+.Ar directory
+.Sh DESCRIPTION
+.Nm Pax
+will read, write, and list the members of an archive file,
+and will copy directory hierarchies.
+.Nm Pax 
+operation is independent of the specific archive format,
+and supports a wide variety of different archive formats.
+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 pax
+will operate under:
+.Em list , read , write ,
+and
+.Em copy.
+.Bl -tag -width 6n
+.It <none>
+.Em List .
+.Nm Pax
+will write to
+.Dv standard output
+a table of contents of the members of the archive file read from
+.Dv standard input ,
+whose pathnames match the specified
+.Ar patterns.
+The table of contents contains one filename per line
+and is written using single line buffering.
+.It Fl r
+.Em Read .
+.Nm Pax
+extracts the members of the archive file read from the
+.Dv standard input ,
+with pathnames matching the specified 
+.Ar patterns.
+The archive format and blocking is automatically determined on input.
+When an extracted file is a directory, the entire file hierarchy
+rooted at that directory is extracted.
+All extracted files are created relative to the current file hierarchy.
+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 Pax
+writes an archive containing the 
+.Ar file
+operands to
+.Dv 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 
+.Dv standard input .
+When a 
+.Ar file
+operand is also a directory, the entire file hierarchy rooted
+at that directory will be included.
+.It Fl r Fl w
+.Em Copy .
+.Nm Pax
+copies the
+.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
+.Dv 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
+.Em 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 pax
+will attempt to recover from media defects and will search through the archive
+to locate and process the largest number of archive members possible (see the
+.Fl E
+option for more details on error handling).
+.Sh OPERANDS
+.Pp
+The
+.Ar directory
+operand specifies a destination directory pathname.
+If the
+.Ar directory
+operand does not exist, or it is not writable by the user,
+or it is not of type directory,
+.Nm Pax
+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 fnmatch 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 pax
+will write these
+.Ar pattern
+operands in a diagnostic message to
+.Dv 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 pax
+will write these
+.Ar file
+operand pathnames in a diagnostic message to
+.Dv standard error
+and then exit with a non-zero exit status.
+.Sh OPTIONS
+.Pp
+The following options are supported:
+.Bl -tag -width 4n
+.It Fl r
+Read an archive file from
+.Dv standard input
+and extract the specified
+.Ar files .
+If any intermediate directories are needed in order to extract an archive
+member, these directories will be created as if
+.Xr mkdir 2
+was called with the bitwise inclusive
+.Dv OR 
+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 pax
+will write a diagnostic message to
+.Dv standard error
+and exit with a non-zero exit status at the completion of operation.
+.It Fl w
+Write files to the
+.Dv standard output
+in the specified archive format.
+When no
+.Ar file
+operands are specified,
+.Dv standard input
+is read for a list of pathnames with one per line without any leading or
+trailing
+.Aq blanks .
+.It Fl a
+Append
+.Ar files
+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 pax
+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
+.Em 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 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.
+Archives larger than 32256 bytes violate the
+.Tn POSIX
+standard and will not be portable to all systems.
+A
+.Ar blocksize
+can end with
+.Li k
+or
+.Li b
+to specify multiplication by 1024 (1K) or 512, respectively.
+A pair of
+.Ar blocksizes
+can be separated by
+.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
+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 f Ar archive
+Specify
+.Ar archive
+as the pathname of the input or output archive, overriding the default
+.Dv standard input
+(for
+.Em list
+and
+.Em read )
+or
+.Dv standard output
+(for
+.Em write ) .
+A single archive may span multiple files and different archive devices.
+When required,
+.Nm pax
+will prompt for the pathname of the file or device of the next volume in the
+archive.
+.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 pax
+will prompt to
+.Pa /dev/tty
+giving the name of the file, its file mode and its modification time.
+.Nm Pax
+will then read a line from
+.Pa /dev/tty .
+If this line is blank, the file or archive member is skipped.
+If this line consists of a single period, the
+file or archive member is processed with no modification to its name.
+Otherwise, its name is replaced with the contents of the line.
+.Nm Pax
+will immediately exit with a non-zero exit status if 
+.Dv <EOF>
+is encountered when reading a response or if
+.Pa /dev/tty
+cannot be opened for reading and writing.
+.It Fl k
+Do not overwrite existing files.
+.It Fl l
+Link files. (The letter ell).
+In the
+.Em copy
+mode (
+.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 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:
+.Cm name=value
+.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 meaning 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
+.Sq 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
+.Sq Preserve
+the file mode bits.
+This 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 pax
+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 pax
+will write a diagnostic message to
+.Dv 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
+.Dl Fl p Ar eme
+is specified, file modification times are still preserved.
+.It Fl s Ar replstr
+Modify the file or archive member names specified by the
+.Ar pattern
+or
+.Ar file
+operands according to the substitution expression
+.Ar replstr ,
+using the syntax of the
+.Xr ed 1
+utility regular expressions.
+The format of these regular expressions are:
+.Dl /old/new/[gp]
+As in
+.Xr ed 1 ,
+.Cm old
+is a basic regular expression and
+.Cm new
+can contain an ampersand (&), \\n (where n is a digit) back-references,
+or subexpression matching.
+The
+.Cm old
+string may also contain
+.Dv <newline>
+characters.
+Any non-null character can be used as a delimiter (/ is shown here).
+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.
+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
+.Dv standard error
+in the following format:
+.Dl <original pathname> >> <new pathname>
+File or archive member names that substitute to the empty string
+are not selected and will be skipped.
+.It Fl t
+Reset the access times of any file or directory read or accessed by
+.Nm pax
+to be the same as they were before being read or accessed by
+.Nm pax .
+.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:
+.Dl <ls -l listing> == <link name>
+For pathnames representing a symbolic link, the output has the format:
+.Dl <ls -l listing> => <link name>
+Where <ls -l listing> is the output format specified by the 
+.Xr ls 1
+utility when used with the
+.Fl l
+option.
+Otherwise for all the other operational modes (
+.Em read , write ,
+and
+.Em copy ) ,
+pathnames are written and flushed to
+.Dv standard error
+without a trailing
+.Dv <newline>
+as soon as processing begins on that file or
+archive member.
+The trailing
+.Dv <newline> ,
+is not buffered, and is written only after the file has been read or written.
+.It Fl x Ar format
+Specify the output archive format, with the default format being
+.Ar ustar .
+.Nm Pax
+currently supports the following formats:
+.Bl -tag -width "sv4cpio"
+.It Ar 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 pax
+and is repaired.
+.It Ar 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 pax
+and is repaired.
+.It Ar sv4cpio
+The System V release 4 cpio.
+The default blocksize for this format is 5120 bytes.
+Inode and device information about a file (used for detecting file hard links
+by this format) which may be truncated by this format is detected by
+.Nm pax
+and is repaired.
+.It Ar sv4crc
+The System V release 4 cpio with file crc checksums.
+The default blocksize for this format is 5120 bytes.
+Inode and device information about a file (used for detecting file hard links
+by this format) which may be truncated by this format is detected by
+.Nm pax
+and is repaired.
+.It Ar tar
+The old BSD tar format as found in BSD4.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:
+.Dl Fl o Cm write_opt=nodir
+.It Ar ustar
+The extended tar interchange format specified in the
+.St -p1003.2
+standard.
+The default blocksize for this format is 10240 bytes.
+Pathnames stored by this format must be 250 characters or less in length.
+.El
+.Pp
+.Nm Pax
+will detect and report any file that it is unable to store or extract
+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 B Ar bytes
+Limit the number of bytes written to a single archive volume to
+.Ar bytes .
+The
+.Ar bytes
+limit can end with
+.Li m ,
+.Li k ,
+or
+.Li b
+to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively.
+A pair of
+.Ar bytes
+limits can be separated by
+.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 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 E Ar limit
+Limit the number of consecutive read faults while trying to read a flawed
+archives to
+.Ar limit .
+With a positive
+.Ar limit ,
+.Nm pax
+will attempt to recover from an archive read error and will
+continue processing starting with the next file stored in the archive.
+A
+.Ar limit
+of 0 will cause
+.Nm pax
+to stop operation after the first read error is detected on an archive volume.
+A
+.Ar limit
+of
+.Li NONE
+will cause
+.Nm pax 
+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 pax
+may get stuck in an infinite loop on a very badly flawed 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 '\\' 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 L
+Follow all symbolic links to perform a logical file system traversal.
+.It Fl P
+Do not follow symbolic links, perform a physical file system traversal.
+This is the default mode.
+.It Fl T Ar [from_date][,to_date][/[c][m]]
+Allow files to be selected based on a file modification or inode change
+time falling within a specified time range of 
+.Ar from_date
+to
+.Ar to_date
+(the dates 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 pax
+is in the 
+.Em write
+or
+.Em copy
+mode, the optional trailing field
+.Ar [c][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
+.Ar m
+specifies the comparison of file modification time (the time when
+the file was last written).
+The
+.Ar c
+specifies the comparison of inode change time (the time when the file
+inode was last changed; e.g. a change of owner, group, mode, etc).
+When 
+.Ar c
+and
+.Ar m
+are both specified, then the modification and inode change times are
+both compared.
+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 pax
+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:
+.Dl [yy[mm[dd[hh]]]]mm[.ss]
+Where
+.Cm yy
+is the last two digits of the year,
+the first
+.Cm mm
+is the month (from 01 to 12),
+.Cm dd
+is the day of the month (from 01 to 31),
+.Cm hh
+is the hour of the day (from 00 to 23),
+the second
+.Cm mm
+is the minute (from 00 to 59),
+and
+.Cm ss
+is the seconds (from 00 to 59).
+The minute field
+.Cm mm
+is required, while the other fields are optional and must be added in the
+following order:
+.Dl Cm hh , dd , mm , yy .
+The 
+.Cm ss
+field may be added independently of the other fields.
+Time ranges are relative to the current time, so
+.Dl Fl T Ar 1234/cm
+would select all files with a modification or inode change time
+of 12:34 PM today or later.
+Multiple 
+.Fl T
+time range can be supplied and checking stops with the first match.
+.It Fl U Ar user
+Select a file based on its
+.Ar user
+name, or when starting with a
+.Cm # ,
+a numeric uid.
+A '\\' can be used to escape the
+.Cm # .
+Multiple 
+.Fl U
+options may be supplied and checking stops with the first match.
+.It Fl X
+When traversing the file hierarchy specified by a pathname,
+do not descend into directories that have a different device ID.
+See the
+.Li st_dev 
+field as described in
+.Xr stat 2
+for more information about device ID's.
+.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.
+.El
+.Pp
+The options that operate on the names of files or archive members (
+.Fl c ,
+.Fl i ,
+.Fl n ,
+.Fl s ,
+.Fl u ,
+.Fl v ,
+.Fl D ,
+.Fl G ,
+.Fl T ,
+.Fl U ,
+.Fl Y ,
+and
+.Fl Z )
+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 EXAMPLES
+The command:
+.Dl pax -w -f /dev/rst0 .\ 
+copies the contents of the current directory to the device
+.Pa /dev/rst0 .
+.Pp
+The command:
+.Dl pax -v -f filename
+gives the verbose table of contents for an archive stored in
+.Pa filename .
+.Pp
+The following commands:
+.Dl mkdir newdir
+.Dl cd olddir
+.Dl pax -rw .\ newdir
+will copy the entire
+.Pa olddir
+directory hierarchy to
+.Pa newdir .
+.Pp
+The command:
+.Dl pax -r -s ',^//*usr//*,,' -f a.pax
+reads the archive 
+.Pa a.pax ,
+with all files rooted in ``/usr'' into the archive extracted relative to the
+current directory.
+.Pp
+The command:
+.Dl pax -rw -i .\ dest_dir
+can be used to interactively select the files to copy from the current
+directory to
+.Pa dest_dir .
+.Pp
+The command:
+.Dl pax -r -pe -U root -G bin -f a.pax
+will extract all files from the archive
+.Pa a.pax
+which are owned by
+.Em root
+with group
+.Em bin
+and will preserve all file permissions.
+.Pp
+The command:
+.Dl pax -r -w -v -Y -Z home /backup
+will 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 .
+.Sh STANDARDS
+The
+.Nm pax
+utility is a superset of the
+.St -p1003.2
+standard.
+The options
+.Fl B ,
+.Fl D ,
+.Fl E ,
+.Fl G ,
+.Fl H ,
+.Fl L ,
+.Fl P ,
+.Fl T ,
+.Fl U ,
+.Fl Y ,
+.Fl Z ,
+the archive formats
+.Ar bcpio ,
+.Ar sv4cpio ,
+.Ar sv4crc ,
+.Ar tar ,
+and the flawed archive handling during
+.Ar list
+and
+.Ar read
+operations are extensions to the
+.Tn POSIX
+standard.
+.Sh SEE ALSO
+.Xr tar 1 ,
+.Xr cpio 1
+.Sh AUTHOR
+Keith Muller at the University of California, San Diego
+.Sh ERRORS
+.Nm pax
+will exit with one of the following values:
+.Bl -tag -width 2n
+.It 0
+All files were processed successfully.
+.It 1 
+An error occurred.
+.El
+.Pp
+Whenever
+.Nm pax
+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
+.Dv standard error
+and a non-zero exit status will be returned, but processing will continue.
+In the case where pax cannot create a link to a file,
+.Nm pax
+will not create a second copy of the file.
+.Pp
+If the extraction of a file from an archive is prematurely terminated by
+a signal or error,
+.Nm pax
+may have only partially extracted a file the user wanted.
+Additionally, the file modes of extracted files and directories
+may have incorrect file bits, and the modification and access times may be
+wrong.
+.Pp
+If the creation of an archive is prematurely terminated by a signal or error,
+.Nm pax
+may have only partially created the archive which may violate the specific
+archive format specification.
+.Pp
+If while doing a
+.Em copy ,
+.Nm pax
+detects a file is about to overwrite itself, the file is not copied,
+a diagnostic message is written to
+.Dv standard error
+and when
+.Nm pax
+completes it will exit with a non-zero exit status.
diff --git a/pax/pax.c b/pax/pax.c
new file mode 100644 (file)
index 0000000..574ed5b
--- /dev/null
+++ b/pax/pax.c
@@ -0,0 +1,426 @@
+/*     $OpenBSD: pax.c,v 1.11 1997/09/01 18:29:58 deraadt 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. 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
+static char copyright[] __attribute__((__unused__)) =
+"@(#) Copyright (c) 1992, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pax.c      8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: pax.c,v 1.11 1997/09/01 18:29:58 deraadt 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 <errno.h>
+#include <fcntl.h>
+#include "pax.h"
+#include "extern.h"
+static int gen_init __P((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 */
+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    zflag;                  /* use gzip */
+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 Dflg except after name mode */
+int    Zflag;                  /* same as uflg except after name mode */
+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    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 */
+
+/*
+ *     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 exisiting 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 writtens by pax are forced to strictly conform to both the
+ *     archive and pax the spceific 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 exisiting file before
+ *     name modification (-D)
+ * 3.10 File inode change time can be checked against exisiting file after
+ *     name modification (-Y)
+ * 3.11        File modification time can be checked against exisiting 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
+ */
+
+#ifdef __STDC__
+int
+main(int argc, char **argv)
+#else
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+#endif
+{
+       /*
+        * Keep a reference to cwd, so we can always come back home.
+        */
+       cwdfd = open(".", O_RDONLY);
+       if (cwdfd < 0) {
+               syswarn(0, errno, "Can't open current working directory.");
+               return(exit_val);
+       }
+
+       /*
+        * 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:
+               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....
+ */
+
+#ifdef __STDC__
+void
+sig_cleanup(int which_sig)
+#else
+void
+sig_cleanup(which_sig)
+       int which_sig;
+#endif
+{
+       /*
+        * 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.
+ */
+
+#ifdef __STDC__
+static int
+gen_init(void)
+#else
+static int
+gen_init()
+#endif
+{
+       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 thorugh 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);
+       }
+       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/pax/pax.h b/pax/pax.h
new file mode 100644 (file)
index 0000000..bdf00a4
--- /dev/null
+++ b/pax/pax.h
@@ -0,0 +1,243 @@
+/*     $OpenBSD: pax.h,v 1.9 1997/07/23 19:15:58 kstailey 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. 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.
+ *
+ *     @(#)pax.h       8.2 (Berkeley) 4/18/94
+ */
+
+/*
+ * 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    /* maximium 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 */
+
+/*
+ * 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 specifc 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 boundry */
+       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)();            /* checks if a buffer is a valid header */
+                               /* returns 1 if it is, o.w. returns a 0 */
+       int (*st_rd)();         /* initialize routine for read. so format */
+                               /* can set up tables etc before it starts */
+                               /* reading an archive */
+       int (*rd)();            /* read header routine. passed a pointer to */
+                               /* 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)();      /* 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)();         /* initialize routine for write operations */
+       int (*wr)();            /* 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)();        /* end write. write the trailer and do any */
+                               /* other format specific functions needed */
+                               /* at the ecnd of a archive write */
+       int (*trail)();         /* returns 0 if a valid trailer, -1 if not */
+                               /* For formats which encode the trailer */
+                               /* 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)();       /* read/process file data from the archive */
+       int (*wr_data)();       /* write/process file data to the archive */
+       int (*options)();       /* process format specific options (-o) */
+} FSUB;
+
+/*
+ * 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_long 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 */ 
+} ARCHD;
+
+/*
+ * 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 */
+} OPLIST;
+
+/*
+ * 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
diff --git a/pax/sel_subs.c b/pax/sel_subs.c
new file mode 100644 (file)
index 0000000..a4e1c22
--- /dev/null
@@ -0,0 +1,662 @@
+/*     $OpenBSD: sel_subs.c,v 1.7 1997/08/17 23:05:09 millert 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. 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[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: sel_subs.c,v 1.7 1997/08/17 23:05:09 millert Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "sel_subs.h"
+#include "extern.h"
+
+static int str_sec __P((register char *, time_t *));
+static int usr_match __P((register ARCHD *));
+static int grp_match __P((register ARCHD *));
+static int trng_match __P((register 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 specfied uid, gid or time range
+ * Return:
+ *     0 if this archive member should be processed, 1 if it should be skipped
+ */
+
+#ifdef __STDC__
+int
+sel_chk(register ARCHD *arcn)
+#else
+int
+sel_chk(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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 then name or the uid/gid starting with
+ * a # on the command line. A \# will eascape the #.
+ */
+
+/*
+ * usr_add()
+ *     add a user match to the user match hash table
+ * Return:
+ *     0 if added ok, -1 otherwise;
+ */
+
+#ifdef __STDC__
+int
+usr_add(register char *str)
+#else
+int
+usr_add(str)
+       register char *str;
+#endif
+{
+       register u_int indx;
+       register USRT *pt;
+       register struct passwd *pw;
+       register 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
+#              ifdef NET2_STAT
+               uid = (uid_t)atoi(str+1);
+#              else
+               uid = (uid_t)strtoul(str+1, NULL, 10);
+#              endif
+       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
+ */
+
+#ifdef __STDC__
+static int
+usr_match(register ARCHD *arcn)
+#else
+static int
+usr_match(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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;
+ */
+
+#ifdef __STDC__
+int
+grp_add(register char *str)
+#else
+int
+grp_add(str)
+       register char *str;
+#endif
+{
+       register u_int indx;
+       register GRPT *pt;
+       register struct group *gr;
+       register 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
+#              ifdef NET2_STAT
+               gid = (gid_t)atoi(str+1);
+#              else
+               gid = (gid_t)strtoul(str+1, NULL, 10);
+#              endif
+       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
+ */
+
+#ifdef __STDC__
+static int
+grp_match(register ARCHD *arcn)
+#else
+static int
+grp_match(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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: [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
+ */
+
+#ifdef __STDC__
+int
+trng_add(register char *str)
+#else
+int
+trng_add(str)
+       register char *str;
+#endif
+{
+       register TIME_RNG *pt;
+       register char *up_pt = NULL;
+       register char *stpt;
+       register char *flgpt;
+       register 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 usee 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);
+                               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: [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
+ */
+
+#ifdef __STDC__
+static int
+trng_match(register ARCHD *arcn)
+#else
+static int
+trng_match(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register 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 [yy[mm[dd[hh]]]]mm[.ss] to gmt
+ *     seconds. Tval already has current time loaded into it at entry.
+ * Return:
+ *     0 if converted ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+static int
+str_sec(register char *str, time_t *tval)
+#else
+static int
+str_sec(str, tval)
+       register char *str;
+       time_t *tval;
+#endif
+{
+       register struct tm *lt;
+       register char *dot = NULL;
+
+       lt = localtime(tval);
+       if ((dot = strchr(str, '.')) != NULL) {
+               /*
+                * seconds (.ss)
+                */
+               *dot++ = '\0';
+               if (strlen(dot) != 2)
+                       return(-1);
+               if ((lt->tm_sec = ATOI2(dot)) > 61)
+                       return(-1);
+       } else
+               lt->tm_sec = 0;
+
+       switch (strlen(str)) {
+       case 10:
+               /*
+                * year (yy)
+                * watch out for year 2000
+                */
+               if ((lt->tm_year = ATOI2(str)) < 69)
+                       lt->tm_year += 100;
+               str += 2;
+               /* FALLTHROUGH */
+       case 8:
+               /*
+                * month (mm)
+                * watch out months are from 0 - 11 internally
+                */
+               if ((lt->tm_mon = ATOI2(str)) > 12)
+                       return(-1);
+               --lt->tm_mon;
+               str += 2;
+               /* FALLTHROUGH */
+       case 6:
+               /*
+                * day (dd)
+                */
+               if ((lt->tm_mday = ATOI2(str)) > 31)
+                       return(-1);
+               str += 2;
+               /* FALLTHROUGH */
+       case 4:
+               /*
+                * hour (hh)
+                */
+               if ((lt->tm_hour = ATOI2(str)) > 23)
+                       return(-1);
+               str += 2;
+               /* FALLTHROUGH */
+       case 2:
+               /*
+                * minute (mm)
+                */
+               if ((lt->tm_min = ATOI2(str)) > 59)
+                       return(-1);
+               break;
+       default:
+               return(-1);
+       }
+       /*
+        * convert broken-down time to GMT clock time seconds
+        */
+       if ((*tval = mktime(lt)) == -1)
+               return(-1);
+       return(0);
+}
diff --git a/pax/sel_subs.h b/pax/sel_subs.h
new file mode 100644 (file)
index 0000000..fc86e9d
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $OpenBSD: sel_subs.h,v 1.2 1996/06/23 14:20:41 deraadt 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. 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.
+ *
+ *     @(#)sel_subs.h  8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * 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(s)       ((((s)[0] - '0') * 10) + ((s)[1] - '0'))
+
+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;
diff --git a/pax/tables.c b/pax/tables.c
new file mode 100644 (file)
index 0000000..a0a964e
--- /dev/null
@@ -0,0 +1,1434 @@
+/*     $OpenBSD: tables.c,v 1.9 1997/09/01 18:30:00 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. 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[] = "@(#)tables.c   8.1 (Berkeley) 5/31/93";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tables.c,v 1.9 1997/09/01 18:30:00 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 databases 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 int dirfd = -1;         /* storage for setting created dir time/mode */
+static u_long dircnt;          /* entries in dir time/mode storage */
+static int ffd = -1;           /* tmp file for file time table name storage */
+
+static DEVT *chk_dev __P((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
+ */
+
+#ifdef __STDC__
+int
+lnk_start(void)
+#else
+int
+lnk_start()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+chk_lnk(register ARCHD *arcn)
+#else
+int
+chk_lnk(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HRDLNK *pt;
+       register HRDLNK **ppt;
+       register 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) {
+               /*
+                * it's 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 = l_strncpy(arcn->ln_name, pt->name,
+                               sizeof(arcn->ln_name) - 1);
+                       arcn->ln_name[arcn->ln_nlen] = '\0';
+                       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.
+ */
+
+#ifdef __STDC__
+void
+purg_lnk(register ARCHD *arcn)
+#else
+void
+purg_lnk(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HRDLNK *pt;
+       register HRDLNK **ppt;
+       register 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
+ */
+
+#ifdef __STDC__
+void
+lnk_end(void)
+#else
+void
+lnk_end()
+#endif
+{
+       register int i;
+       register HRDLNK *pt;
+       register 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 scatch 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 to 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 scatch 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
+ */
+
+#ifdef __STDC__
+int
+ftime_start(void)
+#else
+int
+ftime_start()
+#endif
+{
+       char *pt;
+
+       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
+        */
+       pt = strdup("/tmp/paxXXXXXX");
+       if ((ffd = mkstemp(pt)) < 0) {
+               syswarn(1, errno, "Unable to create temporary file: %s", pt);
+               free(pt);
+               return(-1);
+       }
+       (void)unlink(pt);
+       free(pt);
+
+       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
+ */
+
+#ifdef __STDC__
+int
+chk_ftime(register ARCHD *arcn)
+#else
+int
+chk_ftime(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register FTM *pt;
+       register int namelen;
+       register 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
+ * assignes 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
+ */
+
+#ifdef __STDC__
+int
+name_start(void)
+#else
+int
+name_start()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+add_name(register char *oname, int onamelen, char *nname)
+#else
+int
+add_name(oname, onamelen, nname)
+       register char *oname;
+       int onamelen;
+       char *nname;
+#endif
+{
+       register NAMT *pt;
+       register u_int indx;
+
+       if (ntab == NULL) {
+               /*
+                * should never happen
+                */
+               paxwarn(0, "No interactive rename table, links may fail\n");
+               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)
+ */
+
+#ifdef __STDC__
+void
+sub_name(register char *oname, int *onamelen, size_t onamesize)
+#else
+void
+sub_name(oname, onamelen, onamesize)
+       register char *oname;
+       int *onamelen;
+       size_t onamesize;
+#endif
+{
+       register NAMT *pt;
+       register 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 = l_strncpy(oname, pt->nname, onamesize - 1);
+                       oname[*onamelen] = '\0';
+                       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
+ */
+
+#ifdef __STDC__
+int
+dev_start(void)
+#else
+int
+dev_start()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+add_dev(register ARCHD *arcn)
+#else
+int
+add_dev(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       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 alread 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).
+ */
+
+#ifdef __STDC__
+static DEVT *
+chk_dev(dev_t dev, int add)
+#else
+static DEVT *
+chk_dev(dev, add)
+       dev_t dev;
+       int add;
+#endif
+{
+       register DEVT *pt;
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+map_dev(register ARCHD *arcn, u_long dev_mask, u_long ino_mask)
+#else
+int
+map_dev(arcn, dev_mask, ino_mask)
+       register ARCHD *arcn;
+       u_long dev_mask;
+       u_long ino_mask;
+#endif
+{
+       register DEVT *pt;
+       register 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 to 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 is 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.
+ */
+
+#ifdef __STDC__
+int
+atdir_start(void)
+#else
+int
+atdir_start()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+void
+atdir_end(void)
+#else
+void
+atdir_end()
+#endif
+{
+       register ATDIR *pt;
+       register 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
+ */
+
+#ifdef __STDC__
+void
+add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime)
+#else
+void
+add_atdir(fname, dev, ino, mtime, atime)
+       char *fname;
+       dev_t dev;
+       ino_t ino;
+       time_t mtime;
+       time_t atime;
+#endif
+{
+       register ATDIR *pt;
+       register 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.
+ */
+
+#ifdef __STDC__
+int
+get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime)
+#else
+int
+get_atdir(dev, ino, mtime, atime)
+       dev_t dev;
+       ino_t ino;
+       time_t *mtime;
+       time_t *atime;
+#endif
+{
+       register ATDIR *pt;
+       register ATDIR **ppt;
+       register 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
+ */
+
+#ifdef __STDC__
+int
+dir_start(void)
+#else
+int
+dir_start()
+#endif
+{
+       char *pt;
+
+       if (dirfd != -1)
+               return(0);
+
+       /*
+        * unlink the file so it goes away at termination by itself
+        */
+       pt = strdup("/tmp/paxXXXXXX");
+       if ((dirfd = mkstemp(pt)) >= 0) {
+               (void)unlink(pt);
+               free(pt);
+               return(0);
+       }
+       paxwarn(1, "Unable to create temporary file for directory times: %s", pt);
+       free(pt);
+       return(-1);
+}
+
+/*
+ * 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)
+ */
+
+#ifdef __STDC__
+void
+add_dir(char *name, int nlen, struct stat *psb, int frc_mode)
+#else
+void
+add_dir(name, nlen, psb, frc_mode)
+       char *name;
+       int nlen;
+       struct stat *psb;
+       int frc_mode;
+#endif
+{
+       DIRDATA dblk;
+
+       if (dirfd < 0)
+               return;
+
+       /*
+        * get current position (where file name will start) so we can store it
+        * in the trailer
+        */
+       if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) {
+               paxwarn(1,"Unable to store mode and times for directory: %s",name);
+               return;
+       }
+
+       /*
+        * write the file name followed by the trailer
+        */
+       dblk.nlen = nlen + 1;
+       dblk.mode = psb->st_mode & 0xffff;
+       dblk.mtime = psb->st_mtime;
+       dblk.atime = psb->st_atime;
+       dblk.frc_mode = frc_mode;
+       if ((write(dirfd, name, dblk.nlen) == dblk.nlen) &&
+           (write(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) {
+               ++dircnt;
+               return;
+       }
+
+       paxwarn(1,"Unable to store mode and times for created directory: %s",name);
+       return;
+}
+
+/*
+ * proc_dir()
+ *     process all file modes and times stored for directories CREATED
+ *     by pax
+ */
+
+#ifdef __STDC__
+void
+proc_dir(void)
+#else
+void
+proc_dir()
+#endif
+{
+       char name[PAXPATHLEN+1];
+       DIRDATA dblk;
+       u_long cnt;
+
+       if (dirfd < 0)
+               return;
+       /*
+        * read backwards through the file and process each directory
+        */
+       for (cnt = 0; cnt < dircnt; ++cnt) {
+               /*
+                * read the trailer, then the file name, if this fails
+                * just give up.
+                */
+               if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0)
+                       break;
+               if (read(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk))
+                       break;
+               if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
+                       break;
+               if (read(dirfd, name, dblk.nlen) != dblk.nlen)
+                       break;
+               if (lseek(dirfd, dblk.npos, SEEK_SET) < 0)
+                       break;
+
+               /*
+                * 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)
+                */
+               if (pmode || dblk.frc_mode)
+                       set_pmode(name, dblk.mode);
+               if (patime || pmtime)
+                       set_ftime(name, dblk.mtime, dblk.atime, 0);
+       }
+
+       (void)close(dirfd);
+       dirfd = -1;
+       if (cnt != dircnt)
+               paxwarn(1,"Unable to set mode and times for created directories");
+       return;
+}
+
+/*
+ * 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.
+ */
+
+#ifdef __STDC__
+u_int
+st_hash(char *name, int len, int tabsz)
+#else
+u_int
+st_hash(name, len, tabsz)
+       char *name;
+       int len;
+       int tabsz;
+#endif
+{
+       register char *pt;
+       register char *dest;
+       register char *end;
+       register int i;
+       register u_int key = 0;
+       register int steps;
+       register int res;
+       u_int val;
+
+       /*
+        * 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/pax/tables.h b/pax/tables.h
new file mode 100644 (file)
index 0000000..1d9c813
--- /dev/null
@@ -0,0 +1,175 @@
+/*     $OpenBSD: tables.h,v 1.2 1996/06/23 14:20:43 deraadt 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. 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.
+ *
+ *     @(#)tables.h    8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * 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 (worse 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 */
+
+/*
+ * 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;           /* loacation 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 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).  Entries are recorded into a spool file to make reverse
+ * reading faster.
+ */
+
+typedef struct dirdata {
+       int nlen;       /* length of the directory name (includes \0) */
+       off_t npos;     /* position in file where this dir name starts */
+       mode_t mode;    /* file mode to restore */
+       time_t mtime;   /* mtime to set */
+       time_t atime;   /* atime to set */
+       int frc_mode;   /* do we force mode settings? */
+} DIRDATA;
diff --git a/pax/tar.1 b/pax/tar.1
new file mode 100644 (file)
index 0000000..9e31fb2
--- /dev/null
+++ b/pax/tar.1
@@ -0,0 +1,239 @@
+.\"
+.\" Copyright (c) 1996 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.
+.\" 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 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: tar.1,v 1.13 1997/05/29 15:47:19 millert Exp $
+.\"
+.Dd June 11, 1996
+.Dt TAR 1
+.Os
+.Sh NAME
+.Nm tar
+.Nd tape archiver
+.Sh SYNOPSIS
+.Nm
+.No [-]{crtux}[befhmopvwzHLPXZ014578]
+.Op Ar archive
+.Op Ar blocksize
+.\" XXX how to do this right?
+.No [-C
+.Ar directory
+.No ]
+.No [-s
+.Ar replstr
+.No ]
+.Ar file1
+.Op Ar file2...
+.Sh DESCRIPTION
+The
+.Nm
+command creates, adds files to, or extracts files from an
+archive file in \*Qtar\*U format.  A tar archive is often
+stored on a magnetic tape, but can be a floppy or a regular
+disk file.
+.Pp
+One of the following flags must be present:
+.Bl -tag -width Ar
+.It Fl c
+Create new archive, or overwrite an existing archive,
+adding the specified files to it.
+.It Fl r
+Append the named new files to existing archive.  Note that
+this will only work on media on which an end-of-file mark
+can be overwritten.
+.It Fl t
+List contents of archive.  If any files are named on the
+command line, only those files will be listed.
+.It Fl u
+Alias for
+.Fl r
+.It Fl x
+Extract files from archive.  If any files are named on the
+command line, only those files will be extracted from the
+archive.  If more than one copy of a file exists in the
+archive, later copies will overwrite earlier copies during
+extration.
+.El
+.Pp
+In addition to the flags mentioned above, any of the following
+flags may be used:
+.Bl -tag -width Ar
+.It Fl b Ar "blocking factor"
+Set blocking factor to use for the archive,
+.Nm
+uses 512 byte blocks.  The default is 20, the maximum is 126.
+Archives with a blocking factor larger 63 violate the
+.Tn POSIX
+standard and will not be portable to all systems.
+.It Fl e
+Stop after first error.
+.It Fl f Ar archive
+Filename where the archive is stored.  Defaults to
+.Pa /dev/rst0
+.It Fl h
+Follow symbolic links as if they were normal files
+or directories.
+.It Fl m
+Do not preserve modification time.
+.It Fl O
+Write old-style (non-POSIX) archives.
+.It Fl o
+Don't write directory information that the older (V7) style
+.Nm
+is unable to decode.
+This implies the
+.Fl O
+flag.
+.It Fl p
+Preserve user id, group id, file mode, access and modification
+times if possible.  The user id and group id will only be set
+if the user is the superuser (unless these values correspond
+to the user's user and group ids).
+.It Fl s Ar replstr
+Modify the file or archive member names specified by the
+.Ar pattern
+or
+.Ar file
+operands according to the substitution expression
+.Ar replstr ,
+using the syntax of the
+.Xr ed 1
+utility regular expressions.
+The format of these regular expressions are:
+.Dl /old/new/[gp]
+As in
+.Xr ed 1 ,
+.Cm old
+is a basic regular expression and
+.Cm new
+can contain an ampersand (&), \\n (where n is a digit) back-references,
+or subexpression matching.
+The
+.Cm old
+string may also contain
+.Dv <newline>
+characters.
+Any non-null character can be used as a delimiter (/ is shown here).
+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.
+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
+.Dv standard error
+in the following format:
+.Dl <original pathname> >> <new pathname>
+File or archive member names that substitute to the empty string
+are not selected and will be skipped.
+.It Fl v
+Verbose operation mode.
+.It Fl w
+Interactively rename files.  This option causes
+.Nm
+to prompt the user for the filename to use when storing or
+extracting files in an archive.
+.It Fl z
+Compress archive using gzip.
+.It Fl C Ar directory
+This is a positional argument which sets the working directory for the
+following files.  When extracting, files will be extracted into
+the specified directory; when creating, the specified files will be matched
+from the directory.
+.It Fl H
+Follow symlinks given on command line only.
+.It Fl L
+Follow all symlinks.
+.It Fl P
+Do not strip leading slashes (``/'') from pathnames.
+The default is to strip leading slashes.
+.It Fl X
+Do not cross mount points in the file system.
+.It Fl Z
+Compress archive using compress.
+.El
+.Pp
+The options
+.Op Fl 014578
+can be used to select one of the compiled-in backup devices,
+.Pa /dev/rstN .
+.Sh FILES
+.Bl -tag -width "/dev/rst0"
+.It Pa /dev/rst0
+The default archive name
+.El
+.Sh SEE ALSO
+.Xr pax 1 ,
+.Xr cpio 1
+.Sh AUTHOR
+Keith Muller at the University of California, San Diego
+.Sh ERRORS
+.Nm
+will exit with one of the following values:
+.Bl -tag -width 2n
+.It 0
+All files were processed successfully.
+.It 1
+An error occured.
+.El
+.Pp
+Whenever
+.Nm
+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
+options 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
+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 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
+may have only partially created the archive which may violate the
+specific archive format specification.
diff --git a/pax/tar.c b/pax/tar.c
new file mode 100644 (file)
index 0000000..9044fe5
--- /dev/null
+++ b/pax/tar.c
@@ -0,0 +1,1213 @@
+/*     $OpenBSD: tar.c,v 1.12 1997/09/01 18:30:03 deraadt 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. 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[] = "@(#)tar.c      8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tar.c,v 1.12 1997/09/01 18:30:03 deraadt 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 u_long tar_chksm __P((register char *, register int));
+static char *name_split __P((register char *, register int));
+static int ul_oct __P((u_long, register char *, register int, int));
+#ifndef NET2_STAT
+static int uqd_oct __P((u_quad_t, register char *, register int, int));
+#endif
+
+/*
+ * Routines common to all versions of tar
+ */
+
+static int tar_nodir;                  /* do not write dirs under old tar */
+
+/*
+ * tar_endwr()
+ *     add the tar trailer of two null blocks
+ * Return:
+ *     0 if ok, -1 otherwise (what wr_skip returns)
+ */
+
+#ifdef __STDC__
+int
+tar_endwr(void)
+#else
+int
+tar_endwr()
+#endif
+{
+       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)
+ */
+
+#ifdef __STDC__
+off_t
+tar_endrd(void)
+#else
+off_t
+tar_endrd()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+int
+tar_trail(register char *buf, register int in_resync, register int *cnt)
+#else
+int
+tar_trail(buf, in_resync, cnt)
+       register char *buf;
+       register int in_resync;
+       register int *cnt;
+#endif
+{
+       register 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
+ */
+
+#ifdef __STDC__
+static int
+ul_oct(u_long val, register char *str, register int len, int term)
+#else
+static int
+ul_oct(val, str, len, term)
+       u_long val;
+       register char *str;
+       register int len;
+       int term;
+#endif
+{
+       register 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 NET2_STAT
+/*
+ * 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
+ */
+
+#ifdef __STDC__
+static int
+uqd_oct(u_quad_t val, register char *str, register int len, int term)
+#else
+static int
+uqd_oct(val, str, len, term)
+       u_quad_t val;
+       register char *str;
+       register int len;
+       int term;
+#endif
+{
+       register 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 sume 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
+ */
+
+#ifdef __STDC__
+static u_long
+tar_chksm(register char *blk, register int len)
+#else
+static u_long
+tar_chksm(blk, len)
+       register char *blk;
+       register int len;
+#endif
+{
+       register char *stop;
+       register char *pt;
+       u_long chksm = BLNKSUM; /* inital 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
+ */
+
+#ifdef __STDC__
+int
+tar_id(register char *blk, int size)
+#else
+int
+tar_id(blk, size)
+       register char *blk;
+       int size;
+#endif
+{
+       register HD_TAR *hd;
+       register 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);
+       return(0);
+}
+
+/*
+ * tar_opt()
+ *     handle tar format specific -o options
+ * Return:
+ *     0 if ok -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+tar_opt(void)
+#else
+int
+tar_opt()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+tar_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+tar_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_TAR *hd;
+       register char *pt;
+
+       /*
+        * we only get proper sized buffers passed to us
+        */
+       if (tar_id(buf, BLKMULT) < 0)
+               return(-1);
+       arcn->org_name = arcn->name;
+       arcn->sb.st_nlink = 1;
+       arcn->pat = NULL;
+
+       /*
+        * copy out the name and values in the stat buffer
+        */
+       hd = (HD_TAR *)buf;
+       arcn->nlen = l_strncpy(arcn->name, hd->name, sizeof(arcn->name) - 1);
+       arcn->name[arcn->nlen] = '\0';
+       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);
+       arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+       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->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname,
+                       sizeof(arcn->ln_name) - 1);
+               arcn->ln_name[arcn->ln_nlen] = '\0';
+               arcn->sb.st_mode |= S_IFLNK;
+               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 = l_strncpy(arcn->ln_name, hd->linkname,
+                       sizeof(arcn->ln_name) - 1);
+               arcn->ln_name[arcn->ln_nlen] = '\0';
+
+               /*
+                * 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 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;
+               arcn->ln_name[0] = '\0';
+               arcn->ln_nlen = 0;
+               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 caluculate 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
+ */
+
+#ifdef __STDC__
+int
+tar_wr(register ARCHD *arcn)
+#else
+int
+tar_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_TAR *hd;
+       int len;
+       char hdblk[sizeof(HD_TAR)];
+
+       /*
+        * 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 the unused fields to be
+        * padded with zero. We set the linkflag field (type), the linkname
+        * (or zero if not used),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)
+        */
+       hd = (HD_TAR *)hdblk;
+       l_strncpy(hd->name, arcn->name, sizeof(hd->name) - 1);
+       hd->name[sizeof(hd->name) - 1] = '\0';
+       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;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+               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;
+               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
+               hd->linkname[sizeof(hd->linkname) - 1] = '\0';
+               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;
+               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
+               hd->linkname[sizeof(hd->linkname) - 1] = '\0';
+               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;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+#              ifdef NET2_STAT
+               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(hdblk, sizeof(HD_TAR)), hd->chksum,
+           sizeof(hd->chksum), 3))
+               goto out;
+       if (wr_rdbuf(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
+ */
+
+#ifdef __STDC__
+int
+ustar_strd(void)
+#else
+int
+ustar_strd()
+#endif
+{
+       if ((usrtb_start() < 0) || (grptb_start() < 0))
+               return(-1);
+       return(0);
+}
+
+/*
+ * ustar_stwr()
+ *     initialization for ustar write
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#ifdef __STDC__
+int
+ustar_stwr(void)
+#else
+int
+ustar_stwr()
+#endif
+{
+       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
+ */
+
+#ifdef __STDC__
+int
+ustar_id(char *blk, int size)
+#else
+int
+ustar_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       register 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
+ */
+
+#ifdef __STDC__
+int
+ustar_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+ustar_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_USTAR *hd;
+       register char *dest;
+       register int cnt = 0;
+       dev_t devmajor;
+       dev_t devminor;
+
+       /*
+        * we only get proper sized buffers
+        */
+       if (ustar_id(buf, BLKMULT) < 0)
+               return(-1);
+       arcn->org_name = arcn->name;
+       arcn->sb.st_nlink = 1;
+       arcn->pat = NULL;
+       arcn->nlen = 0;
+       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 = l_strncpy(dest, hd->prefix, sizeof(arcn->name) - 2);
+               dest += cnt;
+               *dest++ = '/';
+               cnt++;
+       }
+       arcn->nlen = cnt + l_strncpy(dest, hd->name, sizeof(arcn->name) - cnt);
+       arcn->name[arcn->nlen] = '\0';
+
+       /*
+        * 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);
+       arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+       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->ln_name[0] = '\0';
+       arcn->ln_nlen = 0;
+       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;
+               }
+               /*
+                * copy the link name
+                */
+               arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname,
+                       sizeof(arcn->ln_name) - 1);
+               arcn->ln_name[arcn->ln_nlen] = '\0';
+               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
+ */
+
+#ifdef __STDC__
+int
+ustar_wr(register ARCHD *arcn)
+#else
+int
+ustar_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_USTAR *hd;
+       register char *pt;
+       char hdblk[sizeof(HD_USTAR)];
+
+       /*
+        * 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);
+               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);
+       }
+       hd = (HD_USTAR *)hdblk;
+       arcn->pad = 0L;
+
+       /*
+        * 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';
+               l_strncpy(hd->prefix, arcn->name, sizeof(hd->prefix) - 1);
+               *pt++ = '/';
+       } else
+               memset(hd->prefix, 0, sizeof(hd->prefix));
+
+       /*
+        * copy the name part. this may be the whole path or the part after
+        * the prefix
+        */
+       l_strncpy(hd->name, pt, sizeof(hd->name) - 1);
+       hd->name[sizeof(hd->name) - 1] = '\0';
+
+       /*
+        * set the fields in the header that are type dependent
+        */
+       switch(arcn->type) {
+       case PAX_DIR:
+               hd->typeflag = DIRTYPE;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+               memset(hd->devmajor, 0, sizeof(hd->devmajor));
+               memset(hd->devminor, 0, sizeof(hd->devminor));
+               if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
+                       goto out;
+               break;
+       case PAX_CHR:
+       case PAX_BLK:
+               if (arcn->type == PAX_CHR)
+                       hd->typeflag = CHRTYPE;
+               else
+                       hd->typeflag = BLKTYPE;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+               if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
+                  sizeof(hd->devmajor), 3) ||
+                  ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
+                  sizeof(hd->devminor), 3) ||
+                  ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
+                       goto out;
+               break;
+       case PAX_FIF:
+               hd->typeflag = FIFOTYPE;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+               memset(hd->devmajor, 0, sizeof(hd->devmajor));
+               memset(hd->devminor, 0, sizeof(hd->devminor));
+               if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
+                       goto out;
+               break;
+       case PAX_SLK:
+       case PAX_HLK:
+       case PAX_HRG:
+               if (arcn->type == PAX_SLK)
+                       hd->typeflag = SYMTYPE;
+               else
+                       hd->typeflag = LNKTYPE;
+               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
+               hd->linkname[sizeof(hd->linkname) - 1] = '\0';
+               memset(hd->devmajor, 0, sizeof(hd->devmajor));
+               memset(hd->devminor, 0, sizeof(hd->devminor));
+               if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
+                       goto out;
+               break;
+       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;
+               memset(hd->linkname, 0, sizeof(hd->linkname));
+               memset(hd->devmajor, 0, sizeof(hd->devmajor));
+               memset(hd->devminor, 0, sizeof(hd->devminor));
+               arcn->pad = TAR_PAD(arcn->sb.st_size);
+#              ifdef NET2_STAT
+               if (ul_oct((u_long)arcn->sb.st_size, hd->size,
+                   sizeof(hd->size), 3)) {
+#              else
+               if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size,
+                   sizeof(hd->size), 3)) {
+#              endif
+                       paxwarn(1,"File is too long for ustar %s",arcn->org_name);
+                       return(1);
+               }
+               break;
+       }
+
+       l_strncpy(hd->magic, TMAGIC, TMAGLEN);
+       l_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_mode, hd->mode, sizeof(hd->mode), 3) ||
+           ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3)  ||
+           ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) ||
+           ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3))
+               goto out;
+       l_strncpy(hd->uname,name_uid(arcn->sb.st_uid, 0),sizeof(hd->uname));
+       l_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), 3))
+               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, "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
+ */
+
+#ifdef __STDC__
+static char *
+name_split(register char *name, register int len)
+#else
+static char *
+name_split(name, len)
+       register char *name;
+       register int len;
+#endif
+{
+       register 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 foward 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;
+       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);
+}
diff --git a/pax/tar.h b/pax/tar.h
new file mode 100644 (file)
index 0000000..79a8ce4
--- /dev/null
+++ b/pax/tar.h
@@ -0,0 +1,151 @@
+/*     $OpenBSD: tar.h,v 1.5 1997/04/16 03:50:25 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. 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.
+ *
+ *     @(#)tar.h       8.2 (Berkeley) 4/18/94
+ */
+
+/*
+ * 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 */
+
+/*
+ * 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;
diff --git a/pax/tty_subs.c b/pax/tty_subs.c
new file mode 100644 (file)
index 0000000..61f4e78
--- /dev/null
@@ -0,0 +1,251 @@
+/*     $OpenBSD: tty_subs.c,v 1.5 1997/07/25 18:58:39 mickey 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. 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[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94";
+#else
+static char rcsid[] __attribute__((__unused__))  = "$OpenBSD: tty_subs.c,v 1.5 1997/07/25 18:58:39 mickey 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"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * 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 termina (if any) for this process. if the
+ *     open fails, future ops that require user input will get an EOF
+ */
+
+#ifdef __STDC__
+int
+tty_init(void)
+#else
+int
+tty_init()
+#endif
+{
+       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.
+ */
+
+#ifdef __STDC__
+void
+tty_prnt(char *fmt, ...)
+#else
+void
+tty_prnt(fmt, va_alist)
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#      ifdef __STDC__
+       va_start(ap, fmt);
+#      else
+       va_start(ap);
+#      endif
+       if (ttyoutf == NULL)
+               return;
+       (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.
+ */
+
+#ifdef __STDC__
+int
+tty_read(char *str, int len)
+#else
+int
+tty_read(str, len)
+       char *str;
+       int len;
+#endif
+{
+       register 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.
+ */
+
+#ifdef __STDC__
+void
+paxwarn(int set, char *fmt, ...)
+#else
+void
+paxwarn(set, fmt, va_alist)
+       int set;
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#      ifdef __STDC__
+       va_start(ap, fmt);
+#      else
+       va_start(ap);
+#      endif
+       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)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.
+ */
+
+#ifdef __STDC__
+void
+syswarn(int set, int errnum, char *fmt, ...)
+#else
+void
+syswarn(set, errnum, fmt, va_alist)
+       int set;
+       int errnum;
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#      ifdef __STDC__
+       va_start(ap, fmt);
+#      else
+       va_start(ap);
+#      endif
+       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)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/rm/Makefile b/rm/Makefile
new file mode 100644 (file)
index 0000000..f557347
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = rm
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = rm.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rm.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/rm/Makefile.postamble b/rm/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/rm/Makefile.preamble b/rm/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/rm/PB.project b/rm/PB.project
new file mode 100644 (file)
index 0000000..9eb1a48
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (rm.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rm.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = rm; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/rm/rm.1 b/rm/rm.1
new file mode 100644 (file)
index 0000000..98bf94f
--- /dev/null
+++ b/rm/rm.1
@@ -0,0 +1,157 @@
+.\"    $NetBSD: rm.1,v 1.9 1997/10/20 08:53:14 enami Exp $
+.\"
+.\" 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
+.\"
+.Dd December 5, 1994
+.Dt RM 1
+.Os
+.Sh NAME
+.Nm rm
+.Nd remove directory entries
+.Sh SYNOPSIS
+.Nm
+.Op Fl f | Fl i
+.Op Fl dPRrW
+.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 flag
+.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 W
+Attempts 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 ``.'' and ``..''.
+.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 SEE ALSO
+.Xr rmdir 1 ,
+.Xr undelete 2 ,
+.Xr unlink 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh BUGS
+The
+.Fl P
+option assumes that the underlying file system is a fixed-block file
+system.
+UFS is a fixed-block file system, LFS is not.
+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.
+.Pp
+Also, historical
+.Bx
+implementations prompted on the standard output,
+not the standard error output.
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/rm/rm.c b/rm/rm.c
new file mode 100644 (file)
index 0000000..38e01b8
--- /dev/null
+++ b/rm/rm.c
@@ -0,0 +1,448 @@
+/*     $NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $  */
+
+/*-
+ * 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
+__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.8 (Berkeley) 4/27/95";
+#else
+__RCSID("$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <locale.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok;
+
+int    check __P((char *, char *, struct stat *));
+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));
+int    main __P((int, char *[]));
+
+#ifdef __APPLE__ /* We're missing this prototype */
+int undelete __P((char *));
+#endif
+
+/*
+ * For the sake of the `-f' flag, check whether an error number indicates the
+ * failure of an operation due to an non-existent file, either per se (ENOENT)
+ * or because its filename argument was illegal (ENAMETOOLONG, ENOTDIR).
+ */
+#define NONEXISTENT(x) \
+    ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)
+
+/*
+ * 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;
+
+       (void)setlocale(LC_ALL, "");
+
+       Pflag = rflag = 0;
+       while ((ch = getopt(argc, argv, "dfiPRrW")) != -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 'W':
+                       Wflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1)
+               usage();
+
+       checkdot(argv);
+
+       if (*argv) {
+               stdin_ok = isatty(STDIN_FILENO);
+
+               if (rflag)
+                       rm_tree(argv);
+               else
+                       rm_file(argv);
+       }
+
+       exit(eval);
+       /* NOTREACHED */
+}
+
+void
+rm_tree(argv)
+       char **argv;
+{
+       FTS *fts;
+       FTSENT *p;
+       int needstat;
+       int flags;
+
+       /*
+        * Remove a file hierarchy.  If forcing removal (-f), or interactive
+        * (-i) or can't ask anyway (stdin_ok), don't stat the file.
+        */
+       needstat = !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,
+               (int (*) __P((const FTSENT **, const FTSENT **)))NULL)))
+               err(1, "%s", "");
+       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));
+                       /* NOTREACHED */
+               case FTS_NS:
+                       /*
+                        * FTS_NS: assume that if can't stat the file, it
+                        * can't be unlinked.
+                        */
+                       if (!needstat)
+                               break;
+                       if (!fflag || !NONEXISTENT(p->fts_errno)) {
+                               warnx("%s: %s",
+                                   p->fts_path, strerror(p->fts_errno));
+                               eval = 1;
+                       }
+                       continue;
+               case FTS_D:
+                       /* Pre-order: give user chance to skip. */
+                       if (!fflag && !check(p->fts_path, p->fts_accpath,
+                           p->fts_statp)) {
+                               (void)fts_set(fts, p, FTS_SKIP);
+                               p->fts_number = SKIPPED;
+                       }
+                       continue;
+               case FTS_DP:
+                       /* Post-order: see if user skipped. */
+                       if (p->fts_number == SKIPPED)
+                               continue;
+                       break;
+               default:
+                       if (!fflag &&
+                           !check(p->fts_path, p->fts_accpath, p->fts_statp))
+                               continue;
+               }
+
+               /*
+                * 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 (!rmdir(p->fts_accpath) ||
+                           (fflag && errno == ENOENT))
+                               continue;
+                       break;
+
+               case FTS_W:
+                       if (!undelete(p->fts_accpath) ||
+                           (fflag && errno == ENOENT))
+                               continue;
+                       break;
+
+               default:
+                       if (Pflag)
+                               rm_overwrite(p->fts_accpath, NULL);
+                       if (!unlink(p->fts_accpath) ||
+                           (fflag && NONEXISTENT(errno)))
+                               continue;
+               }
+               warn("%s", p->fts_path);
+               eval = 1;
+       }
+       if (errno)
+               err(1, "fts_read");
+}
+
+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 || !NONEXISTENT(errno)) {
+                                       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;
+               if (S_ISWHT(sb.st_mode))
+                       rval = undelete(f);
+               else if (S_ISDIR(sb.st_mode))
+                       rval = rmdir(f);
+               else {
+                       if (Pflag)
+                               rm_overwrite(f, &sb);
+                       rval = unlink(f);
+               }
+               if (rval && (!fflag || !NONEXISTENT(errno))) {
+                       warn("%s", f);
+                       eval = 1;
+               }
+       }
+}
+
+/*
+ * 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;
+       off_t len;
+       int fd, wlen;
+       char buf[8 * 1024];
+
+       fd = -1;
+       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;
+
+#define        PASS(byte) {                                                    \
+       memset(buf, byte, sizeof(buf));                                 \
+       for (len = sbp->st_size; len > 0; len -= wlen) {                \
+               wlen = len < sizeof(buf) ? len : sizeof(buf);           \
+               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))
+               return;
+
+err:   eval = 1;
+       warn("%s", file);
+}
+
+
+int
+check(path, name, sp)
+       char *path, *name;
+       struct stat *sp;
+{
+       int ch, first;
+       char modep[15];
+
+       /* 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))
+                       return (1);
+               strmode(sp->st_mode, modep);
+               (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
+                   modep + 1, modep[9] == ' ' ? "" : " ",
+                   user_from_uid(sp->st_uid, 0),
+                   group_from_gid(sp->st_gid, 0), path);
+       }
+       (void)fflush(stderr);
+
+       first = ch = getchar();
+       while (ch != '\n' && ch != EOF)
+               ch = getchar();
+       return (first == 'y' || first == 'Y');
+}
+
+/*
+ * POSIX.2 requires that if "." or ".." are specified as the basename
+ * portion of an operand, a diagnostic message be written to standard
+ * error and nothing more be done with such operands.
+ *
+ * Since POSIX.2 defines basename as the final portion of a path after
+ * trailing slashes have been removed, we'll remove them here.
+ */
+#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;) {
+               /* strip trailing slashes */
+               p = strrchr (*t, '\0');
+               while (--p > *t && *p == '/')
+                       *p = '\0';
+
+               /* extract basename */
+               if ((p = strrchr(*t, '/')) != NULL)
+                       ++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, "usage: rm [-dfiPRrW] file ...\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/rmdir/Makefile b/rmdir/Makefile
new file mode 100644 (file)
index 0000000..9eb48aa
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = rmdir
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = rmdir.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rmdir.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/rmdir/Makefile.postamble b/rmdir/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/rmdir/Makefile.preamble b/rmdir/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/rmdir/PB.project b/rmdir/PB.project
new file mode 100644 (file)
index 0000000..8475318
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (rmdir.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rmdir.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = rmdir; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/rmdir/rmdir.1 b/rmdir/rmdir.1
new file mode 100644 (file)
index 0000000..2d67c7a
--- /dev/null
@@ -0,0 +1,94 @@
+.\"    $NetBSD: rmdir.1,v 1.11 1997/10/20 08:53:22 enami 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.
+.\"
+.\"    @(#)rmdir.1     8.1 (Berkeley) 5/31/93
+.\"
+.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 rmdir 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 Ds
+.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
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/rmdir/rmdir.c b/rmdir/rmdir.c
new file mode 100644 (file)
index 0000000..8f61e93
--- /dev/null
@@ -0,0 +1,136 @@
+/*     $NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp $       */
+
+/*-
+ * 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
+__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";
+#else
+__RCSID("$NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp $");
+#endif
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <unistd.h>
+
+int rm_path __P((char *));
+void usage __P((void));
+int main __P((int, char *[]));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch, errors;
+       int pflag;
+
+       (void)setlocale(LC_ALL, "");
+
+       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++) {
+               char *p;
+
+               /* Delete trailing slashes, per POSIX. */
+               p = *argv + strlen(*argv);
+               while (--p > *argv && *p == '/')
+                       ;
+               *++p = '\0';
+
+               if (rmdir(*argv) < 0) {
+                       warn("%s", *argv);
+                       errors = 1;
+               } else if (pflag)
+                       errors |= rm_path(*argv);
+       }
+
+       exit(errors);
+       /* NOTREACHED */
+}
+
+int
+rm_path(path)
+       char *path;
+{
+       char *p;
+
+       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)fprintf(stderr, "usage: rmdir [-p] directory ...\n");
+       exit(1);
+       /* NOTREACHED */
+}
diff --git a/rmt/Makefile b/rmt/Makefile
new file mode 100644 (file)
index 0000000..805eda7
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = rmt
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = rmt.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rmt.8
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/sbin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/rmt/Makefile.postamble b/rmt/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/rmt/Makefile.preamble b/rmt/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/rmt/PB.project b/rmt/PB.project
new file mode 100644 (file)
index 0000000..08efaea
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (rmt.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rmt.8); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/sbin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = rmt; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/rmt/rmt.8 b/rmt/rmt.8
new file mode 100644 (file)
index 0000000..77a9074
--- /dev/null
+++ b/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/rmt/rmt.c b/rmt/rmt.c
new file mode 100644 (file)
index 0000000..bfb4d37
--- /dev/null
+++ b/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/shar/Makefile b/shar/Makefile
new file mode 100644 (file)
index 0000000..b7a027d
--- /dev/null
@@ -0,0 +1,49 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = shar
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+OTHERLINKED = shar.sh
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble shar.1
+
+OTHERLINKEDOFILES = shar.o
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/shar/Makefile.postamble b/shar/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/shar/Makefile.preamble b/shar/Makefile.preamble
new file mode 100644 (file)
index 0000000..eaa3b46
--- /dev/null
@@ -0,0 +1,3 @@
+SHELLTOOL = shar.sh
+
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/shar/PB.project b/shar/PB.project
new file mode 100644 (file)
index 0000000..ed6df54
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (shar.sh); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, shar.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = shar; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/shar/shar.1 b/shar/shar.1
new file mode 100644 (file)
index 0000000..e6b0f15
--- /dev/null
@@ -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 uuencode 1 ,
+.Xr tar 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/shar/shar.sh b/shar/shar.sh
new file mode 100644 (file)
index 0000000..7048b44
--- /dev/null
@@ -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/tcopy/Makefile b/tcopy/Makefile
new file mode 100644 (file)
index 0000000..0d2d6b1
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = tcopy
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = tcopy.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble tcopy.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/tcopy/Makefile.postamble b/tcopy/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/tcopy/Makefile.preamble b/tcopy/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/tcopy/PB.project b/tcopy/PB.project
new file mode 100644 (file)
index 0000000..d628535
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (tcopy.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, tcopy.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = tcopy; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/tcopy/tcopy.1 b/tcopy/tcopy.1
new file mode 100644 (file)
index 0000000..ed27627
--- /dev/null
@@ -0,0 +1,91 @@
+.\"    $NetBSD: tcopy.1,v 1.5 1997/10/20 00:35:14 lukem Exp $
+.\"
+.\" Copyright (c) 1985, 1990, 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.
+.\"
+.\"     @(#)tcopy.1    8.2 (Berkeley) 4/17/94
+.\"
+.Dd April 17, 1994
+.Dt TCOPY 1
+.Os BSD 4.3
+.Sh NAME
+.Nm tcopy
+.Nd copy and/or verify mag tapes
+.Sh SYNOPSIS
+.Nm
+.Op Fl cvx
+.Op Fl s Ar maxblk
+.Oo Ar src Op Ar dest
+.Oc
+.Sh DESCRIPTION
+.Nm
+is designed to copy magnetic tapes.  The only assumption made
+about the tape is that there are two tape marks at the end.
+.Nm
+with only a source tape
+.Pf ( Ar /dev/rst0
+by default) specified will print
+information about the sizes of records and tape files.  If a destination
+is specified a copy will be made of the source tape.  The blocking on the
+destination tape will be identical to that used on the source tape.  Copying
+a tape will yield the same output as if just printing the sizes.
+.Pp
+Options:
+.Bl -tag -width s_maxblk
+.It Fl c
+Copy
+.Ar src
+to
+.Ar dest
+and then verify that the two tapes are identical.
+.It Fl s Ar maxblk
+Specify a maximum block size,
+.Ar maxblk .
+.It Fl v
+Given the two tapes,
+.ar src
+and
+.Ar dest
+verify that they are identical.
+.It Fl x
+Output all informational messages to the standard error.
+This option is useful when
+.Ar dest
+is
+.Pa /dev/stdout .
+.El
+.Sh SEE ALSO
+.Xr mtio 4
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/tcopy/tcopy.c b/tcopy/tcopy.c
new file mode 100644 (file)
index 0000000..297ad01
--- /dev/null
@@ -0,0 +1,339 @@
+/*     $NetBSD: tcopy.c,v 1.8 1998/08/25 20:59:41 ross Exp $   */
+
+/*
+ * Copyright (c) 1985, 1987, 1993, 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
+__COPYRIGHT("@(#) Copyright (c) 1985, 1987, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)tcopy.c    8.3 (Berkeley) 1/23/95";
+#endif
+__RCSID("$NetBSD: tcopy.c,v 1.8 1998/08/25 20:59:41 ross Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define        MAXREC  (64 * 1024)
+#define        NOCOUNT (-2)
+
+int    filen, guesslen, maxblk = MAXREC;
+long   lastrec, record;
+off_t  size, tsize;
+FILE   *msg = stdout;
+
+void   *getspace __P((int));
+void    intr __P((int));
+int     main __P((int, char **));
+void    usage __P((void));
+void    verify __P((int, int, char *));
+void    writeop __P((int, int));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch, needeof, nw, inp, outp;
+       ssize_t lastnread, nread;
+       enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
+       sig_t oldsig;
+       char *buff, *inf;
+
+       outp = 0;
+       inf = NULL;
+       guesslen = 1;
+       while ((ch = getopt(argc, argv, "cs:vx")) != -1)
+               switch((char)ch) {
+               case 'c':
+                       op = COPYVERIFY;
+                       break;
+               case 's':
+                       maxblk = atoi(optarg);
+                       if (maxblk <= 0) {
+                               warnx("illegal block size");
+                               usage();
+                       }
+                       guesslen = 0;
+                       break;
+               case 'v':
+                       op = VERIFY;
+                       break;
+               case 'x':
+                       msg = stderr;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       switch(argc) {
+       case 0:
+               if (op != READ)
+                       usage();
+               inf = _PATH_DEFTAPE;
+               break;
+       case 1:
+               if (op != READ)
+                       usage();
+               inf = argv[0];
+               break;
+       case 2:
+               if (op == READ)
+                       op = COPY;
+               inf = argv[0];
+               if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
+                   op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) {
+                       err(3, argv[1]);
+               }
+               break;
+       default:
+               usage();
+       }
+
+       if ((inp = open(inf, O_RDONLY, 0)) < 0)
+               err(1, inf);
+
+       buff = getspace(maxblk);
+
+       if (op == VERIFY) {
+               verify(inp, outp, buff);
+               exit(0);
+       }
+
+       if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+               (void) signal(SIGINT, intr);
+
+       needeof = 0;
+       for (lastnread = NOCOUNT;;) {
+               if ((nread = read(inp, buff, maxblk)) == -1) {
+                       while (errno == EINVAL && (maxblk -= 1024)) {
+                               nread = read(inp, buff, maxblk);
+                               if (nread >= 0)
+                                       goto r1;
+                       }
+                       err(1, "read error, file %d, record %ld",
+                           filen, record);
+               } else if (nread != lastnread) {
+                       if (lastnread != 0 && lastnread != NOCOUNT) {
+                               if (lastrec == 0 && nread == 0)
+                                       fprintf(msg, "%ld records\n", record);
+                               else if (record - lastrec > 1)
+                                       fprintf(msg, "records %ld to %ld\n",
+                                           lastrec, record);
+                               else
+                                       fprintf(msg, "record %ld\n", lastrec);
+                       }
+                       if (nread != 0)
+                               fprintf(msg, "file %d: block size %ld: ",
+                                   filen, (long)nread);
+                       (void) fflush(stdout);
+                       lastrec = record;
+               }
+r1:            guesslen = 0;
+               if (nread > 0) {
+                       if (op == COPY || op == COPYVERIFY) {
+                               if (needeof) {
+                                       writeop(outp, MTWEOF);
+                                       needeof = 0;
+                               }
+                               nw = write(outp, buff, nread);
+                               if (nw != nread) {
+                                   int error = errno;
+                                   fprintf(stderr,
+                                       "write error, file %d, record %ld: ",
+                                       filen, record);
+                                   if (nw == -1)
+                                       fprintf(stderr,
+                                               ": %s", strerror(error));
+                                   else
+                                       fprintf(stderr,
+                                           "write (%d) != read (%ld)\n",
+                                           nw, (long)nread);
+                                   fprintf(stderr, "copy aborted\n");
+                                   exit(5);
+                               }
+                       }
+                       size += nread;
+                       record++;
+               } else {
+                       if (lastnread <= 0 && lastnread != NOCOUNT) {
+                               fprintf(msg, "eot\n");
+                               break;
+                       }
+                       fprintf(msg,
+                           "file %d: eof after %ld records: %qd bytes\n",
+                           filen, record, (long long)size);
+                       needeof = 1;
+                       filen++;
+                       tsize += size;
+                       size = record = lastrec = 0;
+                       lastnread = 0;
+               }
+               lastnread = nread;
+       }
+       fprintf(msg, "total length: %qd bytes\n", (long long)tsize);
+       (void)signal(SIGINT, oldsig);
+       if (op == COPY || op == COPYVERIFY) {
+               writeop(outp, MTWEOF);
+               writeop(outp, MTWEOF);
+               if (op == COPYVERIFY) {
+                       writeop(outp, MTREW);
+                       writeop(inp, MTREW);
+                       verify(inp, outp, buff);
+               }
+       }
+       exit(0);
+}
+
+void
+verify(inp, outp, outb)
+       int inp, outp;
+       char *outb;
+{
+       int eot, inmaxblk, inn, outmaxblk, outn;
+       char *inb;
+
+       inb = getspace(maxblk);
+       inmaxblk = outmaxblk = maxblk;
+       for (eot = 0;; guesslen = 0) {
+               if ((inn = read(inp, inb, inmaxblk)) == -1) {
+                       if (guesslen)
+                               while (errno == EINVAL && (inmaxblk -= 1024)) {
+                                       inn = read(inp, inb, inmaxblk);
+                                       if (inn >= 0)
+                                               goto r1;
+                               }
+                       warn("read error");
+                       break;
+               }
+r1:            if ((outn = read(outp, outb, outmaxblk)) == -1) {
+                       if (guesslen)
+                               while (errno == EINVAL && (outmaxblk -= 1024)) {
+                                       outn = read(outp, outb, outmaxblk);
+                                       if (outn >= 0)
+                                               goto r2;
+                               }
+                       warn("read error");
+                       break;
+               }
+r2:            if (inn != outn) {
+                       fprintf(msg,
+                           "%s: tapes have different block sizes; %d != %d.\n",
+                           "tcopy", inn, outn);
+                       break;
+               }
+               if (!inn) {
+                       if (eot++) {
+                               fprintf(msg, "%s: tapes are identical.\n",
+                                       "tcopy");
+                               return;
+                       }
+               } else {
+                       if (memcmp(inb, outb, inn)) {
+                               fprintf(msg,
+                                   "%s: tapes have different data.\n",
+                                       "tcopy");
+                               break;
+                       }
+                       eot = 0;
+               }
+       }
+       exit(1);
+}
+
+void
+intr(signo)
+       int signo;
+{
+       if (record) {
+               if (record - lastrec > 1)
+                       fprintf(msg, "records %ld to %ld\n", lastrec, record);
+               else
+                       fprintf(msg, "record %ld\n", lastrec);
+       }
+       fprintf(msg, "interrupt at file %d: record %ld\n", filen, record);
+       fprintf(msg, "total length: %qd bytes\n", (long long)(tsize + size));
+       exit(1);
+}
+
+void *
+getspace(blk)
+       int blk;
+{
+       void *bp;
+
+       if ((bp = malloc((size_t)blk)) == NULL)
+               errx(11, "no memory");
+
+       return (bp);
+}
+
+void
+writeop(fd, type)
+       int fd, type;
+{
+       struct mtop op;
+
+       op.mt_op = type;
+       op.mt_count = (daddr_t)1;
+       if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
+               err(6, "tape op");
+}
+
+void
+usage()
+{
+
+       fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n");
+       exit(1);
+}
diff --git a/touch/Makefile b/touch/Makefile
new file mode 100644 (file)
index 0000000..774770c
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Generated by the NeXT Project Builder.
+#
+# NOTE: Do NOT change this file -- Project Builder maintains it.
+#
+# Put all of your customizations in files called Makefile.preamble
+# and Makefile.postamble (both optional), and Makefile will include them.
+#
+
+NAME = touch
+
+PROJECTVERSION = 2.8
+PROJECT_TYPE = Tool
+
+CFILES = touch.c
+
+OTHERSRCS = Makefile Makefile.preamble Makefile.postamble touch.1
+
+
+MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles
+CODE_GEN_STYLE = DYNAMIC
+MAKEFILE = tool.make
+NEXTSTEP_INSTALLDIR = /usr/bin
+LIBS = 
+DEBUG_LIBS = $(LIBS)
+PROF_LIBS = $(LIBS)
+
+
+
+
+NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build
+
+NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc
+WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc
+PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc
+NEXTSTEP_JAVA_COMPILER = /usr/bin/javac
+WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe
+PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac
+
+include $(MAKEFILEDIR)/platform.make
+
+-include Makefile.preamble
+
+include $(MAKEFILEDIR)/$(MAKEFILE)
+
+-include Makefile.postamble
+
+-include Makefile.dependencies
diff --git a/touch/Makefile.postamble b/touch/Makefile.postamble
new file mode 100644 (file)
index 0000000..013b558
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common
diff --git a/touch/Makefile.preamble b/touch/Makefile.preamble
new file mode 100644 (file)
index 0000000..9e10e90
--- /dev/null
@@ -0,0 +1 @@
+include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common
diff --git a/touch/PB.project b/touch/PB.project
new file mode 100644 (file)
index 0000000..0f39cfd
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    DYNAMIC_CODE_GEN = YES; 
+    FILESTABLE = {
+        FRAMEWORKS = (); 
+        OTHER_LINKED = (touch.c); 
+        OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, touch.1); 
+    }; 
+    LANGUAGE = English; 
+    LOCALIZABLE_FILES = {}; 
+    MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; 
+    NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; 
+    NEXTSTEP_BUILDTOOL = /bin/gnumake; 
+    NEXTSTEP_INSTALLDIR = /usr/bin; 
+    NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; 
+    NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; 
+    PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; 
+    PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; 
+    PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; 
+    PROJECTNAME = touch; 
+    PROJECTTYPE = Tool; 
+    PROJECTVERSION = 2.8; 
+    WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; 
+    WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; 
+    WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; 
+}
diff --git a/touch/touch.1 b/touch/touch.1
new file mode 100644 (file)
index 0000000..d2bb977
--- /dev/null
@@ -0,0 +1,177 @@
+.\"    $NetBSD: touch.1,v 1.13 1998/01/20 21:18:25 mycroft Exp $
+.\"
+.\" 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
+.\"
+.Dd April 28, 1995
+.Dt TOUCH 1
+.Os
+.Sh NAME
+.Nm touch
+.Nd change file access and modification times
+.Sh SYNOPSIS
+.Nm
+.Op Fl acfhm
+.Op Fl r Ar file
+.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS]
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility sets the modification and access times of files to the
+current time of day.
+If the file doesn't exist, it is created with default permissions.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.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
+.Ar file
+is a symbolic link, access and/or modification time of the link is changed.
+This option implies
+.Fl c .
+.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.
+The argument should be in 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 1 to 12.
+.It Ar DD
+The day of the month, from 1 to 31.
+.It Ar hh
+The hour of the day, from 0 to 23.
+.It Ar mm
+The minute of the hour, from 0 to 59.
+.It Ar SS
+The second of the minute, from 0 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
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr utimes 2
+.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 69 to 99, the year is set to 1969 to 1999,
+otherwise, the year is set in the 21st century.
+.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 .
+.Sh BUGS
+A symbolic link can't be a reference file of access and/or modification time.
diff --git a/touch/touch.c b/touch/touch.c
new file mode 100644 (file)
index 0000000..71499fa
--- /dev/null
@@ -0,0 +1,389 @@
+/*     $NetBSD: touch.c,v 1.22 1998/08/25 20:59:42 ross Exp $  */
+
+/*
+ * 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>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1993\n\
+       The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)touch.c    8.2 (Berkeley) 4/28/95";
+#endif
+__RCSID("$NetBSD: touch.c,v 1.22 1998/08/25 20:59:42 ross Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+int    main __P((int, char **));
+int    rw __P((char *, struct stat *, int));
+void   stime_arg1 __P((char *, struct timeval *));
+void   stime_arg2 __P((char *, int, struct timeval *));
+void   stime_file __P((char *, struct timeval *));
+void   usage __P((void));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat sb;
+       struct timeval tv[2];
+       int aflag, cflag, fflag, hflag, mflag, ch, fd, len, rval, timeset;
+       char *p;
+       int (*change_file_times) __P((const char *, const struct timeval *));
+       int (*get_file_status) __P((const char *, struct stat *));
+
+       setlocale(LC_ALL, "");
+
+       aflag = cflag = fflag = hflag = mflag = timeset = 0;
+       if (gettimeofday(&tv[0], NULL))
+               err(1, "gettimeofday");
+
+       while ((ch = getopt(argc, argv, "acfhmr:t:")) != -1)
+               switch(ch) {
+               case 'a':
+                       aflag = 1;
+                       break;
+               case 'c':
+                       cflag = 1;
+                       break;
+               case 'f':
+                       fflag = 1;
+                       break;
+#ifndef __APPLE__
+               case 'h':
+                       hflag = 1;
+                       break;
+#endif
+               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();
+               }
+       argc -= optind;
+       argv += optind;
+
+       /* Default is both -a and -m. */
+       if (aflag == 0 && mflag == 0)
+               aflag = mflag = 1;
+
+#ifndef __APPLE__
+       if (hflag) {
+               cflag = 1;              /* Don't create new file */
+               change_file_times = lutimes;
+               get_file_status = lstat;
+       } else {
+#endif
+               change_file_times = utimes;
+               get_file_status = stat;
+#ifndef __APPLE__
+       }
+#endif
+
+       /*
+        * 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.
+        */
+       if (!timeset && argc > 1) {
+               (void)strtol(argv[0], &p, 10);
+               len = p - argv[0];
+               if (*p == '\0' && (len == 8 || len == 10)) {
+                       timeset = 1;
+                       stime_arg2(*argv++, len == 10, tv);
+               }
+       }
+
+       /* Otherwise use the current time of day. */
+       if (!timeset)
+               tv[1] = tv[0];
+
+       if (*argv == NULL)
+               usage();
+
+       for (rval = 0; *argv; ++argv) {
+               /* See if the file exists. */
+               if ((*get_file_status)(*argv, &sb)) {
+                       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);
+
+               /* Try utimes(2). */
+               if (!(*change_file_times)(*argv, tv))
+                       continue;
+
+               /* If the user specified a time, nothing else we can do. */
+               if (timeset) {
+                       rval = 1;
+                       warn("%s", *argv);
+               }
+
+               /*
+                * 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 (!(*change_file_times)(*argv, NULL))
+                       continue;
+
+               /* Try reading/writing. */
+               if (!S_ISLNK(sb.st_mode) && rw(*argv, &sb, fflag))
+                       rval = 1;
+       }
+       exit(rval);
+}
+
+#define        ATOI2(s)        ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
+
+void
+stime_arg1(arg, tvp)
+       char *arg;
+       struct timeval *tvp;
+{
+       struct tm *t;
+       time_t tmptime;
+       int yearset;
+       char *p;
+                                       /* Start with the current time. */
+       tmptime = tvp[0].tv_sec;
+       if ((t = localtime(&tmptime)) == 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) * 100 - TM_YEAR_BASE;
+               yearset = 1;
+               /* FALLTHOUGH */
+       case 10:                        /* YYMMDDhhmm */
+               if (yearset) {
+                       t->tm_year += ATOI2(arg);
+               } else {
+                       yearset = ATOI2(arg);
+                       if (yearset < 69)
+                               t->tm_year = yearset + 2000 - TM_YEAR_BASE;
+                       else
+                               t->tm_year = yearset + 1900 - TM_YEAR_BASE;
+               }
+               /* FALLTHROUGH */
+       case 8:                         /* MMDDhhmm */
+               t->tm_mon = ATOI2(arg);
+               --t->tm_mon;            /* Convert from 01-12 to 00-11 */
+               /* FALLTHROUGH */
+       case 6:
+               t->tm_mday = ATOI2(arg);
+               /* FALLTHROUGH */
+       case 4:
+               t->tm_hour = ATOI2(arg);
+               /* FALLTHROUGH */
+       case 2:
+               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(arg, year, tvp)
+       char *arg;
+       int year;
+       struct timeval *tvp;
+{
+       struct tm *t;
+       time_t tmptime;
+                                       /* Start with the current time. */
+       tmptime = tvp[0].tv_sec;
+       if ((t = localtime(&tmptime)) == 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) {
+               year = ATOI2(arg);
+               if (year < 69)
+                       t->tm_year = year + 2000 - TM_YEAR_BASE;
+               else
+                       t->tm_year = year + 1900 - TM_YEAR_BASE;
+       }
+
+       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;
+}
+
+void
+stime_file(fname, tvp)
+       char *fname;
+       struct timeval *tvp;
+{
+       struct stat sb;
+
+       if (stat(fname, &sb))
+               err(1, "%s", fname);
+       TIMESPEC_TO_TIMEVAL(&tvp[0], &sb.st_atimespec);
+       TIMESPEC_TO_TIMEVAL(&tvp[1], &sb.st_mtimespec);
+}
+
+int
+rw(fname, sbp, force)
+       char *fname;
+       struct stat *sbp;
+       int force;
+{
+       int fd, needed_chmod, rval;
+       u_char byte;
+
+       /* Try regular files and directories. */
+       if (!S_ISREG(sbp->st_mode) && !S_ISDIR(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);
+}
+
+__dead void
+usage()
+{
+#ifndef __APPLE__
+       (void)fprintf(stderr,
+           "usage: touch [-acfhm] [-r file] [-t time] file ...\n");
+#else
+       (void)fprintf(stderr,
+           "usage: touch [-acfm] [-r file] [-t time] file ...\n");
+#endif
+       exit(1);
+}