# $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
--- /dev/null
+# $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>
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+/* $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 */
+}
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 \
+++ /dev/null
-# LSC For Now...
-NOGCCERROR:= yes
-PROG= cp
-BINDIR= /bin
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* 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;
-}
(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
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
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 \
+++ /dev/null
-.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)
.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.
.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
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