234 lines
5.5 KiB
C
234 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 1983 Regents of the University of California.
|
|
* All rights reserved. The Berkeley software License Agreement
|
|
* specifies the terms and conditions for redistribution.
|
|
*/
|
|
|
|
/*
|
|
* cp
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/dir.h>
|
|
#include <sys/time.h>
|
|
|
|
int iflag;
|
|
int rflag;
|
|
int pflag;
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
struct stat stb;
|
|
int rc, i;
|
|
|
|
argc--, argv++;
|
|
while (argc > 0 && **argv == '-') {
|
|
(*argv)++;
|
|
while (**argv) switch (*(*argv)++) {
|
|
|
|
case 'i':
|
|
iflag++; break;
|
|
|
|
case 'R':
|
|
case 'r':
|
|
rflag++; break;
|
|
|
|
case 'p': /* preserve mtimes, atimes, and modes */
|
|
pflag++;
|
|
(void) umask(0);
|
|
break;
|
|
|
|
default:
|
|
goto usage;
|
|
}
|
|
argc--; argv++;
|
|
}
|
|
if (argc < 2)
|
|
goto usage;
|
|
if (argc > 2) {
|
|
if (stat(argv[argc-1], &stb) < 0)
|
|
goto usage;
|
|
if ((stb.st_mode&S_IFMT) != S_IFDIR)
|
|
goto usage;
|
|
}
|
|
rc = 0;
|
|
for (i = 0; i < argc-1; i++)
|
|
rc |= copy(argv[i], argv[argc-1]);
|
|
exit(rc);
|
|
usage:
|
|
fprintf(stderr,
|
|
"Usage: cp [-ip] f1 f2; or: cp [-irp] f1 ... fn d2\n");
|
|
exit(1);
|
|
}
|
|
|
|
copy(from, to)
|
|
char *from, *to;
|
|
{
|
|
int fold, fnew, n, exists;
|
|
char *last, destname[MAXPATHLEN + 1], buf[MAXBSIZE];
|
|
struct stat stfrom, stto;
|
|
|
|
fold = open(from, 0);
|
|
if (fold < 0) {
|
|
Perror(from);
|
|
return (1);
|
|
}
|
|
if (fstat(fold, &stfrom) < 0) {
|
|
Perror(from);
|
|
(void) close(fold);
|
|
return (1);
|
|
}
|
|
if (stat(to, &stto) >= 0 &&
|
|
(stto.st_mode&S_IFMT) == S_IFDIR) {
|
|
last = rindex(from, '/');
|
|
if (last) last++; else last = from;
|
|
if (strlen(to) + strlen(last) >= sizeof destname - 1) {
|
|
fprintf(stderr, "cp: %s/%s: Name too long", to, last);
|
|
(void) close(fold);
|
|
return(1);
|
|
}
|
|
(void) sprintf(destname, "%s/%s", to, last);
|
|
to = destname;
|
|
}
|
|
if (rflag && (stfrom.st_mode&S_IFMT) == S_IFDIR) {
|
|
int fixmode = 0; /* cleanup mode after rcopy */
|
|
|
|
(void) close(fold);
|
|
if (stat(to, &stto) < 0) {
|
|
if (mkdir(to, (stfrom.st_mode & 07777) | 0700) < 0) {
|
|
Perror(to);
|
|
return (1);
|
|
}
|
|
fixmode = 1;
|
|
} else if ((stto.st_mode&S_IFMT) != S_IFDIR) {
|
|
fprintf(stderr, "cp: %s: Not a directory.\n", to);
|
|
return (1);
|
|
} else if (pflag)
|
|
fixmode = 1;
|
|
n = rcopy(from, to);
|
|
if (fixmode)
|
|
(void) chmod(to, stfrom.st_mode & 07777);
|
|
return (n);
|
|
}
|
|
|
|
if ((stfrom.st_mode&S_IFMT) == S_IFDIR)
|
|
fprintf(stderr,
|
|
"cp: %s: Is a directory (copying as plain file).\n",
|
|
from);
|
|
|
|
exists = stat(to, &stto) == 0;
|
|
if (exists) {
|
|
if (stfrom.st_dev == stto.st_dev &&
|
|
stfrom.st_ino == stto.st_ino) {
|
|
fprintf(stderr,
|
|
"cp: %s and %s are identical (not copied).\n",
|
|
from, to);
|
|
(void) close(fold);
|
|
return (1);
|
|
}
|
|
if (iflag && isatty(fileno(stdin))) {
|
|
int i, c;
|
|
|
|
fprintf (stderr, "overwrite %s? ", to);
|
|
i = c = getchar();
|
|
while (c != '\n' && c != EOF)
|
|
c = getchar();
|
|
if (i != 'y') {
|
|
(void) close(fold);
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
fnew = creat(to, stfrom.st_mode & 07777);
|
|
if (fnew < 0) {
|
|
Perror(to);
|
|
(void) close(fold); return(1);
|
|
}
|
|
if (exists && pflag)
|
|
(void) fchmod(fnew, stfrom.st_mode & 07777);
|
|
|
|
for (;;) {
|
|
n = read(fold, buf, sizeof buf);
|
|
if (n == 0)
|
|
break;
|
|
if (n < 0) {
|
|
Perror(from);
|
|
(void) close(fold); (void) close(fnew); return (1);
|
|
}
|
|
if (write(fnew, buf, n) != n) {
|
|
Perror(to);
|
|
(void) close(fold); (void) close(fnew); return (1);
|
|
}
|
|
}
|
|
(void) close(fold); (void) close(fnew);
|
|
if (pflag)
|
|
return (setimes(to, &stfrom));
|
|
return (0);
|
|
}
|
|
|
|
rcopy(from, to)
|
|
char *from, *to;
|
|
{
|
|
DIR *fold = opendir(from);
|
|
struct direct *dp;
|
|
struct stat statb;
|
|
int errs = 0;
|
|
char fromname[MAXPATHLEN + 1];
|
|
|
|
if (fold == 0 || (pflag && fstat(fold->dd_fd, &statb) < 0)) {
|
|
Perror(from);
|
|
return (1);
|
|
}
|
|
for (;;) {
|
|
dp = readdir(fold);
|
|
if (dp == 0) {
|
|
closedir(fold);
|
|
if (pflag)
|
|
return (setimes(to, &statb) + errs);
|
|
return (errs);
|
|
}
|
|
if (dp->d_ino == 0)
|
|
continue;
|
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
|
|
continue;
|
|
if (strlen(from)+1+strlen(dp->d_name) >= sizeof fromname - 1) {
|
|
fprintf(stderr, "cp: %s/%s: Name too long.\n",
|
|
from, dp->d_name);
|
|
errs++;
|
|
continue;
|
|
}
|
|
(void) sprintf(fromname, "%s/%s", from, dp->d_name);
|
|
errs += copy(fromname, to);
|
|
}
|
|
}
|
|
|
|
int
|
|
setimes(path, statp)
|
|
char *path;
|
|
struct stat *statp;
|
|
{
|
|
struct timeval tv[2];
|
|
|
|
tv[0].tv_sec = statp->st_atime;
|
|
tv[1].tv_sec = statp->st_mtime;
|
|
tv[0].tv_usec = tv[1].tv_usec = 0;
|
|
if (utimes(path, tv) < 0) {
|
|
Perror(path);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
Perror(s)
|
|
char *s;
|
|
{
|
|
fprintf(stderr, "cp: ");
|
|
perror(s);
|
|
}
|