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>
18 #include <sys/syslimits.h>
28 /* Copy files in this size chunks: */
29 #if __minix && !__minix_vmd
30 #define CHUNK (8192 * sizeof(char *))
32 #define CHUNK (1024 << (sizeof(int) + sizeof(char *)))
37 #define CONFORMING 1 /* Precisely POSIX conforming. */
41 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
42 #define arraylimit(a) ((a) + arraysize(a))
44 char *prog_name
; /* Call name of this program. */
45 int ex_code
= 0; /* Final exit code. */
47 typedef enum identity
{ CP
, MV
, RM
, LN
, CPDIR
, CLONE
} identity_t
;
48 typedef enum action
{ COPY
, MOVE
, REMOVE
, LINK
} action_t
;
50 identity_t identity
; /* How did the user call me? */
51 action_t action
; /* Copying, moving, or linking. */
52 int pflag
= 0; /* -p/-s: Make orginal and copy the same. */
53 int iflag
= 0; /* -i: Interactive overwriting/deleting. */
54 int fflag
= 0; /* -f: Force. */
55 int sflag
= 0; /* -s: Make a symbolic link (ln/clone). */
56 int Sflag
= 0; /* -S: Make a symlink if across devices. */
57 int mflag
= 0; /* -m: Merge trees, no target dir trickery. */
58 int rflag
= 0; /* -r/-R: Recursively copy a tree. */
59 int vflag
= 0; /* -v: Verbose. */
60 int xflag
= 0; /* -x: Don't traverse past mount points. */
61 int xdev
= 0; /* Set when moving or linking cross-device. */
62 int expand
= 0; /* Expand symlinks, ignore links. */
63 int conforming
= CONFORMING
; /* Sometimes standards are a pain. */
65 int fc_mask
; /* File creation mask. */
66 int uid
, gid
; /* Effective uid & gid. */
67 int istty
; /* Can have terminal input. */
70 /* There were no symlinks in medieval times. */
71 #define S_ISLNK(mode) (0)
73 #define symlink(path1, path2) (errno= ENOSYS, -1)
74 #define readlink(path, buf, len) (errno= ENOSYS, -1)
77 void report(const char *label
)
79 if (action
== REMOVE
&& fflag
) return;
80 fprintf(stderr
, "%s: %s: %s\n", prog_name
, label
, strerror(errno
));
84 void fatal(const char *label
)
90 void report2(const char *src
, const char *dst
)
92 fprintf(stderr
, "%s %s %s: %s\n", prog_name
, src
, dst
, strerror(errno
));
97 size_t nchunks
= 0; /* Number of allocated cells. */
100 void *allocate(void *mem
, size_t size
)
101 /* Like realloc, but with checking of the return value. */
104 if (mem
== nil
) nchunks
++;
106 if ((mem
= mem
== nil
? malloc(size
) : realloc(mem
, size
)) == nil
)
111 void deallocate(void *mem
)
112 /* Release a chunk of memory. */
122 typedef struct pathname
{
123 char *path
; /* The actual pathname. */
124 size_t idx
; /* Index for the terminating null byte. */
125 size_t lim
; /* Actual length of the path array. */
128 void path_init(pathname_t
*pp
)
129 /* Initialize a pathname to the null string. */
131 pp
->path
= allocate(nil
, pp
->lim
= NAME_MAX
+ 2);
132 pp
->path
[pp
->idx
= 0]= 0;
135 void path_add(pathname_t
*pp
, const char *name
)
136 /* Add a component to a pathname. */
141 lim
= pp
->idx
+ strlen(name
) + 2;
144 pp
->lim
= lim
+= lim
/2; /* add an extra 50% growing space. */
146 pp
->path
= allocate(pp
->path
, lim
);
149 p
= pp
->path
+ pp
->idx
;
150 if (p
> pp
->path
&& p
[-1] != '/') *p
++ = '/';
153 if (*name
!= '/' || p
== pp
->path
|| p
[-1] != '/') *p
++ = *name
;
157 pp
->idx
= p
- pp
->path
;
160 void path_trunc(pathname_t
*pp
, size_t didx
)
161 /* Delete part of a pathname to a remembered length. */
163 pp
->path
[pp
->idx
= didx
]= 0;
167 const char *path_name(const pathname_t
*pp
)
168 /* Return the actual name as a C string. */
173 size_t path_length(const pathname_t
*pp
)
174 /* The length of the pathname. */
179 void path_drop(pathname_t
*pp
)
180 /* Release the storage occupied by the pathname. */
182 deallocate(pp
->path
);
186 #define path_name(pp) ((const char *) (pp)->path)
187 #define path_length(pp) ((pp)->idx)
188 #define path_drop(pp) deallocate((void *) (pp)->path)
191 char *basename(const char *path
)
192 /* Return the last component of a pathname. (Note: declassifies a const
193 * char * just like strchr.
199 while (*p
== '/') p
++; /* Trailing slashes? */
204 while (*p
!= 0 && *p
!= '/') p
++; /* Skip component. */
206 return (char *) path
;
209 int affirmative(void)
210 /* Get a yes/no answer from the suspecting user. */
218 while ((c
= getchar()) == ' ') {}
219 ok
= (c
== 'y' || c
== 'Y');
220 while (c
!= EOF
&& c
!= '\n') c
= getchar();
225 int writable(const struct stat
*stp
)
226 /* True iff the file with the given attributes allows writing. (And we have
227 * a terminal to ask if ok to overwrite.)
230 if (!istty
|| uid
== 0) return 1;
231 if (stp
->st_uid
== uid
) return stp
->st_mode
& S_IWUSR
;
232 if (stp
->st_gid
== gid
) return stp
->st_mode
& S_IWGRP
;
233 return stp
->st_mode
& S_IWOTH
;
237 #define PATH_MAX 1024
240 static char *link_islink(const struct stat
*stp
, const char *file
)
242 /* Tell if a file, which stat(2) information in '*stp', has been seen
243 * earlier by this function under a different name. If not return a
244 * null pointer with errno set to ENOENT, otherwise return the name of
245 * the link. Return a null pointer with an error code in errno for any
246 * error, using E2BIG for a too long file name.
248 * Use link_islink(nil, nil) to reset all bookkeeping.
250 * Call for a file twice to delete it from the store.
253 typedef struct link
{ /* In-memory link store. */
254 struct link
*next
; /* Hash chain on inode number. */
255 ino_t ino
; /* File's inode number. */
256 off_t off
; /* Offset to more info in temp file. */
258 typedef struct dlink
{ /* On-disk link store. */
259 dev_t dev
; /* Device number. */
260 char file
[PATH_MAX
]; /* Name of earlier seen link. */
262 static link_t
*links
[256]; /* Hash list of known links. */
263 static int tfd
= -1; /* Temp file for file name storage. */
264 static dlink_t dlink
;
270 /* Reset everything. */
271 for (plp
= links
; plp
< arraylimit(links
); plp
++) {
272 while ((lp
= *plp
) != nil
) {
277 if (tfd
!= -1) close(tfd
);
282 /* The file must be a non-directory with more than one link. */
283 if (S_ISDIR(stp
->st_mode
) || stp
->st_nlink
<= 1) {
288 plp
= &links
[stp
->st_ino
% arraysize(links
)];
290 while ((lp
= *plp
) != nil
) {
291 if (lp
->ino
== stp
->st_ino
) {
292 /* May have seen this link before. Get it and check. */
293 if (lseek(tfd
, lp
->off
, SEEK_SET
) == -1) return nil
;
294 if (read(tfd
, &dlink
, sizeof(dlink
)) < 0) return nil
;
296 /* Only need to check the device number. */
297 if (dlink
.dev
== stp
->st_dev
) {
298 if (strcmp(file
, dlink
.file
) == 0) {
299 /* Called twice. Forget about this link. */
306 /* Return the name of the earlier link. */
313 /* First time I see this link. Add it to the store. */
319 tfd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
321 if (errno
!= EEXIST
) return nil
;
328 if ((len
= strlen(file
)) >= PATH_MAX
) {
333 dlink
.dev
= stp
->st_dev
;
334 strcpy(dlink
.file
, file
);
335 len
+= offsetof(dlink_t
, file
) + 1;
336 if ((off
= lseek(tfd
, 0, SEEK_END
)) == -1) return nil
;
337 if (write(tfd
, &dlink
, len
) != len
) return nil
;
339 if ((lp
= malloc(sizeof(*lp
))) == nil
) return nil
;
341 lp
->ino
= stp
->st_ino
;
348 int trylink(const char *src
, const char *dst
, const struct stat
*srcst
,
349 const struct stat
*dstst
)
350 /* Keep the link structure intact if src has been seen before. */
355 if (action
== COPY
&& expand
) return 0;
357 if ((olddst
= link_islink(srcst
, dst
)) == nil
) {
358 /* if (errno != ENOENT) ... */
362 /* Try to link the file copied earlier to the new file. */
363 if (dstst
->st_ino
!= 0) (void) unlink(dst
);
365 if ((linked
= (link(olddst
, dst
) == 0)) && vflag
)
366 printf("ln %s ..\n", olddst
);
371 int copy(const char *src
, const char *dst
, struct stat
*srcst
,
373 /* Copy one file to another and copy (some of) the attributes. */
379 assert(srcst
->st_ino
!= 0);
381 if (dstst
->st_ino
== 0) {
382 /* The file doesn't exist yet. */
384 if (!S_ISREG(srcst
->st_mode
)) {
385 /* Making a new mode 666 regular file. */
386 srcst
->st_mode
= (S_IFREG
| 0666) & fc_mask
;
388 if (!pflag
&& conforming
) {
389 /* Making a new file copying mode with umask applied. */
390 srcst
->st_mode
&= fc_mask
;
393 /* File exists, ask if ok to overwrite if '-i'. */
395 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
396 fprintf(stderr
, "Overwrite %s? (mode = %03o) ",
397 dst
, dstst
->st_mode
& 07777);
398 if (!affirmative()) return 0;
401 if (action
== MOVE
) {
402 /* Don't overwrite, remove first. */
403 if (unlink(dst
) < 0 && errno
!= ENOENT
) {
410 /* Keep the existing mode and ownership. */
411 srcst
->st_mode
= dstst
->st_mode
;
412 srcst
->st_uid
= dstst
->st_uid
;
413 srcst
->st_gid
= dstst
->st_gid
;
418 /* Keep the link structure if possible. */
419 if (trylink(src
, dst
, srcst
, dstst
)) return 1;
421 if ((srcfd
= open(src
, O_RDONLY
)) < 0) {
426 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, srcst
->st_mode
& 0777);
427 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
428 /* Retry adding a "w" bit. */
429 (void) chmod(dst
, dstst
->st_mode
| S_IWUSR
);
430 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
432 if (dstfd
< 0 && fflag
&& errno
== EACCES
) {
433 /* Retry after trying to delete. */
435 dstfd
= open(dst
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0);
443 /* Get current parameters. */
444 if (fstat(dstfd
, dstst
) < 0) {
451 /* Copy the little bytes themselves. */
452 while ((n
= read(srcfd
, buf
, sizeof(buf
))) > 0) {
456 while (n
> 0 && (r
= write(dstfd
, bp
, n
)) > 0) {
463 "%s: Warning: EOF writing to %s\n",
481 /* Copy the ownership. */
482 if ((pflag
|| !conforming
)
483 && S_ISREG(dstst
->st_mode
)
484 && (dstst
->st_uid
!= srcst
->st_uid
485 || dstst
->st_gid
!= srcst
->st_gid
)
487 if (chmod(dst
, 0) == 0) dstst
->st_mode
&= ~07777;
488 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
489 if (errno
!= EPERM
) {
494 dstst
->st_uid
= srcst
->st_uid
;
495 dstst
->st_gid
= srcst
->st_gid
;
499 if (conforming
&& S_ISREG(dstst
->st_mode
)
500 && (dstst
->st_uid
!= srcst
->st_uid
501 || dstst
->st_gid
!= srcst
->st_gid
)
503 /* Suid bits must be cleared in the holy name of
504 * security (and the assumed user stupidity).
506 srcst
->st_mode
&= ~06000;
510 if (S_ISREG(dstst
->st_mode
) && dstst
->st_mode
!= srcst
->st_mode
) {
511 if (chmod(dst
, srcst
->st_mode
) < 0) {
512 if (errno
!= EPERM
) {
516 fprintf(stderr
, "%s: Can't change the mode of %s\n",
521 /* Copy the file modification time. */
522 if ((pflag
|| !conforming
) && S_ISREG(dstst
->st_mode
)) {
525 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
526 ut
.modtime
= srcst
->st_mtime
;
527 if (utime(dst
, &ut
) < 0) {
528 if (errno
!= EPERM
) {
534 "%s: Can't set the time of %s\n",
540 printf(action
== COPY
? "cp %s ..\n" : "mv %s ..\n", src
);
545 void copy1(const char *src
, const char *dst
, struct stat
*srcst
,
547 /* Inspect the source file and then copy it. Treatment of symlinks and
548 * special files is a bit complicated. The filetype and link-structure are
549 * ignored if (expand && !rflag), symlinks and link-structure are ignored
550 * if (expand && rflag), everything is copied precisely if !expand.
555 assert(srcst
->st_ino
!= 0);
557 if (srcst
->st_ino
== dstst
->st_ino
&& srcst
->st_dev
== dstst
->st_dev
) {
558 fprintf(stderr
, "%s: can't copy %s onto itself\n",
564 /* You can forget it if the destination is a directory. */
565 if (dstst
->st_ino
!= 0 && S_ISDIR(dstst
->st_mode
)) {
571 if (S_ISREG(srcst
->st_mode
) || (expand
&& !rflag
)) {
572 if (!copy(src
, dst
, srcst
, dstst
)) return;
574 if (action
== MOVE
&& unlink(src
) < 0) {
581 if (dstst
->st_ino
!= 0) {
582 if (iflag
|| (action
== MOVE
&& !fflag
&& !writable(dstst
))) {
583 fprintf(stderr
, "Replace %s? (mode = %03o) ",
584 dst
, dstst
->st_mode
& 07777);
585 if (!affirmative()) return;
587 if (unlink(dst
) < 0) {
594 /* Apply the file creation mask if so required. */
595 if (!pflag
&& conforming
) srcst
->st_mode
&= fc_mask
;
599 if (S_ISLNK(srcst
->st_mode
)) {
602 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
607 r
= symlink(buf
, dst
);
609 printf("ln -s %s %s\n", buf
, dst
);
611 if (trylink(src
, dst
, srcst
, dstst
)) {
615 if (S_ISFIFO(srcst
->st_mode
)) {
616 r
= mkfifo(dst
, srcst
->st_mode
);
618 printf("mkfifo %s\n", dst
);
620 if (S_ISBLK(srcst
->st_mode
) || S_ISCHR(srcst
->st_mode
)) {
621 r
= mknod(dst
, srcst
->st_mode
, srcst
->st_rdev
);
622 if (vflag
&& r
== 0) {
623 printf("mknod %s %c %d %d\n",
625 S_ISBLK(srcst
->st_mode
) ? 'b' : 'c',
626 (srcst
->st_rdev
>> 8) & 0xFF,
627 (srcst
->st_rdev
>> 0) & 0xFF);
630 fprintf(stderr
, "%s: %s: odd filetype %5o (not copied)\n",
631 prog_name
, src
, srcst
->st_mode
);
636 if (r
< 0 || lstat(dst
, dstst
) < 0) {
641 if (action
== MOVE
&& unlink(src
) < 0) {
643 (void) unlink(dst
); /* Don't want it twice. */
649 if (S_ISLNK(srcst
->st_mode
)) return;
651 /* Copy the ownership. */
652 if ((pflag
|| !conforming
)
653 && (dstst
->st_uid
!= srcst
->st_uid
654 || dstst
->st_gid
!= srcst
->st_gid
)
656 if (chown(dst
, srcst
->st_uid
, srcst
->st_gid
) < 0) {
657 if (errno
!= EPERM
) {
664 /* Copy the file modification time. */
665 if (pflag
|| !conforming
) {
668 ut
.actime
= action
== MOVE
? srcst
->st_atime
: time(nil
);
669 ut
.modtime
= srcst
->st_mtime
;
670 if (utime(dst
, &ut
) < 0) {
671 if (errno
!= EPERM
) {
675 fprintf(stderr
, "%s: Can't set the time of %s\n",
681 void remove1(const char *src
, const struct stat
*srcst
)
683 if (iflag
|| (!fflag
&& !writable(srcst
))) {
684 fprintf(stderr
, "Remove %s? (mode = %03o) ", src
,
685 srcst
->st_mode
& 07777);
686 if (!affirmative()) return;
688 if (unlink(src
) < 0) {
691 if (vflag
) printf("rm %s\n", src
);
695 void link1(const char *src
, const char *dst
, const struct stat
*srcst
,
696 const struct stat
*dstst
)
701 if (dstst
->st_ino
!= 0 && (iflag
|| fflag
)) {
702 if (srcst
->st_ino
== dstst
->st_ino
) {
704 fprintf(stderr
, "%s: Can't link %s onto itself\n",
710 fprintf(stderr
, "Remove %s? ", dst
);
711 if (!affirmative()) return;
714 if (S_ISDIR(dstst
->st_mode
) || unlink(dst
) < 0) {
720 if (!sflag
&& !(rflag
&& S_ISLNK(srcst
->st_mode
)) && !(Sflag
&& xdev
)) {
722 if (link(src
, dst
) < 0) {
723 if (!Sflag
|| errno
!= EXDEV
) {
727 /* Can't do a cross-device link, we have to symlink. */
730 if (vflag
) printf("ln %s..\n", src
);
736 if (!rflag
&& !Sflag
) {
737 /* We can get away with a "don't care if it works" symlink. */
738 if (symlink(src
, dst
) < 0) {
742 if (vflag
) printf("ln -s %s %s\n", src
, dst
);
746 /* If the source is a symlink then it is simply copied. */
747 if (S_ISLNK(srcst
->st_mode
)) {
751 if ((r
= readlink(src
, buf
, sizeof(buf
)-1)) < 0) {
756 if (symlink(buf
, dst
) < 0) {
760 if (vflag
) printf("ln -s %s %s\n", buf
, dst
);
764 /* Make a symlink that has to work, i.e. we must be able to access the
765 * source now, and the link must work.
767 if (dst
[0] == '/' && src
[0] != '/') {
768 /* ln -[rsS] relative/path /full/path. */
770 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
771 prog_name
, src
, dst
);
775 /* Count the number of subdirectories in the destination file and
776 * add one '..' for each.
783 if (p
[1] == '/' || p
[1] == 0) {
784 /* A "." component; skip. */
785 do p
++; while (*p
== '/');
788 if (p
[1] == '.' && (p
[2] == '/' || p
[2] == 0)) {
789 /* A ".." component; oops. */
790 switch (path_length(&sym
)) {
793 "%s: Symlinking %s to %s is too difficult for me to figure out\n",
794 prog_name
, src
, dst
);
800 path_trunc(&sym
, path_length(&sym
) - 3);
803 do p
++; while (*p
== '/');
807 while (*p
!= 0 && *p
!= '/') p
++;
808 while (*p
== '/') p
++;
810 path_add(&sym
, "..");
815 if (symlink(path_name(&sym
), dst
) < 0) {
818 if (vflag
) printf("ln -s %s %s\n", path_name(&sym
), dst
);
823 typedef struct entrylist
{
824 struct entrylist
*next
;
828 int eat_dir(const char *dir
, entrylist_t
**dlist
)
829 /* Make a linked list of all the names in a directory. */
832 struct dirent
*entry
;
834 if ((dp
= opendir(dir
)) == nil
) return 0;
836 while ((entry
= readdir(dp
)) != nil
) {
837 if (strcmp(entry
->d_name
, ".") == 0) continue;
838 if (strcmp(entry
->d_name
, "..") == 0) continue;
840 *dlist
= allocate(nil
, sizeof(**dlist
));
841 (*dlist
)->name
= allocate(nil
, strlen(entry
->d_name
)+1);
842 strcpy((*dlist
)->name
, entry
->d_name
);
843 dlist
= &(*dlist
)->next
;
850 void chop_dlist(entrylist_t
**dlist
)
851 /* Chop an entry of a name list. */
853 entrylist_t
*junk
= *dlist
;
856 deallocate(junk
->name
);
860 void drop_dlist(entrylist_t
*dlist
)
861 /* Get rid of a whole list. */
863 while (dlist
!= nil
) chop_dlist(&dlist
);
866 void do1(pathname_t
*src
, pathname_t
*dst
, int depth
)
867 /* Perform the appropriate action on a source and destination file. */
869 size_t slashsrc
, slashdst
;
870 struct stat srcst
, dstst
;
872 static ino_t topdst_ino
;
873 static dev_t topdst_dev
;
874 static dev_t topsrc_dev
;
877 if (vflag
&& depth
== 0) {
878 char flags
[100], *pf
= flags
;
880 if (pflag
) *pf
++= 'p';
881 if (iflag
) *pf
++= 'i';
882 if (fflag
) *pf
++= 'f';
883 if (sflag
) *pf
++= 's';
884 if (Sflag
) *pf
++= 'S';
885 if (mflag
) *pf
++= 'm';
886 if (rflag
) *pf
++= 'r';
887 if (vflag
) *pf
++= 'v';
888 if (xflag
) *pf
++= 'x';
889 if (expand
) *pf
++= 'L';
890 if (conforming
) *pf
++= 'C';
892 printf(": %s -%s %s %s\n", prog_name
, flags
,
893 path_name(src
), path_name(dst
));
897 /* st_ino == 0 if not stat()'ed yet, or nonexistent. */
901 if (action
!= LINK
|| !sflag
|| rflag
) {
902 /* Source must exist unless symlinking. */
903 if ((expand
? stat
: lstat
)(path_name(src
), &srcst
) < 0) {
904 report(path_name(src
));
910 /* First call: Not cross-device yet, first dst not seen yet,
911 * remember top device number.
915 topsrc_dev
= srcst
.st_dev
;
918 /* Inspect the intended destination unless removing. */
919 if (action
!= REMOVE
) {
920 if ((expand
? stat
: lstat
)(path_name(dst
), &dstst
) < 0) {
921 if (errno
!= ENOENT
) {
922 report(path_name(dst
));
928 if (action
== MOVE
&& !xdev
) {
929 if (dstst
.st_ino
!= 0 && srcst
.st_dev
!= dstst
.st_dev
) {
930 /* It's a cross-device rename, i.e. copy and remove. */
933 if (!mflag
|| dstst
.st_ino
== 0 || !S_ISDIR(dstst
.st_mode
)) {
934 /* Try to simply rename the file (not merging trees). */
936 if (srcst
.st_ino
== dstst
.st_ino
) {
938 "%s: Can't move %s onto itself\n",
939 prog_name
, path_name(src
));
944 if (dstst
.st_ino
!= 0) {
945 if (iflag
|| (!fflag
&& !writable(&dstst
))) {
947 "Replace %s? (mode = %03o) ",
949 dstst
.st_mode
& 07777);
950 if (!affirmative()) return;
952 if (!S_ISDIR(dstst
.st_mode
))
953 (void) unlink(path_name(dst
));
956 if (rename(path_name(src
), path_name(dst
)) == 0) {
959 printf("mv %s %s\n", path_name(src
),
964 if (errno
== EXDEV
) {
967 report2(path_name(src
), path_name(dst
));
973 if (srcst
.st_ino
== 0 || !S_ISDIR(srcst
.st_mode
)) {
974 /* Copy/move/remove/link a single file. */
978 copy1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
981 remove1(path_name(src
), &srcst
);
984 link1(path_name(src
), path_name(dst
), &srcst
, &dstst
);
990 /* Recursively copy/move/remove/link a directory if -r or -R. */
993 report(path_name(src
));
997 /* Ok to remove contents of dir? */
998 if (action
== REMOVE
) {
999 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
1000 /* Don't recurse past a mount point. */
1004 fprintf(stderr
, "Remove contents of %s? ", path_name(src
));
1005 if (!affirmative()) return;
1009 /* Gather the names in the source directory. */
1010 if (!eat_dir(path_name(src
), &dlist
)) {
1011 report(path_name(src
));
1015 /* Check/create the target directory. */
1016 if (action
!= REMOVE
&& dstst
.st_ino
!= 0 && !S_ISDIR(dstst
.st_mode
)) {
1017 if (action
!= MOVE
&& !fflag
) {
1019 report(path_name(dst
));
1023 fprintf(stderr
, "Replace %s? ", path_name(dst
));
1024 if (!affirmative()) {
1029 if (unlink(path_name(dst
)) < 0) {
1030 report(path_name(dst
));
1037 if (action
!= REMOVE
) {
1038 if (dstst
.st_ino
== 0) {
1039 /* Create a new target directory. */
1040 if (!pflag
&& conforming
) srcst
.st_mode
&= fc_mask
;
1042 if (mkdir(path_name(dst
), srcst
.st_mode
| S_IRWXU
) < 0
1043 || stat(path_name(dst
), &dstst
) < 0) {
1044 report(path_name(dst
));
1048 if (vflag
) printf("mkdir %s\n", path_name(dst
));
1050 /* Target directory already exists. */
1051 if (action
== MOVE
&& !mflag
) {
1053 report(path_name(dst
));
1058 /* Keep the existing attributes. */
1059 srcst
.st_mode
= dstst
.st_mode
;
1060 srcst
.st_uid
= dstst
.st_uid
;
1061 srcst
.st_gid
= dstst
.st_gid
;
1062 srcst
.st_mtime
= dstst
.st_mtime
;
1066 if (topdst_ino
== 0) {
1067 /* Remember the top destination. */
1068 topdst_dev
= dstst
.st_dev
;
1069 topdst_ino
= dstst
.st_ino
;
1072 if (srcst
.st_ino
== topdst_ino
&& srcst
.st_dev
== topdst_dev
) {
1073 /* E.g. cp -r /shallow /shallow/deep. */
1075 "%s%s %s/ %s/: infinite recursion avoided\n",
1076 prog_name
, action
!= MOVE
? " -r" : "",
1077 path_name(src
), path_name(dst
));
1082 if (xflag
&& topsrc_dev
!= srcst
.st_dev
) {
1083 /* Don't recurse past a mount point. */
1090 slashsrc
= path_length(src
);
1091 slashdst
= path_length(dst
);
1093 while (dlist
!= nil
) {
1094 path_add(src
, dlist
->name
);
1095 if (action
!= REMOVE
) path_add(dst
, dlist
->name
);
1097 do1(src
, dst
, depth
+1);
1099 path_trunc(src
, slashsrc
);
1100 path_trunc(dst
, slashdst
);
1104 if (action
== MOVE
|| action
== REMOVE
) {
1105 /* The contents of the source directory should have
1106 * been (re)moved above. Get rid of the empty dir.
1108 if (action
== REMOVE
&& iflag
) {
1109 fprintf(stderr
, "Remove directory %s? ",
1111 if (!affirmative()) return;
1113 if (rmdir(path_name(src
)) < 0) {
1114 if (errno
!= ENOTEMPTY
) report(path_name(src
));
1117 if (vflag
) printf("rmdir %s\n", path_name(src
));
1120 if (action
!= REMOVE
) {
1121 /* Set the attributes of a new directory. */
1124 /* Copy the ownership. */
1125 if ((pflag
|| !conforming
)
1126 && (dstst
.st_uid
!= srcst
.st_uid
1127 || dstst
.st_gid
!= srcst
.st_gid
)
1129 if (chown(path_name(dst
), srcst
.st_uid
,
1130 srcst
.st_gid
) < 0) {
1131 if (errno
!= EPERM
) {
1132 report(path_name(dst
));
1138 /* Copy the mode. */
1139 if (dstst
.st_mode
!= srcst
.st_mode
) {
1140 if (chmod(path_name(dst
), srcst
.st_mode
) < 0) {
1141 report(path_name(dst
));
1146 /* Copy the file modification time. */
1147 if (dstst
.st_mtime
!= srcst
.st_mtime
) {
1148 ut
.actime
= action
== MOVE
? srcst
.st_atime
: time(nil
);
1149 ut
.modtime
= srcst
.st_mtime
;
1150 if (utime(path_name(dst
), &ut
) < 0) {
1151 if (errno
!= EPERM
) {
1152 report(path_name(dst
));
1156 "%s: Can't set the time of %s\n",
1157 prog_name
, path_name(dst
));
1165 char *flags1
, *flags2
;
1169 flags1
= "pifsmrRvx";
1177 fprintf(stderr
, "Usage: rm [-ifrRvx] file ...\n");
1180 flags1
= "ifsSmrRvx";
1192 fprintf(stderr
, "Usage: %s [-%s] file1 file2\n", prog_name
, flags1
);
1194 fprintf(stderr
, " %s [-%s] file ... dir\n", prog_name
, flags2
);
1198 int main(int argc
, char **argv
)
1203 pathname_t src
, dst
;
1207 /* The first argument is the call name while debugging. */
1208 if (argc
< 2) exit(-1);
1216 /* Call name of this program. */
1217 prog_name
= basename(argv
[0]);
1219 /* Required action. */
1220 if (strcmp(prog_name
, "cp") == 0) {
1226 if (strcmp(prog_name
, "mv") == 0) {
1232 if (strcmp(prog_name
, "rm") == 0) {
1237 if (strcmp(prog_name
, "ln") == 0) {
1242 if (strcmp(prog_name
, "cpdir") == 0) {
1246 rflag
= mflag
= pflag
= 1;
1249 if (strcmp(prog_name
, "clone") == 0) {
1253 rflag
= mflag
= fflag
= 1;
1256 "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n",
1261 /* Who am I?, where am I?, how protective am I? */
1269 while (i
< argc
&& argv
[i
][0] == '-') {
1270 char *opt
= argv
[i
++] + 1;
1272 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
1275 /* Flag supported? */
1276 if (strchr(flags
, *opt
) == nil
) usage();
1284 if (action
== MOVE
) fflag
= 0;
1288 if (action
== MOVE
) iflag
= 0;
1291 if (action
== LINK
) {
1294 /* Forget about POSIX, do it right. */
1331 /* 'ln dir/file' is to be read as 'ln dir/file .'. */
1332 if ((argc
- i
) == 1 && action
== LINK
) argv
[argc
++]= ".";
1335 if ((argc
- i
) < 2) usage();
1341 if (action
!= REMOVE
&& !mflag
1342 && stat(argv
[argc
-1], &st
) >= 0 && S_ISDIR(st
.st_mode
)
1344 /* The last argument is a directory, this means we have to
1345 * throw the whole lot into this directory. This is the
1346 * Right Thing unless you use -r.
1348 path_add(&dst
, argv
[argc
-1]);
1349 slash
= path_length(&dst
);
1352 path_add(&src
, argv
[i
]);
1353 path_add(&dst
, basename(argv
[i
]));
1357 path_trunc(&src
, 0);
1358 path_trunc(&dst
, slash
);
1359 } while (++i
< argc
-1);
1361 if (action
== REMOVE
|| (argc
- i
) == 2) {
1362 /* Just two files (or many files for rm). */
1364 path_add(&src
, argv
[i
]);
1365 if (action
!= REMOVE
) path_add(&dst
, argv
[i
+1]);
1368 path_trunc(&src
, 0);
1369 } while (action
== REMOVE
&& ++i
< argc
);
1378 fprintf(stderr
, "(%ld chunks of memory not freed)\n",