📄 cp.c
字号:
if (!affirmative()) return; } errno= EISDIR; if (S_ISDIR(dstst->st_mode) || unlink(dst) < 0) { report(dst); return; } } if (!sflag && !(rflag && S_ISLNK(srcst->st_mode)) && !(Sflag && xdev)) { /* A normal link. */ if (link(src, dst) < 0) { if (!Sflag || errno != EXDEV) { report2(src, dst); return; } /* Can't do a cross-device link, we have to symlink. */ xdev= 1; } else { if (vflag) printf("ln %s %s\n", src, dst); return; } } /* Do a symlink. */ if (!rflag && !Sflag) { /* We can get away with a "don't care if it works" symlink. */ if (symlink(src, dst) < 0) { report(dst); return; } if (vflag) printf("ln -s %s %s\n", src, dst); return; } /* If the source is a symlink then it is simply copied. */ if (S_ISLNK(srcst->st_mode)) { int r; char buf[1024+1]; if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) { report(src); return; } buf[r]= 0; if (symlink(buf, dst) < 0) { report(dst); return; } if (vflag) printf("ln -s %s %s\n", buf, dst); return; } /* Make a symlink that has to work, i.e. we must be able to access the * source now, and the link must work. */ if (dst[0] == '/' && src[0] != '/') { /* ln -[rsS] relative/path /full/path. */ fprintf(stderr, "%s: Symlinking %s to %s can't be made to work, too difficult\n", prog_name, src, dst); exit(1); } /* Count the number of subdirectories in the destination file and * add one '..' for each. */ path_init(&sym); if (src[0] != '/') { p= dst; while (*p != 0) { while (*p != 0 && *p != '/') p++; while (*p == '/') p++; if (*p == 0) break; path_add(&sym, ".."); } } path_add(&sym, src); if (symlink(path_name(&sym), dst) < 0) { report(dst); } else { if (vflag) printf("ln -s %s %s\n", path_name(&sym), dst); } path_drop(&sym);}typedef struct entrylist { struct entrylist *next; char *name;} entrylist_t;int eat_dir(const char *dir, entrylist_t **dlist)/* Make a linked list of all the names in a directory. */{ DIR *dp; struct dirent *entry; if ((dp= opendir(dir)) == nil) return 0; while ((entry= readdir(dp)) != nil) { if (strcmp(entry->d_name, ".") == 0) continue; if (strcmp(entry->d_name, "..") == 0) continue; *dlist= allocate(nil, sizeof(**dlist)); (*dlist)->name= allocate(nil, strlen(entry->d_name)+1); strcpy((*dlist)->name, entry->d_name); dlist= &(*dlist)->next; } closedir(dp); *dlist= nil; return 1;}void chop_dlist(entrylist_t **dlist)/* Chop an entry of a name list. */{ entrylist_t *junk= *dlist; *dlist= junk->next; deallocate(junk->name); deallocate(junk);}void drop_dlist(entrylist_t *dlist)/* Get rid of a whole list. */{ while (dlist != nil) chop_dlist(&dlist);}void do1(pathname_t *src, pathname_t *dst, int depth)/* Perform the appropriate action on a source and destination file. */{ size_t slashsrc, slashdst; struct stat srcst, dstst; entrylist_t *dlist; static ino_t topdst_ino; static dev_t topdst_dev; static dev_t topsrc_dev;#if DEBUG if (vflag && depth == 0) { char flags[100], *pf= flags; if (pflag) *pf++= 'p'; if (iflag) *pf++= 'i'; if (fflag) *pf++= 'f'; if (sflag) *pf++= 's'; if (Sflag) *pf++= 'S'; if (mflag) *pf++= 'm'; if (rflag) *pf++= 'r'; if (vflag) *pf++= 'v'; if (xflag) *pf++= 'x'; if (expand) *pf++= 'L'; if (conforming) *pf++= 'C'; *pf= 0; printf(": %s -%s %s %s\n", prog_name, flags, path_name(src), path_name(dst)); }#endif /* st_ino == 0 if not stat()'ed yet, or nonexistent. */ srcst.st_ino= 0; dstst.st_ino= 0; if (action != LINK || !sflag || rflag) { /* Source must exist unless symlinking. */ if ((expand ? stat : lstat)(path_name(src), &srcst) < 0) { report(path_name(src)); return; } } if (depth == 0) { /* First call: Not cross-device yet, first dst not seen yet, * remember top device number. */ xdev= 0; topdst_ino= 0; topsrc_dev= srcst.st_dev; } /* Inspect the intended destination unless removing. */ if (action != REMOVE) { if ((expand ? stat : lstat)(path_name(dst), &dstst) < 0) { if (errno != ENOENT) { report(path_name(dst)); return; } } } if (action == MOVE && !xdev) { if (dstst.st_ino != 0 && srcst.st_dev != dstst.st_dev) { /* It's a cross-device rename, i.e. copy and remove. */ xdev= 1; } else if (!mflag || dstst.st_ino == 0 || !S_ISDIR(dstst.st_mode)) { /* Try to simply rename the file (not merging trees). */ if (srcst.st_ino == dstst.st_ino) { fprintf(stderr, "%s: Can't move %s onto itself\n", prog_name, path_name(src)); ex_code= 1; return; } if (dstst.st_ino != 0) { if (iflag || (!fflag && !writable(&dstst))) { fprintf(stderr, "Replace %s? (mode = %03o) ", path_name(dst), dstst.st_mode & 07777); if (!affirmative()) return; } if (!S_ISDIR(dstst.st_mode)) (void) unlink(path_name(dst)); } if (rename(path_name(src), path_name(dst)) == 0) { /* Success. */ if (vflag) { printf("mv %s %s\n", path_name(src), path_name(dst)); } return; } if (errno == EXDEV) { xdev= 1; } else { report2(path_name(src), path_name(dst)); return; } } } if (!S_ISDIR(srcst.st_mode)) { /* Copy/move/remove/link a single file. */ switch (action) { case COPY: case MOVE: copy1(path_name(src), path_name(dst), &srcst, &dstst); break; case REMOVE: remove1(path_name(src), &srcst); break; case LINK: link1(path_name(src), path_name(dst), &srcst, &dstst); break; } return; } /* Recursively copy/move/remove/link a directory if -r or -R. */ if (!rflag) { errno= EISDIR; report(path_name(src)); return; } /* Ok to remove contents of dir? */ if (action == REMOVE) { if (xflag && topsrc_dev != srcst.st_dev) { /* Don't recurse past a mount point. */ return; } if (iflag) { fprintf(stderr, "Remove contents of %s? ", path_name(src)); if (!affirmative()) return; } } /* Gather the names in the source directory. */ if (!eat_dir(path_name(src), &dlist)) { report(path_name(src)); return; } /* Check/create the target directory. */ if (action != REMOVE && dstst.st_ino != 0 && !S_ISDIR(dstst.st_mode)) { if (action != MOVE && !fflag) { errno= ENOTDIR; report(path_name(dst)); return; } if (iflag) { fprintf(stderr, "Replace %s? ", path_name(dst)); if (!affirmative()) { drop_dlist(dlist); return; } } if (unlink(path_name(dst)) < 0) { report(path_name(dst)); drop_dlist(dlist); return; } dstst.st_ino= 0; } if (action != REMOVE) { if (dstst.st_ino == 0) { /* Create a new target directory. */ if (!pflag && conforming) srcst.st_mode&= fc_mask; if (mkdir(path_name(dst), srcst.st_mode | S_IRWXU) < 0 || stat(path_name(dst), &dstst) < 0) { report(path_name(dst)); drop_dlist(dlist); return; } if (vflag) printf("mkdir %s\n", path_name(dst)); } else { /* Target directory already exists. */ if (action == MOVE && !mflag) { errno= EEXIST; report(path_name(dst)); drop_dlist(dlist); return; } if (!pflag) { /* Keep the existing attributes. */ srcst.st_mode= dstst.st_mode; srcst.st_uid= dstst.st_uid; srcst.st_gid= dstst.st_gid; srcst.st_mtime= dstst.st_mtime; } } if (topdst_ino == 0) { /* Remember the top destination. */ topdst_dev= dstst.st_dev; topdst_ino= dstst.st_ino; } if (srcst.st_ino == topdst_ino && srcst.st_dev == topdst_dev) { /* E.g. cp -r /shallow /shallow/deep. */ fprintf(stderr, "%s%s %s/ %s/: infinite recursion avoided\n", prog_name, action != MOVE ? " -r" : "", path_name(src), path_name(dst)); drop_dlist(dlist); return; } if (xflag && topsrc_dev != srcst.st_dev) { /* Don't recurse past a mount point. */ drop_dlist(dlist); return; } } /* Go down. */ slashsrc= path_length(src); slashdst= path_length(dst); while (dlist != nil) { path_add(src, dlist->name); if (action != REMOVE) path_add(dst, dlist->name); do1(src, dst, depth+1); path_trunc(src, slashsrc); path_trunc(dst, slashdst); chop_dlist(&dlist); } if (action == MOVE || action == REMOVE) { /* The contents of the source directory should have * been (re)moved above. Get rid of the empty dir. */ if (action == REMOVE && iflag) { fprintf(stderr, "Remove directory %s? ", path_name(src)); if (!affirmative()) return; } if (rmdir(path_name(src)) < 0) { if (errno != ENOTEMPTY) report(path_name(src)); return; } if (vflag) printf("rmdir %s\n", path_name(src)); } if (action != REMOVE) { /* Set the attributes of a new directory. */ struct utimbuf ut; /* Copy the ownership. */ if ((pflag || !conforming) && (dstst.st_uid != srcst.st_uid || dstst.st_gid != srcst.st_gid) ) { if (chown(path_name(dst), srcst.st_uid, srcst.st_gid) < 0) { if (errno != EPERM) { report(path_name(dst)); return; } } } /* Copy the mode. */ if (dstst.st_mode != srcst.st_mode) { if (chmod(path_name(dst), srcst.st_mode) < 0) { report(path_name(dst)); return; } } /* Copy the file modification time. */ if (dstst.st_mtime != srcst.st_mtime) { ut.actime= action == MOVE ? srcst.st_atime : time(nil); ut.modtime= srcst.st_mtime; if (utime(path_name(dst), &ut) < 0) { if (errno != EPERM) { report(path_name(dst)); return; } fprintf(stderr, "%s: Can't set the time of %s\n", prog_name, path_name(dst)); } } }}void usage(void){ char *flags1, *flags2; switch (identity) { case CP: flags1= "pifsmrRvx"; flags2= "pifsrRvx"; break; case MV: flags1= "ifsmvx"; flags2= "ifsvx"; break; case RM: fprintf(stderr, "Usage: rm [-ifrRvx] file ...\n"); exit(1); case LN: flags1= "ifsSmrRvx"; flags2= "ifsSrRvx"; break; case CPDIR: flags1= "ifvx"; flags2= nil; break; case CLONE: flags1= "ifsSvx"; flags2= nil; break; } fprintf(stderr, "Usage: %s [-%s] file1 file2\n", prog_name, flags1); if (flags2 != nil) fprintf(stderr, " %s [-%s] file ... dir\n", prog_name, flags2); exit(1);}void main(int argc, char **argv){ int i; char *flags; struct stat st; pathname_t src, dst; size_t slash;#if DEBUG >= 3 /* The first argument is the call name while debugging. */ if (argc < 2) exit(-1); argv++; argc--;#endif#if DEBUG vflag= isatty(1);#endif /* Call name of this program. */ prog_name= basename(argv[0]); /* Required action. */ if (strcmp(prog_name, "cp") == 0) { identity= CP; action= COPY; flags= "pifsmrRvx"; expand= 1; } else if (strcmp(prog_name, "mv") == 0) { identity= MV; action= MOVE; flags= "ifsmvx"; rflag= pflag= 1; } else if (strcmp(prog_name, "rm") == 0) { identity= RM; action= REMOVE; flags= "ifrRvx"; } else if (strcmp(prog_name, "ln") == 0) { identity= LN; action= LINK; flags= "ifsSmrRvx"; } else if (strcmp(prog_name, "cpdir") == 0) { identity= CPDIR; action= COPY; flags= "pifsmrRvx"; rflag= mflag= pflag= 1; conforming= 0; } else if (strcmp(prog_name, "clone") == 0) { identity= CLONE; action= LINK; flags= "ifsSmrRvx"; rflag= mflag= fflag= 1; } else { fprintf(stderr, "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n", prog_name); exit(1); } /* Who am I?, where am I?, how protective am I? */ uid= geteuid(); gid= getegid(); istty= isatty(0); fc_mask= ~umask(0); /* Gather flags. */ i= 1; while (i < argc && argv[i][0] == '-') { char *opt= argv[i++] + 1; if (opt[0] == '-' && opt[1] == 0) break; /* -- */ while (*opt != 0) { /* Flag supported? */ if (strchr(flags, *opt) == nil) usage(); switch (*opt++) { case 'p': pflag= 1; break; case 'i': iflag= 1; if (action == MOVE) fflag= 0; break; case 'f': fflag= 1; if (action == MOVE) iflag= 0; break; case 's': if (action == LINK) { sflag= 1; } else { /* Forget about POSIX, do it right. */ conforming= 0; } break; case 'S': Sflag= 1; break; case 'm': mflag= 1; break; case 'r': expand= 0; /*FALL THROUGH*/ case 'R': rflag= 1; break; case 'v': vflag= 1; break; case 'x': xflag= 1; break; default: assert(0); } } } switch (action) { case REMOVE: if (i == argc) usage(); break; case LINK: /* 'ln dir/file' is to be read as 'ln dir/file .'. */ if ((argc - i) == 1 && action == LINK) argv[argc++]= "."; /*FALL THROUGH*/ default: if ((argc - i) < 2) usage(); } path_init(&src); path_init(&dst); if (action != REMOVE && !mflag && stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) { /* The last argument is a directory, this means we have to * throw the whole lot into this directory. This is the * Right Thing unless you use -r. */ path_add(&dst, argv[argc-1]); slash= path_length(&dst); do { path_add(&src, argv[i]); path_add(&dst, basename(argv[i])); do1(&src, &dst, 0); path_trunc(&src, 0); path_trunc(&dst, slash); } while (++i < argc-1); } else if (action == REMOVE || (argc - i) == 2) { /* Just two files (or many files for rm). */ do { path_add(&src, argv[i]); if (action != REMOVE) path_add(&dst, argv[i+1]); do1(&src, &dst, 0); path_trunc(&src, 0); } while (action == REMOVE && ++i < argc); } else { usage(); } path_drop(&src); path_drop(&dst);#if DEBUG (void) trylink(nil, nil, nil, nil); if (nchunks != 0) { fprintf(stderr, "(%ld chunks of memory not freed)\n", (long) nchunks); }#endif exit(ex_code);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -