]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD cp command.
authorZachary Storer <zacts.3.14159@gmail.com>
Sun, 27 Jul 2014 12:19:54 +0000 (06:19 -0600)
committerLionel Sambuc <lionel@minix3.org>
Mon, 28 Jul 2014 15:06:34 +0000 (17:06 +0200)
Change-Id: I61943a2ef6d5945b9e10e520e5ebf85e99ac668a

13 files changed:
bin/Makefile
bin/cp/Makefile [new file with mode: 0644]
bin/cp/cp.1 [new file with mode: 0644]
bin/cp/cp.c [new file with mode: 0644]
bin/cp/extern.h [new file with mode: 0644]
bin/cp/utils.c [new file with mode: 0644]
commands/Makefile
commands/cp/Makefile [deleted file]
commands/cp/cp.c [deleted file]
commands/setup/setup.sh
man/man1/Makefile
man/man1/cp.1 [deleted file]
man/man8/usage.8

index dc696df055453a86eb5b7d2755b5f22a4d538b34..7066ea3b758750135dfdc66dec0a30a7948c90cd 100644 (file)
@@ -1,7 +1,7 @@
 #      $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
 #      @(#)Makefile    8.1 (Berkeley) 5/31/93
 
-SUBDIR=        cat chmod date df echo ed expr hostname \
+SUBDIR=        cat chmod cp date df echo ed expr hostname \
        kill ksh ln ls mkdir mv pax pwd rm rmdir \
        sleep stty sync test
 
diff --git a/bin/cp/Makefile b/bin/cp/Makefile
new file mode 100644 (file)
index 0000000..4760d2c
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile,v 1.9 1997/07/20 22:36:37 christos Exp $
+#      @(#)Makefile    8.1 (Berkeley) 5/31/93
+
+PROG=  cp
+SRCS=  cp.c utils.c
+
+.include <bsd.prog.mk>
diff --git a/bin/cp/cp.1 b/bin/cp/cp.1
new file mode 100644 (file)
index 0000000..f71e457
--- /dev/null
@@ -0,0 +1,254 @@
+.\"    $NetBSD: cp.1,v 1.42 2012/03/25 22:37:08 wiz 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. 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 March 25, 2012
+.Dt CP 1
+.Os
+.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 alNpv
+.Ar source_file target_file
+.Nm cp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f | i
+.Op Fl alNpv
+.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 a
+Archive mode.
+Same as
+.Fl RpP .
+.It Fl f
+For each 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 H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl i
+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 L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl l
+Create hard links to regular files in a hierarchy instead of copying.
+.It Fl N
+When used with
+.Fl p ,
+don't copy file flags.
+.It Fl P
+No symbolic links are followed.
+This is the default.
+.It Fl p
+Causes
+.Nm
+to preserve in the copy as many of the modification time, access time,
+file flags, file mode, user ID, group ID, and extended attributes,
+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.
+.Pp
+Extended attributes from all accessible namespaces are copied;
+others are ignored.
+If an error occurs during this copy, a message is displayed and
+.Nm
+skips the other extended attributes for that file.
+.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
+followed, 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's umask.
+.Pp
+Note that
+.Nm
+copies hard linked files as separate files.
+If you need to preserve hard links, consider using a utility like
+.Xr pax 1
+instead.
+.It Fl v
+Causes
+.Nm
+to be verbose, showing files as they are copied.
+.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
+.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), as well as the
+.Fl P
+flag cause symbolic links to be followed as described above.
+The
+.Fl H
+and
+.Fl L
+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.
+.Sh EXIT STATUS
+.Ex -std cp
+.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 pax 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.
+.Pp
+The
+.Fl a
+and
+.Fl l
+flags are non-standard extensions.
+They are intended to be compatible with the same options which
+other implementations, namely GNU coreutils and
+.Fx ,
+of this utility have.
+.Pp
+The
+.Fl v
+option is an extension to
+.St -p1003.2 .
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
new file mode 100644 (file)
index 0000000..4bbe1b7
--- /dev/null
@@ -0,0 +1,548 @@
+/* $NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos 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. 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\
+ The Regents of the University of California.  All rights reserved.");
+#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.58 2012/01/04 15:58:37 christos 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 <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.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';                                   \
+}
+
+static char empty[] = "";
+PATH_T to = { .p_end = to.p_path, .target_end = empty  };
+
+uid_t myuid;
+int Hflag, Lflag, Rflag, Pflag, fflag, iflag, lflag, pflag, rflag, vflag, Nflag;
+mode_t myumask;
+sig_atomic_t pinfo;
+
+enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
+
+static int copy(char *[], enum op, int);
+
+static void
+progress(int sig __unused)
+{
+
+       pinfo++;
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct stat to_stat, tmp_stat;
+       enum op type;
+       int ch, fts_options, r, have_trailing_slash;
+       char *target, **src;
+
+       setprogname(argv[0]);
+       (void)setlocale(LC_ALL, "");
+
+       Hflag = Lflag = Pflag = Rflag = 0;
+       while ((ch = getopt(argc, argv, "HLNPRfailprv")) != -1) 
+               switch (ch) {
+               case 'H':
+                       Hflag = 1;
+                       Lflag = Pflag = 0;
+                       break;
+               case 'L':
+                       Lflag = 1;
+                       Hflag = Pflag = 0;
+                       break;
+               case 'N':
+                       Nflag = 1;
+                       break;
+               case 'P':
+                       Pflag = 1;
+                       Hflag = Lflag = 0;
+                       break;
+               case 'R':
+                       Rflag = 1;
+                       break;
+               case 'a':
+                       Pflag = 1;
+                       pflag = 1;
+                       Rflag = 1;
+                       Hflag = Lflag = 0;
+                       break;
+               case 'f':
+                       fflag = 1;
+                       iflag = 0;
+                       break;
+               case 'i':
+                       iflag = isatty(fileno(stdin));
+                       fflag = 0;
+                       break;
+               case 'l':
+                       lflag = 1;
+                       break;
+               case 'p':
+                       pflag = 1;
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               case 'v':
+                       vflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 2)
+               usage();
+
+       fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
+       if (rflag) {
+               if (Rflag) {
+                       errx(EXIT_FAILURE,
+                   "the -R and -r options may not be specified together.");
+                       /* NOTREACHED */
+               }
+               if (Hflag || Lflag || Pflag) {
+                       errx(EXIT_FAILURE,
+       "the -H, -L, and -P options may not be specified with the -r option.");
+                       /* NOTREACHED */
+               }
+               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 if (!Pflag) {
+               fts_options &= ~FTS_PHYSICAL;
+               fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
+       }
+
+       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 (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
+               errx(EXIT_FAILURE, "%s: name too long", target);
+       to.p_end = to.p_path + strlen(to.p_path);
+       have_trailing_slash = (to.p_end[-1] == '/');
+       if (have_trailing_slash)
+               STRIP_TRAILING_SLASH(to);
+       to.target_end = to.p_end;
+
+       /* Set end of argument list for fts(3). */
+       argv[argc] = NULL;     
+       
+       (void)signal(SIGINFO, progress);
+       
+       /*
+        * 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".
+        */
+       if (Pflag)
+               r = lstat(to.p_path, &to_stat);
+       else
+               r = stat(to.p_path, &to_stat);
+       if (r == -1 && errno != ENOENT) {
+               err(EXIT_FAILURE, "%s", to.p_path);
+               /* NOTREACHED */
+       }
+       if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
+               /*
+                * Case (1).  Target is not a directory.
+                */ 
+               if (argc > 1)
+                       usage();
+               /*
+                * 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(EXIT_FAILURE, "%s", *argv);
+                               /* NOTREACHED */
+                       }
+                       
+                       if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
+                               type = DIR_TO_DNE;
+                       else
+                               type = FILE_TO_FILE;
+               } else
+                       type = FILE_TO_FILE;
+
+               if (have_trailing_slash && type == FILE_TO_FILE) {
+                       if (r == -1)
+                               errx(1, "directory %s does not exist",
+                                    to.p_path);
+                       else
+                               errx(1, "%s is not a directory", to.p_path);
+               }
+       } else {
+               /*
+                * Case (2).  Target is a directory.
+                */
+               type = FILE_TO_DIR;
+       }
+
+       /*
+        * make "cp -rp src/ dst" behave like "cp -rp src dst" not
+        * like "cp -rp src/. dst"
+        */
+       for (src = argv; *src; src++) {
+               size_t len = strlen(*src);
+               while (len-- > 1 && (*src)[len] == '/')
+                       (*src)[len] = '\0';
+       }
+
+       exit(copy(argv, type, fts_options));
+       /* NOTREACHED */
+}
+
+static int dnestack[MAXPATHLEN]; /* unlikely we'll have more nested dirs */
+static ssize_t dnesp;
+static void
+pushdne(int dne)
+{
+
+       dnestack[dnesp++] = dne;
+       assert(dnesp < MAXPATHLEN);
+}
+
+static int
+popdne(void)
+{
+       int rv;
+
+       rv = dnestack[--dnesp];
+       assert(dnesp >= 0);
+       return rv;
+}
+
+static int
+copy(char *argv[], enum op type, int fts_options)
+{
+       struct stat to_stat;
+       FTS *ftsp;
+       FTSENT *curr;
+       int base, dne, sval;
+       int this_failed, any_failed;
+       size_t nlen;
+       char *p, *target_mid;
+
+       base = 0;       /* XXX gcc -Wuninitialized (see comment below) */
+
+       if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
+               err(EXIT_FAILURE, "%s", argv[0]);
+               /* NOTREACHED */
+       for (any_failed = 0; (curr = fts_read(ftsp)) != NULL;) {
+               this_failed = 0;
+               switch (curr->fts_info) {
+               case FTS_NS:
+               case FTS_DNR:
+               case FTS_ERR:
+                       warnx("%s: %s", curr->fts_path,
+                                       strerror(curr->fts_errno));
+                       this_failed = any_failed = 1;
+                       continue;
+               case FTS_DC:                    /* Warn, continue. */
+                       warnx("%s: directory causes a cycle", curr->fts_path);
+                       this_failed = any_failed = 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);
+                               this_failed = any_failed = 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;
+                       target_mid = to.target_end;
+                       if (*p != '/' && target_mid[-1] != '/')
+                               *target_mid++ = '/';
+                       *target_mid = 0;
+
+                       if (target_mid - to.p_path + nlen >= PATH_MAX) {
+                               warnx("%s%s: name too long (not copied)",
+                                   to.p_path, p);
+                               this_failed = any_failed = 1;
+                               continue;
+                       }
+                       (void)strncat(target_mid, p, nlen);
+                       to.p_end = target_mid + nlen;
+                       *to.p_end = 0;
+                       STRIP_TRAILING_SLASH(to);
+               }
+
+               sval = Pflag ? lstat(to.p_path, &to_stat) : stat(to.p_path, &to_stat);
+               /* Not an error but need to remember it happened */
+               if (sval == -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);
+                               this_failed = any_failed = 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);
+                               this_failed = any_failed = 1;
+                               continue;
+                       }
+                       dne = 0;
+               }
+
+               switch (curr->fts_statp->st_mode & S_IFMT) {
+               case S_IFLNK:
+                       /* Catch special case of a non dangling symlink */
+                       if((fts_options & FTS_LOGICAL) ||
+                          ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) {
+                               if (copy_file(curr, dne))
+                                       this_failed = any_failed = 1;
+                       } else {        
+                               if (copy_link(curr, !dne))
+                                       this_failed = any_failed = 1;
+                       }
+                       break;
+               case S_IFDIR:
+                       if (!Rflag && !rflag) {
+                               if (curr->fts_info == FTS_D)
+                                       warnx("%s is a directory (not copied).",
+                                           curr->fts_path);
+                               (void)fts_set(ftsp, curr, FTS_SKIP);
+                               this_failed = any_failed = 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..
+                                */
+                               pushdne(dne);
+                               if (dne) {
+                                       if (mkdir(to.p_path, 
+                                           curr->fts_statp->st_mode | S_IRWXU) < 0)
+                                               err(EXIT_FAILURE, "%s",
+                                                   to.p_path);
+                                               /* NOTREACHED */
+                               } else if (!S_ISDIR(to_stat.st_mode)) {
+                                       errno = ENOTDIR;
+                                       err(EXIT_FAILURE, "%s",
+                                               to.p_path);
+                                       /* NOTREACHED */
+                               }
+                       }
+                       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))
+                                       this_failed = any_failed = 1;
+                               else if ((dne = popdne()))
+                                       (void)chmod(to.p_path, 
+                                           curr->fts_statp->st_mode);
+                       }
+                       else
+                       {
+                               warnx("directory %s encountered when not expected.",
+                                   curr->fts_path);
+                               this_failed = any_failed = 1;
+                               break;
+                       }
+
+                       break;
+               case S_IFBLK:
+               case S_IFCHR:
+                       if (Rflag) {
+                               if (copy_special(curr->fts_statp, !dne))
+                                       this_failed = any_failed = 1;
+                       } else
+                               if (copy_file(curr, dne))
+                                       this_failed = any_failed = 1;
+                       break;
+               case S_IFIFO:
+                       if (Rflag) {
+                               if (copy_fifo(curr->fts_statp, !dne))
+                                       this_failed = any_failed = 1;
+                       } else 
+                               if (copy_file(curr, dne))
+                                       this_failed = any_failed = 1;
+                       break;
+               default:
+                       if (copy_file(curr, dne))
+                               this_failed = any_failed = 1;
+                       break;
+               }
+               if (vflag && !this_failed)
+                       (void)printf("%s -> %s\n", curr->fts_path, to.p_path);
+       }
+       if (errno) {
+               err(EXIT_FAILURE, "fts_read");
+               /* NOTREACHED */
+       }
+       (void)fts_close(ftsp);
+       return (any_failed);
+}
diff --git a/bin/cp/extern.h b/bin/cp/extern.h
new file mode 100644 (file)
index 0000000..e393844
--- /dev/null
@@ -0,0 +1,61 @@
+/* $NetBSD: extern.h,v 1.17 2012/01/04 15:58:37 christos 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. 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
+ */
+
+#ifndef _EXTERN_H_
+#define _EXTERN_H_
+
+typedef struct {
+       char *p_end;                    /* pointer to NULL at end of path */
+       char *target_end;               /* pointer to end of target base */
+       char p_path[MAXPATHLEN + 1];    /* pointer to the start of a path */
+} PATH_T;
+
+extern PATH_T to;
+extern uid_t myuid;
+extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, lflag, pflag, Nflag;
+extern mode_t myumask;
+extern sig_atomic_t pinfo;
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int    copy_fifo(struct stat *, int);
+int    copy_file(FTSENT *, int);
+int    copy_link(FTSENT *, int);
+int    copy_special(struct stat *, int);
+int    set_utimes(const char *, struct stat *);
+int    setfile(struct stat *, int);
+void   usage(void) __attribute__((__noreturn__));
+__END_DECLS
+
+#endif /* !_EXTERN_H_ */
diff --git a/bin/cp/utils.c b/bin/cp/utils.c
new file mode 100644 (file)
index 0000000..88f8b3d
--- /dev/null
@@ -0,0 +1,429 @@
+/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos 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. 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.41 2012/01/04 15:58:37 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/extattr.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define        MMAP_MAX_SIZE   (8 * 1048576)
+#define        MMAP_MAX_WRITE  (64 * 1024)
+
+int
+set_utimes(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 (lutimes(file, tv)) {
+       warn("lutimes: %s", file);
+       return (1);
+    }
+    return (0);
+}
+
+struct finfo {
+       const char *from;
+       const char *to;
+       size_t size;
+};
+
+static void
+progress(const struct finfo *fi, size_t written)
+{
+       int pcent = (int)((100.0 * written) / fi->size);
+
+       pinfo = 0;
+       (void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n",
+           fi->from, fi->to, written, fi->size, pcent);
+}
+
+int
+copy_file(FTSENT *entp, int dne)
+{
+       static char buf[MAXBSIZE];
+       struct stat to_stat, *fs;
+       int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount;
+       char *p;
+       size_t ptotal = 0;
+       
+       if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
+               warn("%s", entp->fts_path);
+               return (1);
+       }
+
+       to_fd = -1;
+       fs = entp->fts_statp;
+       tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag);
+
+       /*
+        * 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) {
+               struct stat sb;
+               int sval;
+
+               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);
+                       }
+               }
+
+               sval = tolnk ?
+                       lstat(to.p_path, &sb) : stat(to.p_path, &sb);
+               if (sval == -1) {
+                       warn("stat: %s", to.p_path);
+                       (void)close(from_fd);
+                       return (1);
+               }
+
+               if (!(tolnk && S_ISLNK(sb.st_mode)))
+                       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 || tolnk)) {
+               /*
+                * 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;
+
+       /* if hard linking then simply close the open fds, link and return */
+       if (lflag) {
+               (void)close(from_fd);
+               (void)close(to_fd);
+               (void)unlink(to.p_path);
+               if (link(entp->fts_path, to.p_path)) {
+                       warn("%s", to.p_path);
+                       return (1);
+               }
+               return (0);
+       }
+       /* NOTREACHED */
+
+       /*
+        * 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) {
+               struct finfo fi;
+
+               fi.from = entp->fts_path;
+               fi.to = to.p_path;
+               fi.size = (size_t)fs->st_size;
+
+               /*
+                * 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.
+                */
+               bool use_read;
+
+               use_read = true;
+               if (fs->st_size <= MMAP_MAX_SIZE) {
+                       size_t fsize = (size_t)fs->st_size;
+                       p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED,
+                           from_fd, (off_t)0);
+                       if (p != MAP_FAILED) {
+                               size_t remainder;
+
+                               use_read = false;
+
+#if !defined(__minix)
+                               (void) madvise(p, (size_t)fs->st_size,
+                                    MADV_SEQUENTIAL);
+#endif /* !defined(__minix) */
+
+                               /*
+                                * Write out the data in small chunks to
+                                * avoid locking the output file for a
+                                * long time if the reading the data from
+                                * the source is slow.
+                                */
+                               remainder = fsize;
+                               do {
+                                       ssize_t chunk;
+
+                                       chunk = (remainder > MMAP_MAX_WRITE) ?
+                                           MMAP_MAX_WRITE : remainder;
+                                       if (write(to_fd, &p[fsize - remainder],
+                                           chunk) != chunk) {
+                                               warn("%s", to.p_path);
+                                               rval = 1;
+                                               break;
+                                       }
+                                       remainder -= chunk;
+                                       ptotal += chunk;
+                                       if (pinfo)
+                                               progress(&fi, ptotal);
+                               } while (remainder > 0);
+
+                               if (munmap(p, fsize) < 0) {
+                                       warn("%s", entp->fts_path);
+                                       rval = 1;
+                               }
+                       }
+               }
+
+               if (use_read) {
+                       while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+                               wcount = write(to_fd, buf, (size_t)rcount);
+                               if (rcount != wcount || wcount == -1) {
+                                       warn("%s", to.p_path);
+                                       rval = 1;
+                                       break;
+                               }
+                               ptotal += wcount;
+                               if (pinfo)
+                                       progress(&fi, ptotal);
+                       }
+                       if (rcount < 0) {
+                               warn("%s", entp->fts_path);
+                               rval = 1;
+                       }
+               }
+       }
+
+#if !defined(__minix)
+       if (pflag && (fcpxattr(from_fd, to_fd) != 0))
+               warn("%s: error copying extended attributes", to.p_path);
+#endif /* !defined(__minix) */
+
+       (void)close(from_fd);
+
+       if (rval == 1) {
+               (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)
+       if (!pflag && dne
+           && 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;
+               }
+       }
+       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(FTSENT *p, int exists)
+{
+       int len;
+       char target[MAXPATHLEN];
+
+       if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -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(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(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(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) :
+           lchown(to.p_path, fs->st_uid, fs->st_gid)) {
+               if (errno != EPERM) {
+                       warn("chown: %s", to.p_path);
+                       rval = 1;
+               }
+               fs->st_mode &= ~(S_ISUID | S_ISGID);
+       }
+       if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) {
+               warn("chmod: %s", to.p_path);
+               rval = 1;
+       }
+
+#if !defined(__minix)
+       if (!islink && !Nflag) {
+               unsigned long fflags = fs->st_flags;
+               /*
+                * 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, fflags) :
+                   chflags(to.p_path, fflags)) == -1)
+                       if (errno != EOPNOTSUPP || fs->st_flags != 0) {
+                               warn("chflags: %s", to.p_path);
+                               rval = 1;
+                       }
+       }
+#endif /* !defined(__minix) */
+
+       /* 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)
+{
+       (void)fprintf(stderr,
+           "usage: %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n"
+           "       %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n",
+           getprogname(), getprogname());
+       exit(1);
+       /* NOTREACHED */
+}
index 767fc4c55a819dbd793c329e535b0f6c97104e2c..1eb4cdee375120e173f7014be230c1b3b7d11971 100644 (file)
@@ -5,7 +5,7 @@
 SUBDIR=        add_route arp ash at backup btrace \
        cawf cd cdprobe \
        ci cleantmp cmp co \
-       compress cp crc cron crontab \
+       compress crc cron crontab \
        dd decomp16 DESCRIBE devmand devsize dhcpd \
        dhrystone diff diskctl \
        eject fbdctl \
diff --git a/commands/cp/Makefile b/commands/cp/Makefile
deleted file mode 100644 (file)
index 51fd070..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# LSC For Now...
-NOGCCERROR:= yes
-PROG=  cp
-BINDIR=        /bin
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/cp/cp.c b/commands/cp/cp.c
deleted file mode 100644 (file)
index 470a94b..0000000
+++ /dev/null
@@ -1,1384 +0,0 @@
-/*     cp 1.12 - copy files                            Author: Kees J. Bot
- *     mv      - move files                                    20 Jul 1993
- *     rm      - remove files
- *     ln      - make a link
- *     cpdir   - copy a directory tree (cp -psmr)
- *     clone   - make a link farm (ln -fmr)
- */
-#define nil 0
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <sys/syslimits.h>
-#include <utime.h>
-#include <dirent.h>
-#include <errno.h>
-#ifndef DEBUG
-#define DEBUG  0
-#define NDEBUG 1
-#endif
-#include <assert.h>
-
-/* Copy files in this size chunks: */
-#if __minix && !__minix_vmd
-#define CHUNK  (8192 * sizeof(char *))
-#else
-#define CHUNK  (1024 << (sizeof(int) + sizeof(char *)))
-#endif
-
-
-#ifndef CONFORMING
-#define CONFORMING     1       /* Precisely POSIX conforming. */
-#endif
-
-
-#define arraysize(a)   (sizeof(a) / sizeof((a)[0]))
-#define arraylimit(a)  ((a) + arraysize(a))
-
-char *prog_name;               /* Call name of this program. */
-int ex_code= 0;                        /* Final exit code. */
-
-typedef enum identity { CP, MV, RM, LN, CPDIR, CLONE } identity_t;
-typedef enum action { COPY, MOVE, REMOVE, LINK } action_t;
-
-identity_t identity;           /* How did the user call me? */
-action_t action;               /* Copying, moving, or linking. */
-int pflag= 0;                  /* -p/-s: Make orginal and copy the same. */
-int iflag= 0;                  /* -i: Interactive overwriting/deleting. */
-int fflag= 0;                  /* -f: Force. */
-int sflag= 0;                  /* -s: Make a symbolic link (ln/clone). */
-int Sflag= 0;                  /* -S: Make a symlink if across devices. */
-int mflag= 0;                  /* -m: Merge trees, no target dir trickery. */
-int rflag= 0;                  /* -r/-R: Recursively copy a tree. */
-int vflag= 0;                  /* -v: Verbose. */
-int xflag= 0;                  /* -x: Don't traverse past mount points. */
-int xdev= 0;                   /* Set when moving or linking cross-device. */
-int expand= 0;                 /* Expand symlinks, ignore links. */
-int conforming= CONFORMING;    /* Sometimes standards are a pain. */
-
-int fc_mask;                   /* File creation mask. */
-int uid, gid;                  /* Effective uid & gid. */
-int istty;                     /* Can have terminal input. */
-
-#ifndef S_ISLNK
-/* There were no symlinks in medieval times. */
-#define S_ISLNK(mode)                  (0)
-#define lstat                          stat
-#define symlink(path1, path2)          (errno= ENOSYS, -1)
-#define readlink(path, buf, len)       (errno= ENOSYS, -1)
-#endif
-
-void report(const char *label)
-{
-    if (action == REMOVE && fflag) return;
-    fprintf(stderr, "%s: %s: %s\n", prog_name, label, strerror(errno));
-    ex_code= 1;
-}
-
-void fatal(const char *label)
-{
-    report(label);
-    exit(1);
-}
-
-void report2(const char *src, const char *dst)
-{
-    fprintf(stderr, "%s %s %s: %s\n", prog_name, src, dst, strerror(errno));
-    ex_code= 1;
-}
-
-#if DEBUG
-size_t nchunks= 0;     /* Number of allocated cells. */
-#endif
-
-void *allocate(void *mem, size_t size)
-/* Like realloc, but with checking of the return value. */
-{
-#if DEBUG
-    if (mem == nil) nchunks++;
-#endif
-    if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil)
-       fatal("malloc()");
-    return mem;
-}
-
-void deallocate(void *mem)
-/* Release a chunk of memory. */
-{
-    if (mem != nil) {
-#if DEBUG
-       nchunks--;
-#endif
-       free(mem);
-    }
-}
-
-typedef struct pathname {
-    char               *path;  /* The actual pathname. */
-    size_t             idx;    /* Index for the terminating null byte. */
-    size_t             lim;    /* Actual length of the path array. */
-} pathname_t;
-
-void path_init(pathname_t *pp)
-/* Initialize a pathname to the null string. */
-{
-    pp->path= allocate(nil, pp->lim= NAME_MAX + 2);
-    pp->path[pp->idx= 0]= 0;
-}
-
-void path_add(pathname_t *pp, const char *name)
-/* Add a component to a pathname. */
-{
-    size_t lim;
-    char *p;
-
-    lim= pp->idx + strlen(name) + 2;
-
-    if (lim > pp->lim) {
-       pp->lim= lim += lim/2;  /* add an extra 50% growing space. */
-
-       pp->path= allocate(pp->path, lim);
-    }
-
-    p= pp->path + pp->idx;
-    if (p > pp->path && p[-1] != '/') *p++ = '/';
-
-    while (*name != 0) {
-       if (*name != '/' || p == pp->path || p[-1] != '/') *p++ = *name;
-       name++;
-    }
-    *p = 0;
-    pp->idx= p - pp->path;
-}
-
-void path_trunc(pathname_t *pp, size_t didx)
-/* Delete part of a pathname to a remembered length. */
-{
-    pp->path[pp->idx= didx]= 0;
-}
-
-#if DEBUG
-const char *path_name(const pathname_t *pp)
-/* Return the actual name as a C string. */
-{
-    return pp->path;
-}
-
-size_t path_length(const pathname_t *pp)
-/* The length of the pathname. */
-{
-    return pp->idx;
-}
-
-void path_drop(pathname_t *pp)
-/* Release the storage occupied by the pathname. */
-{
-    deallocate(pp->path);
-}
-
-#else /* !DEBUG */
-#define path_name(pp)          ((const char *) (pp)->path)
-#define path_length(pp)                ((pp)->idx)
-#define path_drop(pp)          deallocate((void *) (pp)->path)
-#endif /* !DEBUG */
-
-char *basename(const char *path)
-/* Return the last component of a pathname.  (Note: declassifies a const
- * char * just like strchr.
- */
-{
-    const char *p= path;
-
-    for (;;) {
-       while (*p == '/') p++;                  /* Trailing slashes? */
-
-       if (*p == 0) break;
-
-       path= p;
-       while (*p != 0 && *p != '/') p++;       /* Skip component. */
-    }
-    return (char *) path;
-}
-
-int affirmative(void)
-/* Get a yes/no answer from the suspecting user. */
-{
-    int c;
-    int ok;
-
-    fflush(stdout);
-    fflush(stderr);
-
-    while ((c= getchar()) == ' ') {}
-    ok= (c == 'y' || c == 'Y');
-    while (c != EOF && c != '\n') c= getchar();
-
-    return ok;
-}
-
-int writable(const struct stat *stp)
-/* True iff the file with the given attributes allows writing.  (And we have
- * a terminal to ask if ok to overwrite.)
- */
-{
-    if (!istty || uid == 0) return 1;
-    if (stp->st_uid == uid) return stp->st_mode & S_IWUSR;
-    if (stp->st_gid == gid) return stp->st_mode & S_IWGRP;
-    return stp->st_mode & S_IWOTH;
-}
-
-#ifndef PATH_MAX
-#define PATH_MAX       1024
-#endif
-
-static char *link_islink(const struct stat *stp, const char *file)
-{
-    /* Tell if a file, which stat(2) information in '*stp', has been seen
-     * earlier by this function under a different name.  If not return a
-     * null pointer with errno set to ENOENT, otherwise return the name of
-     * the link.  Return a null pointer with an error code in errno for any
-     * error, using E2BIG for a too long file name.
-     *
-     * Use link_islink(nil, nil) to reset all bookkeeping.
-     *
-     * Call for a file twice to delete it from the store.
-     */
-
-    typedef struct link {      /* In-memory link store. */
-       struct link     *next;          /* Hash chain on inode number. */
-       ino_t           ino;            /* File's inode number. */
-       off_t           off;            /* Offset to more info in temp file. */
-    } link_t;
-    typedef struct dlink {     /* On-disk link store. */
-       dev_t           dev;            /* Device number. */
-       char            file[PATH_MAX]; /* Name of earlier seen link. */
-    } dlink_t;
-    static link_t *links[256];         /* Hash list of known links. */
-    static int tfd= -1;                        /* Temp file for file name storage. */
-    static dlink_t dlink;
-    link_t *lp, **plp;
-    size_t len;
-    off_t off;
-
-    if (file == nil) {
-       /* Reset everything. */
-       for (plp= links; plp < arraylimit(links); plp++) {
-           while ((lp= *plp) != nil) {
-               *plp= lp->next;
-               free(lp);
-           }
-       }
-       if (tfd != -1) close(tfd);
-       tfd= -1;
-       return nil;
-    }
-
-    /* The file must be a non-directory with more than one link. */
-    if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) {
-       errno= ENOENT;
-       return nil;
-    }
-
-    plp= &links[stp->st_ino % arraysize(links)];
-
-    while ((lp= *plp) != nil) {
-       if (lp->ino == stp->st_ino) {
-           /* May have seen this link before.  Get it and check. */
-           if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
-           if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
-
-           /* Only need to check the device number. */
-           if (dlink.dev == stp->st_dev) {
-               if (strcmp(file, dlink.file) == 0) {
-                   /* Called twice.  Forget about this link. */
-                   *plp= lp->next;
-                   free(lp);
-                   errno= ENOENT;
-                   return nil;
-               }
-
-               /* Return the name of the earlier link. */
-               return dlink.file;
-           }
-       }
-       plp= &lp->next;
-    }
-
-    /* First time I see this link.  Add it to the store. */
-    if (tfd == -1) {
-       for (;;) {
-           char *tmp;
-
-           tmp= tmpnam(nil);
-           tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600);
-           if (tfd < 0) {
-               if (errno != EEXIST) return nil;
-           } else {
-               (void) unlink(tmp);
-               break;
-           }
-       }
-    }
-    if ((len= strlen(file)) >= PATH_MAX) {
-       errno= E2BIG;
-       return nil;
-    }
-
-    dlink.dev= stp->st_dev;
-    strcpy(dlink.file, file);
-    len += offsetof(dlink_t, file) + 1;
-    if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil;
-    if (write(tfd, &dlink, len) != len) return nil;
-
-    if ((lp= malloc(sizeof(*lp))) == nil) return nil;
-    lp->next= nil;
-    lp->ino= stp->st_ino;
-    lp->off= off;
-    *plp= lp;
-    errno= ENOENT;
-    return nil;
-}
-
-int trylink(const char *src, const char *dst, const struct stat *srcst,
-                       const struct stat *dstst)
-/* Keep the link structure intact if src has been seen before. */
-{
-    char *olddst;
-    int linked;
-
-    if (action == COPY && expand) return 0;
-
-    if ((olddst= link_islink(srcst, dst)) == nil) {
-       /* if (errno != ENOENT) ... */
-       return 0;
-    }
-
-    /* Try to link the file copied earlier to the new file. */
-    if (dstst->st_ino != 0) (void) unlink(dst);
-
-    if ((linked= (link(olddst, dst) == 0)) && vflag)
-       printf("ln %s ..\n", olddst);
-
-    return linked;
-}
-
-int copy(const char *src, const char *dst, struct stat *srcst,
-                       struct stat *dstst)
-/* Copy one file to another and copy (some of) the attributes. */
-{
-    char buf[CHUNK];
-    int srcfd, dstfd;
-    ssize_t n;
-
-    assert(srcst->st_ino != 0);
-
-    if (dstst->st_ino == 0) {
-       /* The file doesn't exist yet. */
-
-       if (!S_ISREG(srcst->st_mode)) {
-           /* Making a new mode 666 regular file. */
-           srcst->st_mode= (S_IFREG | 0666) & fc_mask;
-       } else
-       if (!pflag && conforming) {
-           /* Making a new file copying mode with umask applied. */
-           srcst->st_mode &= fc_mask;
-       }
-    } else {
-       /* File exists, ask if ok to overwrite if '-i'. */
-
-       if (iflag || (action == MOVE && !fflag && !writable(dstst))) {
-           fprintf(stderr, "Overwrite %s? (mode = %03o) ",
-                       dst, dstst->st_mode & 07777);
-           if (!affirmative()) return 0;
-       }
-
-       if (action == MOVE) {
-           /* Don't overwrite, remove first. */
-           if (unlink(dst) < 0 && errno != ENOENT) {
-               report(dst);
-               return 0;
-           }
-       } else {
-           /* Overwrite. */
-           if (!pflag) {
-               /* Keep the existing mode and ownership. */
-               srcst->st_mode= dstst->st_mode;
-               srcst->st_uid= dstst->st_uid;
-               srcst->st_gid= dstst->st_gid;
-           }
-       }
-    }
-
-    /* Keep the link structure if possible. */
-    if (trylink(src, dst, srcst, dstst)) return 1;
-
-    if ((srcfd= open(src, O_RDONLY)) < 0) {
-       report(src);
-       return 0;
-    }
-
-    dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, srcst->st_mode & 0777);
-    if (dstfd < 0 && fflag && errno == EACCES) {
-       /* Retry adding a "w" bit. */
-       (void) chmod(dst, dstst->st_mode | S_IWUSR);
-       dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0);
-    }
-    if (dstfd < 0 && fflag && errno == EACCES) {
-       /* Retry after trying to delete. */
-       (void) unlink(dst);
-       dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0);
-    }
-    if (dstfd < 0) {
-       report(dst);
-       close(srcfd);
-       return 0;
-    }
-
-    /* Get current parameters. */
-    if (fstat(dstfd, dstst) < 0) {
-       report(dst);
-       close(srcfd);
-       close(dstfd);
-       return 0;
-    }
-
-    /* Copy the little bytes themselves. */
-    while ((n= read(srcfd, buf, sizeof(buf))) > 0) {
-       char *bp = buf;
-       ssize_t r;
-
-       while (n > 0 && (r= write(dstfd, bp, n)) > 0) {
-           bp += r;
-           n -= r;
-       }
-       if (r <= 0) {
-           if (r == 0) {
-               fprintf(stderr,
-                   "%s: Warning: EOF writing to %s\n",
-                   prog_name, dst);
-               break;
-           }
-           fatal(dst);
-       }
-    }
-
-    if (n < 0) {
-       report(src);
-       close(srcfd);
-       close(dstfd);
-       return 0;
-    }
-
-    close(srcfd);
-    close(dstfd);
-
-    /* Copy the ownership. */
-    if ((pflag || !conforming)
-       && S_ISREG(dstst->st_mode)
-       && (dstst->st_uid != srcst->st_uid
-               || dstst->st_gid != srcst->st_gid)
-    ) {
-       if (chmod(dst, 0) == 0) dstst->st_mode&= ~07777;
-       if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) {
-           if (errno != EPERM) {
-               report(dst);
-               return 0;
-           }
-       } else {
-           dstst->st_uid= srcst->st_uid;
-           dstst->st_gid= srcst->st_gid;
-       }
-    }
-
-    if (conforming && S_ISREG(dstst->st_mode)
-       && (dstst->st_uid != srcst->st_uid
-               || dstst->st_gid != srcst->st_gid)
-    ) {
-       /* Suid bits must be cleared in the holy name of
-        * security (and the assumed user stupidity).
-        */
-       srcst->st_mode&= ~06000;
-    }
-
-    /* Copy the mode. */
-    if (S_ISREG(dstst->st_mode) && dstst->st_mode != srcst->st_mode) {
-       if (chmod(dst, srcst->st_mode) < 0) {
-           if (errno != EPERM) {
-               report(dst);
-               return 0;
-           }
-           fprintf(stderr, "%s: Can't change the mode of %s\n",
-               prog_name, dst);
-       }
-    }
-
-    /* Copy the file modification time. */
-    if ((pflag || !conforming) && S_ISREG(dstst->st_mode)) {
-       struct utimbuf ut;
-
-       ut.actime= action == MOVE ? srcst->st_atime : time(nil);
-       ut.modtime= srcst->st_mtime;
-       if (utime(dst, &ut) < 0) {
-           if (errno != EPERM) {
-               report(dst);
-               return 0;
-           }
-           if (pflag) {
-               fprintf(stderr,
-                   "%s: Can't set the time of %s\n",
-                   prog_name, dst);
-           }
-       }
-    }
-    if (vflag) {
-       printf(action == COPY ? "cp %s ..\n" : "mv %s ..\n", src);
-    }
-    return 1;
-}
-
-void copy1(const char *src, const char *dst, struct stat *srcst,
-                           struct stat *dstst)
-/* Inspect the source file and then copy it.  Treatment of symlinks and
- * special files is a bit complicated.  The filetype and link-structure are
- * ignored if (expand && !rflag), symlinks and link-structure are ignored
- * if (expand && rflag), everything is copied precisely if !expand.
- */
-{
-    int r, linked;
-
-    assert(srcst->st_ino != 0);
-
-    if (srcst->st_ino == dstst->st_ino && srcst->st_dev == dstst->st_dev) {
-       fprintf(stderr, "%s: can't copy %s onto itself\n",
-           prog_name, src);
-       ex_code= 1;
-       return;
-    }
-
-    /* You can forget it if the destination is a directory. */
-    if (dstst->st_ino != 0 && S_ISDIR(dstst->st_mode)) {
-       errno= EISDIR;
-       report(dst);
-       return;
-    }
-
-    if (S_ISREG(srcst->st_mode) || (expand && !rflag)) {
-       if (!copy(src, dst, srcst, dstst)) return;
-
-       if (action == MOVE && unlink(src) < 0) {
-           report(src);
-           return;
-       }
-       return;
-    }
-
-    if (dstst->st_ino != 0) {
-       if (iflag || (action == MOVE && !fflag && !writable(dstst))) {
-           fprintf(stderr, "Replace %s? (mode = %03o) ",
-               dst, dstst->st_mode & 07777);
-           if (!affirmative()) return;
-       }
-       if (unlink(dst) < 0) {
-           report(dst);
-           return;
-       }
-       dstst->st_ino= 0;
-    }
-
-    /* Apply the file creation mask if so required. */
-    if (!pflag && conforming) srcst->st_mode &= fc_mask;
-
-    linked= 0;
-
-    if (S_ISLNK(srcst->st_mode)) {
-       char buf[1024+1];
-
-       if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) {
-           report(src);
-           return;
-       }
-       buf[r]= 0;
-       r= symlink(buf, dst);
-       if (vflag && r == 0)
-           printf("ln -s %s %s\n", buf, dst);
-    } else
-    if (trylink(src, dst, srcst, dstst)) {
-       linked= 1;
-       r= 0;
-    } else
-    if (S_ISFIFO(srcst->st_mode)) {
-       r= mkfifo(dst, srcst->st_mode);
-       if (vflag && r == 0)
-           printf("mkfifo %s\n", dst);
-    } else
-    if (S_ISBLK(srcst->st_mode) || S_ISCHR(srcst->st_mode)) {
-       r= mknod(dst, srcst->st_mode, srcst->st_rdev);
-       if (vflag && r == 0) {
-           printf("mknod %s %c %d %d\n",
-               dst,
-               S_ISBLK(srcst->st_mode) ? 'b' : 'c',
-               (srcst->st_rdev >> 8) & 0xFF,
-               (srcst->st_rdev >> 0) & 0xFF);
-       }
-    } else {
-       fprintf(stderr, "%s: %s: odd filetype %5o (not copied)\n",
-           prog_name, src, srcst->st_mode);
-       ex_code= 1;
-       return;
-    }
-
-    if (r < 0 || lstat(dst, dstst) < 0) {
-       report(dst);
-       return;
-    }
-
-    if (action == MOVE && unlink(src) < 0) {
-       report(src);
-       (void) unlink(dst);     /* Don't want it twice. */
-       return;
-    }
-
-    if (linked) return;
-
-    if (S_ISLNK(srcst->st_mode)) return;
-
-    /* Copy the ownership. */
-    if ((pflag || !conforming)
-       && (dstst->st_uid != srcst->st_uid
-               || dstst->st_gid != srcst->st_gid)
-    ) {
-       if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) {
-           if (errno != EPERM) {
-               report(dst);
-               return;
-           }
-       }
-    }
-
-    /* Copy the file modification time. */
-    if (pflag || !conforming) {
-       struct utimbuf ut;
-
-       ut.actime= action == MOVE ? srcst->st_atime : time(nil);
-       ut.modtime= srcst->st_mtime;
-       if (utime(dst, &ut) < 0) {
-           if (errno != EPERM) {
-               report(dst);
-               return;
-           }
-           fprintf(stderr, "%s: Can't set the time of %s\n",
-               prog_name, dst);
-       }
-    }
-}
-
-void remove1(const char *src, const struct stat *srcst)
-{
-    if (iflag || (!fflag && !writable(srcst))) {
-       fprintf(stderr, "Remove %s? (mode = %03o) ", src,
-                       srcst->st_mode & 07777);
-       if (!affirmative()) return;
-    }
-    if (unlink(src) < 0) {
-       report(src);
-    } else {
-       if (vflag) printf("rm %s\n", src);
-    }
-}
-
-void link1(const char *src, const char *dst, const struct stat *srcst,
-                           const struct stat *dstst)
-{
-    pathname_t sym;
-    const char *p;
-
-    if (dstst->st_ino != 0 && (iflag || fflag)) {
-       if (srcst->st_ino == dstst->st_ino) {
-           if (fflag) return;
-           fprintf(stderr, "%s: Can't link %s onto itself\n",
-               prog_name, src);
-           ex_code= 1;
-           return;
-       }
-       if (iflag) {
-           fprintf(stderr, "Remove %s? ", dst);
-           if (!affirmative()) return;
-       }
-       errno= EISDIR;
-       if (S_ISDIR(dstst->st_mode) || unlink(dst) < 0) {
-           report(dst);
-           return;
-       }
-    }
-
-    if (!sflag && !(rflag && S_ISLNK(srcst->st_mode)) && !(Sflag && xdev)) {
-       /* A normal link. */
-       if (link(src, dst) < 0) {
-           if (!Sflag || errno != EXDEV) {
-               report2(src, dst);
-               return;
-           }
-           /* Can't do a cross-device link, we have to symlink. */
-           xdev= 1;
-       } else {
-           if (vflag) printf("ln %s..\n", src);
-           return;
-       }
-    }
-
-    /* Do a symlink. */
-    if (!rflag && !Sflag) {
-       /* We can get away with a "don't care if it works" symlink. */
-       if (symlink(src, dst) < 0) {
-           report(dst);
-           return;
-       }
-       if (vflag) printf("ln -s %s %s\n", src, dst);
-       return;
-    }
-
-    /* If the source is a symlink then it is simply copied. */
-    if (S_ISLNK(srcst->st_mode)) {
-       int r;
-       char buf[1024+1];
-
-       if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) {
-           report(src);
-           return;
-       }
-       buf[r]= 0;
-       if (symlink(buf, dst) < 0) {
-           report(dst);
-           return;
-       }
-       if (vflag) printf("ln -s %s %s\n", buf, dst);
-       return;
-    }
-
-    /* Make a symlink that has to work, i.e. we must be able to access the
-     * source now, and the link must work.
-     */
-    if (dst[0] == '/' && src[0] != '/') {
-       /* ln -[rsS] relative/path /full/path. */
-       fprintf(stderr,
-           "%s: Symlinking %s to %s is too difficult for me to figure out\n",
-           prog_name, src, dst);
-       exit(1);
-    }
-
-    /* Count the number of subdirectories in the destination file and
-     * add one '..' for each.
-     */
-    path_init(&sym);
-    if (src[0] != '/') {
-       p= dst;
-       while (*p != 0) {
-           if (p[0] == '.') {
-               if (p[1] == '/' || p[1] == 0) {
-                   /* A "." component; skip. */
-                   do p++; while (*p == '/');
-                   continue;
-               } else
-               if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
-                   /* A ".." component; oops. */
-                   switch (path_length(&sym)) {
-                   case 0:
-                       fprintf(stderr,
-           "%s: Symlinking %s to %s is too difficult for me to figure out\n",
-                           prog_name, src, dst);
-                       exit(1);
-                   case 2:
-                       path_trunc(&sym, 0);
-                       break;
-                   default:
-                       path_trunc(&sym, path_length(&sym) - 3);
-                   }
-                   p++;
-                   do p++; while (*p == '/');
-                   continue;
-               }
-           }
-           while (*p != 0 && *p != '/') p++;
-           while (*p == '/') p++;
-           if (*p == 0) break;
-           path_add(&sym, "..");
-       }
-    }
-    path_add(&sym, src);
-
-    if (symlink(path_name(&sym), dst) < 0) {
-       report(dst);
-    } else {
-       if (vflag) printf("ln -s %s %s\n", path_name(&sym), dst);
-    }
-    path_drop(&sym);
-}
-
-typedef struct entrylist {
-    struct entrylist   *next;
-    char                       *name;
-} entrylist_t;
-
-int eat_dir(const char *dir, entrylist_t **dlist)
-/* Make a linked list of all the names in a directory. */
-{
-    DIR *dp;
-    struct dirent *entry;
-
-    if ((dp= opendir(dir)) == nil) return 0;
-
-    while ((entry= readdir(dp)) != nil) {
-       if (strcmp(entry->d_name, ".") == 0) continue;
-       if (strcmp(entry->d_name, "..") == 0) continue;
-
-       *dlist= allocate(nil, sizeof(**dlist));
-       (*dlist)->name= allocate(nil, strlen(entry->d_name)+1);
-       strcpy((*dlist)->name, entry->d_name);
-       dlist= &(*dlist)->next;
-    }
-    closedir(dp);
-    *dlist= nil;
-    return 1;
-}
-
-void chop_dlist(entrylist_t **dlist)
-/* Chop an entry of a name list. */
-{
-    entrylist_t *junk= *dlist;
-
-    *dlist= junk->next;
-    deallocate(junk->name);
-    deallocate(junk);
-}
-
-void drop_dlist(entrylist_t *dlist)
-/* Get rid of a whole list. */
-{
-    while (dlist != nil) chop_dlist(&dlist);
-}
-
-void do1(pathname_t *src, pathname_t *dst, int depth)
-/* Perform the appropriate action on a source and destination file. */
-{
-    size_t slashsrc, slashdst;
-    struct stat srcst, dstst;
-    entrylist_t *dlist;
-    static ino_t topdst_ino;
-    static dev_t topdst_dev;
-    static dev_t topsrc_dev;
-
-#if DEBUG
-    if (vflag && depth == 0) {
-       char flags[100], *pf= flags;
-
-       if (pflag) *pf++= 'p';
-       if (iflag) *pf++= 'i';
-       if (fflag) *pf++= 'f';
-       if (sflag) *pf++= 's';
-       if (Sflag) *pf++= 'S';
-       if (mflag) *pf++= 'm';
-       if (rflag) *pf++= 'r';
-       if (vflag) *pf++= 'v';
-       if (xflag) *pf++= 'x';
-       if (expand) *pf++= 'L';
-       if (conforming) *pf++= 'C';
-       *pf= 0;
-       printf(": %s -%s %s %s\n", prog_name, flags,
-                   path_name(src), path_name(dst));
-    }
-#endif
-
-    /* st_ino == 0 if not stat()'ed yet, or nonexistent. */
-    srcst.st_ino= 0;
-    dstst.st_ino= 0;
-
-    if (action != LINK || !sflag || rflag) {
-       /* Source must exist unless symlinking. */
-       if ((expand ? stat : lstat)(path_name(src), &srcst) < 0) {
-           report(path_name(src));
-           return;
-       }
-    }
-
-    if (depth == 0) {
-       /* First call: Not cross-device yet, first dst not seen yet,
-        * remember top device number.
-        */
-       xdev= 0;
-       topdst_ino= 0;
-       topsrc_dev= srcst.st_dev;
-    }
-
-    /* Inspect the intended destination unless removing. */
-    if (action != REMOVE) {
-       if ((expand ? stat : lstat)(path_name(dst), &dstst) < 0) {
-           if (errno != ENOENT) {
-               report(path_name(dst));
-               return;
-           }
-       }
-    }
-
-    if (action == MOVE && !xdev) {
-       if (dstst.st_ino != 0 && srcst.st_dev != dstst.st_dev) {
-           /* It's a cross-device rename, i.e. copy and remove. */
-           xdev= 1;
-       } else
-       if (!mflag || dstst.st_ino == 0 || !S_ISDIR(dstst.st_mode)) {
-           /* Try to simply rename the file (not merging trees). */
-
-           if (srcst.st_ino == dstst.st_ino) {
-               fprintf(stderr,
-                   "%s: Can't move %s onto itself\n",
-                   prog_name, path_name(src));
-               ex_code= 1;
-               return;
-           }
-
-           if (dstst.st_ino != 0) {
-               if (iflag || (!fflag && !writable(&dstst))) {
-                   fprintf(stderr,
-                       "Replace %s? (mode = %03o) ",
-                       path_name(dst),
-                       dstst.st_mode & 07777);
-                   if (!affirmative()) return;
-               }
-               if (!S_ISDIR(dstst.st_mode))
-                   (void) unlink(path_name(dst));
-           }
-
-           if (rename(path_name(src), path_name(dst)) == 0) {
-               /* Success. */
-               if (vflag) {
-                   printf("mv %s %s\n", path_name(src),
-                           path_name(dst));
-               }
-               return;
-           }
-           if (errno == EXDEV) {
-               xdev= 1;
-           } else {
-               report2(path_name(src), path_name(dst));
-               return;
-           }
-       }
-    }
-
-    if (srcst.st_ino == 0 || !S_ISDIR(srcst.st_mode)) {
-       /* Copy/move/remove/link a single file. */
-       switch (action) {
-       case COPY:
-       case MOVE:
-           copy1(path_name(src), path_name(dst), &srcst, &dstst);
-           break;
-       case REMOVE:
-           remove1(path_name(src), &srcst);
-           break;
-       case LINK:
-           link1(path_name(src), path_name(dst), &srcst, &dstst);
-           break;
-       }
-       return;
-    }
-
-    /* Recursively copy/move/remove/link a directory if -r or -R. */
-    if (!rflag) {
-       errno= EISDIR;
-       report(path_name(src));
-       return;
-    }
-
-    /* Ok to remove contents of dir? */
-    if (action == REMOVE) {
-       if (xflag && topsrc_dev != srcst.st_dev) {
-           /* Don't recurse past a mount point. */
-           return;
-       }
-       if (iflag) {
-           fprintf(stderr, "Remove contents of %s? ", path_name(src));
-           if (!affirmative()) return;
-       }
-    }
-
-    /* Gather the names in the source directory. */
-    if (!eat_dir(path_name(src), &dlist)) {
-       report(path_name(src));
-       return;
-    }
-
-    /* Check/create the target directory. */
-    if (action != REMOVE && dstst.st_ino != 0 && !S_ISDIR(dstst.st_mode)) {
-       if (action != MOVE && !fflag) {
-           errno= ENOTDIR;
-           report(path_name(dst));
-           return;
-       }
-       if (iflag) {
-           fprintf(stderr, "Replace %s? ", path_name(dst));
-           if (!affirmative()) {
-               drop_dlist(dlist);
-               return;
-           }
-       }
-       if (unlink(path_name(dst)) < 0) {
-           report(path_name(dst));
-           drop_dlist(dlist);
-           return;
-       }
-       dstst.st_ino= 0;
-    }
-
-    if (action != REMOVE) {
-       if (dstst.st_ino == 0) {
-           /* Create a new target directory. */
-           if (!pflag && conforming) srcst.st_mode&= fc_mask;
-
-           if (mkdir(path_name(dst), srcst.st_mode | S_IRWXU) < 0
-                   || stat(path_name(dst), &dstst) < 0) {
-               report(path_name(dst));
-               drop_dlist(dlist);
-               return;
-           }
-           if (vflag) printf("mkdir %s\n", path_name(dst));
-       } else {
-           /* Target directory already exists. */
-           if (action == MOVE && !mflag) {
-               errno= EEXIST;
-               report(path_name(dst));
-               drop_dlist(dlist);
-               return;
-           }
-           if (!pflag) {
-               /* Keep the existing attributes. */
-               srcst.st_mode= dstst.st_mode;
-               srcst.st_uid= dstst.st_uid;
-               srcst.st_gid= dstst.st_gid;
-               srcst.st_mtime= dstst.st_mtime;
-           }
-       }
-
-       if (topdst_ino == 0) {
-           /* Remember the top destination. */
-           topdst_dev= dstst.st_dev;
-           topdst_ino= dstst.st_ino;
-       }
-
-       if (srcst.st_ino == topdst_ino && srcst.st_dev == topdst_dev) {
-           /* E.g. cp -r /shallow /shallow/deep. */
-           fprintf(stderr,
-               "%s%s %s/ %s/: infinite recursion avoided\n",
-               prog_name, action != MOVE ? " -r" : "",
-               path_name(src), path_name(dst));
-           drop_dlist(dlist);
-           return;
-       }
-
-       if (xflag && topsrc_dev != srcst.st_dev) {
-           /* Don't recurse past a mount point. */
-           drop_dlist(dlist);
-           return;
-       }
-    }
-
-    /* Go down. */
-    slashsrc= path_length(src);
-    slashdst= path_length(dst);
-
-    while (dlist != nil) {
-       path_add(src, dlist->name);
-       if (action != REMOVE) path_add(dst, dlist->name);
-
-       do1(src, dst, depth+1);
-
-       path_trunc(src, slashsrc);
-       path_trunc(dst, slashdst);
-       chop_dlist(&dlist);
-    }
-
-    if (action == MOVE || action == REMOVE) {
-       /* The contents of the source directory should have
-        * been (re)moved above.  Get rid of the empty dir.
-        */
-       if (action == REMOVE && iflag) {
-           fprintf(stderr, "Remove directory %s? ",
-                           path_name(src));
-           if (!affirmative()) return;
-       }
-       if (rmdir(path_name(src)) < 0) {
-           if (errno != ENOTEMPTY) report(path_name(src));
-           return;
-       }
-       if (vflag) printf("rmdir %s\n", path_name(src));
-    }
-
-    if (action != REMOVE) {
-       /* Set the attributes of a new directory. */
-       struct utimbuf ut;
-
-       /* Copy the ownership. */
-       if ((pflag || !conforming)
-           && (dstst.st_uid != srcst.st_uid
-               || dstst.st_gid != srcst.st_gid)
-       ) {
-           if (chown(path_name(dst), srcst.st_uid,
-                           srcst.st_gid) < 0) {
-               if (errno != EPERM) {
-                   report(path_name(dst));
-                   return;
-               }
-           }
-       }
-
-       /* Copy the mode. */
-       if (dstst.st_mode != srcst.st_mode) {
-           if (chmod(path_name(dst), srcst.st_mode) < 0) {
-               report(path_name(dst));
-               return;
-           }
-       }
-
-       /* Copy the file modification time. */
-       if (dstst.st_mtime != srcst.st_mtime) {
-           ut.actime= action == MOVE ? srcst.st_atime : time(nil);
-           ut.modtime= srcst.st_mtime;
-           if (utime(path_name(dst), &ut) < 0) {
-               if (errno != EPERM) {
-                   report(path_name(dst));
-                   return;
-               }
-               fprintf(stderr,
-                   "%s: Can't set the time of %s\n",
-                   prog_name, path_name(dst));
-           }
-       }
-    }
-}
-
-void usage(void)
-{
-    char *flags1, *flags2;
-
-    switch (identity) {
-    case CP:
-       flags1= "pifsmrRvx";
-       flags2= "pifsrRvx";
-       break;
-    case MV:
-       flags1= "ifsmvx";
-       flags2= "ifsvx";
-       break;
-    case RM:
-       fprintf(stderr, "Usage: rm [-ifrRvx] file ...\n");
-       exit(1);
-    case LN:
-       flags1= "ifsSmrRvx";
-       flags2= "ifsSrRvx";
-       break;
-    case CPDIR:
-       flags1= "ifvx";
-       flags2= nil;
-       break;
-    case CLONE:
-       flags1= "ifsSvx";
-       flags2= nil;
-       break;
-    }
-    fprintf(stderr, "Usage: %s [-%s] file1 file2\n", prog_name, flags1);
-  if (flags2 != nil)
-    fprintf(stderr, "       %s [-%s] file ... dir\n", prog_name, flags2);
-    exit(1);
-}
-
-int main(int argc, char **argv)
-{
-    int i;
-    char *flags;
-    struct stat st;
-    pathname_t src, dst;
-    size_t slash;
-
-#if DEBUG >= 3
-    /* The first argument is the call name while debugging. */
-    if (argc < 2) exit(-1);
-    argv++;
-    argc--;
-#endif
-#if DEBUG
-    vflag= isatty(1);
-#endif
-
-    /* Call name of this program. */
-    prog_name= basename(argv[0]);
-
-    /* Required action. */
-    if (strcmp(prog_name, "cp") == 0) {
-       identity= CP;
-       action= COPY;
-       flags= "pifsmrRvx";
-       expand= 1;
-    } else
-    if (strcmp(prog_name, "mv") == 0) {
-       identity= MV;
-       action= MOVE;
-       flags= "ifsmvx";
-       rflag= pflag= 1;
-    } else
-    if (strcmp(prog_name, "rm") == 0) {
-       identity= RM;
-       action= REMOVE;
-       flags= "ifrRvx";
-    } else
-    if (strcmp(prog_name, "ln") == 0) {
-       identity= LN;
-       action= LINK;
-       flags= "ifsSmrRvx";
-    } else
-    if (strcmp(prog_name, "cpdir") == 0) {
-       identity= CPDIR;
-       action= COPY;
-       flags= "pifsmrRvx";
-       rflag= mflag= pflag= 1;
-       conforming= 0;
-    } else
-    if (strcmp(prog_name, "clone") == 0) {
-       identity= CLONE;
-       action= LINK;
-       flags= "ifsSmrRvx";
-       rflag= mflag= fflag= 1;
-    } else {
-       fprintf(stderr,
-           "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n",
-           prog_name);
-       exit(1);
-    }
-
-    /* Who am I?, where am I?, how protective am I? */
-    uid= geteuid();
-    gid= getegid();
-    istty= isatty(0);
-    fc_mask= ~umask(0);
-
-    /* Gather flags. */
-    i= 1;
-    while (i < argc && argv[i][0] == '-') {
-       char *opt= argv[i++] + 1;
-
-       if (opt[0] == '-' && opt[1] == 0) break;        /* -- */
-
-       while (*opt != 0) {
-           /* Flag supported? */
-           if (strchr(flags, *opt) == nil) usage();
-
-           switch (*opt++) {
-           case 'p':
-               pflag= 1;
-               break;
-           case 'i':
-               iflag= 1;
-               if (action == MOVE) fflag= 0;
-               break;
-           case 'f':
-               fflag= 1;
-               if (action == MOVE) iflag= 0;
-               break;
-           case 's':
-               if (action == LINK) {
-                   sflag= 1;
-               } else {
-                   /* Forget about POSIX, do it right. */
-                   conforming= 0;
-               }
-               break;
-           case 'S':
-               Sflag= 1;
-               break;
-           case 'm':
-               mflag= 1;
-               break;
-           case 'r':
-               expand= 0;
-               /*FALL THROUGH*/
-           case 'R':
-               rflag= 1;
-               break;
-           case 'v':
-               vflag= 1;
-               break;
-           case 'x':
-               xflag= 1;
-               break;
-           default:
-               assert(0);
-           }
-       }
-    }
-
-    switch (action) {
-    case REMOVE:
-       if (i == argc) {
-           if (fflag)
-               exit(0);
-           usage();
-       }
-       break;
-    case LINK:
-       /* 'ln dir/file' is to be read as 'ln dir/file .'. */
-       if ((argc - i) == 1 && action == LINK) argv[argc++]= ".";
-       /*FALL THROUGH*/
-    default:
-       if ((argc - i) < 2) usage();
-    }
-
-    path_init(&src);
-    path_init(&dst);
-
-    if (action != REMOVE && !mflag
-       && stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)
-    ) {
-       /* The last argument is a directory, this means we have to
-        * throw the whole lot into this directory.  This is the
-        * Right Thing unless you use -r.
-        */
-       path_add(&dst, argv[argc-1]);
-       slash= path_length(&dst);
-
-       do {
-           path_add(&src, argv[i]);
-           path_add(&dst, basename(argv[i]));
-
-           do1(&src, &dst, 0);
-
-           path_trunc(&src, 0);
-           path_trunc(&dst, slash);
-       } while (++i < argc-1);
-    } else
-    if (action == REMOVE || (argc - i) == 2) {
-       /* Just two files (or many files for rm). */
-       do {
-           path_add(&src, argv[i]);
-           if (action != REMOVE) path_add(&dst, argv[i+1]);
-
-           do1(&src, &dst, 0);
-           path_trunc(&src, 0);
-       } while (action == REMOVE && ++i < argc);
-    } else {
-       usage();
-    }
-    path_drop(&src);
-    path_drop(&dst);
-
-#if DEBUG
-    if (nchunks != 0) {
-       fprintf(stderr, "(%ld chunks of memory not freed)\n",
-           (long) nchunks);
-    }
-#endif
-    exit(ex_code);
-    return ex_code;
-}
index 9b92015989d0674cb82564aa882812b1f3d8dbcc..192d04abf814ae669b3fca06cfb2eacdf80e9293 100644 (file)
@@ -695,17 +695,14 @@ mount /dev/$usr /mnt >/dev/null || exit           # Mount the intended /usr.
 
 (cd /usr || exit 1
  list="`ls | fgrep -v install`"
- for d in $list
- do    
-       cp -psmr -v $d /mnt/$d
- done
+       pax -rw -pe -v $list /mnt 2>&1
 ) | progressbar "$USRFILES" || exit    # Copy the usr floppy.
 
 umount /dev/$usr >/dev/null || exit            # Unmount the intended /usr.
 mount /dev/$root /mnt >/dev/null || exit
 
 # Running from the installation CD.
-cp -psmr -vx / /mnt | progressbar "$ROOTFILES" || exit
+pax -rw -pe -vX / /mnt 2>&1 | progressbar "$ROOTFILES" || exit
 chmod o-w /mnt/usr
 cp /mnt/etc/motd.install /mnt/etc/motd
 
@@ -753,7 +750,7 @@ then        if mount /dev/$home /home 2>/dev/null
                do      h=`eval echo "~$u"`
                        if mkdir $h
                        then    echo " * Creating home directory for $u in $h"
-                               cp -psmr /usr/ast $h
+                               pax -rw -pe /usr/ast $h
                                chown -R $u:operator $h
                        else    echo " * Couldn't create $h"
                        fi
index 412dcec276f5b2a6a2da74a1d407e28135e4f4c0..b39017f58772394729d0bb602690b22c0f22b41c 100644 (file)
@@ -1,7 +1,7 @@
 MAN=   ash.1 at.1 \
        bsfilt.1 cawf.1 chgrp.1 \
        cmp.1 compress.1 \
-       cp.1 crc.1 crontab.1 dd.1 \
+       crc.1 crontab.1 dd.1 \
        dhrystone.1 dosdir.1 dosread.1 doswrite.1 \
        eject.1 \
        flexdoc.1 format.1 \
diff --git a/man/man1/cp.1 b/man/man1/cp.1
deleted file mode 100644 (file)
index 511628b..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-.TH CP 1
-.SH NAME
-cp, mv, rm, ln, cpdir, clone \- copy, move, remove, link
-.SH SYNOPSIS
-.B cp
-.RB [ \-pifsmrRvx ]
-.I file1 file2
-.br
-.B cp
-.RB [ \-pifsrRvx ]
-.IR file " ... " dir
-.PP
-.B mv
-.RB [ \-ifsmvx ]
-.I file1 file2
-.br
-.B mv
-.RB [ \-ifsvx ]
-.IR file " ... " dir
-.PP
-.B rm
-.RB [ \-ifrRvx ]
-.IR file " ..."
-.PP
-.B ln
-.RB [ \-ifsSmrRvx ]
-.I file1 file2
-.br
-.B ln
-.RB [ \-ifsSrRvx ]
-.IR file " ... " dir
-.PP
-.B cpdir
-.RB [ \-ifvx ]
-.I file1 file2
-.PP
-.B clone
-.RB [ \-ifsSvx ]
-.I file1 file2
-.SH DESCRIPTION
-.de SP
-.if t .sp 0.4
-.if n .sp
-..
-The utilities
-.BR cp ,
-.BR mv ,
-.BR rm ,
-and
-.B ln
-do basic file management: copying, renaming or moving, deletion, and
-creating links.  (The
-.B cpdir
-and
-.B clone
-utilities are easy to use aliases for copying or linking whole trees.
-They are the same as
-.B cp \-psmr
-and
-.BR "ln \-fmr" )
-.PP
-The first synopsis form of the utilities
-.BR cp ,
-.BR mv ,
-and
-.B ln
-is used if only two arguments are given, and the second argument is not a
-directory.  The source and target file are then the two files given.
-.PP
-If the second synopsis form is used then the last argument must be a
-directory.  Each of the files is copied, moved or linked into this directory.
-.PP
-A file is by default copied by
-.B cp
-without looking at its type, so symlinks are followed and devices are opened
-and read from or written to.  Links between files are ignored.  This
-behavior can be changed by using the proper options.
-.PP
-The
-.B mv
-utility uses the
-.BR rename (2)
-call to rename or move files.  If source and target are on different devices
-however, then
-.B mv
-will use
-.B cp \-pr
-to copy the files or directory trees.
-.PP
-Each utility continues with the next file on errors, except on I/O errors.
-.SH OPTIONS
-.TP
-.B \-p
-Copy the file attributes like mode, owner, group and time of last
-modification.  Normally only the mode is copied to a new file with the file
-creation mask applied.  Setuid bits are cleared if setting the ownership
-fails.
-.TP
-.B \-i
-Ask if ok to overwrite, replace or remove.
-.B Mv
-and
-.B rm
-will ask this automatically if interactive and the target file is writable.
-.B Cp
-will fail if the target cannot be written,
-.B ln
-will always fail if the target exists.
-.TP
-.B \-f
-Makes
-.B cp
-remove a target file before copying if it is not writable,
-.B mv
-removes an existing target without asking,
-.B rm
-does not report any errors, and
-.B ln
-removes an existing target file before linking.  The last of
-.B \-i
-and
-.B \-f
-wins for
-.B mv
-if both flags are set, the other utilities do something sensible, like asking
-before forcefully removing.
-.TP
-.B \-s
-Make a symlink instead of a normal link.  For utilities other than
-.B ln
-this flag means "copy similar".  The modified time is always copied for
-.B cp \-s
-and the other attributes are copied if a new file is created.  The normal
-\s-2POSIX\s+2 required patronizing like applying the file creation mask or
-clearing setuid bits is not done.
-.TP
-.B \-S
-Make a symlink if a normal link cannot be made because source and target are
-on different devices.  The symlink is required to really refer back to the
-source, meaning that a/b must exist in the call
-.BR "ln \-S a/b c/d" ,
-and that the symlink from c/d must lead back to a/b.  So the symlink will be
-created as if
-.B "ln \-s ../a/b c/d"
-was called.  If the target is a full path, but the source is not then an
-error will be given saying that this is "too difficult."
-.TP
-.B \-m
-Merge trees.  The first synopsis form is assumed, and the files from one
-tree are merged into the other.  There is no "if it's a directory the put
-it into that directory" trickery here.
-.TP
-.BR \-r ", " \-R
-Recursively copy, remove, or link.  If the source is a directory then the
-files in this directory are copied to similarly named files in the target
-directory.  Special files are copied as new special files, they are not read
-or written.  Symlinks are still expanded and the link structure ignored with
-.BR \-R .
-The
-.B \-r
-flag does copy symlinks as symlinks and keeps the link structure intact.
-(Note that
-.B \-R
-is invented by \s-2POSIX\s+2 as a replacement for the classic
-.B \-r
-option of older copy commands that did read special files.  The standard
-says that
-.B \-r
-is implementation defined, so that's why this flag is better than
-.B \-R
-in this implementation of
-.BR cp .)
-For
-.B rm
-and
-.B ln
-both flags mean the same.
-.B Ln
-will recursively link the files in the trees, except symlinks, they are
-copied.  If symlinks are created with
-.B ln \-rs
-or
-.B ln \-rS
-then they are required "to work" as described with the
-.B \-S
-flag.
-.TP
-.B \-v
-Verbose.  Show what is done on standard output.
-.TP
-.B \-x
-Do not cross mount points.  Empty directories will be created if the source
-directory is a mount point on a copy, move or link.  A mount point will not
-be removed or traversed recursively.  This flag allows one to copy the root
-device, e.g.
-.BR "cpdir \-x / /mnt" .
-.SH "SEE ALSO"
-.BR cat (1),
-.BR mkdir (1),
-.BR rmdir (1),
-.BR mkdir (2),
-.BR rmdir (2),
-.BR link (2),
-.BR unlink (2),
-.BR rename (2),
-.BR open (2),
-.BR read (2),
-.BR write (2),
-.BR opendir (3).
-.SH NOTES
-All the utilities described are links to the same program.
-.SH BUGS
-.B Mv
-should first copy a tree across devices and then remove the source tree if
-there was no error.  Instead, each file in the tree is copied and
-immediately removed.  On error you may be left with two half-filled trees,
-together containing all of the files.  You may have to restart the move with
-.BR "mv \-m" .
-.PP
-.B Rm
-should be able to remove arbitrarily deep trees.
-.SH AUTHOR
-Kees J. Bot (kjb@cs.vu.nl)
index 73ffb8af1495f901699921a7dc2315f503c879a9..e0ee5520bd7106b5430fe4c61fe18266637d8356 100644 (file)
@@ -311,7 +311,7 @@ Next make a file system for on-disk /usr and copy the floppy /usr on to it.
 .PP
 .XB "mkfs\0/dev/c0d0p1s2"
 .XB "mount\0/dev/c0d0p1s2\0/mnt"
-.XB "cp -psmr\0\-v\0/usr\0/mnt"
+.XB "pax -rw -pe\0\-v\0/usr\0/mnt"
 .PP
 This will create a file system on /dev/c0d0p1s2, mount it on /mnt, and copy the
 contents of the \s-2USR\s+2 floppy onto it.
@@ -330,7 +330,7 @@ fill it from the floppy:
 .XB "mkfs\0\-i\0512\0/dev/c0d0p1s0"
 .XB "mount\0/dev/fd0\0/fd0"
 .XB "mount\0/dev/c0d0p1s0\0/mnt"
-.XB "cp -psmr\0\-v\0/fd0\0/mnt"
+.XB "pax -rw -pe\0\-v\0/fd0\0/mnt"
 .XB "umount\0/dev/fd0"
 .PP
 Remove
@@ -646,7 +646,7 @@ This is slower then a RAM disk, but saves a lot of memory.
 The automatic installation script knows how to handle this new situation.
 If you install manually then you have to use
 .PP
-.XB "cp -psmr\0\-vx\0/\0/mnt"
+.XB "pax -rw -pe\0\-vx\0/\0/mnt"
 .PP
 to copy the root device to disk.  When it is time to fill /usr and you only
 have one floppy drive then hit DEL to get out of the installation script and