+ link[len] = '\0';
+ if (exists && unlink(to.p_path)) {
+ error(to.p_path);
+ return;
+ }
+ if (symlink(link, to.p_path)) {
+ error(link);
+ return;
+ }
+}
+
+copy_fifo(from_stat, exists)
+ struct stat *from_stat;
+ int exists;
+{
+ if (exists && unlink(to.p_path)) {
+ error(to.p_path);
+ return;
+ }
+ if (mkfifo(to.p_path, from_stat->st_mode)) {
+ error(to.p_path);
+ return;
+ }
+ if (pflag)
+ setfile(from_stat, 0);
+}
+
+copy_special(from_stat, exists)
+ struct stat *from_stat;
+ int exists;
+{
+ if (exists && unlink(to.p_path)) {
+ error(to.p_path);
+ return;
+ }
+ if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
+ error(to.p_path);
+ return;
+ }
+ if (pflag)
+ setfile(from_stat, 0);
+}
+
+setfile(fs, fd)
+ register struct stat *fs;
+ int fd;
+{
+ static struct timeval tv[2];
+ char path[100];
+
+ fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
+
+ tv[0].tv_sec = fs->st_atime;
+ tv[1].tv_sec = fs->st_mtime;
+ if (utimes(to.p_path, tv)) {
+ (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path);
+ error(path);
+ }
+ /*
+ * 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) :
+ chown(to.p_path, fs->st_uid, fs->st_gid)) {
+ if (errno != EPERM) {
+ (void)snprintf(path, sizeof(path),
+ "chown: %s", to.p_path);
+ error(path);
+ }
+ fs->st_mode &= ~(S_ISUID|S_ISGID);
+ }
+ if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
+ (void)snprintf(path, sizeof(path), "chown: %s", to.p_path);
+ error(path);
+ }
+}
+
+error(s)
+ char *s;
+{
+ exit_val = 1;
+ (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+"usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n");
+ exit(1);