📄 cp.c
字号:
/* cp 1.9 - copy files Author: Kees J. Bot * mv - move files 20 Jul 1993 * rm - remove files * ln - make a link * cpdir - copy a directory tree (cp -psmr) * clone - make a link farm (ln -fmr) */#define nil 0#include <stdio.h>#include <sys/types.h>#include <stdlib.h>#include <string.h>#include <stddef.h>#include <unistd.h>#include <fcntl.h>#include <time.h>#include <sys/stat.h>#include <utime.h>#include <dirent.h>#include <errno.h>#ifndef DEBUG#define DEBUG 0#define NDEBUG 1#endif#include <assert.h>/* Copy files in this size chunks: */#if __minix && !__minix_vmd#define CHUNK (8192 * sizeof(char *))#else#define CHUNK (1024 << (sizeof(int) + sizeof(char *)))#endif#ifndef CONFORMING#define CONFORMING 1 /* Precisely POSIX conforming. */#endif#define arraysize(a) (sizeof(a) / sizeof((a)[0]))#define arraylimit(a) ((a) + arraysize(a))char *prog_name; /* Call name of this program. */int ex_code= 0; /* Final exit code. */typedef enum identity { CP, MV, RM, LN, CPDIR, CLONE } identity_t;typedef enum action { COPY, MOVE, REMOVE, LINK } action_t;identity_t identity; /* How did the user call me? */action_t action; /* Copying, moving, or linking. */int pflag= 0; /* -p/-s: Make orginal and copy the same. */int iflag= 0; /* -i: Interactive overwriting/deleting. */int fflag= 0; /* -f: Force. */int sflag= 0; /* -s: Make a symbolic link (ln/clone). */int Sflag= 0; /* -S: Make a symlink if across devices. */int mflag= 0; /* -m: Merge trees, no target dir trickery. */int rflag= 0; /* -r/-R: Recursively copy a tree. */int vflag= 0; /* -v: Verbose. */int xflag= 0; /* -x: Don't traverse past mount points. */int xdev= 0; /* Set when moving or linking cross-device. */int expand= 0; /* Expand symlinks, ignore links. */int conforming= CONFORMING; /* Sometimes standards are a pain. */int fc_mask; /* File creation mask. */int uid, gid; /* Effective uid & gid. */int istty; /* Can have terminal input. */#ifndef S_ISLNK/* There were no symlinks in medieval times. */#define S_ISLNK(mode) (0)#define lstat stat#define symlink(path1, path2) (errno= ENOSYS, -1)#define readlink(path, buf, len) (errno= ENOSYS, -1)#endifvoid report(const char *label){ if (action == REMOVE && fflag) return; fprintf(stderr, "%s: %s: %s\n", prog_name, label, strerror(errno)); ex_code= 1;}void fatal(const char *label){ report(label); exit(1);}void report2(const char *src, const char *dst){ fprintf(stderr, "%s %s %s: %s\n", prog_name, src, dst, strerror(errno)); ex_code= 1;}#if DEBUGsize_t nchunks= 0; /* Number of allocated cells. */#endifvoid *allocate(void *mem, size_t size)/* Like realloc, but with checking of the return value. */{#if DEBUG if (mem == nil) nchunks++;#endif if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil) fatal("malloc()"); return mem;}void deallocate(void *mem)/* Release a chunk of memory. */{ if (mem != nil) {#if DEBUG nchunks--;#endif free(mem); }}typedef struct pathname { char *path; /* The actual pathname. */ size_t idx; /* Index for the terminating null byte. */ size_t lim; /* Actual length of the path array. */} pathname_t;void path_init(pathname_t *pp)/* Initialize a pathname to the null string. */{ pp->path= allocate(nil, pp->lim= 16); pp->path[pp->idx= 0]= 0;}void path_add(pathname_t *pp, const char *name)/* Add a component to a pathname. */{ size_t lim; char *p; lim= pp->idx + strlen(name) + 2; if (lim > pp->lim) { pp->lim= lim += lim/2; /* add an extra 50% growing space. */ pp->path= allocate(pp->path, lim); } p= pp->path + pp->idx; if (p > pp->path && p[-1] != '/') *p++ = '/'; while (*name != 0) { if (*name != '/' || p == pp->path || p[-1] != '/') *p++ = *name; name++; } *p = 0; pp->idx= p - pp->path;}void path_trunc(pathname_t *pp, size_t didx)/* Delete part of a pathname to a remembered length. */{ pp->path[pp->idx= didx]= 0;}#if DEBUGconst char *path_name(const pathname_t *pp)/* Return the actual name as a C string. */{ return pp->path;}size_t path_length(const pathname_t *pp)/* The length of the pathname. */{ return pp->idx;}void path_drop(pathname_t *pp)/* Release the storage occupied by the pathname. */{ deallocate(pp->path);}#else /* !DEBUG */#define path_name(pp) ((const char *) (pp)->path)#define path_length(pp) ((pp)->idx)#define path_drop(pp) deallocate((void *) (pp)->path)#endif /* !DEBUG */char *basename(const char *path)/* Return the last component of a pathname. (Note: declassifies a const * char * just like strchr. */{ const char *p= path; for (;;) { while (*p == '/') p++; /* Trailing slashes? */ if (*p == 0) break; path= p; while (*p != 0 && *p != '/') p++; /* Skip component. */ } return (char *) path;}int affirmative(void)/* Get a yes/no answer from the suspecting user. */{ int c; int ok; fflush(stdout); fflush(stderr); while ((c= getchar()) == ' ') {} ok= (c == 'y' || c == 'Y'); while (c != EOF && c != '\n') c= getchar(); return ok;}int writable(const struct stat *stp)/* True iff the file with the given attributes allows writing. (And we have * a terminal to ask if ok to overwrite.) */{ if (!istty || uid == 0) return 1; if (stp->st_uid == uid) return stp->st_mode & S_IWUSR; if (stp->st_gid == gid) return stp->st_mode & S_IWGRP; return stp->st_mode & S_IWOTH;}int trylink(const char *src, const char *dst, struct stat *srcst, struct stat *dstst)/* Keep the link structure intact if src has been seen before. */{ typedef struct oldlink { struct oldlink *next; char *olddst; dev_t dev; ino_t ino; ino_t count; } oldlink_t; static oldlink_t *oldies[0x100]; oldlink_t *op, **pop; int found, linked;#if DEBUG if (src == nil) { /* Time to clean house (consistency freak on the loose). */ for (pop= oldies; pop < arraylimit(oldies); pop++) { while ((op= *pop) != nil) { *pop= op->next; deallocate(op->olddst); deallocate(op); } } return 0; }#endif if (action == COPY && expand) return 0; pop= &oldies[(unsigned) srcst->st_ino % arraysize(oldies)]; found= 0; while ((op= *pop) != nil && srcst->st_ino <= op->ino) { if (srcst->st_ino == op->ino && srcst->st_dev == op->dev) { found= 1; break; } pop= &op->next; } if (!found) { if (srcst->st_nlink > 1) { /* Remember this one for later. */ op= allocate(nil, sizeof(*op)); op->olddst= allocate(nil, (strlen(dst) + 1) * sizeof(*op->olddst)); strcpy(op->olddst, dst); op->ino= srcst->st_ino; op->dev= srcst->st_dev; op->count= srcst->st_nlink; op->next= *pop; *pop= op; } return 0; } /* Try to link the file copied earlier to the new file. */ if (dstst->st_ino != 0) (void) unlink(dst); if ((linked= (link(op->olddst, dst) == 0)) && vflag) printf("ln %s %s\n", op->olddst, dst); if (--op->count == 1) { /* All the links to the file have been seen. */ *pop= op->next; deallocate(op->olddst); deallocate(op); } return linked;}int copy(const char *src, const char *dst, struct stat *srcst, struct stat *dstst)/* Copy one file to another and copy (some of) the attributes. */{ char buf[CHUNK]; int srcfd, dstfd; ssize_t n; assert(srcst->st_ino != 0); if (dstst->st_ino == 0) { /* The file doesn't exist yet. */ if (!S_ISREG(srcst->st_mode)) { /* Making a new mode 666 regular file. */ srcst->st_mode= (S_IFREG | 0666) & fc_mask; } else if (!pflag && conforming) { /* Making a new file copying mode with umask applied. */ srcst->st_mode &= fc_mask; } } else { /* File exists, ask if ok to overwrite if '-i'. */ if (iflag || (action == MOVE && !fflag && !writable(dstst))) { fprintf(stderr, "Overwrite %s? (mode = %03o) ", dst, dstst->st_mode & 07777); if (!affirmative()) return 0; } if (action == MOVE) { /* Don't overwrite, remove first. */ if (unlink(dst) < 0 && errno != ENOENT) { report(dst); return 0; } } else { /* Overwrite. */ if (!pflag) { /* Keep the existing mode and ownership. */ srcst->st_mode= dstst->st_mode; srcst->st_uid= dstst->st_uid; srcst->st_gid= dstst->st_gid; } } } /* Keep the link structure if possible. */ if (trylink(src, dst, srcst, dstst)) return 1; if ((srcfd= open(src, O_RDONLY)) < 0) { report(src); return 0; } dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, srcst->st_mode & 0777); if (dstfd < 0 && fflag && errno == EACCES) { /* Retry adding a "w" bit. */ (void) chmod(dst, dstst->st_mode | S_IWUSR); dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0); } if (dstfd < 0 && fflag && errno == EACCES) { /* Retry after trying to delete. */ (void) unlink(dst); dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0); } if (dstfd < 0) { report(dst); close(srcfd); return 0; } /* Get current parameters. */ if (fstat(dstfd, dstst) < 0) { report(dst); close(srcfd); close(dstfd); return 0; } /* Copy the little bytes themselves. */ while ((n= read(srcfd, buf, sizeof(buf))) > 0) { char *bp = buf; ssize_t r; while (n > 0 && (r= write(dstfd, bp, n)) > 0) { bp += r; n -= r; } if (r <= 0) { if (r == 0) { fprintf(stderr, "%s: Warning: EOF writing to %s\n", prog_name, dst); break; } fatal(dst); } } if (n < 0) { report(src); close(srcfd); close(dstfd); return 0; } close(srcfd); close(dstfd); /* Copy the ownership. */ if ((pflag || !conforming) && S_ISREG(dstst->st_mode) && (dstst->st_uid != srcst->st_uid || dstst->st_gid != srcst->st_gid) ) { if (chmod(dst, 0) == 0) dstst->st_mode&= ~07777; if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) { if (errno != EPERM) { report(dst); return 0; } } else { dstst->st_uid= srcst->st_uid; dstst->st_gid= srcst->st_gid; } } if (conforming && S_ISREG(dstst->st_mode) && (dstst->st_uid != srcst->st_uid || dstst->st_gid != srcst->st_gid) ) { /* Suid bits must be cleared in the holy name of * security (and the assumed user stupidity). */ srcst->st_mode&= ~06000; } /* Copy the mode. */ if (S_ISREG(dstst->st_mode) && dstst->st_mode != srcst->st_mode) { if (chmod(dst, srcst->st_mode) < 0) { if (errno != EPERM) { report(dst); return 0; } fprintf(stderr, "%s: Can't change the mode of %s\n", prog_name, dst); } } /* Copy the file modification time. */ if ((pflag || !conforming) && S_ISREG(dstst->st_mode)) { struct utimbuf ut; ut.actime= action == MOVE ? srcst->st_atime : time(nil); ut.modtime= srcst->st_mtime; if (utime(dst, &ut) < 0) { if (errno != EPERM) { report(dst); return 0; } if (pflag) { fprintf(stderr, "%s: Can't set the time of %s\n", prog_name, dst); } } } if (vflag) { printf(action == COPY ? "cp %s %s\n" : "mv %s %s\n", src, dst); } return 1;}void copy1(const char *src, const char *dst, struct stat *srcst, struct stat *dstst)/* Inspect the source file and then copy it. Treatment of symlinks and * special files is a bit complicated. The filetype and link-structure are * ignored if (expand && !rflag), symlinks and link-structure are ignored * if (expand && rflag), everything is copied precisely if !expand. */{ int r, linked; assert(srcst->st_ino != 0); if (srcst->st_ino == dstst->st_ino && srcst->st_dev == dstst->st_dev) { fprintf(stderr, "%s: can't copy %s onto itself\n", prog_name, src); ex_code= 1; return; } /* You can forget it if the destination is a directory. */ if (dstst->st_ino != 0 && S_ISDIR(dstst->st_mode)) { errno= EISDIR; report(dst); return; } if (S_ISREG(srcst->st_mode) || (expand && !rflag)) { if (!copy(src, dst, srcst, dstst)) return; if (action == MOVE && unlink(src) < 0) { report(src); return; } return; } if (dstst->st_ino != 0) { if (iflag || (action == MOVE && !fflag && !writable(dstst))) { fprintf(stderr, "Replace %s? (mode = %03o) ", dst, dstst->st_mode & 07777); if (!affirmative()) return; } if (unlink(dst) < 0) { report(dst); return; } dstst->st_ino= 0; } /* Apply the file creation mask if so required. */ if (!pflag && conforming) srcst->st_mode &= fc_mask; linked= 0; if (S_ISLNK(srcst->st_mode)) { char buf[1024+1]; if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) { report(src); return; } buf[r]= 0; r= symlink(buf, dst); if (vflag && r == 0) printf("ln -s %s %s\n", buf, dst); } else if (trylink(src, dst, srcst, dstst)) { linked= 1; r= 0; } else if (S_ISFIFO(srcst->st_mode)) { r= mkfifo(dst, srcst->st_mode); if (vflag && r == 0) printf("mkfifo %s\n", dst); } else if (S_ISBLK(srcst->st_mode) || S_ISCHR(srcst->st_mode)) { r= mknod(dst, srcst->st_mode, srcst->st_rdev); if (vflag && r == 0) { printf("mknod %s %c %d %d\n", dst, S_ISBLK(srcst->st_mode) ? 'b' : 'c', (srcst->st_rdev >> 8) & 0xFF, (srcst->st_rdev >> 0) & 0xFF); } } else { fprintf(stderr, "%s: %s: odd filetype %5o (not copied)\n", prog_name, src, srcst->st_mode); ex_code= 1; return; } if (r < 0 || lstat(dst, dstst) < 0) { report(dst); return; } if (action == MOVE && unlink(src) < 0) { report(src); (void) unlink(dst); /* Don't want it twice. */ return; } if (linked) return; if (S_ISLNK(srcst->st_mode)) return; /* Copy the ownership. */ if ((pflag || !conforming) && (dstst->st_uid != srcst->st_uid || dstst->st_gid != srcst->st_gid) ) { if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) { if (errno != EPERM) { report(dst); return; } } } /* Copy the file modification time. */ if (pflag || !conforming) { struct utimbuf ut; ut.actime= action == MOVE ? srcst->st_atime : time(nil); ut.modtime= srcst->st_mtime; if (utime(dst, &ut) < 0) { if (errno != EPERM) { report(dst); return; } fprintf(stderr, "%s: Can't set the time of %s\n", prog_name, dst); } }}void remove1(const char *src, struct stat *srcst){ if (iflag || (!fflag && !writable(srcst))) { fprintf(stderr, "Remove %s? (mode = %03o) ", src, srcst->st_mode & 07777); if (!affirmative()) return; } if (unlink(src) < 0) { report(src); } else { if (vflag) printf("rm %s\n", src); }}void link1(const char *src, const char *dst, struct stat *srcst, struct stat *dstst){ pathname_t sym; const char *p; if (dstst->st_ino != 0 && (iflag || fflag)) { if (srcst->st_ino == dstst->st_ino) { if (fflag) return; fprintf(stderr, "%s: Can't link %s onto itself\n", prog_name, src); ex_code= 1; return; } if (iflag) { fprintf(stderr, "Remove %s? ", dst);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -