ftp.nice.ch/pub/next/unix/developer/cvs.950905.s.tar.gz#/cvs-1.5.1/src/recurse.c

This is recurse.c in view mode; [Download] [Up]

/*
 * Copyright (c) 1992, Brian Berliner and Jeff Polk
 * 
 * You may distribute under the terms of the GNU General Public License as
 * specified in the README file that comes with the CVS 1.4 kit.
 * 
 * General recursion handler
 * 
 */

#include "cvs.h"
#include "save-cwd.h"

#ifndef lint
static const char rcsid[] = "$CVSid: @(#)recurse.c 1.31 94/09/30 $";
USE(rcsid)
#endif

static int do_dir_proc PROTO((Node * p, void *closure));
static int do_file_proc PROTO((Node * p, void *closure));
static void addlist PROTO((List ** listp, char *key));
static int unroll_files_proc PROTO((Node *p, void *closure));
static void addfile PROTO((List **listp, char *dir, char *file));


/*
 * Local static versions eliminates the need for globals
 */
static int (*fileproc) ();
static int (*filesdoneproc) ();
static Dtype (*direntproc) ();
static int (*dirleaveproc) ();
static int which;
static Dtype flags;
static int aflag;
static int readlock;
static int dosrcs;
static char update_dir[PATH_MAX];
static char *repository = NULL;
static List *entries = NULL;
static List *srcfiles = NULL;

static List *filelist = NULL; /* holds list of files on which to operate */
static List *dirlist = NULL; /* holds list of directories on which to operate */

struct recursion_frame {
  int (*fileproc)();
  int (*filesdoneproc) ();
  Dtype (*direntproc) ();
  int (*dirleaveproc) ();
  Dtype flags;
  int which;
  int aflag;
  int readlock;
  int dosrcs;
};

/*
 * Called to start a recursive command.
 *
 * Command line arguments dictate the directories and files on which
 * we operate.  In the special case of no arguments, we default to
 * ".".
 *
 * The general algorithm is as follows.
 */
int
start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
		 argc, argv, local, which, aflag, readlock,
		 update_preload, dosrcs, wd_is_repos)
    int (*fileproc) ();
    int (*filesdoneproc) ();
    Dtype (*direntproc) ();
    int (*dirleaveproc) ();
    int argc;
    char **argv;
    int local;
    int which;
    int aflag;
    int readlock;
    char *update_preload;
    int dosrcs;
    int wd_is_repos;	/* Set if caller has already cd'd to the repository */
{
    int i, err = 0;
    Dtype flags;
    List *files_by_dir = NULL;
    struct recursion_frame frame;

    if (update_preload == NULL)
	update_dir[0] = '\0';
    else
	(void) strcpy (update_dir, update_preload);

    if (local)
	flags = R_SKIP_DIRS;
    else
	flags = R_PROCESS;

    /* clean up from any previous calls to start_recursion */
    if (repository)
    {
	free (repository);
	repository = (char *) NULL;
    }
    if (entries)
    {
	Entries_Close (entries);
	entries = NULL;
    }
    if (srcfiles)
	dellist (&srcfiles);
    if (filelist)
	dellist (&filelist); /* FIXME-krp: no longer correct. */
/* FIXME-krp: clean up files_by_dir */
    if (dirlist)
	dellist (&dirlist);

    if (argc == 0)
    {

	/*
	 * There were no arguments, so we'll probably just recurse. The
	 * exception to the rule is when we are called from a directory
	 * without any CVS administration files.  That has always meant to
	 * process each of the sub-directories, so we pretend like we were
	 * called with the list of sub-dirs of the current dir as args
	 */
	if ((which & W_LOCAL) && !isdir (CVSADM))
	    dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
	else
	    addlist (&dirlist, ".");

	err += do_recursion (fileproc, filesdoneproc, direntproc,
			    dirleaveproc, flags, which, aflag,
			    readlock, dosrcs);
	return(err);
    }


    /*
     * There were arguments, so we have to handle them by hand. To do
     * that, we set up the filelist and dirlist with the arguments and
     * call do_recursion.  do_recursion recognizes the fact that the
     * lists are non-null when it starts and doesn't update them.
     *
     * explicitly named directories are stored in dirlist.
     * explicitly named files are stored in filelist.
     * other possibility is named entities whicha are not currently in
     * the working directory.
     */
    
    for (i = 0; i < argc; i++)
    {
	/* if this argument is a directory, then add it to the list of
	   directories. */

	if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i]))
	    addlist (&dirlist, argv[i]);
	else
	{
	    /* otherwise, split argument into directory and component names. */
	    char *dir;
	    char *comp;
	    char tmp[PATH_MAX];
	    char *file_to_try;

	    dir = xstrdup (argv[i]);
	    if ((comp = strrchr (dir, '/')) == NULL)
	    {
		/* no dir component.  What we have is an implied "./" */
		comp = dir;
		dir = xstrdup(".");
	    }
	    else
	    {
		char *p = comp;

		*p++ = '\0';
		comp = xstrdup (p);
	    }

	    /* if this argument exists as a file in the current
	       working directory tree, then add it to the files list.  */

	    if (wd_is_repos)
	    {
		/* If doing rtag, we've done a chdir to the repository. */
		sprintf (tmp, "%s%s", argv[i], RCSEXT);
		file_to_try = tmp;
	    }
	    else
	      file_to_try = argv[i];

	    if(isfile(file_to_try))
		addfile (&files_by_dir, dir, comp);
	    else if (isdir (dir))
	    {
		if (isdir (CVSADM))
		{
		    /* otherwise, look for it in the repository. */
		    char *save_update_dir;
		    char *repos;
		
		    /* save & set (aka push) update_dir */
		    save_update_dir = xstrdup (update_dir);

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

		    (void) strcat (update_dir, dir);
		
		    /* look for it in the repository. */
		    repos = Name_Repository (dir, update_dir);
		    (void) sprintf (tmp, "%s/%s", repos, comp);

		    if (!wrap_name_has (comp, WRAP_TOCVS) && isdir(tmp))
			addlist (&dirlist, argv[i]);
		    else
			addfile (&files_by_dir, dir, comp);

		    (void) sprintf (update_dir, "%s", save_update_dir);
		    free (save_update_dir);
		}
		else
		    addfile (&files_by_dir, dir, comp);
	    }
	    else
		error (1, 0, "no such directory `%s'", dir);

	    free (dir);
	    free (comp);
	}
    }

    /* At this point we have looped over all named arguments and built
       a coupla lists.  Now we unroll the lists, setting up and
       calling do_recursion. */

    frame.fileproc = fileproc;
    frame.filesdoneproc = filesdoneproc;
    frame.direntproc = direntproc;
    frame.dirleaveproc = dirleaveproc;
    frame.flags = flags;
    frame.which = which;
    frame.aflag = aflag;
    frame.readlock = readlock;
    frame.dosrcs = dosrcs;
    err += walklist (files_by_dir, unroll_files_proc, (void *) &frame);

    /* then do_recursion on the dirlist. */
    if (dirlist != NULL)
	err += do_recursion (frame.fileproc, frame.filesdoneproc,
			     frame.direntproc, frame.dirleaveproc,
			     frame.flags, frame.which, frame.aflag,
			     frame.readlock, frame.dosrcs);


    return (err);
}

/*
 * Implement the recursive policies on the local directory.  This may be
 * called directly, or may be called by start_recursion
 */
int
do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
	      xflags, xwhich, xaflag, xreadlock, xdosrcs)
    int (*xfileproc) ();
    int (*xfilesdoneproc) ();
    Dtype (*xdirentproc) ();
    int (*xdirleaveproc) ();
    Dtype xflags;
    int xwhich;
    int xaflag;
    int xreadlock;
    int xdosrcs;
{
    int err = 0;
    int dodoneproc = 1;
    char *srepository;

    /* do nothing if told */
    if (xflags == R_SKIP_ALL)
	return (0);

    /* set up the static vars */
    fileproc = xfileproc;
    filesdoneproc = xfilesdoneproc;
    direntproc = xdirentproc;
    dirleaveproc = xdirleaveproc;
    flags = xflags;
    which = xwhich;
    aflag = xaflag;
    readlock = noexec ? 0 : xreadlock;
    dosrcs = xdosrcs;

    /*
     * Fill in repository with the current repository
     */
    if (which & W_LOCAL)
    {
	if (isdir (CVSADM))
	    repository = Name_Repository ((char *) NULL, update_dir);
	else
	    repository = NULL;
    }
    else
    {
	repository = xmalloc (PATH_MAX);
	(void) getwd (repository);
    }
    srepository = repository;		/* remember what to free */

    /*
     * The filesdoneproc needs to be called for each directory where files
     * processed, or each directory that is processed by a call where no
     * directories were passed in.  In fact, the only time we don't want to
     * call back the filesdoneproc is when we are processing directories that
     * were passed in on the command line (or in the special case of `.' when
     * we were called with no args
     */
    if (dirlist != NULL && filelist == NULL)
	dodoneproc = 0;

    /*
     * If filelist or dirlist is already set, we don't look again. Otherwise,
     * find the files and directories
     */
    if (filelist == NULL && dirlist == NULL)
    {
	/* both lists were NULL, so start from scratch */
	if (fileproc != NULL && flags != R_SKIP_FILES)
	{
	    int lwhich = which;

	    /* be sure to look in the attic if we have sticky tags/date */
	    if ((lwhich & W_ATTIC) == 0)
		if (isreadable (CVSADM_TAG))
		    lwhich |= W_ATTIC;

	    /* find the files and fill in entries if appropriate */
	    filelist = Find_Names (repository, lwhich, aflag, &entries);
	}

	/* find sub-directories if we will recurse */
	if (flags != R_SKIP_DIRS)
	    dirlist = Find_Dirs (repository, which);
    }
    else
    {
	/* something was passed on the command line */
	if (filelist != NULL && fileproc != NULL)
	{
	    /* we will process files, so pre-parse entries */
	    if (which & W_LOCAL)
		entries = Entries_Open (aflag);
	}
    }

    /* process the files (if any) */
    if (filelist != NULL)
    {
	/* read lock it if necessary */
	if (readlock && repository && Reader_Lock (repository) != 0)
	    error (1, 0, "read lock failed - giving up");

	/* pre-parse the source files */
	if (dosrcs && repository)
	    srcfiles = RCS_parsefiles (filelist, repository);
	else
	    srcfiles = (List *) NULL;

	/* process the files */
	err += walklist (filelist, do_file_proc, NULL);

	/* unlock it */
	if (readlock)
	    Lock_Cleanup ();

	/* clean up */
	dellist (&filelist);
	dellist (&srcfiles);
	Entries_Close (entries);
	entries = NULL;
    }

    /* call-back files done proc (if any) */
    if (dodoneproc && filesdoneproc != NULL)
	err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");

    /* process the directories (if necessary) */
    if (dirlist != NULL)
	err += walklist (dirlist, do_dir_proc, NULL);
#ifdef notdef
    else if (dirleaveproc != NULL)
	err += dirleaveproc(".", err, ".");
#endif
    dellist (&dirlist);

    /* free the saved copy of the pointer if necessary */
    if (srepository)
    {
	free (srepository);
	repository = (char *) NULL;
    }

    return (err);
}

/*
 * Process each of the files in the list with the callback proc
 */
static int
do_file_proc (p, closure)
    Node *p;
    void *closure;
{
    if (fileproc != NULL)
	return (fileproc (p->key, update_dir, repository, entries, srcfiles));
    else
	return (0);
}

/*
 * Process each of the directories in the list (recursing as we go)
 */
static int
do_dir_proc (p, closure)
    Node *p;
    void *closure;
{
    char *dir = p->key;
    char newrepos[PATH_MAX];
    List *sdirlist;
    char *srepository;
    char *cp;
    Dtype dir_return = R_PROCESS;
    int stripped_dot = 0;
    int err = 0;
    struct saved_cwd cwd;

    /* 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[0] = '\0';
	else
	    (void) sprintf (newrepos, "%s/%s", repository, dir);
    }
    else
    {
	if (update_dir[0] == '\0')
	    (void) strcpy (update_dir, dir);

	if (repository == NULL)
	    newrepos[0] = '\0';
	else
	    (void) strcpy (newrepos, repository);
    }

    /* call-back dir entry proc (if any) */
    if (direntproc != NULL)
	dir_return = direntproc (dir, newrepos, update_dir);

    /* only process the dir if the return code was 0 */
    if (dir_return != R_SKIP_ALL)
    {
	/* save our current directory and static vars */
        if (save_cwd (&cwd))
	    exit (1);
	sdirlist = dirlist;
	srepository = repository;
	dirlist = NULL;

	/* cd to the sub-directory */
	if (chdir (dir) < 0)
	    error (1, errno, "could not chdir to %s", dir);

	/* honor the global SKIP_DIRS (a.k.a. local) */
	if (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 */
	err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
			    dir_return, which, aflag, readlock, dosrcs);

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

	/* call-back dir leave proc (if any) */
	if (dirleaveproc != NULL)
	    err = dirleaveproc (dir, err, update_dir);

	/* get back to where we started and restore state vars */
	if (restore_cwd (&cwd, NULL))
	    exit (1);
	free_cwd (&cwd);
	dirlist = sdirlist;
	repository = srepository;
    }

    /* put back update_dir */
    if ((cp = strrchr (update_dir, '/')) != NULL)
	*cp = '\0';
    else
	update_dir[0] = '\0';

    return (err);
}

/*
 * Add a node to a list allocating the list if necessary.
 */
static void
addlist (listp, key)
    List **listp;
    char *key;
{
    Node *p;

    if (*listp == NULL)
	*listp = getlist ();
    p = getnode ();
    p->type = FILES;
    p->key = xstrdup (key);
    if (addnode (*listp, p) != 0)
	freenode (p);
}

static void
addfile (listp, dir, file)
    List **listp;
    char *dir;
    char *file;
{
    Node *n;

    /* add this dir. */
    addlist (listp, dir);

    n = findnode (*listp, dir);
    if (n == NULL)
    {
	error (1, 0, "can't find recently added dir node `%s' in start_recursion.",
	       dir);
    }

    n->type = DIRS;
    addlist ((List **) &n->data, file);
    return;
}

static int
unroll_files_proc (p, closure)
    Node *p;
    void *closure;
{
    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;

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

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

    if (strcmp(p->key, ".") != 0)
    {
        if (save_cwd (&cwd))
	    exit (1);
	if (chdir (p->key) < 0)
	    error (1, errno, "could not chdir to %s", p->key);

	save_update_dir = xstrdup (update_dir);

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

	(void) strcat (update_dir, p->key);
    }

    err += do_recursion (frame->fileproc, frame->filesdoneproc,
			 frame->direntproc, frame->dirleaveproc,
			 frame->flags, frame->which, frame->aflag,
			 frame->readlock, frame->dosrcs);

    if (save_update_dir != NULL)
    {
	(void) strcpy (update_dir, save_update_dir);
	free (save_update_dir);

	if (restore_cwd (&cwd, NULL))
	    exit (1);
	free_cwd (&cwd);
    }

    dirlist = save_dirlist;
    filelist = NULL;
    return(err);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.