Exemple #1
0
static long
count_entry (char *ent, int top, dev_t last_dev)
{
  long size;

  if (((top && opt_dereference_arguments)
       ? stat (ent, &stat_buf)
       : (*xstat) (ent, &stat_buf)) < 0)
    {
      error (0, errno, "%s", path->text);
      exit_status = 1;
      return 0;
    }

  if (!opt_count_all
      && stat_buf.st_nlink > 1
      && hash_insert (stat_buf.st_ino, stat_buf.st_dev))
    return 0;			/* Have counted this already.  */

  if (output_size == size_bytes)
    size = stat_buf.st_size;
  else
    size = ST_NBLOCKS (stat_buf);

  tot_size += size;

  if (S_ISDIR (stat_buf.st_mode))
    {
      unsigned pathlen;
      dev_t dir_dev;
      char *name_space;
      char *namep;
      struct saved_cwd cwd;
      int through_symlink;
      struct stat e_buf;

      dir_dev = stat_buf.st_dev;

      if (opt_one_file_system && !top && last_dev != dir_dev)
	return 0;		/* Don't enter a new file system.  */

#ifndef S_ISDIR
# define S_ISDIR(s) 0
#endif
      /* If we're dereferencing symlinks and we're about to chdir through
	 a symlink, remember the current directory so we can return to it
	 later.  In other cases, chdir ("..") works fine.  */
      through_symlink = (xstat == stat
			 && lstat (ent, &e_buf) == 0
			 && S_ISLNK (e_buf.st_mode));
      if (through_symlink)
	if (save_cwd (&cwd))
	  exit (1);

      if (chdir (ent) < 0)
	{
	  error (0, errno, _("cannot change to directory %s"), path->text);
	  exit_status = 1;
	  return 0;
	}

      errno = 0;
      name_space = savedir (".", stat_buf.st_size);
      if (name_space == NULL)
	{
	  if (errno)
	    {
	      error (0, errno, "%s", path->text);
	      if (through_symlink)
		{
		  if (restore_cwd (&cwd, "..", path->text))
		    exit (1);
		  free_cwd (&cwd);
		}
	      else if (chdir ("..") < 0)
		  error (1, errno, _("cannot change to `..' from directory %s"),
			 path->text);
	      exit_status = 1;
	      return 0;
	    }
	  else
	    error (1, 0, _("virtual memory exhausted"));
	}

      /* Remember the current path.  */

      str_concatc (path, "/");
      pathlen = path->length;

      namep = name_space;
      while (*namep != 0)
	{
	  str_concatc (path, namep);

	  size += count_entry (namep, 0, dir_dev);

	  str_trunc (path, pathlen);
	  namep += strlen (namep) + 1;
	}
      free (name_space);
      if (through_symlink)
	{
	  restore_cwd (&cwd, "..", path->text);
	  free_cwd (&cwd);
	}
      else if (chdir ("..") < 0)
        error (1, errno,
	       _("cannot change to `..' from directory %s"), path->text);

      str_trunc (path, pathlen - 1); /* Remove the "/" we added.  */
      if (!opt_summarize_only || top)
	{
	  if (opt_human_readable)
	    {
	      char buf[LONGEST_HUMAN_READABLE + 1];
	      printf("%s\t%s\n",
		     human_readable (size, buf, LONGEST_HUMAN_READABLE + 1),
		     path->length > 0 ? path->text : "/");
	    }
	  else
	    {
	      printf ("%ld\t%s\n", (output_size == size_bytes
				    ? size
				    : convert_blocks (size, output_size)),
		      path->length > 0 ? path->text : "/");
	    }
	  fflush (stdout);
	}
      return opt_separate_dirs ? 0 : size;
    }
  else if (opt_all || top)
    {
      /* FIXME: make this an option.  */
      int print_only_dir_size = 0;
      if (!print_only_dir_size)
	{
	  if (opt_human_readable)
	    {
	      char buf[LONGEST_HUMAN_READABLE + 1];
	      printf("%s\t%s\n",
		     human_readable (size, buf, LONGEST_HUMAN_READABLE + 1),
		     path->length > 0 ? path->text : "/");
	    }
	  else
	    {
	      printf ("%ld\t%s\n", output_size == size_bytes ? size
		      : convert_blocks (size, output_size),
		      path->text);
	    }
	  fflush (stdout);
	}
    }

  return size;
}
Exemple #2
0
int
openat_permissive (int fd, char const *file, int flags, mode_t mode,
                   int *cwd_errno)
{
    struct saved_cwd saved_cwd;
    int saved_errno;
    int err;
    bool save_ok;

    if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
        return open (file, flags, mode);

    {
        char buf[OPENAT_BUFFER_SIZE];
        char *proc_file = openat_proc_name (buf, fd, file);
        if (proc_file)
        {
            int open_result = open (proc_file, flags, mode);
            int open_errno = errno;
            if (proc_file != buf)
                free (proc_file);
            /* If the syscall succeeds, or if it fails with an unexpected
               errno value, then return right away.  Otherwise, fall through
               and resort to using save_cwd/restore_cwd.  */
            if (0 <= open_result || ! EXPECTED_ERRNO (open_errno))
            {
                errno = open_errno;
                return open_result;
            }
        }
    }

    save_ok = (save_cwd (&saved_cwd) == 0);
    if (! save_ok)
    {
        if (! cwd_errno)
            openat_save_fail (errno);
        *cwd_errno = errno;
    }
    if (0 <= fd && fd == saved_cwd.desc)
    {
        /* If saving the working directory collides with the user's
           requested fd, then the user's fd must have been closed to
           begin with.  */
        free_cwd (&saved_cwd);
        errno = EBADF;
        return -1;
    }

    err = fchdir (fd);
    saved_errno = errno;

    if (! err)
    {
        err = open (file, flags, mode);
        saved_errno = errno;
        if (save_ok && restore_cwd (&saved_cwd) != 0)
        {
            if (! cwd_errno)
            {
                /* Don't write a message to just-created fd 2.  */
                saved_errno = errno;
                if (err == STDERR_FILENO)
                    close (err);
                openat_restore_fail (saved_errno);
            }
            *cwd_errno = errno;
        }
    }

    free_cwd (&saved_cwd);
    errno = saved_errno;
    return err;
}
Exemple #3
0
TEARDOWN()
{
	view_teardown(&lwin);

	restore_cwd(saved_cwd);
}
Exemple #4
0
static void
du_files (char **files)
{
  struct saved_cwd cwd;
  ino_t initial_ino;		/* Initial directory's inode. */
  dev_t initial_dev;		/* Initial directory's device. */
  int i;			/* Index in FILES. */

  if (save_cwd (&cwd))
    exit (1);

  /* Remember the inode and device number of the current directory.  */
  if (stat (".", &stat_buf))
    error (1, errno, _("current directory"));
  initial_ino = stat_buf.st_ino;
  initial_dev = stat_buf.st_dev;

  for (i = 0; files[i]; i++)
    {
      char *arg;
      int s;

      arg = files[i];

      /* Delete final slash in the argument, unless the slash is alone.  */
      s = strlen (arg) - 1;
      if (s != 0)
	{
	  if (arg[s] == '\\')
	    arg[s] = 0;

	  str_copyc (path, arg);
	}
      else if (arg[0] == '\\')
	str_trunc (path, 0);	/* Null path for root directory.  */
      else
	str_copyc (path, arg);

      if (!opt_combined_arguments)
	hash_reset ();

      count_entry (arg, 1, 0);

      /* chdir if `count_entry' has changed the working directory.  */
      if (stat (".", &stat_buf))
	error (1, errno, ".");
      if (stat_buf.st_ino != initial_ino || stat_buf.st_dev != initial_dev)
	{
	  if (restore_cwd (&cwd, _("starting directory"), NULL))
	    exit (1);
	}
    }

  if (opt_combined_arguments)
    {
      if (opt_human_readable)
	{
	  char buf[LONGEST_HUMAN_READABLE + 1];
	  printf("%s\ttotal\n", human_readable (tot_size, buf,
						LONGEST_HUMAN_READABLE + 1));
	}
      else
	{
	  printf (_("%ld\ttotal\n"), output_size == size_bytes ? tot_size
		  : convert_blocks (tot_size, output_size == size_kilobytes));
	}
      fflush (stdout);
    }

  free_cwd (&cwd);
}
Exemple #5
0
/* An empty LogHistory string in CVSROOT/config will turn logging off.
 */
void
history_write (int type, const char *update_dir, const char *revs,
               const char *name, const char *repository)
{
    const char *fname;
    char *workdir;
    char *username = getcaller ();
    int fd;
    char *line;
    char *slash = "", *cp;
    const char *cp2, *repos;
    int i;
    static char *tilde = "";
    static char *PrCurDir = NULL;
    time_t now;

    if (logoff)			/* History is turned off by noexec or
				 * readonlyfs.
				 */
	return;
    if (!strchr (config->logHistory, type))	
	return;

    if (nolock)
	goto out;

    repos = Short_Repository (repository);

    if (!PrCurDir)
    {
	char *pwdir;

	pwdir = get_homedir ();
	PrCurDir = CurDir;
	if (pwdir != NULL)
	{
	    /* Assumes neither CurDir nor pwdir ends in '/' */
	    i = strlen (pwdir);
	    if (!strncmp (CurDir, pwdir, i))
	    {
		PrCurDir += i;		/* Point to '/' separator */
		tilde = "~";
	    }
	    else
	    {
		/* Try harder to find a "homedir" */
		struct saved_cwd cwd;
		char *homedir;

		if (save_cwd (&cwd))
		    error (1, errno, "Failed to save current directory.");

		if (CVS_CHDIR (pwdir) < 0 || (homedir = xgetcwd ()) == NULL)
		    homedir = pwdir;

		if (restore_cwd (&cwd))
		    error (1, errno,
		           "Failed to restore current directory, `%s'.",
		           cwd.name);
		free_cwd (&cwd);

		i = strlen (homedir);
		if (!strncmp (CurDir, homedir, i))
		{
		    PrCurDir += i;	/* Point to '/' separator */
		    tilde = "~";
		}

		if (homedir != pwdir)
		    free (homedir);
	    }
	}
    }

    if (type == 'T')
    {
	repos = update_dir;
	update_dir = "";
    }
    else if (update_dir && *update_dir)
	slash = "/";
    else
	update_dir = "";

    workdir = Xasprintf ("%s%s%s%s", tilde, PrCurDir, slash, update_dir);

    /*
     * "workdir" is the directory where the file "name" is. ("^~" == $HOME)
     * "repos"	is the Repository, relative to $CVSROOT where the RCS file is.
     *
     * "$workdir/$name" is the working file name.
     * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository.
     *
     * First, note that the history format was intended to save space, not
     * to be human readable.
     *
     * The working file directory ("workdir") and the Repository ("repos")
     * usually end with the same one or more directory elements.  To avoid
     * duplication (and save space), the "workdir" field ends with
     * an integer offset into the "repos" field.  This offset indicates the
     * beginning of the "tail" of "repos", after which all characters are
     * duplicates.
     *
     * In other words, if the "workdir" field has a '*' (a very stupid thing
     * to put in a filename) in it, then every thing following the last '*'
     * is a hex offset into "repos" of the first character from "repos" to
     * append to "workdir" to finish the pathname.
     *
     * It might be easier to look at an example:
     *
     *  M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
     *
     * Indicates that the workdir is really "~/work/cvs/examples", saving
     * 10 characters, where "~/work*d" would save 6 characters and mean that
     * the workdir is really "~/work/examples".  It will mean more on
     * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term
     *
     * "workdir" is always an absolute pathname (~/xxx is an absolute path)
     * "repos" is always a relative pathname.  So we can assume that we will
     * never run into the top of "workdir" -- there will always be a '/' or
     * a '~' at the head of "workdir" that is not matched by anything in
     * "repos".  On the other hand, we *can* run off the top of "repos".
     *
     * Only "compress" if we save characters.
     */

    cp = workdir + strlen (workdir) - 1;
    cp2 = repos + strlen (repos) - 1;
    for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--)
	i++;

    if (i > 2)
    {
	i = strlen (repos) - i;
	(void) sprintf ((cp + 1), "*%x", i);
    }

    if (!revs)
	revs = "";
    now = time (NULL);
    line = Xasprintf ("%c%08lx|%s|%s|%s|%s|%s\n", type, (long) now,
		      username, workdir, repos, revs, name);

    fname = get_history_log_name (now);

    if (!history_lock (current_parsed_root->directory))
	/* history_lock() will already have printed an error on failure.  */
	goto out;

    fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666);
    if (fd < 0)
    {
	if (!really_quiet)
            error (0, errno,
		   "warning: cannot open history file `%s' for write", fname);
        goto out;
    }

    TRACE (TRACE_FUNCTION, "open (`%s', a)", fname);

    /* Lessen some race conditions on non-Posix-compliant hosts.
     *
     * FIXME:  I'm guessing the following was necessary for NFS when multiple
     * simultaneous writes to the same file are possible, since NFS does not
     * natively support append mode and it must be emulated via lseek().  Now
     * that the history file is locked for write, the following lseek() may be
     * unnecessary.
     */
    if (lseek (fd, (off_t) 0, SEEK_END) == -1)
	error (1, errno, "cannot seek to end of history file: %s", fname);

    if (write (fd, line, strlen (line)) < 0)
	error (1, errno, "cannot write to history file: %s", fname);
    free (line);
    if (close (fd) != 0)
	error (1, errno, "cannot close history file: %s", fname);
    free (workdir);
 out:
    clear_history_lock ();
}
Exemple #6
0
/* Call FUNC to operate on a pair of files, where FILE1 is relative to FD1,
   and FILE2 is relative to FD2.  If possible, do it without changing the
   working directory.  Otherwise, resort to using save_cwd/fchdir,
   FUNC, restore_cwd (up to two times).  If either the save_cwd or the
   restore_cwd fails, then give a diagnostic and exit nonzero.  */
int
at_func2 (int fd1, char const *file1,
          int fd2, char const *file2,
          int (*func) (char const *file1, char const *file2))
{
  struct saved_cwd saved_cwd;
  int saved_errno;
  int err;
  char *file1_alt;
  char *file2_alt;
  struct stat st1;
  struct stat st2;

  /* There are 16 possible scenarios, based on whether an fd is
     AT_FDCWD or real, and whether a file is absolute or relative:

         fd1  file1 fd2  file2  action
     0   cwd  abs   cwd  abs    direct call
     1   cwd  abs   cwd  rel    direct call
     2   cwd  abs   fd   abs    direct call
     3   cwd  abs   fd   rel    chdir to fd2
     4   cwd  rel   cwd  abs    direct call
     5   cwd  rel   cwd  rel    direct call
     6   cwd  rel   fd   abs    direct call
     7   cwd  rel   fd   rel    convert file1 to abs, then case 3
     8   fd   abs   cwd  abs    direct call
     9   fd   abs   cwd  rel    direct call
     10  fd   abs   fd   abs    direct call
     11  fd   abs   fd   rel    chdir to fd2
     12  fd   rel   cwd  abs    chdir to fd1
     13  fd   rel   cwd  rel    convert file2 to abs, then case 12
     14  fd   rel   fd   abs    chdir to fd1
     15a fd1  rel   fd1  rel    chdir to fd1
     15b fd1  rel   fd2  rel    chdir to fd1, then case 7

     Try some optimizations to reduce fd to AT_FDCWD, or to at least
     avoid converting an absolute name or doing a double chdir.  */

  if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
      && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2)))
    return func (file1, file2); /* Case 0-2, 4-6, 8-10.  */

  /* If /proc/self/fd works, we don't need any stat or chdir.  */
  {
    char proc_buf1[OPENAT_BUFFER_SIZE];
    char *proc_file1 = ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
                        ? (char *) file1
                        : openat_proc_name (proc_buf1, fd1, file1));
    if (proc_file1)
      {
        char proc_buf2[OPENAT_BUFFER_SIZE];
        char *proc_file2 = ((fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))
                            ? (char *) file2
                            : openat_proc_name (proc_buf2, fd2, file2));
        if (proc_file2)
          {
            int proc_result = func (proc_file1, proc_file2);
            int proc_errno = errno;
            if (proc_file1 != proc_buf1 && proc_file1 != file1)
              free (proc_file1);
            if (proc_file2 != proc_buf2 && proc_file2 != file2)
              free (proc_file2);
            /* If the syscall succeeds, or if it fails with an unexpected
               errno value, then return right away.  Otherwise, fall through
               and resort to using save_cwd/restore_cwd.  */
            if (0 <= proc_result)
              return proc_result;
            if (! EXPECTED_ERRNO (proc_errno))
              {
                errno = proc_errno;
                return proc_result;
              }
          }
        else if (proc_file1 != proc_buf1 && proc_file1 != file1)
          free (proc_file1);
      }
  }

  /* Cases 3, 7, 11-15 remain.  Time to normalize directory fds, if
     possible.  */
  if (IS_ABSOLUTE_FILE_NAME (file1))
    fd1 = AT_FDCWD; /* Case 11 reduced to 3.  */
  else if (IS_ABSOLUTE_FILE_NAME (file2))
    fd2 = AT_FDCWD; /* Case 14 reduced to 12.  */

  /* Cases 3, 7, 12, 13, 15 remain.  */

  if (fd1 == AT_FDCWD) /* Cases 3, 7.  */
    {
      if (stat (".", &st1) == -1 || fstat (fd2, &st2) == -1)
        return -1;
      if (!S_ISDIR (st2.st_mode))
        {
          errno = ENOTDIR;
          return -1;
        }
      if (SAME_INODE (st1, st2)) /* Reduced to cases 1, 5.  */
        return func (file1, file2);
    }
  else if (fd2 == AT_FDCWD) /* Cases 12, 13.  */
    {
      if (stat (".", &st2) == -1 || fstat (fd1, &st1) == -1)
        return -1;
      if (!S_ISDIR (st1.st_mode))
        {
          errno = ENOTDIR;
          return -1;
        }
      if (SAME_INODE (st1, st2)) /* Reduced to cases 4, 5.  */
        return func (file1, file2);
    }
  else if (fd1 != fd2) /* Case 15b.  */
    {
      if (fstat (fd1, &st1) == -1 || fstat (fd2, &st2) == -1)
        return -1;
      if (!S_ISDIR (st1.st_mode) || !S_ISDIR (st2.st_mode))
        {
          errno = ENOTDIR;
          return -1;
        }
      if (SAME_INODE (st1, st2)) /* Reduced to case 15a.  */
        {
          fd2 = fd1;
          if (stat (".", &st1) == 0 && SAME_INODE (st1, st2))
            return func (file1, file2); /* Further reduced to case 5.  */
        }
    }
  else /* Case 15a.  */
    {
      if (fstat (fd1, &st1) == -1)
        return -1;
      if (!S_ISDIR (st1.st_mode))
        {
          errno = ENOTDIR;
          return -1;
        }
      if (stat (".", &st2) == 0 && SAME_INODE (st1, st2))
        return func (file1, file2); /* Reduced to case 5.  */
    }

  /* Cases 3, 7, 12, 13, 15a, 15b remain.  With all reductions in
     place, it is time to start changing directories.  */

  if (save_cwd (&saved_cwd) != 0)
    openat_save_fail (errno);

  if (fd1 != AT_FDCWD && fd2 != AT_FDCWD && fd1 != fd2) /* Case 15b.  */
    {
      if (fchdir (fd1) != 0)
        {
          saved_errno = errno;
          free_cwd (&saved_cwd);
          errno = saved_errno;
          return -1;
        }
      fd1 = AT_FDCWD; /* Reduced to case 7.  */
    }

  /* Cases 3, 7, 12, 13, 15a remain.  Convert one relative name to
     absolute, if necessary.  */

  file1_alt = (char *) file1;
  file2_alt = (char *) file2;

  if (fd1 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file1)) /* Case 7.  */
    {
      /* It would be nicer to use:
         file1_alt = file_name_concat (xgetcwd (), file1, NULL);
         but libraries should not call xalloc_die.  */
      char *cwd = getcwd (NULL, 0);
      if (!cwd)
        {
          saved_errno = errno;
          free_cwd (&saved_cwd);
          errno = saved_errno;
          return -1;
        }
      file1_alt = mfile_name_concat (cwd, file1, NULL);
      if (!file1_alt)
        {
          saved_errno = errno;
          free (cwd);
          free_cwd (&saved_cwd);
          errno = saved_errno;
          return -1;
        }
      free (cwd); /* Reduced to case 3.  */
    }
  else if (fd2 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file2)) /* Case 13.  */
    {
      char *cwd = getcwd (NULL, 0);
      if (!cwd)
        {
          saved_errno = errno;
          free_cwd (&saved_cwd);
          errno = saved_errno;
          return -1;
        }
      file2_alt = mfile_name_concat (cwd, file2, NULL);
      if (!file2_alt)
        {
          saved_errno = errno;
          free (cwd);
          free_cwd (&saved_cwd);
          errno = saved_errno;
          return -1;
        }
      free (cwd); /* Reduced to case 12.  */
    }

  /* Cases 3, 12, 15a remain.  Change to the correct directory.  */
  if (fchdir (fd1 == AT_FDCWD ? fd2 : fd1) != 0)
    {
      saved_errno = errno;
      free_cwd (&saved_cwd);
      if (file1 != file1_alt)
        free (file1_alt);
      else if (file2 != file2_alt)
        free (file2_alt);
      errno = saved_errno;
      return -1;
    }

  /* Finally safe to perform the user's function, then clean up.  */

  err = func (file1_alt, file2_alt);
  saved_errno = (err < 0 ? errno : 0);

  if (file1 != file1_alt)
    free (file1_alt);
  else if (file2 != file2_alt)
    free (file2_alt);

  if (restore_cwd (&saved_cwd) != 0)
    openat_restore_fail (errno);

  free_cwd (&saved_cwd);

  if (saved_errno)
    errno = saved_errno;
  return err;
}
/* Return the root mountpoint of the file system on which FILE exists, in
   malloced storage.  FILE_STAT should be the result of stating FILE.
   Give a diagnostic and return NULL if unable to determine the mount point.
   Exit if unable to restore current working directory.  */
extern char *
find_mount_point (char const *file, struct stat const *file_stat)
{
  struct saved_cwd cwd;
  struct stat last_stat;
  char *mp = NULL;		/* The malloc'd mount point.  */

  if (save_cwd (&cwd) != 0)
    {
      error (0, errno, _("cannot get current directory"));
      return NULL;
    }

  if (S_ISDIR (file_stat->st_mode))
    /* FILE is a directory, so just chdir there directly.  */
    {
      last_stat = *file_stat;
      if (chdir (file) < 0)
        {
          error (0, errno, _("cannot change to directory %s"), quoteaf (file));
          return NULL;
        }
    }
  else
    /* FILE is some other kind of file; use its directory.  */
    {
      char *xdir = dir_name (file);
      char *dir;
      ASSIGN_STRDUPA (dir, xdir);
      free (xdir);

      if (chdir (dir) < 0)
        {
          error (0, errno, _("cannot change to directory %s"), quoteaf (dir));
          return NULL;
        }

      if (stat (".", &last_stat) < 0)
        {
          error (0, errno, _("cannot stat current directory (now %s)"),
                 quoteaf (dir));
          goto done;
        }
    }

  /* Now walk up FILE's parents until we find another file system or /,
     chdiring as we go.  LAST_STAT holds stat information for the last place
     we visited.  */
  while (true)
    {
      struct stat st;
      if (stat ("..", &st) < 0)
        {
          error (0, errno, _("cannot stat %s"), quoteaf (".."));
          goto done;
        }
      if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
        /* cwd is the mount point.  */
        break;
      if (chdir ("..") < 0)
        {
          error (0, errno, _("cannot change to directory %s"), quoteaf (".."));
          goto done;
        }
      last_stat = st;
    }

  /* Finally reached a mount point, see what it's called.  */
  mp = xgetcwd ();

done:
  /* Restore the original cwd.  */
  {
    int save_errno = errno;
    if (restore_cwd (&cwd) != 0)
      error (EXIT_FAILURE, errno,
             _("failed to return to initial working directory"));
    free_cwd (&cwd);
    errno = save_errno;
  }

  return mp;
}
Exemple #8
0
int release (int argc, char **argv)
{
    int i, c;
    char *repository;
    char *thisarg;
    int arg_start_idx;
    int err = 0;
    short delete_flag = 0;
	short export_flag = 0;
	short yes_flag=0;
    struct saved_cwd cwd;

#ifdef SERVER_SUPPORT
    if (server_active)
		return release_server (argc, argv);
#endif

    /* Everything from here on is client or local.  */
    if (argc == -1)
		usage (release_usage);
    optind = 0;
    while ((c = getopt (argc, argv, "+Qdeqfy")) != -1)
    {
	switch (c)
	{
	    case 'Q':
	    case 'q':
		error (1, 0,
		       "-q or -Q must be specified before \"%s\"",
		       command_name);
		break;
	    case 'd':
		delete_flag++;
		break;
		case 'f':
		force_delete++;
		break;
		case 'e':
		export_flag++;
		break;
		case 'y':
		yes_flag=1;
		break;
	    case '?':
	    default:
		usage (release_usage);
		break;
	}
    }
    argc -= optind;
    argv += optind;

    /* Remember the directory where "cvs release" was invoked because
       all args are relative to this directory and we chdir around.
       */
    if (save_cwd (&cwd))
        error_exit ();

    arg_start_idx = 0;

    for (i = arg_start_idx; i < argc; i++)
    {
		thisarg = argv[i];

        if (isdir (thisarg))
        {
			if (CVS_CHDIR (thisarg) < 0)
			{
				if (!really_quiet)
					error (0, errno, "can't chdir to: %s", thisarg);
				continue;
			}
			if (!isdir (CVSADM))
			{
				if (!really_quiet)
					error (0, 0, "no repository directory: %s", thisarg);
				if (restore_cwd (&cwd, NULL))
					error_exit ();
				continue;
		    }
		}
		else
		{
			if (!really_quiet)
				error (0, 0, "no such directory: %s", thisarg);
		    continue;
		}

		repository = Name_Repository ((char *) NULL, (char *) NULL);

		if (!really_quiet)
		{
			char *tmp;
			modified_files = 0;
			start_recursion (release_fileproc, (FILESDONEPROC) NULL,
				 (PREDIRENTPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
				 (void *) NULL, 0, NULL, 0, W_LOCAL,
				 0, 0, (char *) NULL, NULL, 0, NULL, NULL);

	
			tmp=(char*)xmalloc(strlen(thisarg)+1024);
			if(modified_files)
				sprintf (tmp,"You have [%d] altered files in this repository.\n", modified_files);
			else
				*tmp='\0';

			sprintf(tmp+strlen(tmp),"Are you sure you want to release %sdirectory '%s': ",
				delete_flag ? "(and delete) " : export_flag?"(and export) " : "", thisarg);

			if(!yes_flag)
				c=yesno_prompt(tmp,"Modified files",0);
			else
			{
				printf("%sy\n",tmp);
				c='y';
			}

			xfree(tmp);
			if (c!='y')			/* "No" */
			{
				(void) fprintf (stderr, "** `%s' aborted by user choice.\n",
					command_name);
				xfree (repository);
				if (restore_cwd (&cwd, NULL))
						error_exit ();
				continue;
			}
		}

		if (!(current_parsed_root->isremote
			&& (!supported_request ("noop")
				|| !supported_request ("Notify")))
	    )
		{
			/* We are chdir'ed into the directory in question.  
			So don't pass args to unedit.  */
			int argc = 1;
			char *argv[3];
			argv[0] = "dummy";
			argv[1] = NULL;
			err += unedit (argc, argv);
			/* Unedit will have killed our lockserver connection */
			if(!current_parsed_root->isremote)
				lock_register_client(CVS_Username,current_parsed_root->directory);

		}

        if (current_parsed_root->isremote)
        {
			send_to_server ("Argument ", 0);
			send_to_server (thisarg, 0);
			send_to_server ("\n", 1);
			send_to_server ("release\n", 0);
		}
        else
        {
		    history_write ('F', thisarg, "", thisarg, "", NULL, NULL); /* F == Free */
        }

        xfree (repository);

		if (restore_cwd (&cwd, NULL))
		    error_exit ();

		if(!noexec)
		{
			if(delete_flag)
			{
				start_recursion (release_delete_fileproc, (FILESDONEPROC) NULL,
						(PREDIRENTPROC) NULL, (DIRENTPROC) NULL, release_delete_dirleaveproc,
						(void *) NULL, 1, &thisarg, 0, W_LOCAL,
						0, 0, (char *) NULL, NULL, 0, NULL, NULL);
			}
			else if(export_flag)
			{
				start_recursion (NULL, (FILESDONEPROC) NULL,
						(PREDIRENTPROC) NULL, (DIRENTPROC) NULL, release_export_dirleaveproc,
						(void *) NULL, 1, &thisarg, 0, W_LOCAL,
						0, 0, (char *) NULL, NULL, 0, NULL, NULL);
			}
		}

        if (current_parsed_root->isremote)
		    err += get_server_responses ();
    }

    if (restore_cwd (&cwd, NULL))
		error_exit ();
    free_cwd (&cwd);

    if (current_parsed_root->isremote)
    {
	/* Unfortunately, client.c doesn't offer a way to close
	   the connection without waiting for responses.  The extra
	   network turnaround here is quite unnecessary other than
	   that....  */
		send_to_server ("noop\n", 0);
		err += get_responses_and_close ();
    }

    return err;
}
Exemple #9
0
TEARDOWN()
{
	restore_cwd(saved_cwd);
}
Exemple #10
0
/*
 * Process each of the directories in the list (recursing as we go)
 */
static int do_dir_proc (Node *p, void *closure)
{
    struct frame_and_entries *frent = (struct frame_and_entries *) closure;
    struct recursion_frame *frame = frent->frame;
    struct recursion_frame xframe;
    char *dir = p->key;
    char *newrepos, *virtrepos;
    List *sdirlist;
    char *srepository;
	char *smapped_repository;
    Dtype dir_return = R_PROCESS;
	Dtype hint;
    int stripped_dot = 0;
    int err = 0;
    struct saved_cwd cwd;
    char *saved_update_dir;
    int process_this_directory = 1;
	int directory_opened = 0;
	char *old_update_repos = NULL;
	int directory_not_valid = 0;

    if (fncmp (dir, CVSADM) == 0
		|| fncmp (dir, CVSDUMMY) == 0)
    {
	/* This seems to most often happen when users (beginning users,
	   generally), try "cvs ci *" or something similar.  On that
	   theory, it is possible that we should just silently skip the
	   CVSADM directories, but on the other hand, using a wildcard
	   like this isn't necessarily a practice to encourage (it operates
	   only on files which exist in the working directory, unlike
	   regular CVS recursion).  */

	/* FIXME-reentrancy: printed_cvs_msg should be in a "command
	   struct" or some such, so that it gets cleared for each new
	   command (this is possible using the remote protocol and a
	   custom-written client).  The struct recursion_frame is not
	   far back enough though, some commands (commit at least)
	   will call start_recursion several times.  An alternate solution
	   would be to take this whole check and move it to a new function
	   validate_arguments or some such that all the commands call
	   and which snips the offending directory from the argc,argv
	   vector.  */
	static int printed_cvs_msg = 0;
	if (!printed_cvs_msg)
	{
	    error (0, 0, "warning: directory %s specified in argument",
		   dir);
	    error (0, 0, "but CVS uses %s for its own purposes; skipping %s directory", dir, dir);
	    printed_cvs_msg = 1;
	}
	return 0;
    }

    saved_update_dir = update_dir;
    update_dir = (char*)xmalloc (strlen (saved_update_dir)
			  + strlen (dir)
			  + 5);
    strcpy (update_dir, saved_update_dir);

    /* set up update_dir - skip dots if not at start */
    if (strcmp (dir, ".") != 0)
    {
	if (update_dir[0] != '\0')
	{
	    (void) strcat (update_dir, "/");
	    (void) strcat (update_dir, dir);
	}
	else
	    (void) strcpy (update_dir, dir);

	/*
	 * Here we need a plausible repository name for the sub-directory. We
	 * create one by concatenating the new directory name onto the
	 * previous repository name.  The only case where the name should be
	 * used is in the case where we are creating a new sub-directory for
	 * update -d and in that case the generated name will be correct.
	 */
	if (repository == NULL)
	{
	    newrepos = xstrdup ("");
		virtrepos = xstrdup ("");
	}
	else
	{
		if(frame->which&W_LOCAL)
		{
			char *d=(char*)xmalloc(strlen(dir)+strlen(CVSADM_REP)+32);
			sprintf(d,"%s/%s",dir,CVSADM_REP);
			if(isfile(d))
				virtrepos = Name_Repository(dir,update_dir);
			else
			{
				virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5);
				sprintf (virtrepos, "%s/%s", repository, dir);
			}
			xfree(d);

		}
		else
		{
			virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5);
			sprintf (virtrepos, "%s/%s", repository, dir);
		}
		if(!current_parsed_root->isremote)
		{
			newrepos = map_repository(virtrepos);
			if(!newrepos)
				error(1,0,"Internal error - couldn't map %s to anything",virtrepos);
		}
		else
			newrepos = xstrdup(virtrepos);
	}
    }
    else
    {
	if (update_dir[0] == '\0')
	    (void) strcpy (update_dir, dir);

	if (repository == NULL)
	{
	    newrepos = xstrdup ("");
		virtrepos = xstrdup ("");
	}
	else
	{
	    newrepos = xstrdup (mapped_repository);
		virtrepos = xstrdup (repository);
	}
    }

	/* Check to see that the CVSADM directory, if it exists, seems to be
       well-formed.  It can be missing files if the user hit ^C in the
       middle of a previous run.  We want to (a) make this a nonfatal
       error, and (b) make sure we print which directory has the
       problem.

       Do this before the direntproc, so that (1) the direntproc
       doesn't have to guess/deduce whether we will skip the directory
       (e.g. send_dirent_proc and whether to send the directory), and
       (2) so that the warm fuzzy doesn't get printed if we skip the
       directory.  */
    if (frame->which & W_LOCAL)
    {
	char *cvsadmdir;

	cvsadmdir = (char*)xmalloc (strlen (dir)
			     + sizeof (CVSADM_REP)
			     + sizeof (CVSADM_ENT)
			     + 80);

	strcpy (cvsadmdir, dir);
	strcat (cvsadmdir, "/");
	strcat (cvsadmdir, CVSADM);
	if (isdir (cvsadmdir))
	{
	    strcpy (cvsadmdir, dir);
	    strcat (cvsadmdir, "/");
	    strcat (cvsadmdir, CVSADM_REP);
	    if (!isfile (cvsadmdir))
	    {
		/* Some commands like update may have printed "? foo" but
		   if we were planning to recurse, and don't on account of
		   CVS/Repository, we want to say why.  */
		error (0, 0, "ignoring %s (%s missing)", update_dir,
		       CVSADM_REP);
		dir_return = R_SKIP_ALL;
	    }

	    /* Likewise for CVS/Entries.  */
	    if (dir_return != R_SKIP_ALL)
	    {
		strcpy (cvsadmdir, dir);
		strcat (cvsadmdir, "/");
		strcat (cvsadmdir, CVSADM_ENT);
		if (!isfile (cvsadmdir))
		{
		    /* Some commands like update may have printed "? foo" but
		       if we were planning to recurse, and don't on account of
		       CVS/Repository, we want to say why.  */
		    error (0, 0, "ignoring %s (%s missing)", update_dir,
			   CVSADM_ENT);
		    dir_return = R_SKIP_ALL;
		}
	    }
	}
	xfree (cvsadmdir);
    }

    /* Only process this directory if the root matches.  This nearly
       duplicates code in do_recursion. */

	/* If -d was specified, it should override CVS/Root.

	   In the single-repository case, it is long-standing CVS behavior
	   and makes sense - the user might want another access method,
	   another server (which mounts the same repository), &c.

	   In the multiple-repository case, -d overrides all CVS/Root
	   files.  That is the only plausible generalization I can
	   think of.  */
    if (!(frame->which&W_FAKE) && CVSroot_cmdline == NULL && !server_active)
    {
	char *this_root = Name_Root (dir, update_dir);
	if (this_root != NULL)
	{
	    if (findnode_fn (root_directories, this_root) == NULL)
	    {
		/* Add it to our list. */

		Node *n = getnode ();
		n->type = NT_UNKNOWN;
		n->key = xstrdup (this_root);

		if (addnode (root_directories, n))
		    error (1, 0, "cannot add new CVSROOT %s", this_root);

	    }

	    process_this_directory = (fncmp (current_parsed_root->original, this_root) == 0);

	    xfree (this_root);
	}
    }
    /*
     * Do we have access to this directory?
     */
	if(!current_parsed_root->isremote)
	{
		const char *tag=NULL;
		const char *date=NULL;
		int nonbranch=0;
		const char *message;
		const char *v_msg;

		/* before we do anything else, see if we have any
		   per-directory tags */
		ParseTag (&tag, &date, &nonbranch, NULL);
		if (! verify_access (frame->permproc, newrepos, NULL, update_dir, frame->tag?frame->tag:tag,&message, &v_msg))
		{
			if(frame->permproc!=verify_read)
			{
			  if(tag)
			    error (0, 0, "User '%s' cannot %s %s on tag/branch %s", CVS_Username, v_msg, fn_root(virtrepos), tag);
			  else
			    error (0, 0, "User '%s' cannot %s %s", CVS_Username, v_msg, fn_root(virtrepos));
			  if(message)
				error(0,0,"%s",message);	
			}
			dir_return = R_SKIP_ALL;
		}
		xfree(tag);
		xfree(date);
	}

	if(dir_return!=R_SKIP_ALL)
	{
		/* Generic behavior.  I don't see a reason to make the caller specify
		a direntproc just to get this.  */
		if ((frame->which & (W_LOCAL|W_FAKE)))
		{
			if(!isdir (dir))
				hint = R_SKIP_ALL;
			else
				hint = R_PROCESS;
		}
		else if(!isdir(newrepos))
			hint = R_SKIP_ALL;
		else
			hint = R_PROCESS;
	}

    if (dir_return == R_SKIP_ALL || dir_return == R_ERROR)
	;
    else if(process_this_directory)
	{
		int directory_opened_status=-1;
		const char *dirversion=NULL;
		const char *dirtag=NULL;
		const char *dirdate=NULL;
		int dirnonbranch=0;

		if(frame->predirentproc != NULL)
		{
			frame->predirentproc (frame->callerdat, dir, newrepos,
							update_dir, frent->entries, virtrepos, hint);
		}

		/* before we do anything else, see if we have any
			per-directory tags */
		ParseTag_Dir (dir, &dirtag, &dirdate, &dirnonbranch, &dirversion);
		directory_opened_status=open_directory(newrepos,dir,dirtag,dirdate,dirnonbranch,dirversion,current_parsed_root->isremote);
		if (directory_opened_status!=-1)
			directory_opened = 1;
		xfree(dirversion);
		xfree(dirtag);
		xfree(dirdate);
		if(!current_parsed_root->isremote)
			fileattr_startdir(newrepos);
	}

	/* call-back dir entry proc (if any) */
    if (dir_return == R_SKIP_ALL || dir_return == R_ERROR)
		;
    else if (frame->direntproc != NULL)
    {
	/* If we're doing the actual processing, call direntproc.
           Otherwise, assume that we need to process this directory
           and recurse. FIXME. */

	if (process_this_directory)
	    dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
					    update_dir, frent->entries, virtrepos, hint);
	else
	    dir_return = R_PROCESS;
    }
    else
    {
	/* Generic behavior.  I don't see a reason to make the caller specify
	   a direntproc just to get this.  */
		dir_return = hint;
    }

    /* only process the dir if the return code was 0 */
    if (dir_return != R_SKIP_ALL && dir_return !=R_ERROR)
    {
		/* save our current directory and static vars */
        if (save_cwd (&cwd))
			error_exit ();

		sdirlist = dirlist;
		srepository = repository;
		smapped_repository = mapped_repository;
		dirlist = NULL;

		/* cd to the sub-directory */
		if (!(frame->which&(W_LOCAL|W_FAKE)))
		{
			if ( CVS_CHDIR (newrepos) < 0)
			{
				error (1, errno, "could not chdir to %s", fn_root(newrepos));
			}
		}
		else
		{
			if ( !(frame->which&W_FAKE) && CVS_CHDIR (dir) < 0)
			{
				if(!noexec)
					error (1, errno, "could not chdir to %s", fn_root(update_dir));
				else
					directory_not_valid=1;
			}

		}
		old_update_repos = update_repos;
		update_repos = xstrdup(newrepos);

		/* honor the global SKIP_DIRS (a.k.a. local) */
		if (frame->flags == R_SKIP_DIRS)
			dir_return = R_SKIP_DIRS;

		/* remember if the `.' will be stripped for subsequent dirs */
		if (strcmp (update_dir, ".") == 0)
		{
			update_dir[0] = '\0';
			stripped_dot = 1;
		}

		/* make the recursive call */
		xframe = *frame;
		xframe.flags = dir_return;
		if(directory_not_valid)
		{
			xframe.which &= ~W_LOCAL;
			xframe.which |= W_FAKE;
		}
		err += do_recursion (&xframe, 0);

		/* put the `.' back if necessary */
		if (stripped_dot)
			(void) strcpy (update_dir, ".");

		/* call-back dir leave proc (if any) */
		if (process_this_directory && frame->dirleaveproc != NULL)
			err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir,
						frent->entries);

		if(directory_opened)
			close_directory();

		/* get back to where we started and restore state vars */
		if (restore_cwd (&cwd, NULL))
			error_exit ();

		xfree(update_repos);
		update_repos = old_update_repos;

		free_cwd (&cwd);
		dirlist = sdirlist;
		repository = srepository;
		mapped_repository = smapped_repository;
    }
	else
	{
		if(directory_opened)
			close_directory();
		fileattr_write();
		fileattr_free();
	}

    xfree (newrepos);
	xfree (virtrepos);
    xfree (update_dir);
    update_dir = saved_update_dir;

	if(dir_return == R_ERROR)
		err++;

    return (err);
}
Exemple #11
0
static int unroll_files_proc (Node *p, void *closure)
{
	int directory_opened = 0;
    Node *n;
    struct recursion_frame *frame = (struct recursion_frame *) closure;
    int err = 0;
    List *save_dirlist;
    char *save_update_dir = NULL;
    struct saved_cwd cwd;
	char *dir = p->key;

    /* if this dir was also an explicitly named argument, then skip
       it.  We'll catch it later when we do dirs. */
    n = findnode_fn (dirlist, dir);
    if (n != NULL)
	return (0);

    /* otherwise, call dorecusion for this list of files. */
    filelist = (List *) p->data;
    p->data = NULL;
    save_dirlist = dirlist;
    dirlist = NULL;

    if (strcmp(dir, ".") != 0)
    {
        if (save_cwd (&cwd))
	    error_exit ();
	if ( CVS_CHDIR (dir) < 0)
	    error (1, errno, "could not chdir to %s", dir);

	save_update_dir = update_dir;
	update_dir = (char*)xmalloc (strlen (save_update_dir)
				  + strlen (dir)
				  + 5);
	strcpy (update_dir, save_update_dir);

	if (*update_dir != '\0')
	    (void) strcat (update_dir, "/");

	(void) strcat (update_dir, dir);
    }


	if(frame->which&W_LOCAL)
	{
		int directory_opened_status=-1;
		const char *version;
		const char *tag;
		const char *date;
		int nonbranch;
		char *repository = Name_Repository(NULL,update_dir);

		ParseTag(&tag, &date, &nonbranch, &version);
		directory_opened_status=open_directory(repository,".",tag,date,nonbranch,version,current_parsed_root->isremote);
		if (directory_opened_status!=-1)
			directory_opened = 1;
		xfree(repository);
		xfree(version);
		xfree(tag);
		xfree(date);
	}
	else
	{
		int directory_opened_status=-1;
		/* we can't simply call Name_Repository() here, if we do we get:
		-> Name_Repository((null),cvsnt/src)
		cvs server: in directory cvsnt/src:
		cvs [server aborted]: CVS directory without administration files present.  Cannot continue until this directory is deleted or renamed.
		*/
		directory_opened_status=open_directory(NULL,".",NULL,NULL,0,NULL,1); /* We want to open the directory anyway, so treat it as 'remote'.  This only affects rlog, etc. */
		if (directory_opened_status!=-1)
			directory_opened = 1;
	}

    err += do_recursion (frame, 1);

	if(directory_opened)
		close_directory();

    if (save_update_dir != NULL)
    {
	xfree (update_dir);
	update_dir = save_update_dir;

	if (restore_cwd (&cwd, NULL))
	    error_exit ();
	free_cwd (&cwd);
    }

    dirlist = save_dirlist;
    if (filelist)
	dellist (&filelist);
    return(err);
}
Exemple #12
0
/*
 * This is the recursive function that processes a module name.
 * It calls back the passed routine for each directory of a module
 * It runs the post checkout or post tag proc from the modules file
 */
int
my_module (DBM *db, char *mname, enum mtype m_type, char *msg,
            CALLBACKPROC callback_proc, char *where, int shorten,
            int local_specified, int run_module_prog, int build_dirs,
            char *extra_arg, List *stack)
{
    char *checkout_prog = NULL;
    char *export_prog = NULL;
    char *tag_prog = NULL;
    struct saved_cwd cwd;
    int cwd_saved = 0;
    char *line;
    int modargc;
    int xmodargc;
    char **modargv = NULL;
    char **xmodargv = NULL;
    /* Found entry from modules file, including options and such.  */
    char *value = NULL;
    char *mwhere = NULL;
    char *mfile = NULL;
    char *spec_opt = NULL;
    char *xvalue = NULL;
    int alias = 0;
    datum key, val;
    char *cp;
    int c, err = 0;
    int nonalias_opt = 0;

#ifdef SERVER_SUPPORT
    int restore_server_dir = 0;
    char *server_dir_to_restore = NULL;
#endif

    TRACE (TRACE_FUNCTION, "my_module (%s, %s, %s, %s)",
           mname ? mname : "(null)", msg ? msg : "(null)",
           where ? where : "NULL", extra_arg ? extra_arg : "NULL");

    /* Don't process absolute directories.  Anything else could be a security
     * problem.  Before this check was put in place:
     *
     *   $ cvs -d:fork:/cvsroot co /foo
     *   cvs server: warning: cannot make directory CVS in /: Permission denied
     *   cvs [server aborted]: cannot make directory /foo: Permission denied
     *   $
     */
    if (ISABSOLUTE (mname))
	error (1, 0, "Absolute module reference invalid: `%s'", mname);

    /* Similarly for directories that attempt to step above the root of the
     * repository.
     */
    if (pathname_levels (mname) > 0)
	error (1, 0, "up-level in module reference (`..') invalid: `%s'.",
               mname);

    /* if this is a directory to ignore, add it to that list */
    if (mname[0] == '!' && mname[1] != '\0')
    {
	ign_dir_add (mname+1);
	goto do_module_return;
    }

    /* strip extra stuff from the module name */
    strip_trailing_slashes (mname);

    /*
     * Look up the module using the following scheme:
     *	1) look for mname as a module name
     *	2) look for mname as a directory
     *	3) look for mname as a file
     *  4) take mname up to the first slash and look it up as a module name
     *	   (this is for checking out only part of a module)
     */

    /* look it up as a module name */
    key.dptr = mname;
    key.dsize = strlen (key.dptr);
    if (db != NULL)
	val = dbm_fetch (db, key);
    else
	val.dptr = NULL;
    if (val.dptr != NULL)
    {
	/* copy and null terminate the value */
	value = xmalloc (val.dsize + 1);
	memcpy (value, val.dptr, val.dsize);
	value[val.dsize] = '\0';

	/* If the line ends in a comment, strip it off */
	if ((cp = strchr (value, '#')) != NULL)
	    *cp = '\0';
	else
	    cp = value + val.dsize;

	/* Always strip trailing spaces */
	while (cp > value && isspace ((unsigned char) *--cp))
	    *cp = '\0';

	mwhere = xstrdup (mname);
	goto found;
    }
    else
    {
	char *file;
	char *attic_file;
	char *acp;
	int is_found = 0;

	/* check to see if mname is a directory or file */
	file = xmalloc (strlen (current_parsed_root->directory)
			+ strlen (mname) + sizeof(RCSEXT) + 2);
	(void) sprintf (file, "%s/%s", current_parsed_root->directory, mname);
	attic_file = xmalloc (strlen (current_parsed_root->directory)
			      + strlen (mname)
			      + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3);
	if ((acp = strrchr (mname, '/')) != NULL)
	{
	    *acp = '\0';
	    (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory,
			    mname, CVSATTIC, acp + 1, RCSEXT);
	    *acp = '/';
	}
	else
	    (void) sprintf (attic_file, "%s/%s/%s%s",
	                    current_parsed_root->directory,
			    CVSATTIC, mname, RCSEXT);

	if (isdir (file))
	{
	    modargv = xmalloc (sizeof (*modargv));
	    modargv[0] = xstrdup (mname);
	    modargc = 1;
	    is_found = 1;
	}
	else
	{
	    (void) strcat (file, RCSEXT);
	    if (isfile (file) || isfile (attic_file))
	    {
		/* if mname was a file, we have to split it into "dir file" */
		if ((cp = strrchr (mname, '/')) != NULL && cp != mname)
		{
		    modargv = xnmalloc (2, sizeof (*modargv));
		    modargv[0] = xmalloc (strlen (mname) + 2);
		    strncpy (modargv[0], mname, cp - mname);
		    modargv[0][cp - mname] = '\0';
		    modargv[1] = xstrdup (cp + 1);
		    modargc = 2;
		}
		else
		{
		    /*
		     * the only '/' at the beginning or no '/' at all
		     * means the file we are interested in is in CVSROOT
		     * itself so the directory should be '.'
		     */
		    if (cp == mname)
		    {
			/* drop the leading / if specified */
			modargv = xnmalloc (2, sizeof (*modargv));
			modargv[0] = xstrdup (".");
			modargv[1] = xstrdup (mname + 1);
			modargc = 2;
		    }
		    else
		    {
			/* otherwise just copy it */
			modargv = xnmalloc (2, sizeof (*modargv));
			modargv[0] = xstrdup (".");
			modargv[1] = xstrdup (mname);
			modargc = 2;
		    }
		}
		is_found = 1;
	    }
	}
	free (attic_file);
	free (file);

	if (is_found)
	{
	    assert (value == NULL);

	    /* OK, we have now set up modargv with the actual
	       file/directory we want to work on.  We duplicate a
	       small amount of code here because the vast majority of
	       the code after the "found" label does not pertain to
	       the case where we found a file/directory rather than
	       finding an entry in the modules file.  */
	    if (save_cwd (&cwd))
		error (1, errno, "Failed to save current directory.");
	    cwd_saved = 1;

	    err += callback_proc (modargc, modargv, where, mwhere, mfile,
				  shorten,
				  local_specified, mname, msg);

	    free_names (&modargc, modargv);

	    /* cd back to where we started.  */
	    if (restore_cwd (&cwd))
		error (1, errno, "Failed to restore current directory, `%s'.",
		       cwd.name);
	    free_cwd (&cwd);
	    cwd_saved = 0;

	    goto do_module_return;
	}
    }

    /* look up everything to the first / as a module */
    if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL)
    {
	/* Make the slash the new end of the string temporarily */
	*cp = '\0';
	key.dptr = mname;
	key.dsize = strlen (key.dptr);

	/* do the lookup */
	if (db != NULL)
	    val = dbm_fetch (db, key);
	else
	    val.dptr = NULL;

	/* if we found it, clean up the value and life is good */
	if (val.dptr != NULL)
	{
	    char *cp2;

	    /* copy and null terminate the value */
	    value = xmalloc (val.dsize + 1);
	    memcpy (value, val.dptr, val.dsize);
	    value[val.dsize] = '\0';

	    /* If the line ends in a comment, strip it off */
	    if ((cp2 = strchr (value, '#')) != NULL)
		*cp2 = '\0';
	    else
		cp2 = value + val.dsize;

	    /* Always strip trailing spaces */
	    while (cp2 > value  &&  isspace ((unsigned char) *--cp2))
		*cp2 = '\0';

	    /* mwhere gets just the module name */
	    mwhere = xstrdup (mname);
	    mfile = cp + 1;
	    assert (strlen (mfile));

	    /* put the / back in mname */
	    *cp = '/';

	    goto found;
	}

	/* put the / back in mname */
	*cp = '/';
    }

    /* if we got here, we couldn't find it using our search, so give up */
    error (0, 0, "cannot find module `%s' - ignored", mname);
    err++;
    goto do_module_return;


    /*
     * At this point, we found what we were looking for in one
     * of the many different forms.
     */
  found:

    /* remember where we start */
    if (save_cwd (&cwd))
	error (1, errno, "Failed to save current directory.");
    cwd_saved = 1;

    assert (value != NULL);

    /* search the value for the special delimiter and save for later */
    if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL)
    {
	*cp = '\0';			/* null out the special char */
	spec_opt = cp + 1;		/* save the options for later */

	/* strip whitespace if necessary */
	while (cp > value  &&  isspace ((unsigned char) *--cp))
	    *cp = '\0';
    }

    /* don't do special options only part of a module was specified */
    if (mfile != NULL)
	spec_opt = NULL;

    /*
     * value now contains one of the following:
     *    1) dir
     *	  2) dir file
     *    3) the value from modules without any special args
     *		    [ args ] dir [file] [file] ...
     *	     or     -a module [ module ] ...
     */

    /* Put the value on a line with XXX prepended for getopt to eat */
    line = Xasprintf ("XXX %s", value);

    /* turn the line into an argv[] array */
    line2argv (&xmodargc, &xmodargv, line, " \t");
    free (line);
    modargc = xmodargc;
    modargv = xmodargv;

    /* parse the args */
    getoptreset ();
    while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
    {
	switch (c)
	{
	    case 'a':
		alias = 1;
		break;
	    case 'd':
		if (mwhere)
		    free (mwhere);
		mwhere = xstrdup (optarg);
		nonalias_opt = 1;
		break;
	    case 'l':
		local_specified = 1;
		nonalias_opt = 1;
		break;
	    case 'o':
		if (checkout_prog)
		    free (checkout_prog);
		checkout_prog = xstrdup (optarg);
		nonalias_opt = 1;
		break;
	    case 'e':
		if (export_prog)
		    free (export_prog);
		export_prog = xstrdup (optarg);
		nonalias_opt = 1;
		break;
	    case 't':
		if (tag_prog)
		    free (tag_prog);
		tag_prog = xstrdup (optarg);
		nonalias_opt = 1;
		break;
	    case '?':
		error (0, 0,
		       "modules file has invalid option for key %s value %s",
		       key.dptr, value);
		err++;
		goto do_module_return;
	}
    }
    modargc -= optind;
    modargv += optind;
    if (modargc == 0  &&  spec_opt == NULL)
    {
	error (0, 0, "modules file missing directory for module %s", mname);
	++err;
	goto do_module_return;
    }

    if (alias && nonalias_opt)
    {
	/* The documentation has never said it is valid to specify
	   -a along with another option.  And I believe that in the past
	   CVS has ignored the options other than -a, more or less, in this
	   situation.  */
	error (0, 0, "\
-a cannot be specified in the modules file along with other options");
	++err;
	goto do_module_return;
    }
Exemple #13
0
/* Rebuild the checked out administrative files in directory DIR.  */
int mkmodules (char *dir)
{
    struct saved_cwd cwd;
    char *temp;
    char *cp, *last, *fname;
#ifdef MY_NDBM
    DBM *db;
#endif
    FILE *fp;
    char *line = NULL;
    size_t line_allocated = 0;
    const struct admin_file *fileptr;
    mode_t mode;

    if (noexec)
	return 0;

    if (save_cwd (&cwd))
	error_exit ();

    if ( CVS_CHDIR (dir) < 0)
	error (1, errno, "cannot chdir to %s", dir);

    /*
     * First, do the work necessary to update the "modules" database.
     */
    temp = make_tempfile ();
    switch (checkout_file (CVSROOTADM_MODULES, dir, temp, NULL))
    {

	case 0:			/* everything ok */
#ifdef MY_NDBM
	    /* open it, to generate any duplicate errors */
	    if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
		dbm_close (db);
#else
	    write_dbmfile (temp);
	    rename_dbmfile (temp);
#endif
	    rename_rcsfile (temp, CVSROOTADM_MODULES);
	    break;

	default:
	    error (0, 0,
		"'cvs checkout' is less functional without a %s file",
		CVSROOTADM_MODULES);
	    break;
    }					/* switch on checkout_file() */

    if (unlink_file (temp) < 0
	&& !existence_error (errno))
	error (0, errno, "cannot remove %s", temp);
    xfree (temp);

    /* Checkout the files that need it in CVSROOT dir */
    for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
	if (fileptr->errormsg == NULL)
	    continue;
	temp = make_tempfile ();
	if (checkout_file (fileptr->filename, dir, temp, NULL) == 0)
	    rename_rcsfile (temp, fileptr->filename);

	if (unlink_file (temp) < 0
	    && !existence_error (errno))
	    error (0, errno, "cannot remove %s", temp);
	xfree (temp);
    }

    fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
    if (fp)
    {
	/*
	 * File format:
	 *  [<whitespace>]<filename><whitespace><error message><end-of-line>
	 *
	 * comment lines begin with '#'
	 */
	while (getline (&line, &line_allocated, fp) >= 0)
	{
	    /* skip lines starting with # */
	    if (line[0] == '#')
		continue;

	    if ((last = strrchr (line, '\n')) != NULL)
		*last = '\0';			/* strip the newline */

	    /* Skip leading white space. */
	    for (fname = line;
		 *fname && isspace ((unsigned char) *fname);
		 fname++)
		;

	    /* Find end of filename. */
	    for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
		;
	    *cp = '\0';

	    if(isabsolute(fname) || pathname_levels(fname)>0 || !fncmp(fname,"checkoutlist"))
		{
			error(0,0,"Invalid filename '%s' in checkoutlist", fname);
			continue;
		}

	    temp = make_tempfile ();
	    if (checkout_file (fname, dir, temp, &mode) == 0)
	    {
	        chmod(temp,mode);
		rename_rcsfile (temp, fname);
	    }
	    else
	    {
		for (cp++;
		     cp < last && *last && isspace ((unsigned char) *last);
		     cp++)
		    ;
		if (cp < last && *cp)
		    error (0, 0, cp, fname);
	    }
	    if (unlink_file (temp) < 0
		&& !existence_error (errno))
		error (0, errno, "cannot remove %s", temp);
	    xfree (temp);
	}
	if (line)
	    xfree (line);
	if (ferror (fp))
	    error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
	if (fclose (fp) < 0)
	    error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
    }
    else
    {
	/* Error from CVS_FOPEN.  */
	if (!existence_error (errno))
	    error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
    }

    if (restore_cwd (&cwd, NULL))
	error_exit ();
    free_cwd (&cwd);

    return (0);
}