Minix1.5/commands/cpdir.c

Compare this file to the similar file:
Show the results in this format:

/* cpdir - copy directory  	 	Author: Erik Baalbergen */

/* Use "cpdir [-v] src dst" to make a copy dst of directory src.
   Cpdir should behave like the UNIX shell command
  (cd src; tar cf - .) | (mkdir dst; cd dst; tar xf -)

   The -m "merge" flag enables you to copy into an existing directory.
   The -s "similar" flag preserves the full mode, uid, gid and times.
   The -v "verbose" flag enables you to see what's going on when running cpdir.

   Work yet to be done:

  - link checks, i.e. am I not overwriting a file/directory by itself?

  Please report bugs and suggestions to erikb@cs.vu.nl
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

#define MKDIR1 "/bin/mkdir"
#define MKDIR2 "/usr/bin/mkdir"

#define BUFSIZE 1024
#define PLEN	256
#define DIRSIZ	16

#define MAXLINKS 512

struct {
  unsigned short ino;
  unsigned short dev;
  char *path;
} links[MAXLINKS];
int nlinks = 0;

char *prog;
int vflag = 0;			/* verbose */
int mflag = 0;			/* force */
int sflag = 0;			/* similar */
char *strcpy();
char *malloc();

main(argc, argv)
char *argv[];
{
  int rc = 0;
  char *p, *s;
  struct stat st;

  prog = *argv++;
  if ((p = *argv) && *p == '-') {
	argv++;
	argc--;
	while (*++p) {
		switch (*p) {
		    case 'v':	vflag = 1;	break;
		    case 'm':	mflag = 1;	break;
		    case 's':	sflag = 1;	break;
		    default:
			fatal("illegal flag %s", p);
		}
	}
  }
  if (argc != 3) fatal("Usage: cpdir [-msv] source destination");
  s = *argv++;
  if (stat(s, &st) < 0) fatal("can't get file status of %s", s);
  if ((st.st_mode & S_IFMT) != S_IFDIR) fatal("%s is not a directory", s);
  cpdir(&st, s, *argv);
  exit(0);
}

cpdir(sp, s, d)
struct stat *sp;
char *s, *d;
{
  char spath[PLEN], dpath[PLEN];
  char ent[DIRSIZ + 1];
  register char *send = spath, *dend = dpath;
  int fd, n;
  struct stat st;
  static first = 1;
  static dev_t dev;
  static ino_t ino;

  if ((fd = open(s, O_RDONLY)) < 0) {
	nonfatal("can't read directory %s", s);
	return;
  }
  if (
      mflag == 0
      ||
      stat(d, &st) != 0
      ||
      (st.st_mode & S_IFMT) != S_IFDIR
	) {
	make_dir(d);
	if (sflag) similar(sp, d);
  }
  if (first) {
	stat(d, &st);
	dev = st.st_dev;
	ino = st.st_ino;
	first = 0;
  }
  if (sp->st_dev == dev && sp->st_ino == ino) {
	nonfatal("%s skipped to avoid an endless loop", s);
	return;
  }
  while (*send++ = *s++) {
  }
  while (*dend++ = *d++) {
  }
  send[-1] = '/';
  dend[-1] = '/';
  ent[DIRSIZ] = '\0';
  while ((n = read(fd, ent, DIRSIZ)) == DIRSIZ) {
	if (!((ent[0] == '\0' && ent[1] == '\0') || (ent[2] == '.') &&
	      (ent[3] == '\0' || (ent[3] == '.' && ent[4] == '\0'))
	      )) {
		strcpy(send, ent + 2);
		strcpy(dend, ent + 2);
		if (stat(spath, &st) < 0)
			fatal("can't get file status of %s", spath);
		if ((st.st_mode & S_IFMT) != S_IFDIR && st.st_nlink > 1)
			if (cplink(st, spath, dpath) == 1) continue;
		switch (st.st_mode & S_IFMT) {
		    case S_IFDIR:
			cpdir(&st, spath, dpath);
			break;
		    case S_IFREG:
			cp(&st, spath, dpath);
			break;
		    default:
			cpspec(&st, spath, dpath);
			break;
		}
	}
  }
  close(fd);
  if (n) fatal("error in reading directory %s", spath);
}

make_dir(s)
char *s;
{
  int pid, status;

  if (vflag) printf("mkdir %s\n", s);
  if ((pid = fork()) == 0) {
	execl(MKDIR1, "mkdir", s, (char *) 0);
	execl(MKDIR2, "mkdir", s, (char *) 0);
	fatal("can't execute %s or %s", MKDIR1, MKDIR2);
  }
  if (pid == -1) fatal("can't fork", prog);
  wait(&status);
  if (status) fatal("can't create %s", s);
}

cp(sp, s, d)
struct stat *sp;
char *s, *d;
{
  char buf[BUFSIZE];
  int sfd, dfd, n;

  if (vflag) printf("cp %s %s\n", s, d);
  if ((sfd = open(s, O_RDONLY)) < 0)
	nonfatal("can't read %s", s);
  else {
	if ((dfd = creat(d, sp->st_mode & 0777)) < 0)
		fatal("can't create %s", d);
	while ((n = read(sfd, buf, BUFSIZE)) > 0)
		if (write(dfd, buf, n) != n)
			fatal("error in writing file %s", d);
	close(sfd);
	close(dfd);
	if (n) fatal("error in reading file %s", s);
	if (sflag) similar(sp, d);
  }
}

similar(sp, d)
struct stat *sp;
char *d;
{
  time_t timep[2];

  chmod(d, sp->st_mode);
  chown(d, sp->st_uid, sp->st_gid);
  timep[0] = sp->st_atime;
  timep[1] = sp->st_mtime;
  utime(d, timep);
}

nonfatal(s, a)
char *s, *a;
{
  fprintf(stderr, "%s: ", prog);
  fprintf(stderr, s, a);
  fprintf(stderr, "\n");
}

fatal(s, a)
char *s, *a;
{
  nonfatal(s, a);
  exit(1);
}

cpspec(sp, s, d)
struct stat *sp;
char *s, *d;
{
  if (vflag) {
	printf("copy special file %s to %s.", s, d);
	printf(" Major/minor = %d/%d.",
	       sp->st_rdev >> 8, sp->st_rdev & 0177);
	printf(" Mode = %o.\n", sp->st_mode);
  }
  if (mknod(d, sp->st_mode, sp->st_rdev, 0) < 0) {
	perror("mknod");
	nonfatal("Cannot create special file %s.\n", d);
  }
  if (sflag) similar(sp, d);
}

cplink(st, spath, dpath)
struct stat st;
char *spath, *dpath;
{
  /* Handle files that are links. Returns 0 if file must be copied.
   * Returns 1 if file has been successfully linked. */
  int i;
  int linkent;

  linkent = -1;
  for (i = 0; i < nlinks; i++) {
	if (links[i].dev == st.st_dev
	    && links[i].ino == st.st_ino)
		linkent = i;
  }
  if (linkent >= 0) {		/* It's already in the link table *//* we
			 * must have copied it earlier. So just link
			 * to the saved dest path. 		Don't copy it
			 * twice. */
	if (vflag) printf("ln %s %s\n", links[linkent].path, dpath);
	if (link(links[linkent].path, dpath) < 0)
		fatal("Could not link to %s\n", dpath);
	return(1);		/* Don't try to copy it */
  } else {			/* Make an entry in the link table */
	if (nlinks >= MAXLINKS) fatal("Too many links at %s\n", dpath);
	links[nlinks].dev = st.st_dev;
	links[nlinks].ino = st.st_ino;
	links[nlinks].path = malloc(strlen(dpath) + 1);
	if (links[nlinks].path == NULL)
		fatal("No more memory at %s\n", dpath);
	strcpy(links[nlinks].path, dpath);
	nlinks++;
	/* Go ahead and copy it the first time */
	return(0);
  }
}