1 /* cp 1.12 - copy files Author: Kees J. Bot
2 * mv - move files 20 Jul 1993
5 * cpdir - copy a directory tree (cp -psmr)
6 * clone - make a link farm (ln -fmr)
10 #include <sys/types.h>
29 /* Copy files in this size chunks: */
30 #if __minix && !__minix_vmd
31 #define CHUNK (8192 * sizeof(char *))
33 #define CHUNK (1024 << (sizeof(int) + sizeof(char *)))
38 #define CONFORMING 1 /* Precisely POSIX conforming. */
42 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
43 #define arraylimit(a) ((a) + arraysize(a))
45 char *prog_name
; /* Call name of this program. */
46 int ex_code
= 0; /* Final exit code. */
48 typedef enum identity
{ CP
, MV
, RM
, LN
, CPDIR
, CLONE
} identity_t
;
49 typedef enum action
{ COPY
, MOVE
, REMOVE
, LINK
} action_t
;
51 identity_t identity
; /* How did the user call me? */
52 action_t action
; /* Copying, moving, or linking. */
53 int pflag
= 0; /* -p/-s: Make orginal and copy the same. */
54 int iflag
= 0; /* -i: Interactive overwriting/deleting. */
55 int fflag
= 0; /* -f: Force. */
56 int sflag
= 0; /* -s: Make a symbolic link (ln/clone). */
57 int Sflag
= 0; /* -S: Make a symlink if across devices. */
58 int mflag
= 0; /* -m: Merge trees, no target dir trickery. */
59 int rflag
= 0; /* -r/-R: Recursively copy a tree. */
60 int vflag
= 0; /* -v: Verbose. */
61 int xflag
= 0; /* -x: Don't traverse past mount points. */
62 int xdev
= 0; /* Set when moving or linking cross-device. */
63 int expand
= 0; /* Expand symlinks, ignore links. */
64 int conforming
= CONFORMING
; /* Sometimes standards are a pain. */
66 int fc_mask
; /* File creation mask. */
67 int uid
, gid
; /* Effective uid & gid. */
68 int istty
; /* Can have terminal input. */
71 /* There were no symlinks in medieval times. */
72 #define S_ISLNK(mode) (0)
74 #define symlink(path1, path2) (errno= ENOSYS, -1)
75 #define readlink(path, buf, len) (errno= ENOSYS, -1)
78 void report(const char *label
)
80 if (action
== REMOVE
&& fflag
) return;
81 fprintf(stderr
, "%s: %s: %s\n", prog_name
, label
, strerror(errno
));
85 void fatal(const char *label
)
91 void report2(const char *src
, const char *dst
)
93 fprintf(stderr
, "%s %s %s: %s\n", prog_name
, src
, dst
, strerror(errno
));
98 size_t nchunks
= 0; /* Number of allocated cells. */
101 void *allocate(void *mem
, size_t size
)
102 /* Like realloc, but with checking of the return value. */
105 if (mem
== nil
) nchunks
++;
107 if ((mem
= mem
== nil
? malloc(size
) : realloc(mem
, size
)) == nil
)
112 void deallocate(void *mem
)
113 /* Release a chunk of memory. */
123 typedef struct pathname
{
124 char *path
; /* The actual pathname. */
125 size_t idx
; /* Index for the terminating null byte. */
126 size_t lim
; /* Actual length of the path array. */
129 void path_init(pathname_t
*pp
)
130 /* Initialize a pathname to the null string. */
132 pp
->path
= allocate(nil
, pp
->lim
= DIRSIZ
+ 2);
133 pp
->path
[pp
->idx
= 0]= 0;
136 void path_add(pathname_t
*pp
, const char *name
)
137 /* Add a component to a pathname. */
142 lim
= pp
->idx
+ strlen(name
) + 2;
145 pp
->lim
= lim
+= lim
/2; /* add an extra 50% growing space. */
147 pp
->path
= allocate(pp
->path
, lim
);
150 p
= pp
->path
+ pp
->idx
;
151 if (p
> pp
->path
&& p
[-1] != '/') *p
++ = '/';
154 if (*name
!= '/' || p
== pp
->path
|| p
[-1] != '/') *p
++ = *name
;
158 pp
->idx
= p
- pp
->path
;
161 void path_trunc(pathname_t
*pp
, size_t didx
)
162 /* Delete part of a pathname to a remembered length. */
164 pp
->path
[pp
->idx
= didx
]= 0;
168 const char *path_name(const pathname_t
*pp
)
169 /* Return the actual name as a C string. */
174 size_t path_length(const pathname_t
*pp
)
175 /* The length of the pathname. */
180 void path_drop(pathname_t
*pp
)
181 /* Release the storage occupied by the pathname. */
183 deallocate(pp
->path
);
187 #define path_name(pp) ((const char *) (pp)->path)
188 #define path_length(pp) ((pp)->idx)
189 #define path_drop(pp) deallocate((void *) (pp)->path)
192 char *basename(const char *path
)
193 /* Return the last component of a pathname. (Note: declassifies a const
194 * char * just like strchr.
200 while (*p
== '/') p
++; /* Trailing slashes? */
205 while (*p
!= 0 && *p
!= '/') p
++; /* Skip component. */
207 return (char *) path
;
210 int affirmative(void)
211 /* Get a yes/no answer from the suspecting user. */
219 while ((c
= getchar()) == ' ') {}
220 ok
= (c
== 'y' || c
== 'Y');
221 while (c
!= EOF
&& c
!= '\n') c
= getchar();
226 int writable(const struct stat
*stp
)
227 /* True iff the file with the given attributes allows writing. (And we have
228 * a terminal to ask if ok to overwrite.)
231 if (!istty
|| uid
== 0) return 1;
232 if (stp
->st_uid
== uid
) return stp
->st_mode
& S_IWUSR
;
233 if (stp
->st_gid
== gid
) return stp
->st_mode
& S_IWGRP
;
234 return stp
->st_mode
& S_IWOTH
;
238 #define PATH_MAX 1024
241 static char *link_islink(struct stat
*stp
, const char *file
)
243 /* Tell if a file, which stat(2) information in '*stp', has been seen
244 * earlier by this function under a different name. If not return a
245 * null pointer with errno set to ENOENT, otherwise return the name of
246 * the link. Return a null pointer with an error code in errno for any
247 * error, using E2BIG for a too long file name.
249 * Use link_islink(nil, nil) to reset all bookkeeping.
251 * Call for a file twice to delete it from the store.
254 typedef struct link
{ /* In-memory link store. */
255 struct link
*next
; /* Hash chain on inode number. */
256 ino_t ino
; /* File's inode number. */
257 off_t off
; /* Offset to more info in temp file. */
259 typedef struct dlink
{ /* On-disk link store. */
260 dev_t dev
; /* Device number. */
261 char file
[PATH_MAX
]; /* Name of earlier seen link. */
263 static link_t
*links
[256]; /* Hash list of known links. */
264 static int tfd
= -1; /* Temp file for file name storage. */
265 static dlink_t dlink
;
271 /* Reset everything. */
272 for (plp
= links
; plp
< arraylimit(links
); plp
++) {
273 while ((lp
= *plp
) != nil
) {
278 if (tfd
!= -1) close(tfd
);
283 /* The file must be a non-directory with more than one link. */
284 if (S_ISDIR(stp
->st_mode
) || stp
->st_nlink
<= 1) {
289 plp
= &links
[stp
->st_ino
% arraysize(links
)];
291 while ((lp
= *plp
) != nil
) {
292 if (lp
->ino
== stp
->st_ino
) {
293 /* May have seen this link before. Get it and check. */
294 if (lseek(tfd
, lp
->off
, SEEK_SET
) == -1) return nil
;
295 if (read(tfd
, &dlink
, sizeof(dlink
)) < 0) return nil
;
297 /* Only need to check the device number. */
298 if (dlink
.dev
== stp
->st_dev
) {
299 if (strcmp(file
, dlink
.file
) == 0) {
300 /* Called twice. Forget about this link. */
307 /* Return the name of the earlier link. */
314 /* First time I see this link. Add it to the store. */
320 tfd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
322 if (errno
!= EEXIST
) return nil
;
329 if ((len
= strlen(file
)) >= PATH_MAX
) {
334 dlink
.dev
= stp
->st_dev
;
335 strcpy(dlink
.file
, file
);
336 len
+= offsetof(dlink_t
, file
) + 1;
337 if ((off
= lseek(tfd
, 0, SEEK_END
)) == -1) return nil
;
338 if (write(tfd
, &dlink
, len
) != len
) return nil
;
340 if ((lp
= malloc(sizeof(*lp
))) == nil
) return nil
;
342 lp
->ino
= stp
->st_ino
;
349 int trylink(const char *src
, const char *dst
, struct stat
*srcst
,
351 /* Keep the link structure intact if src has been seen before. */
356 if (action
== COPY
&& expand
) return 0;
358 if ((olddst
= link_islink(srcst
, dst
)) == nil
) {
359 /* if (errno != ENOENT) ... */
363 /* Try to link the file copied earlier to the new file. */
364 if (dstst
->st_ino
!= 0) (void) unlink(dst
);
366 if ((linked
= (link(olddst
, dst
) == 0)) && vflag
)
367 printf("ln %s ..\n", olddst
);
372 int copy(const char *src
, const char *dst
, struct stat
*srcst
,
374 /* Copy one file to another and copy (some of) the attributes. */
380 assert(srcst
->st_ino
!= 0);
382 if (dstst
->st_ino
== 0) {
383 /* The file doesn't exist yet. */
385 if (!S_ISREG(srcst
->st_mode
)) {
386 /* Making a new mode 666 regular file. */
387 srcst
->st_mode
= (S_IFREG
| 0666) & fc_mask
;
389 if (!pflag
&& conforming
) {
390 /* Making a new file copying mode with umask applied. */
391 srcst
->st_mode
&= fc_mask
;
394 /* File exists, ask if ok to overwrite if '-i'. */
396 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
397 fprintf(stderr
, "Overwrite %s? (mode = %03o) ",
398 dst
, dstst
->st_mode
& 07777);
399 if (!affirmative()) return 0;
402 if (action
== MOVE
) {
403 /* Don't overwrite, remove first. */
404 if (unlink(dst
) < 0 && errno
!= ENOENT
) {
411 /* Keep the existing mode and ownership. */
412 srcst
->st_mode
= dstst
->st_mode
;
413 srcst
->st_uid
= dstst
->st_uid
;
414 srcst
->st_gid
= dstst
->st_gid
;
419 /* Keep the link structure if possible. */
420 if (trylink(src
, dst
, srcst
, dstst
)) return 1;
422 if ((srcfd
= open(src
, O_RDONLY
)) < 0) {
427 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, srcst
->st_mode
& 0777);
428 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
429 /* Retry adding a "w" bit. */
430 (void) chmod(dst
, dstst
->st_mode
| S_IWUSR
);
431 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
433 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
434 /* Retry after trying to delete. */
436 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
444 /* Get current parameters. */
445 if (fstat(dstfd
, dstst
) < 0) {
452 /* Copy the little bytes themselves. */
453 while ((n
= read(srcfd
, buf
, sizeof(buf
))) > 0) {
457 while (n
> 0 && (r
= write(dstfd
, bp
, n
)) > 0) {
464 "%s: Warning: EOF writing to %s\n",
482 /* Copy the ownership. */
483 if ((pflag
|| !conforming
)
484 && S_ISREG(dstst
->st_mode
)
485 && (dstst
->st_uid
!= srcst
->st_uid
486 || dstst
->st_gid
!= srcst
->st_gid
)
488 if (chmod(dst
, 0) == 0) dstst
->st_mode
&= ~07777;
489 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
490 if (errno
!= EPERM
) {
495 dstst
->st_uid
= srcst
->st_uid
;
496 dstst
->st_gid
= srcst
->st_gid
;
500 if (conforming
&& S_ISREG(dstst
->st_mode
)
501 && (dstst
->st_uid
!= srcst
->st_uid
502 || dstst
->st_gid
!= srcst
->st_gid
)
504 /* Suid bits must be cleared in the holy name of
505 * security (and the assumed user stupidity).
507 srcst
->st_mode
&= ~06000;
511 if (S_ISREG(dstst
->st_mode
) && dstst
->st_mode
!= srcst
->st_mode
) {
512 if (chmod(dst
, srcst
->st_mode
) < 0) {
513 if (errno
!= EPERM
) {
517 fprintf(stderr
, "%s: Can't change the mode of %s\n",
522 /* Copy the file modification time. */
523 if ((pflag
|| !conforming
) && S_ISREG(dstst
->st_mode
)) {
526 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
527 ut
.modtime
= srcst
->st_mtime
;
528 if (utime(dst
, &ut
) < 0) {
529 if (errno
!= EPERM
) {
535 "%s: Can't set the time of %s\n",
541 printf(action
== COPY
? "cp %s ..\n" : "mv %s ..\n", src
);
546 void copy1(const char *src
, const char *dst
, struct stat
*srcst
,
548 /* Inspect the source file and then copy it. Treatment of symlinks and
549 * special files is a bit complicated. The filetype and link-structure are
550 * ignored if (expand && !rflag), symlinks and link-structure are ignored
551 * if (expand && rflag), everything is copied precisely if !expand.
556 assert(srcst
->st_ino
!= 0);
558 if (srcst
->st_ino
== dstst
->st_ino
&& srcst
->st_dev
== dstst
->st_dev
) {
559 fprintf(stderr
, "%s: can't copy %s onto itself\n",
565 /* You can forget it if the destination is a directory. */
566 if (dstst
->st_ino
!= 0 && S_ISDIR(dstst
->st_mode
)) {
572 if (S_ISREG(srcst
->st_mode
) || (expand
&& !rflag
)) {
573 if (!copy(src
, dst
, srcst
, dstst
)) return;
575 if (action
== MOVE
&& unlink(src
) < 0) {
582 if (dstst
->st_ino
!= 0) {
583 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
584 fprintf(stderr
, "Replace %s? (mode = %03o) ",
585 dst
, dstst
->st_mode
& 07777);
586 if (!affirmative()) return;
588 if (unlink(dst
) < 0) {
595 /* Apply the file creation mask if so required. */
596 if (!pflag
&& conforming
) srcst
->st_mode
&= fc_mask
;
600 if (S_ISLNK(srcst
->st_mode
)) {
603 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
608 r
= symlink(buf
, dst
);
610 printf("ln -s %s %s\n", buf
, dst
);
612 if (trylink(src
, dst
, srcst
, dstst
)) {
616 if (S_ISFIFO(srcst
->st_mode
)) {
617 r
= mkfifo(dst
, srcst
->st_mode
);
619 printf("mkfifo %s\n", dst
);
621 if (S_ISBLK(srcst
->st_mode
) || S_ISCHR(srcst
->st_mode
)) {
622 r
= mknod(dst
, srcst
->st_mode
, srcst
->st_rdev
);
623 if (vflag
&& r
== 0) {
624 printf("mknod %s %c %d %d\n",
626 S_ISBLK(srcst
->st_mode
) ? 'b' : 'c',
627 (srcst
->st_rdev
>> 8) & 0xFF,
628 (srcst
->st_rdev
>> 0) & 0xFF);
631 fprintf(stderr
, "%s: %s: odd filetype %5o (not copied)\n",
632 prog_name
, src
, srcst
->st_mode
);
637 if (r
< 0 || lstat(dst
, dstst
) < 0) {
642 if (action
== MOVE
&& unlink(src
) < 0) {
644 (void) unlink(dst
); /* Don't want it twice. */
650 if (S_ISLNK(srcst
->st_mode
)) return;
652 /* Copy the ownership. */
653 if ((pflag
|| !conforming
)
654 && (dstst
->st_uid
!= srcst
->st_uid
655 || dstst
->st_gid
!= srcst
->st_gid
)
657 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
658 if (errno
!= EPERM
) {
665 /* Copy the file modification time. */
666 if (pflag
|| !conforming
) {
669 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
670 ut
.modtime
= srcst
->st_mtime
;
671 if (utime(dst
, &ut
) < 0) {
672 if (errno
!= EPERM
) {
676 fprintf(stderr
, "%s: Can't set the time of %s\n",
682 void remove1(const char *src
, struct stat
*srcst
)
684 if (iflag
|| (!fflag
&& !writable(srcst
))) {
685 fprintf(stderr
, "Remove %s? (mode = %03o) ", src
,
686 srcst
->st_mode
& 07777);
687 if (!affirmative()) return;
689 if (unlink(src
) < 0) {
692 if (vflag
) printf("rm %s\n", src
);
696 void link1(const char *src
, const char *dst
, struct stat
*srcst
,
702 if (dstst
->st_ino
!= 0 && (iflag
|| fflag
)) {
703 if (srcst
->st_ino
== dstst
->st_ino
) {
705 fprintf(stderr
, "%s: Can't link %s onto itself\n",
711 fprintf(stderr
, "Remove %s? ", dst
);
712 if (!affirmative()) return;
715 if (S_ISDIR(dstst
->st_mode
) || unlink(dst
) < 0) {
721 if (!sflag
&& !(rflag
&& S_ISLNK(srcst
->st_mode
)) && !(Sflag
&& xdev
)) {
723 if (link(src
, dst
) < 0) {
724 if (!Sflag
|| errno
!= EXDEV
) {
728 /* Can't do a cross-device link, we have to symlink. */
731 if (vflag
) printf("ln %s..\n", src
);
737 if (!rflag
&& !Sflag
) {
738 /* We can get away with a "don't care if it works" symlink. */
739 if (symlink(src
, dst
) < 0) {
743 if (vflag
) printf("ln -s %s %s\n", src
, dst
);
747 /* If the source is a symlink then it is simply copied. */
748 if (S_ISLNK(srcst
->st_mode
)) {
752 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
757 if (symlink(buf
, dst
) < 0) {
761 if (vflag
) printf("ln -s %s %s\n", buf
, dst
);
765 /* Make a symlink that has to work, i.e. we must be able to access the
766 * source now, and the link must work.
768 if (dst
[0] == '/' && src
[0] != '/') {
769 /* ln -[rsS] relative/path /full/path. */
771 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
772 prog_name
, src
, dst
);
776 /* Count the number of subdirectories in the destination file and
777 * add one '..' for each.
784 if (p
[1] == '/' || p
[1] == 0) {
785 /* A "." component; skip. */
786 do p
++; while (*p
== '/');
789 if (p
[1] == '.' && (p
[2] == '/' || p
[2] == 0)) {
790 /* A ".." component; oops. */
791 switch (path_length(&sym
)) {
794 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
795 prog_name
, src
, dst
);
801 path_trunc(&sym
, path_length(&sym
) - 3);
804 do p
++; while (*p
== '/');
808 while (*p
!= 0 && *p
!= '/') p
++;
809 while (*p
== '/') p
++;
811 path_add(&sym
, "..");
816 if (symlink(path_name(&sym
), dst
) < 0) {
819 if (vflag
) printf("ln -s %s %s\n", path_name(&sym
), dst
);
824 typedef struct entrylist
{
825 struct entrylist
*next
;
829 int eat_dir(const char *dir
, entrylist_t
**dlist
)
830 /* Make a linked list of all the names in a directory. */
833 struct dirent
*entry
;
835 if ((dp
= opendir(dir
)) == nil
) return 0;
837 while ((entry
= readdir(dp
)) != nil
) {
838 if (strcmp(entry
->d_name
, ".") == 0) continue;
839 if (strcmp(entry
->d_name
, "..") == 0) continue;
841 *dlist
= allocate(nil
, sizeof(**dlist
));
842 (*dlist
)->name
= allocate(nil
, strlen(entry
->d_name
)+1);
843 strcpy((*dlist
)->name
, entry
->d_name
);
844 dlist
= &(*dlist
)->next
;
851 void chop_dlist(entrylist_t
**dlist
)
852 /* Chop an entry of a name list. */
854 entrylist_t
*junk
= *dlist
;
857 deallocate(junk
->name
);
861 void drop_dlist(entrylist_t
*dlist
)
862 /* Get rid of a whole list. */
864 while (dlist
!= nil
) chop_dlist(&dlist
);
867 void do1(pathname_t
*src
, pathname_t
*dst
, int depth
)
868 /* Perform the appropriate action on a source and destination file. */
870 size_t slashsrc
, slashdst
;
871 struct stat srcst
, dstst
;
873 static ino_t topdst_ino
;
874 static dev_t topdst_dev
;
875 static dev_t topsrc_dev
;
878 if (vflag
&& depth
== 0) {
879 char flags
[100], *pf
= flags
;
881 if (pflag
) *pf
++= 'p';
882 if (iflag
) *pf
++= 'i';
883 if (fflag
) *pf
++= 'f';
884 if (sflag
) *pf
++= 's';
885 if (Sflag
) *pf
++= 'S';
886 if (mflag
) *pf
++= 'm';
887 if (rflag
) *pf
++= 'r';
888 if (vflag
) *pf
++= 'v';
889 if (xflag
) *pf
++= 'x';
890 if (expand
) *pf
++= 'L';
891 if (conforming
) *pf
++= 'C';
893 printf(": %s -%s %s %s\n", prog_name
, flags
,
894 path_name(src
), path_name(dst
));
898 /* st_ino == 0 if not stat()'ed yet, or nonexistent. */
902 if (action
!= LINK
|| !sflag
|| rflag
) {
903 /* Source must exist unless symlinking. */
904 if ((expand
? stat
: lstat
)(path_name(src
), &srcst
) < 0) {
905 report(path_name(src
));
911 /* First call: Not cross-device yet, first dst not seen yet,
912 * remember top device number.
916 topsrc_dev
= srcst
.st_dev
;
919 /* Inspect the intended destination unless removing. */
920 if (action
!= REMOVE
) {
921 if ((expand
? stat
: lstat
)(path_name(dst
), &dstst
) < 0) {
922 if (errno
!= ENOENT
) {
923 report(path_name(dst
));
929 if (action
== MOVE
&& !xdev
) {
930 if (dstst
.st_ino
!= 0 && srcst
.st_dev
!= dstst
.st_dev
) {
931 /* It's a cross-device rename, i.e. copy and remove. */
934 if (!mflag
|| dstst
.st_ino
== 0 || !S_ISDIR(dstst
.st_mode
)) {
935 /* Try to simply rename the file (not merging trees). */
937 if (srcst
.st_ino
== dstst
.st_ino
) {
939 "%s: Can't move %s onto itself\n",
940 prog_name
, path_name(src
));
945 if (dstst
.st_ino
!= 0) {
946 if (iflag
|| (!fflag
&& !writable(&dstst
))) {
948 "Replace %s? (mode = %03o) ",
950 dstst
.st_mode
& 07777);
951 if (!affirmative()) return;
953 if (!S_ISDIR(dstst
.st_mode
))
954 (void) unlink(path_name(dst
));
957 if (rename(path_name(src
), path_name(dst
)) == 0) {
960 printf("mv %s %s\n", path_name(src
),
965 if (errno
== EXDEV
) {
968 report2(path_name(src
), path_name(dst
));
974 if (srcst
.st_ino
== 0 || !S_ISDIR(srcst
.st_mode
)) {
975 /* Copy/move/remove/link a single file. */
979 copy1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
982 remove1(path_name(src
), &srcst
);
985 link1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
991 /* Recursively copy/move/remove/link a directory if -r or -R. */
994 report(path_name(src
));
998 /* Ok to remove contents of dir? */
999 if (action
== REMOVE
) {
1000 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
1001 /* Don't recurse past a mount point. */
1005 fprintf(stderr
, "Remove contents of %s? ", path_name(src
));
1006 if (!affirmative()) return;
1010 /* Gather the names in the source directory. */
1011 if (!eat_dir(path_name(src
), &dlist
)) {
1012 report(path_name(src
));
1016 /* Check/create the target directory. */
1017 if (action
!= REMOVE
&& dstst
.st_ino
!= 0 && !S_ISDIR(dstst
.st_mode
)) {
1018 if (action
!= MOVE
&& !fflag
) {
1020 report(path_name(dst
));
1024 fprintf(stderr
, "Replace %s? ", path_name(dst
));
1025 if (!affirmative()) {
1030 if (unlink(path_name(dst
)) < 0) {
1031 report(path_name(dst
));
1038 if (action
!= REMOVE
) {
1039 if (dstst
.st_ino
== 0) {
1040 /* Create a new target directory. */
1041 if (!pflag
&& conforming
) srcst
.st_mode
&= fc_mask
;
1043 if (mkdir(path_name(dst
), srcst
.st_mode
| S_IRWXU
) < 0
1044 || stat(path_name(dst
), &dstst
) < 0) {
1045 report(path_name(dst
));
1049 if (vflag
) printf("mkdir %s\n", path_name(dst
));
1051 /* Target directory already exists. */
1052 if (action
== MOVE
&& !mflag
) {
1054 report(path_name(dst
));
1059 /* Keep the existing attributes. */
1060 srcst
.st_mode
= dstst
.st_mode
;
1061 srcst
.st_uid
= dstst
.st_uid
;
1062 srcst
.st_gid
= dstst
.st_gid
;
1063 srcst
.st_mtime
= dstst
.st_mtime
;
1067 if (topdst_ino
== 0) {
1068 /* Remember the top destination. */
1069 topdst_dev
= dstst
.st_dev
;
1070 topdst_ino
= dstst
.st_ino
;
1073 if (srcst
.st_ino
== topdst_ino
&& srcst
.st_dev
== topdst_dev
) {
1074 /* E.g. cp -r /shallow /shallow/deep. */
1076 "%s%s %s/ %s/: infinite recursion avoided\n",
1077 prog_name
, action
!= MOVE
? " -r" : "",
1078 path_name(src
), path_name(dst
));
1083 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
1084 /* Don't recurse past a mount point. */
1091 slashsrc
= path_length(src
);
1092 slashdst
= path_length(dst
);
1094 while (dlist
!= nil
) {
1095 path_add(src
, dlist
->name
);
1096 if (action
!= REMOVE
) path_add(dst
, dlist
->name
);
1098 do1(src
, dst
, depth
+1);
1100 path_trunc(src
, slashsrc
);
1101 path_trunc(dst
, slashdst
);
1105 if (action
== MOVE
|| action
== REMOVE
) {
1106 /* The contents of the source directory should have
1107 * been (re)moved above. Get rid of the empty dir.
1109 if (action
== REMOVE
&& iflag
) {
1110 fprintf(stderr
, "Remove directory %s? ",
1112 if (!affirmative()) return;
1114 if (rmdir(path_name(src
)) < 0) {
1115 if (errno
!= ENOTEMPTY
) report(path_name(src
));
1118 if (vflag
) printf("rmdir %s\n", path_name(src
));
1121 if (action
!= REMOVE
) {
1122 /* Set the attributes of a new directory. */
1125 /* Copy the ownership. */
1126 if ((pflag
|| !conforming
)
1127 && (dstst
.st_uid
!= srcst
.st_uid
1128 || dstst
.st_gid
!= srcst
.st_gid
)
1130 if (chown(path_name(dst
), srcst
.st_uid
,
1131 srcst
.st_gid
) < 0) {
1132 if (errno
!= EPERM
) {
1133 report(path_name(dst
));
1139 /* Copy the mode. */
1140 if (dstst
.st_mode
!= srcst
.st_mode
) {
1141 if (chmod(path_name(dst
), srcst
.st_mode
) < 0) {
1142 report(path_name(dst
));
1147 /* Copy the file modification time. */
1148 if (dstst
.st_mtime
!= srcst
.st_mtime
) {
1149 ut
.actime
= action
== MOVE
? srcst
.st_atime
: time(nil
);
1150 ut
.modtime
= srcst
.st_mtime
;
1151 if (utime(path_name(dst
), &ut
) < 0) {
1152 if (errno
!= EPERM
) {
1153 report(path_name(dst
));
1157 "%s: Can't set the time of %s\n",
1158 prog_name
, path_name(dst
));
1166 char *flags1
, *flags2
;
1170 flags1
= "pifsmrRvx";
1178 fprintf(stderr
, "Usage: rm [-ifrRvx] file ...\n");
1181 flags1
= "ifsSmrRvx";
1193 fprintf(stderr
, "Usage: %s [-%s] file1 file2\n", prog_name
, flags1
);
1195 fprintf(stderr
, " %s [-%s] file ... dir\n", prog_name
, flags2
);
1199 void main(int argc
, char **argv
)
1204 pathname_t src
, dst
;
1208 /* The first argument is the call name while debugging. */
1209 if (argc
< 2) exit(-1);
1217 /* Call name of this program. */
1218 prog_name
= basename(argv
[0]);
1220 /* Required action. */
1221 if (strcmp(prog_name
, "cp") == 0) {
1227 if (strcmp(prog_name
, "mv") == 0) {
1233 if (strcmp(prog_name
, "rm") == 0) {
1238 if (strcmp(prog_name
, "ln") == 0) {
1243 if (strcmp(prog_name
, "cpdir") == 0) {
1247 rflag
= mflag
= pflag
= 1;
1250 if (strcmp(prog_name
, "clone") == 0) {
1254 rflag
= mflag
= fflag
= 1;
1257 "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n",
1262 /* Who am I?, where am I?, how protective am I? */
1270 while (i
< argc
&& argv
[i
][0] == '-') {
1271 char *opt
= argv
[i
++] + 1;
1273 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
1276 /* Flag supported? */
1277 if (strchr(flags
, *opt
) == nil
) usage();
1285 if (action
== MOVE
) fflag
= 0;
1289 if (action
== MOVE
) iflag
= 0;
1292 if (action
== LINK
) {
1295 /* Forget about POSIX, do it right. */
1325 if (i
== argc
) usage();
1328 /* 'ln dir/file' is to be read as 'ln dir/file .'. */
1329 if ((argc
- i
) == 1 && action
== LINK
) argv
[argc
++]= ".";
1332 if ((argc
- i
) < 2) usage();
1338 if (action
!= REMOVE
&& !mflag
1339 && stat(argv
[argc
-1], &st
) >= 0 && S_ISDIR(st
.st_mode
)
1341 /* The last argument is a directory, this means we have to
1342 * throw the whole lot into this directory. This is the
1343 * Right Thing unless you use -r.
1345 path_add(&dst
, argv
[argc
-1]);
1346 slash
= path_length(&dst
);
1349 path_add(&src
, argv
[i
]);
1350 path_add(&dst
, basename(argv
[i
]));
1354 path_trunc(&src
, 0);
1355 path_trunc(&dst
, slash
);
1356 } while (++i
< argc
-1);
1358 if (action
== REMOVE
|| (argc
- i
) == 2) {
1359 /* Just two files (or many files for rm). */
1361 path_add(&src
, argv
[i
]);
1362 if (action
!= REMOVE
) path_add(&dst
, argv
[i
+1]);
1365 path_trunc(&src
, 0);
1366 } while (action
== REMOVE
&& ++i
< argc
);
1375 fprintf(stderr
, "(%ld chunks of memory not freed)\n",