// clone.cpp,v 4.6 2001/08/18 12:43:31 schmidt Exp

#include "ace/OS.h"

ACE_RCSID(bin, clone, "clone.cpp,v 4.6 2001/08/18 12:43:31 schmidt Exp")

#if 0
#if defined (USG)
#define lstat stat
#else
extern "C" char *getwd (char *);
#define getcwd(str,len) (getwd(str))
#endif
#endif

#ifndef MAXPATHLEN
#define MAXPATHLEN	1024
#endif

#ifndef BLKDEV_IOSIZE
#define BLKDEV_IOSIZE	1024
#endif

#ifndef linux
extern char *sys_errlist[];
#endif
static void clone (char* s_path, char* d_path, int sroot_flag, int level);

static char *pname;
static int errors = 0;

static char* src_path = 0;
static char* dst_path = 0;

static int quiet_flag = 0;
static int verbose_flag = 0;
static int force_flag = 0;
#ifndef USG
static int symlink_flag = 0;
#endif
static int copy_flag = 0;
static int sccs_flag = 0;

static void
usage (void)
{
#ifdef USG
  fprintf (stderr, "%s: usage: '%s [-q][-v][-f][-c] pathname1 pathname2'\n", pname, pname);
#else
  fprintf (stderr, "%s: usage: '%s [-q][-v][-f][-S][-c | -s | -S] pathname1 pathname2'\n", pname, pname);
#endif
  exit (1);
}

/* abspath(): return the absolutized pathname for the given relative
   pathname.  Note that if that pathname is already absolute, it may
   still be returned in a modified form because this routine also
   eliminates redundant slashes and single dots and eliminates double
   dots to get a shortest possible pathname from the given input
   pathname.  The absolutization of relative pathnames is made by
   assuming that the given pathname is to be taken as relative to the
   first argument (cwd) or to the current directory if cwd is null.  */

static char *
abspath (char *cwd, char *rel_pathname)
{
  static char cwd_buffer[MAXPATHLEN + 1];
  char abs_buffer[MAXPATHLEN + 1];
  register char *endp;
  register char *p;
  register char *inp = abs_buffer;
  register char *outp = abs_buffer;

  /* Setup the current working directory as needed.  */

  if (!cwd)
    {
      if (!cwd_buffer[0])
        getcwd (cwd_buffer, MAXPATHLEN);
      cwd = cwd_buffer;
    }
  else if (*cwd != '/')
    abort ();			/* base path must be absolute */

  /* Copy the  pathname (possibly preceeded by the current working
     directory name) into the absolutization buffer.  */

  endp = abs_buffer;
  if (rel_pathname[0] != '/')
    {
      p = cwd;
      while (*endp++ = *p++)
        continue;
      *(endp-1) = '/';			/* overwrite null */
    }
  p = rel_pathname;
  while (*endp++ = *p++)
    continue;
  if (endp[-1] == '/')
    *endp = (char) 0;

  /* Now make a copy of abs_buffer into abs_buffer, shortening the
     pathname (by taking out slashes and dots) as we go.  */

  *outp++ = *inp++;		/* copy first slash */
  for (;;)
    {
      if (!inp[0])
        break;
      else if (inp[0] == '/' && outp[-1] == '/')
	{
          inp++;
          continue;
	}
      else if (inp[0] == '.' && outp[-1] == '/')
	{
          if (!inp[1])
      	    break;
          else if (inp[1] == '/')
	    {
      	      inp += 2;
      	      continue;
	    }
          else if ((inp[1] == '.') && (inp[2] == 0 || inp[2] == '/'))
	    {
      	      inp += (inp[2] == '/') ? 3 : 2;
      	      outp -= 2;
      	      while (outp >= abs_buffer && *outp != '/')
      		outp--;
      	      if (outp < abs_buffer)
		{
		  /* Catch cases like /.. where we try to backup to a
		     point above the absolute root of the logical file
		     system.  */

      		  fprintf (stderr, "%s: fatal: invalid pathname: %s\n",
		    pname, rel_pathname);
      		  exit (1);
      		}
      	      *++outp = (char) 0;
      	      continue;
            }
        }
      *outp++ = *inp++;
    }

  /* On exit, make sure that there is a trailing null, and make sure that
     the last character of the returned string is *not* a slash.  */

  *outp = (char) 0;
  if (outp[-1] == '/')
    *--outp  = (char) 0;

  /* Make a copy (in the heap) of the stuff left in the absolutization
     buffer and return a pointer to the copy.  */

  return strcpy ((char *) malloc (outp - abs_buffer + 1), abs_buffer);
}

static char*
path_concat (const char* s1, const char* s2)
{
  int s1_len;
  char* ret_val = (char *) malloc ((s1_len = strlen (s1)) + strlen (s2) + 2);

  strcpy (ret_val, s1);
  ret_val[s1_len] = '/';
  strcpy (&ret_val[s1_len+1], s2);
  return ret_val;
}

/* Decide if the given path (which may be relative to . or absolute) designa
tes
   a point within the original "src_path" directory, and return non-zero if 
it
   does, or zero otherwise.  */

static int
in_original_tree (char* other_path)
{
  char* abs_src_path = abspath (NULL, src_path);
  char* abs_src_path_slash = path_concat (abs_src_path, "");
  char* abs_other_path = abspath (NULL, other_path);
  int ret_val =  !strncmp (abs_src_path_slash, abs_other_path, strlen (abs_src_path_slash));

  free (abs_src_path);
  free (abs_src_path_slash);
  free (abs_other_path);
  return ret_val;
}

static void
fix_mode (int new_mode, char* d_path)
{
  if (chmod (d_path, new_mode))
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: warning: can't chmod on output entity %s: %s\n",
	  pname, d_path, sys_errlist[errno]);
    }
}

static int
remove_item (char* s_path, char* d_path)
{
  struct stat dst_stat_buf;
  DIR* dirp;
  char containing_dir[MAXPATHLEN + 1];

  if (lstat (d_path, &dst_stat_buf) == -1)
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: error: cannot get status of %s: %s\n",
	  pname, d_path, sys_errlist[errno]);
      return -1;
    }

  /* Before wasting a lot of time sniffing at the thing we are trying to
     delete, first make sure that we have write permission into the
     directory that contains this thing.  Otherwise, it is all a waste
     of time.  */

  if (*d_path == '/')
    strcpy(containing_dir, d_path);
  else
    {
      containing_dir[0] = '.';
      containing_dir[1] = '/';
      strcpy(containing_dir+2, d_path);
    }
  *(strrchr (containing_dir, '/')) = '\0';
  if (containing_dir[0] == '\0')
    {
      containing_dir[0] = '/';
      containing_dir[1] = '\0';
    }
  if (access (containing_dir, W_OK))
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: error: don't have write access to %s: %s\n",
	  pname, containing_dir, sys_errlist[errno]);
      return -1;
    }
  
  switch (dst_stat_buf.st_mode & S_IFMT)
    {
      case  S_IFDIR:
	if (access (d_path, R_OK) != 0)
	  {
	    if (!quiet_flag)
              fprintf (stderr, "%s: error: don't have read permission for directory %s\n",
	        pname, d_path);
            return -1;
	  }
	if (access (d_path, X_OK) != 0)
	  {
	    if (!quiet_flag)
              fprintf (stderr, 
                "%s: error: don't have search permission for directory %s\n",
	        pname, d_path);
            return -1;
	  }
	if (access (d_path, W_OK) != 0)
	  {
	    if (!quiet_flag)
              fprintf (stderr, 
                "%s: error: don't have write permission for directory %s\n",
	        pname, d_path);
            return -1;
	  }
	if ((dirp = opendir (d_path)) == NULL)
	  {
	    if (!quiet_flag)
              fprintf (stderr, 
                "%s: error: can't open directory %s for reading: %s\n",
	        pname, d_path, sys_errlist[errno]);
            return -1;
	  }
	for (;;)
	  {
	    struct dirent* dir_entry_p;
	    char* new_s_path;
	    char* new_d_path;

	    if ((dir_entry_p = readdir (dirp)) == NULL)
	      break;
	    if (!strcmp (dir_entry_p->d_name, "."))
	      continue;
	    if (!strcmp (dir_entry_p->d_name, ".."))
	      continue;
	    new_s_path = path_concat (s_path, dir_entry_p->d_name);
	    new_d_path = path_concat (d_path, dir_entry_p->d_name);
	    if (remove_item (new_s_path, new_d_path))
	      {
		closedir (dirp);
		return -1;
	      }
	    free (new_s_path);
	    free (new_d_path);
	  }
	closedir (dirp);
	if (rmdir (d_path))
	  {
	    if (!quiet_flag)
	      fprintf (stderr, "%s: error: can't delete existing directory %s: %s\n",
	        pname, d_path, sys_errlist[errno]);
	    return -1;
	  }
	if (!quiet_flag)
	  fprintf (stderr, "%s: removed directory %s\n",
	    pname, d_path);
	break;

      /* Note that symbolic links can be treated just like normal files
	 when the time comes for deleting them.  Unlinking a symbolic link
	 just deletes the link and *not* the thing it points to.  */

      default:
	if (unlink (d_path))
	  {
	    if (!quiet_flag)
              fprintf (stderr, "%s: error: can't delete existing file %s: %s\n",
	        pname, d_path, sys_errlist[errno]);
            return -1;
	  }
	if (!quiet_flag)
	  fprintf (stderr, "%s: removed file %s\n",
	    pname, d_path);
	break;
    }
  return 0;
}

#ifndef USG
static void
mk_symbolic_link (const char *s_path,
                  const char *d_path,
                  int level)
{
  int result = 0;
 
  if (s_path[0] == '/' || level < 2)
    result = symlink (s_path, d_path);
  else
    {
      int len;
      char *new_s_path = (char *) malloc (len = strlen(s_path) + 3 * level);
      int i;
      char *cp = new_s_path;
       
      for (i = 0; i < level-1; i++)
        {
 	  strcpy (cp, "../");
 	  cp += 3;
 	}
      strcpy (cp, s_path);
      result = symlink (new_s_path, d_path);
    }
  if (result)
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: error: can't symlink %s to %s: %s\n",
                 pname, s_path, d_path, sys_errlist[errno]);
    }
  else
    {
      if (verbose_flag)
        fprintf (stderr, "%s: created symlink %s -> %s\n",
                 pname, d_path, s_path);
    }
}
#endif

static void
mk_hard_link (char *s_path, char *d_path)
{
  if (link (s_path, d_path))
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: error: can't link %s to %s: %s\n",
          pname, s_path, d_path, sys_errlist[errno]);
    }
  else
    {
      if (verbose_flag)
        fprintf (stderr, "%s: created hard link %s = %s\n",
          pname, d_path, s_path);
    }
}

static void
copy_file (char *s_path, char *d_path)
{
  int input, output;
  struct stat src_stat_buf;

  if (lstat (s_path, &src_stat_buf) == -1)
    {
      if (!quiet_flag)
	{
          fprintf (stderr, "%s: error: can't get status of %s: %s\n",
	    pname, s_path, sys_errlist[errno]);
          fprintf (stderr, "%s: input entity %s will be ignored\n",
	    pname, s_path);
	}
      return;
    }

  if ((input = open (s_path, O_RDONLY, 0)) == -1)
    {
      if (!quiet_flag)
        {
          fprintf (stderr, "%s: error: can't open input file %s: %s\n",
	    pname, d_path, sys_errlist[errno]);
	  fprintf (stderr, "%s: input file %s will be ignored\n",
	    pname, s_path);
	}
	return;
    }

  if ((output = open (d_path, O_CREAT | O_WRONLY, src_stat_buf.st_mode & 07777)) == -1)
    {
      if (!quiet_flag)
        {
          fprintf (stderr, "%s: error: can't create output file %s: %s\n",
	    pname, d_path, sys_errlist[errno]);
	  fprintf (stderr, "%s: input file %s will be ignored\n",
	    pname, s_path);
	}
	return;
    }

  for (;;)
    {
      int rlen, wlen;
      char block_buf[BLKDEV_IOSIZE];

      if ((rlen = read (input, block_buf, BLKDEV_IOSIZE)) == -1)
        {
          if (!quiet_flag)
	    {
	      fprintf (stderr, "%s: error: bad read from input file %s: %s\n",
	        pname, s_path, sys_errlist[errno]);
	      fprintf (stderr, "%s: input file %s was not fully copied\n",
	        pname, s_path);
	    }
	  break;
	}

    if (rlen == 0)
      break;

    if ((wlen = write (output, block_buf, rlen)) == -1 || wlen != rlen)
      {
        if (!quiet_flag)
          {
	    fprintf (stderr, "%s: error: bad write to output file %s: %s\n",
	      pname, s_path, sys_errlist[errno]);
	    fprintf (stderr, "%s: input file %s not fully copied\n",
	      pname, s_path);
	  }
	break;
      }
    }

  close (output);
  close (input);

  fix_mode (src_stat_buf.st_mode & 07777, d_path);

  if (verbose_flag)
    fprintf (stderr, "%s: created file copy %s = %s\n",
      pname, d_path, s_path);
}

static void
symlink_SCCS (char* s_path, char* d_path)
{
  struct stat dst_stat_buf;
  char symlink_buf[MAXPATHLEN + 1];
  int count;

  if (access (d_path, F_OK))			/* Does d_path exit? */
    {
      if (errno != ENOENT)
	{
	  if (!quiet_flag)
	    {
	      fprintf (stderr, "%s: error: can't check accessability of %s: %s\n",
	        pname, d_path, sys_errlist[errno]);
	      fprintf (stderr, "%s: input %s will be ignored\n",
	        pname, s_path);
	    }
	  return;
	}
    }
  else						/* d_path exists.  What is it? */
    {
      if (lstat (d_path, &dst_stat_buf) == -1)
	{
	  if (!quiet_flag)
	    {
	      fprintf (stderr, "%s: error: unable to get status of %s: %s\n",
	        pname, d_path, sys_errlist[errno]);
              fprintf (stderr, "%s: input %s will be ignored\n",
	        pname, s_path);
	    }
	  return;
	}

      if (S_ISLNK(dst_stat_buf.st_mode))	/* d_path is a symbolic link */
	{
          if ((count = readlink (d_path, symlink_buf, MAXPATHLEN)) == -1)
            {
              fprintf (stderr, "%s: error: can't read symlink %s: %s\n",
                pname, d_path, sys_errlist[errno]);
              fprintf (stderr, "%s: input file %s will be ignored\n",
                pname, s_path);
              return;
            }
          symlink_buf[count] = '\0';

          if (!strcmp(s_path, symlink_buf))	/* symlink = s_path. Done */
            {
              return;
            }
          else					/* symlink != s_path */
            {
	      if (force_flag)
                {
                  if (remove_item (s_path, d_path) != 0)
	            return;
                }
              else
	        {
	          if (!quiet_flag)
		    {
	              fprintf (stderr, "%s: error: Symbolic link %s already exists \
			but does not point to %s\n",
	                pname, d_path, s_path);
	              fprintf (stderr, "%s: input s %s will be ignored\n",
	                pname, s_path);
		    }
                  return;
                }
	    }
        }
      else					/* d_path is NOT a symbolic link */
	{
          if (force_flag)
	    {
              if (remove_item (s_path, d_path))
	        return;
	    }
          else
	    {
	      if (!quiet_flag)
		{
	          fprintf (stderr, "%s: error: output already exists: %s\n",
	            pname, d_path);
	          fprintf (stderr, "%s: input %s will be ignored\n",
	            pname, s_path);
		}
              return;
	    }
	}
    }
   
  if (symlink (s_path, d_path))
    {
      if (!quiet_flag)
        fprintf (stderr, "%s: error: can't symlink %s to %s: %s\n",
          pname, s_path, d_path, sys_errlist[errno]);
    }
  else
    {
      if (verbose_flag)
        fprintf (stderr, "%s: created symlink %s -> %s\n",
        pname, d_path, s_path);
    }
}

static void
clone_dir (char* s_path, char* d_path, int level)
{
  DIR* dirp;

  if (access (s_path, R_OK) != 0)
    {
      if (!quiet_flag)
        {
          fprintf (stderr, 
            "%s: error: don't have read permission for input directory %s\n"
,
	    pname, s_path);
          fprintf (stderr, "%s: input directory %s will be ignored\n",
	    pname, s_path);
	 }
        return;
    }

  if (access (s_path, X_OK) != 0)
    {
      if (!quiet_flag)
        {
           fprintf (stderr, 
             "%s: error: don't have search permission for input directory %s\n",
	     pname, s_path);
           fprintf (stderr, "%s: input directory %s will be ignored\n",
	     pname, s_path);
	 }
       return;
    }

  if ((dirp = opendir (s_path)) == NULL)
    {
      if (!quiet_flag)
        {
          fprintf (stderr, "%s: error: can't open directory %s for reading: %s\n",
            pname, s_path, sys_errlist[errno]);
          fprintf (stderr, "%s: input directory %s will be ignored\n",
            pname, s_path);
        }
      return;
    }

  for (;;)
    {
      struct dirent* dir_entry_p;
      char* new_s_path;
      char* new_d_path;
      char  symlink_buf[MAXPATHLEN + 1];
      int   len;

      if ((dir_entry_p = readdir (dirp)) == NULL)
        break;
      if (!strcmp (dir_entry_p->d_name, "."))
        continue;
      if (!strcmp (dir_entry_p->d_name, ".."))
        continue;

      new_s_path = path_concat (s_path, dir_entry_p->d_name);
      new_d_path = path_concat (d_path, dir_entry_p->d_name);

      if (sccs_flag && !strcmp (dir_entry_p->d_name, "SCCS"))
        symlink_SCCS(new_s_path, new_d_path);
      else
        clone (new_s_path, new_d_path, 0, level+1);

      free (new_s_path);
      free (new_d_path);
    }

  closedir (dirp);
}

static void
clone_symbolic_link (char* s_path,char* d_path)
{
  char symlink_buf[MAXPATHLEN + 1];
  int count;

  if ((count = readlink (s_path, symlink_buf, MAXPATHLEN)) == -1)
    {
      fprintf (stderr, "%s: error: can't read symlink %s: %s\n",
        pname, s_path, sys_errlist[errno]);
      fprintf (stderr, "%s: input file %s will be ignored\n",
        pname, s_path);
      return;
    }
  symlink_buf[count] = '\0';

  if (symlink_buf[0] == '/')		/* symlink is absolute */
    {
      if (in_original_tree (symlink_buf))
        {
           if (!quiet_flag)
	     fprintf (stderr, 
	       "%s: warning: absolute symlink points into source tree %s -> %s\n",
	       pname, s_path, symlink_buf);
	}
    }
  else					/* symlink is relative */
    {
      char* src_root_relative = path_concat (s_path, symlink_buf);
      int in_orig = in_original_tree (src_root_relative);

      free (src_root_relative);
      if (!in_orig)
        {
          if (!quiet_flag)
	    fprintf (stderr, 
              "%s: warning: relative symlink points out of source tree %s -> %s\n",
	      pname, s_path, symlink_buf);
	}
    }

  mk_symbolic_link(symlink_buf, d_path, 0); /* Make an identical symlink.  */
}


/* clone: clone the item designated by s_path as the new item d_path.  */

#define IS_DIR(STAT_BUF) (((STAT_BUF).st_mode & S_IFMT) == S_IFDIR)

static void
clone (char* s_path, char* d_path, int sroot_flag, int level)
{
  struct stat src_stat_buf;
  struct stat dst_stat_buf;
  int dir_already_exists = 0;
  const char* intype = "file";

  if (lstat (s_path, &src_stat_buf) == -1)
    {
      if (!quiet_flag)
	{
          fprintf (stderr, "%s: error: can't get status of %s: %s\n",
	    pname, s_path, sys_errlist[errno]);
          fprintf (stderr, "%s: input entity %s will be ignored\n",
	    pname, s_path);
	}
      return;
    }
  if (sccs_flag && sroot_flag && S_ISLNK (src_stat_buf.st_mode))
    {

      /* If root of the source path is a symbolic link and
	 SCCS cloning is enabled, clone the target of the link */

      if (stat(s_path, &src_stat_buf) == -1)
	{
          if (!quiet_flag)
	    {
              fprintf (stderr, "%s: error: can't get status of %s: %s\n",
	        pname, s_path, sys_errlist[errno]);
              fprintf (stderr, "%s: input entity %s will be ignored\n",
	        pname, s_path);
            }	
          return;
	}
    }
  if (IS_DIR (src_stat_buf))
    intype = "directory";
  if (access (d_path, 0))
    {
      if (errno != ENOENT)
	{
	  if (!quiet_flag)
	    {
	      fprintf (stderr, "%s: error: can't check accessability of %s: %s\n",
	        pname, d_path, sys_errlist[errno]);
	      fprintf (stderr, "%s: input %s %s will be ignored\n",
	        pname, intype, s_path);
	    }
	  return;
	}
    }
  else
    {
      const char* outtype = "file";

      if (lstat (d_path, &dst_stat_buf) == -1)
	{
	  if (!quiet_flag)
	    {
	      fprintf (stderr, "%s: error: unable to get status of %s: %s\n"
,
	        pname, d_path, sys_errlist[errno]);
              fprintf (stderr, "%s: input %s %s will be ignored\n",
	        pname, intype, s_path);
	    }
	  return;
	}
      if (IS_DIR (dst_stat_buf))
	outtype = "directory";
      if (IS_DIR (src_stat_buf) && IS_DIR (dst_stat_buf))
	{
	  dir_already_exists = -1;

	  /* Have to make sure that we have full access to the output
	     directory (at least temporarily).  */

	  chmod (d_path, (dst_stat_buf.st_mode & 07777) | 0700);
	  if (access (d_path, R_OK | W_OK | X_OK) != 0)
	    {
	      if (!quiet_flag)
		{
	          fprintf (stderr, 
                    "%s: error: too few permissions for existing directory %s\n",
	            pname, d_path);
		  fprintf (stderr, "%s: input directory %s will be ignored\n",
	            pname, s_path);
		}
              return;
	    }
	}
      else
	{
          if (force_flag)
	    {
              if (remove_item (s_path, d_path))
	        return;
	    }
          else
	    {
	      if (!quiet_flag)
		{
	          fprintf (stderr, "%s: error: output %s already exists: %s\n",
	            pname, outtype, d_path);
	          fprintf (stderr, "%s: input %s %s will be ignored\n",
	            pname, intype, s_path);
		}
              return;
	    }
	}
    }

  switch (src_stat_buf.st_mode & S_IFMT)
    {
      case  S_IFDIR:				/* Clone a directory */

        if (!dir_already_exists)
          {
            /* Don't let others sneak in. 
               Only we can write the new directory (for now).  */
      
            if (mkdir (d_path, 0700))
              {
                if (!quiet_flag)
      	          {
                    fprintf (stderr, "%s: error: can't create output directory %s: %s\n",
	              pname, d_path, sys_errlist[errno]);
                    fprintf (stderr, "%s: input directory %s will be ignored\n",
	              pname, s_path);
	          }
                return;
	      }
            if (verbose_flag)
              fprintf (stderr, "%s: created new output directory: %s\n",
                pname, d_path);
          }

	clone_dir(s_path, d_path, level);

	/* By default, output directories which existed before this
	   program was executed are reset back to their original
	   permissions (when we are done adding things to them).  For
	   output directories which are actually created by this program
	   however, these have their permissions set so that they are
	   essentially the same as the permissions for their corresponding
	   input directories, except that the owner is given full
	   permissions.  */

	if (dir_already_exists)
	  fix_mode (dst_stat_buf.st_mode & 07777, d_path);
	else
	  fix_mode ((src_stat_buf.st_mode & 07777) | 0700, d_path);
	break;

#ifndef USG
      case S_IFLNK:				/* Clone a symbolic link */

	if (!sccs_flag)
          clone_symbolic_link (s_path, d_path);
	break;
#endif

      default:					/* Clone a normal file */

	if (sccs_flag)
	  break;

#ifndef USG
	if (symlink_flag)
	  mk_symbolic_link(s_path, d_path, level);
	else
#endif
	if (copy_flag)
	  copy_file(s_path, d_path);
	else
	  mk_hard_link(s_path, d_path);

	break;
    } /* switch */
}

int
main (int argc, char *argv[])
{
  char **argn;

  pname = (pname = strrchr (argv[0], '/')) ? pname+1 : argv[0];
  for (argn = argv+1; *argn; argn++)
    {
      if (**argn != '-')
	{
	  if (!src_path)
	    src_path = *argn;
	  else if (!dst_path)
	    dst_path = *argn;
	  else
	    usage ();
	}
      else
	{
          switch (* ((*argn)+1))
	    {
              case 0:
          	fprintf (stderr, "%s: invalid option: -\n", pname);
          	errors = -1;
          	break;

	      case 'q':
		quiet_flag = -1;
		break;

	      case 'v':
		verbose_flag = -1;
		break;

	      case 'f':
		force_flag = -1;
		break;

#ifndef USG
	      case 'S':
		sccs_flag = -1;

		if (copy_flag)
		  errors++;
		break;
#endif

#ifndef USG
	      case 's':
		symlink_flag = -1;
		if (copy_flag)
		  errors++;
		break;
#endif

	      case 'c':
		copy_flag = -1;
#ifndef USG
		if (symlink_flag)
		  errors++;

		if (sccs_flag) 
		  errors++;
#endif
		break;

              default:
          	fprintf (stderr, "%s: invalid option: -%c\n",
          		pname, *((*argn)+1));
          	errors = -1;
            }
        }
    }
  if (errors || src_path == 0 || dst_path == 0)
    usage ();
#if 0 // ndef USG
  if (symlink_flag && *src_path != '/')
    {
      fprintf (stderr, "%s: error: source root pathname must be absolute when using -s\n",
	pname);
      exit (1);
    }
#endif
  if (access (src_path, 0) == -1)
    {
      fprintf (stderr, "%s: error: accessing source root entity %s: %s\n",
	pname, src_path, sys_errlist[errno]);
      exit (1);
    }
  umask (0);			/* disable all masking */
  clone (src_path, dst_path, 1, 0);
  return 0;
}
