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>
27 /* Copy files in this size chunks: */
28 #if __minix && !__minix_vmd
29 #define CHUNK (8192 * sizeof(char *))
31 #define CHUNK (1024 << (sizeof(int) + sizeof(char *)))
36 #define CONFORMING 1 /* Precisely POSIX conforming. */
40 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
41 #define arraylimit(a) ((a) + arraysize(a))
43 char *prog_name
; /* Call name of this program. */
44 int ex_code
= 0; /* Final exit code. */
46 typedef enum identity
{ CP
, MV
, RM
, LN
, CPDIR
, CLONE
} identity_t
;
47 typedef enum action
{ COPY
, MOVE
, REMOVE
, LINK
} action_t
;
49 identity_t identity
; /* How did the user call me? */
50 action_t action
; /* Copying, moving, or linking. */
51 int pflag
= 0; /* -p/-s: Make orginal and copy the same. */
52 int iflag
= 0; /* -i: Interactive overwriting/deleting. */
53 int fflag
= 0; /* -f: Force. */
54 int sflag
= 0; /* -s: Make a symbolic link (ln/clone). */
55 int Sflag
= 0; /* -S: Make a symlink if across devices. */
56 int mflag
= 0; /* -m: Merge trees, no target dir trickery. */
57 int rflag
= 0; /* -r/-R: Recursively copy a tree. */
58 int vflag
= 0; /* -v: Verbose. */
59 int xflag
= 0; /* -x: Don't traverse past mount points. */
60 int xdev
= 0; /* Set when moving or linking cross-device. */
61 int expand
= 0; /* Expand symlinks, ignore links. */
62 int conforming
= CONFORMING
; /* Sometimes standards are a pain. */
64 int fc_mask
; /* File creation mask. */
65 int uid
, gid
; /* Effective uid & gid. */
66 int istty
; /* Can have terminal input. */
69 /* There were no symlinks in medieval times. */
70 #define S_ISLNK(mode) (0)
72 #define symlink(path1, path2) (errno= ENOSYS, -1)
73 #define readlink(path, buf, len) (errno= ENOSYS, -1)
76 void report(const char *label
)
78 if (action
== REMOVE
&& fflag
) return;
79 fprintf(stderr
, "%s: %s: %s\n", prog_name
, label
, strerror(errno
));
83 void fatal(const char *label
)
89 void report2(const char *src
, const char *dst
)
91 fprintf(stderr
, "%s %s %s: %s\n", prog_name
, src
, dst
, strerror(errno
));
96 size_t nchunks
= 0; /* Number of allocated cells. */
99 void *allocate(void *mem
, size_t size
)
100 /* Like realloc, but with checking of the return value. */
103 if (mem
== nil
) nchunks
++;
105 if ((mem
= mem
== nil
? malloc(size
) : realloc(mem
, size
)) == nil
)
110 void deallocate(void *mem
)
111 /* Release a chunk of memory. */
121 typedef struct pathname
{
122 char *path
; /* The actual pathname. */
123 size_t idx
; /* Index for the terminating null byte. */
124 size_t lim
; /* Actual length of the path array. */
127 void path_init(pathname_t
*pp
)
128 /* Initialize a pathname to the null string. */
130 pp
->path
= allocate(nil
, pp
->lim
= NAME_MAX
+ 2);
131 pp
->path
[pp
->idx
= 0]= 0;
134 void path_add(pathname_t
*pp
, const char *name
)
135 /* Add a component to a pathname. */
140 lim
= pp
->idx
+ strlen(name
) + 2;
143 pp
->lim
= lim
+= lim
/2; /* add an extra 50% growing space. */
145 pp
->path
= allocate(pp
->path
, lim
);
148 p
= pp
->path
+ pp
->idx
;
149 if (p
> pp
->path
&& p
[-1] != '/') *p
++ = '/';
152 if (*name
!= '/' || p
== pp
->path
|| p
[-1] != '/') *p
++ = *name
;
156 pp
->idx
= p
- pp
->path
;
159 void path_trunc(pathname_t
*pp
, size_t didx
)
160 /* Delete part of a pathname to a remembered length. */
162 pp
->path
[pp
->idx
= didx
]= 0;
166 const char *path_name(const pathname_t
*pp
)
167 /* Return the actual name as a C string. */
172 size_t path_length(const pathname_t
*pp
)
173 /* The length of the pathname. */
178 void path_drop(pathname_t
*pp
)
179 /* Release the storage occupied by the pathname. */
181 deallocate(pp
->path
);
185 #define path_name(pp) ((const char *) (pp)->path)
186 #define path_length(pp) ((pp)->idx)
187 #define path_drop(pp) deallocate((void *) (pp)->path)
190 char *basename(const char *path
)
191 /* Return the last component of a pathname. (Note: declassifies a const
192 * char * just like strchr.
198 while (*p
== '/') p
++; /* Trailing slashes? */
203 while (*p
!= 0 && *p
!= '/') p
++; /* Skip component. */
205 return (char *) path
;
208 int affirmative(void)
209 /* Get a yes/no answer from the suspecting user. */
217 while ((c
= getchar()) == ' ') {}
218 ok
= (c
== 'y' || c
== 'Y');
219 while (c
!= EOF
&& c
!= '\n') c
= getchar();
224 int writable(const struct stat
*stp
)
225 /* True iff the file with the given attributes allows writing. (And we have
226 * a terminal to ask if ok to overwrite.)
229 if (!istty
|| uid
== 0) return 1;
230 if (stp
->st_uid
== uid
) return stp
->st_mode
& S_IWUSR
;
231 if (stp
->st_gid
== gid
) return stp
->st_mode
& S_IWGRP
;
232 return stp
->st_mode
& S_IWOTH
;
236 #define PATH_MAX 1024
239 static char *link_islink(const struct stat
*stp
, const char *file
)
241 /* Tell if a file, which stat(2) information in '*stp', has been seen
242 * earlier by this function under a different name. If not return a
243 * null pointer with errno set to ENOENT, otherwise return the name of
244 * the link. Return a null pointer with an error code in errno for any
245 * error, using E2BIG for a too long file name.
247 * Use link_islink(nil, nil) to reset all bookkeeping.
249 * Call for a file twice to delete it from the store.
252 typedef struct link
{ /* In-memory link store. */
253 struct link
*next
; /* Hash chain on inode number. */
254 ino_t ino
; /* File's inode number. */
255 off_t off
; /* Offset to more info in temp file. */
257 typedef struct dlink
{ /* On-disk link store. */
258 dev_t dev
; /* Device number. */
259 char file
[PATH_MAX
]; /* Name of earlier seen link. */
261 static link_t
*links
[256]; /* Hash list of known links. */
262 static int tfd
= -1; /* Temp file for file name storage. */
263 static dlink_t dlink
;
269 /* Reset everything. */
270 for (plp
= links
; plp
< arraylimit(links
); plp
++) {
271 while ((lp
= *plp
) != nil
) {
276 if (tfd
!= -1) close(tfd
);
281 /* The file must be a non-directory with more than one link. */
282 if (S_ISDIR(stp
->st_mode
) || stp
->st_nlink
<= 1) {
287 plp
= &links
[stp
->st_ino
% arraysize(links
)];
289 while ((lp
= *plp
) != nil
) {
290 if (lp
->ino
== stp
->st_ino
) {
291 /* May have seen this link before. Get it and check. */
292 if (lseek(tfd
, lp
->off
, SEEK_SET
) == -1) return nil
;
293 if (read(tfd
, &dlink
, sizeof(dlink
)) < 0) return nil
;
295 /* Only need to check the device number. */
296 if (dlink
.dev
== stp
->st_dev
) {
297 if (strcmp(file
, dlink
.file
) == 0) {
298 /* Called twice. Forget about this link. */
305 /* Return the name of the earlier link. */
312 /* First time I see this link. Add it to the store. */
318 tfd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
320 if (errno
!= EEXIST
) return nil
;
327 if ((len
= strlen(file
)) >= PATH_MAX
) {
332 dlink
.dev
= stp
->st_dev
;
333 strcpy(dlink
.file
, file
);
334 len
+= offsetof(dlink_t
, file
) + 1;
335 if ((off
= lseek(tfd
, 0, SEEK_END
)) == -1) return nil
;
336 if (write(tfd
, &dlink
, len
) != len
) return nil
;
338 if ((lp
= malloc(sizeof(*lp
))) == nil
) return nil
;
340 lp
->ino
= stp
->st_ino
;
347 int trylink(const char *src
, const char *dst
, const struct stat
*srcst
,
348 const struct stat
*dstst
)
349 /* Keep the link structure intact if src has been seen before. */
354 if (action
== COPY
&& expand
) return 0;
356 if ((olddst
= link_islink(srcst
, dst
)) == nil
) {
357 /* if (errno != ENOENT) ... */
361 /* Try to link the file copied earlier to the new file. */
362 if (dstst
->st_ino
!= 0) (void) unlink(dst
);
364 if ((linked
= (link(olddst
, dst
) == 0)) && vflag
)
365 printf("ln %s ..\n", olddst
);
370 int copy(const char *src
, const char *dst
, struct stat
*srcst
,
372 /* Copy one file to another and copy (some of) the attributes. */
378 assert(srcst
->st_ino
!= 0);
380 if (dstst
->st_ino
== 0) {
381 /* The file doesn't exist yet. */
383 if (!S_ISREG(srcst
->st_mode
)) {
384 /* Making a new mode 666 regular file. */
385 srcst
->st_mode
= (S_IFREG
| 0666) & fc_mask
;
387 if (!pflag
&& conforming
) {
388 /* Making a new file copying mode with umask applied. */
389 srcst
->st_mode
&= fc_mask
;
392 /* File exists, ask if ok to overwrite if '-i'. */
394 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
395 fprintf(stderr
, "Overwrite %s? (mode = %03o) ",
396 dst
, dstst
->st_mode
& 07777);
397 if (!affirmative()) return 0;
400 if (action
== MOVE
) {
401 /* Don't overwrite, remove first. */
402 if (unlink(dst
) < 0 && errno
!= ENOENT
) {
409 /* Keep the existing mode and ownership. */
410 srcst
->st_mode
= dstst
->st_mode
;
411 srcst
->st_uid
= dstst
->st_uid
;
412 srcst
->st_gid
= dstst
->st_gid
;
417 /* Keep the link structure if possible. */
418 if (trylink(src
, dst
, srcst
, dstst
)) return 1;
420 if ((srcfd
= open(src
, O_RDONLY
)) < 0) {
425 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, srcst
->st_mode
& 0777);
426 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
427 /* Retry adding a "w" bit. */
428 (void) chmod(dst
, dstst
->st_mode
| S_IWUSR
);
429 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
431 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
432 /* Retry after trying to delete. */
434 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
442 /* Get current parameters. */
443 if (fstat(dstfd
, dstst
) < 0) {
450 /* Copy the little bytes themselves. */
451 while ((n
= read(srcfd
, buf
, sizeof(buf
))) > 0) {
455 while (n
> 0 && (r
= write(dstfd
, bp
, n
)) > 0) {
462 "%s: Warning: EOF writing to %s\n",
480 /* Copy the ownership. */
481 if ((pflag
|| !conforming
)
482 && S_ISREG(dstst
->st_mode
)
483 && (dstst
->st_uid
!= srcst
->st_uid
484 || dstst
->st_gid
!= srcst
->st_gid
)
486 if (chmod(dst
, 0) == 0) dstst
->st_mode
&= ~07777;
487 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
488 if (errno
!= EPERM
) {
493 dstst
->st_uid
= srcst
->st_uid
;
494 dstst
->st_gid
= srcst
->st_gid
;
498 if (conforming
&& S_ISREG(dstst
->st_mode
)
499 && (dstst
->st_uid
!= srcst
->st_uid
500 || dstst
->st_gid
!= srcst
->st_gid
)
502 /* Suid bits must be cleared in the holy name of
503 * security (and the assumed user stupidity).
505 srcst
->st_mode
&= ~06000;
509 if (S_ISREG(dstst
->st_mode
) && dstst
->st_mode
!= srcst
->st_mode
) {
510 if (chmod(dst
, srcst
->st_mode
) < 0) {
511 if (errno
!= EPERM
) {
515 fprintf(stderr
, "%s: Can't change the mode of %s\n",
520 /* Copy the file modification time. */
521 if ((pflag
|| !conforming
) && S_ISREG(dstst
->st_mode
)) {
524 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
525 ut
.modtime
= srcst
->st_mtime
;
526 if (utime(dst
, &ut
) < 0) {
527 if (errno
!= EPERM
) {
533 "%s: Can't set the time of %s\n",
539 printf(action
== COPY
? "cp %s ..\n" : "mv %s ..\n", src
);
544 void copy1(const char *src
, const char *dst
, struct stat
*srcst
,
546 /* Inspect the source file and then copy it. Treatment of symlinks and
547 * special files is a bit complicated. The filetype and link-structure are
548 * ignored if (expand && !rflag), symlinks and link-structure are ignored
549 * if (expand && rflag), everything is copied precisely if !expand.
554 assert(srcst
->st_ino
!= 0);
556 if (srcst
->st_ino
== dstst
->st_ino
&& srcst
->st_dev
== dstst
->st_dev
) {
557 fprintf(stderr
, "%s: can't copy %s onto itself\n",
563 /* You can forget it if the destination is a directory. */
564 if (dstst
->st_ino
!= 0 && S_ISDIR(dstst
->st_mode
)) {
570 if (S_ISREG(srcst
->st_mode
) || (expand
&& !rflag
)) {
571 if (!copy(src
, dst
, srcst
, dstst
)) return;
573 if (action
== MOVE
&& unlink(src
) < 0) {
580 if (dstst
->st_ino
!= 0) {
581 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
582 fprintf(stderr
, "Replace %s? (mode = %03o) ",
583 dst
, dstst
->st_mode
& 07777);
584 if (!affirmative()) return;
586 if (unlink(dst
) < 0) {
593 /* Apply the file creation mask if so required. */
594 if (!pflag
&& conforming
) srcst
->st_mode
&= fc_mask
;
598 if (S_ISLNK(srcst
->st_mode
)) {
601 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
606 r
= symlink(buf
, dst
);
608 printf("ln -s %s %s\n", buf
, dst
);
610 if (trylink(src
, dst
, srcst
, dstst
)) {
614 if (S_ISFIFO(srcst
->st_mode
)) {
615 r
= mkfifo(dst
, srcst
->st_mode
);
617 printf("mkfifo %s\n", dst
);
619 if (S_ISBLK(srcst
->st_mode
) || S_ISCHR(srcst
->st_mode
)) {
620 r
= mknod(dst
, srcst
->st_mode
, srcst
->st_rdev
);
621 if (vflag
&& r
== 0) {
622 printf("mknod %s %c %d %d\n",
624 S_ISBLK(srcst
->st_mode
) ? 'b' : 'c',
625 (srcst
->st_rdev
>> 8) & 0xFF,
626 (srcst
->st_rdev
>> 0) & 0xFF);
629 fprintf(stderr
, "%s: %s: odd filetype %5o (not copied)\n",
630 prog_name
, src
, srcst
->st_mode
);
635 if (r
< 0 || lstat(dst
, dstst
) < 0) {
640 if (action
== MOVE
&& unlink(src
) < 0) {
642 (void) unlink(dst
); /* Don't want it twice. */
648 if (S_ISLNK(srcst
->st_mode
)) return;
650 /* Copy the ownership. */
651 if ((pflag
|| !conforming
)
652 && (dstst
->st_uid
!= srcst
->st_uid
653 || dstst
->st_gid
!= srcst
->st_gid
)
655 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
656 if (errno
!= EPERM
) {
663 /* Copy the file modification time. */
664 if (pflag
|| !conforming
) {
667 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
668 ut
.modtime
= srcst
->st_mtime
;
669 if (utime(dst
, &ut
) < 0) {
670 if (errno
!= EPERM
) {
674 fprintf(stderr
, "%s: Can't set the time of %s\n",
680 void remove1(const char *src
, const struct stat
*srcst
)
682 if (iflag
|| (!fflag
&& !writable(srcst
))) {
683 fprintf(stderr
, "Remove %s? (mode = %03o) ", src
,
684 srcst
->st_mode
& 07777);
685 if (!affirmative()) return;
687 if (unlink(src
) < 0) {
690 if (vflag
) printf("rm %s\n", src
);
694 void link1(const char *src
, const char *dst
, const struct stat
*srcst
,
695 const struct stat
*dstst
)
700 if (dstst
->st_ino
!= 0 && (iflag
|| fflag
)) {
701 if (srcst
->st_ino
== dstst
->st_ino
) {
703 fprintf(stderr
, "%s: Can't link %s onto itself\n",
709 fprintf(stderr
, "Remove %s? ", dst
);
710 if (!affirmative()) return;
713 if (S_ISDIR(dstst
->st_mode
) || unlink(dst
) < 0) {
719 if (!sflag
&& !(rflag
&& S_ISLNK(srcst
->st_mode
)) && !(Sflag
&& xdev
)) {
721 if (link(src
, dst
) < 0) {
722 if (!Sflag
|| errno
!= EXDEV
) {
726 /* Can't do a cross-device link, we have to symlink. */
729 if (vflag
) printf("ln %s..\n", src
);
735 if (!rflag
&& !Sflag
) {
736 /* We can get away with a "don't care if it works" symlink. */
737 if (symlink(src
, dst
) < 0) {
741 if (vflag
) printf("ln -s %s %s\n", src
, dst
);
745 /* If the source is a symlink then it is simply copied. */
746 if (S_ISLNK(srcst
->st_mode
)) {
750 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
755 if (symlink(buf
, dst
) < 0) {
759 if (vflag
) printf("ln -s %s %s\n", buf
, dst
);
763 /* Make a symlink that has to work, i.e. we must be able to access the
764 * source now, and the link must work.
766 if (dst
[0] == '/' && src
[0] != '/') {
767 /* ln -[rsS] relative/path /full/path. */
769 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
770 prog_name
, src
, dst
);
774 /* Count the number of subdirectories in the destination file and
775 * add one '..' for each.
782 if (p
[1] == '/' || p
[1] == 0) {
783 /* A "." component; skip. */
784 do p
++; while (*p
== '/');
787 if (p
[1] == '.' && (p
[2] == '/' || p
[2] == 0)) {
788 /* A ".." component; oops. */
789 switch (path_length(&sym
)) {
792 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
793 prog_name
, src
, dst
);
799 path_trunc(&sym
, path_length(&sym
) - 3);
802 do p
++; while (*p
== '/');
806 while (*p
!= 0 && *p
!= '/') p
++;
807 while (*p
== '/') p
++;
809 path_add(&sym
, "..");
814 if (symlink(path_name(&sym
), dst
) < 0) {
817 if (vflag
) printf("ln -s %s %s\n", path_name(&sym
), dst
);
822 typedef struct entrylist
{
823 struct entrylist
*next
;
827 int eat_dir(const char *dir
, entrylist_t
**dlist
)
828 /* Make a linked list of all the names in a directory. */
831 struct dirent
*entry
;
833 if ((dp
= opendir(dir
)) == nil
) return 0;
835 while ((entry
= readdir(dp
)) != nil
) {
836 if (strcmp(entry
->d_name
, ".") == 0) continue;
837 if (strcmp(entry
->d_name
, "..") == 0) continue;
839 *dlist
= allocate(nil
, sizeof(**dlist
));
840 (*dlist
)->name
= allocate(nil
, strlen(entry
->d_name
)+1);
841 strcpy((*dlist
)->name
, entry
->d_name
);
842 dlist
= &(*dlist
)->next
;
849 void chop_dlist(entrylist_t
**dlist
)
850 /* Chop an entry of a name list. */
852 entrylist_t
*junk
= *dlist
;
855 deallocate(junk
->name
);
859 void drop_dlist(entrylist_t
*dlist
)
860 /* Get rid of a whole list. */
862 while (dlist
!= nil
) chop_dlist(&dlist
);
865 void do1(pathname_t
*src
, pathname_t
*dst
, int depth
)
866 /* Perform the appropriate action on a source and destination file. */
868 size_t slashsrc
, slashdst
;
869 struct stat srcst
, dstst
;
871 static ino_t topdst_ino
;
872 static dev_t topdst_dev
;
873 static dev_t topsrc_dev
;
876 if (vflag
&& depth
== 0) {
877 char flags
[100], *pf
= flags
;
879 if (pflag
) *pf
++= 'p';
880 if (iflag
) *pf
++= 'i';
881 if (fflag
) *pf
++= 'f';
882 if (sflag
) *pf
++= 's';
883 if (Sflag
) *pf
++= 'S';
884 if (mflag
) *pf
++= 'm';
885 if (rflag
) *pf
++= 'r';
886 if (vflag
) *pf
++= 'v';
887 if (xflag
) *pf
++= 'x';
888 if (expand
) *pf
++= 'L';
889 if (conforming
) *pf
++= 'C';
891 printf(": %s -%s %s %s\n", prog_name
, flags
,
892 path_name(src
), path_name(dst
));
896 /* st_ino == 0 if not stat()'ed yet, or nonexistent. */
900 if (action
!= LINK
|| !sflag
|| rflag
) {
901 /* Source must exist unless symlinking. */
902 if ((expand
? stat
: lstat
)(path_name(src
), &srcst
) < 0) {
903 report(path_name(src
));
909 /* First call: Not cross-device yet, first dst not seen yet,
910 * remember top device number.
914 topsrc_dev
= srcst
.st_dev
;
917 /* Inspect the intended destination unless removing. */
918 if (action
!= REMOVE
) {
919 if ((expand
? stat
: lstat
)(path_name(dst
), &dstst
) < 0) {
920 if (errno
!= ENOENT
) {
921 report(path_name(dst
));
927 if (action
== MOVE
&& !xdev
) {
928 if (dstst
.st_ino
!= 0 && srcst
.st_dev
!= dstst
.st_dev
) {
929 /* It's a cross-device rename, i.e. copy and remove. */
932 if (!mflag
|| dstst
.st_ino
== 0 || !S_ISDIR(dstst
.st_mode
)) {
933 /* Try to simply rename the file (not merging trees). */
935 if (srcst
.st_ino
== dstst
.st_ino
) {
937 "%s: Can't move %s onto itself\n",
938 prog_name
, path_name(src
));
943 if (dstst
.st_ino
!= 0) {
944 if (iflag
|| (!fflag
&& !writable(&dstst
))) {
946 "Replace %s? (mode = %03o) ",
948 dstst
.st_mode
& 07777);
949 if (!affirmative()) return;
951 if (!S_ISDIR(dstst
.st_mode
))
952 (void) unlink(path_name(dst
));
955 if (rename(path_name(src
), path_name(dst
)) == 0) {
958 printf("mv %s %s\n", path_name(src
),
963 if (errno
== EXDEV
) {
966 report2(path_name(src
), path_name(dst
));
972 if (srcst
.st_ino
== 0 || !S_ISDIR(srcst
.st_mode
)) {
973 /* Copy/move/remove/link a single file. */
977 copy1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
980 remove1(path_name(src
), &srcst
);
983 link1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
989 /* Recursively copy/move/remove/link a directory if -r or -R. */
992 report(path_name(src
));
996 /* Ok to remove contents of dir? */
997 if (action
== REMOVE
) {
998 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
999 /* Don't recurse past a mount point. */
1003 fprintf(stderr
, "Remove contents of %s? ", path_name(src
));
1004 if (!affirmative()) return;
1008 /* Gather the names in the source directory. */
1009 if (!eat_dir(path_name(src
), &dlist
)) {
1010 report(path_name(src
));
1014 /* Check/create the target directory. */
1015 if (action
!= REMOVE
&& dstst
.st_ino
!= 0 && !S_ISDIR(dstst
.st_mode
)) {
1016 if (action
!= MOVE
&& !fflag
) {
1018 report(path_name(dst
));
1022 fprintf(stderr
, "Replace %s? ", path_name(dst
));
1023 if (!affirmative()) {
1028 if (unlink(path_name(dst
)) < 0) {
1029 report(path_name(dst
));
1036 if (action
!= REMOVE
) {
1037 if (dstst
.st_ino
== 0) {
1038 /* Create a new target directory. */
1039 if (!pflag
&& conforming
) srcst
.st_mode
&= fc_mask
;
1041 if (mkdir(path_name(dst
), srcst
.st_mode
| S_IRWXU
) < 0
1042 || stat(path_name(dst
), &dstst
) < 0) {
1043 report(path_name(dst
));
1047 if (vflag
) printf("mkdir %s\n", path_name(dst
));
1049 /* Target directory already exists. */
1050 if (action
== MOVE
&& !mflag
) {
1052 report(path_name(dst
));
1057 /* Keep the existing attributes. */
1058 srcst
.st_mode
= dstst
.st_mode
;
1059 srcst
.st_uid
= dstst
.st_uid
;
1060 srcst
.st_gid
= dstst
.st_gid
;
1061 srcst
.st_mtime
= dstst
.st_mtime
;
1065 if (topdst_ino
== 0) {
1066 /* Remember the top destination. */
1067 topdst_dev
= dstst
.st_dev
;
1068 topdst_ino
= dstst
.st_ino
;
1071 if (srcst
.st_ino
== topdst_ino
&& srcst
.st_dev
== topdst_dev
) {
1072 /* E.g. cp -r /shallow /shallow/deep. */
1074 "%s%s %s/ %s/: infinite recursion avoided\n",
1075 prog_name
, action
!= MOVE
? " -r" : "",
1076 path_name(src
), path_name(dst
));
1081 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
1082 /* Don't recurse past a mount point. */
1089 slashsrc
= path_length(src
);
1090 slashdst
= path_length(dst
);
1092 while (dlist
!= nil
) {
1093 path_add(src
, dlist
->name
);
1094 if (action
!= REMOVE
) path_add(dst
, dlist
->name
);
1096 do1(src
, dst
, depth
+1);
1098 path_trunc(src
, slashsrc
);
1099 path_trunc(dst
, slashdst
);
1103 if (action
== MOVE
|| action
== REMOVE
) {
1104 /* The contents of the source directory should have
1105 * been (re)moved above. Get rid of the empty dir.
1107 if (action
== REMOVE
&& iflag
) {
1108 fprintf(stderr
, "Remove directory %s? ",
1110 if (!affirmative()) return;
1112 if (rmdir(path_name(src
)) < 0) {
1113 if (errno
!= ENOTEMPTY
) report(path_name(src
));
1116 if (vflag
) printf("rmdir %s\n", path_name(src
));
1119 if (action
!= REMOVE
) {
1120 /* Set the attributes of a new directory. */
1123 /* Copy the ownership. */
1124 if ((pflag
|| !conforming
)
1125 && (dstst
.st_uid
!= srcst
.st_uid
1126 || dstst
.st_gid
!= srcst
.st_gid
)
1128 if (chown(path_name(dst
), srcst
.st_uid
,
1129 srcst
.st_gid
) < 0) {
1130 if (errno
!= EPERM
) {
1131 report(path_name(dst
));
1137 /* Copy the mode. */
1138 if (dstst
.st_mode
!= srcst
.st_mode
) {
1139 if (chmod(path_name(dst
), srcst
.st_mode
) < 0) {
1140 report(path_name(dst
));
1145 /* Copy the file modification time. */
1146 if (dstst
.st_mtime
!= srcst
.st_mtime
) {
1147 ut
.actime
= action
== MOVE
? srcst
.st_atime
: time(nil
);
1148 ut
.modtime
= srcst
.st_mtime
;
1149 if (utime(path_name(dst
), &ut
) < 0) {
1150 if (errno
!= EPERM
) {
1151 report(path_name(dst
));
1155 "%s: Can't set the time of %s\n",
1156 prog_name
, path_name(dst
));
1164 char *flags1
, *flags2
;
1168 flags1
= "pifsmrRvx";
1176 fprintf(stderr
, "Usage: rm [-ifrRvx] file ...\n");
1179 flags1
= "ifsSmrRvx";
1191 fprintf(stderr
, "Usage: %s [-%s] file1 file2\n", prog_name
, flags1
);
1193 fprintf(stderr
, " %s [-%s] file ... dir\n", prog_name
, flags2
);
1197 int main(int argc
, char **argv
)
1202 pathname_t src
, dst
;
1206 /* The first argument is the call name while debugging. */
1207 if (argc
< 2) exit(-1);
1215 /* Call name of this program. */
1216 prog_name
= basename(argv
[0]);
1218 /* Required action. */
1219 if (strcmp(prog_name
, "cp") == 0) {
1225 if (strcmp(prog_name
, "mv") == 0) {
1231 if (strcmp(prog_name
, "rm") == 0) {
1236 if (strcmp(prog_name
, "ln") == 0) {
1241 if (strcmp(prog_name
, "cpdir") == 0) {
1245 rflag
= mflag
= pflag
= 1;
1248 if (strcmp(prog_name
, "clone") == 0) {
1252 rflag
= mflag
= fflag
= 1;
1255 "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n",
1260 /* Who am I?, where am I?, how protective am I? */
1268 while (i
< argc
&& argv
[i
][0] == '-') {
1269 char *opt
= argv
[i
++] + 1;
1271 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
1274 /* Flag supported? */
1275 if (strchr(flags
, *opt
) == nil
) usage();
1283 if (action
== MOVE
) fflag
= 0;
1287 if (action
== MOVE
) iflag
= 0;
1290 if (action
== LINK
) {
1293 /* Forget about POSIX, do it right. */
1330 /* 'ln dir/file' is to be read as 'ln dir/file .'. */
1331 if ((argc
- i
) == 1 && action
== LINK
) argv
[argc
++]= ".";
1334 if ((argc
- i
) < 2) usage();
1340 if (action
!= REMOVE
&& !mflag
1341 && stat(argv
[argc
-1], &st
) >= 0 && S_ISDIR(st
.st_mode
)
1343 /* The last argument is a directory, this means we have to
1344 * throw the whole lot into this directory. This is the
1345 * Right Thing unless you use -r.
1347 path_add(&dst
, argv
[argc
-1]);
1348 slash
= path_length(&dst
);
1351 path_add(&src
, argv
[i
]);
1352 path_add(&dst
, basename(argv
[i
]));
1356 path_trunc(&src
, 0);
1357 path_trunc(&dst
, slash
);
1358 } while (++i
< argc
-1);
1360 if (action
== REMOVE
|| (argc
- i
) == 2) {
1361 /* Just two files (or many files for rm). */
1363 path_add(&src
, argv
[i
]);
1364 if (action
!= REMOVE
) path_add(&dst
, argv
[i
+1]);
1367 path_trunc(&src
, 0);
1368 } while (action
== REMOVE
&& ++i
< argc
);
1377 fprintf(stderr
, "(%ld chunks of memory not freed)\n",