386BSD 0.1 development
[unix-history] / usr / othersrc / public / cvs-1.3 / src / recurse.c
/*
* 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.3 kit.
*
* General recursion handler
*
*/
#include "cvs.h"
#ifndef lint
static char rcsid[] = "@(#)recurse.c 1.22 92/04/10";
#endif
#if __STDC__
static int do_dir_proc (Node * p);
static int do_file_proc (Node * p);
static void addlist (List ** listp, char *key);
#else
static int do_file_proc ();
static int do_dir_proc ();
static void addlist ();
#endif /* __STDC__ */
/*
* 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;
static List *dirlist = NULL;
/*
* Called to start a recursive command Command line arguments are processed
* if present, otherwise the local directory is processed.
*/
int
start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
argc, argv, local, which, aflag, readlock,
update_preload, dosrcs)
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 i, err = 0;
Dtype flags;
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)
dellist (&entries);
if (srcfiles)
dellist (&srcfiles);
if (filelist)
dellist (&filelist);
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) && !isdir (OCVSADM))
dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
else
addlist (&dirlist, ".");
err += do_recursion (fileproc, filesdoneproc, direntproc,
dirleaveproc, flags, which, aflag,
readlock, dosrcs);
}
else
{
/*
* 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
*/
/* look for args with /-s in them */
for (i = 0; i < argc; i++)
if (index (argv[i], '/') != NULL)
break;
/* if we didn't find any hard one's, do it the easy way */
if (i == argc)
{
/* set up the lists */
for (i = 0; i < argc; i++)
{
if (isdir (argv[i]))
addlist (&dirlist, argv[i]);
else
{
if (isdir (CVSADM) || isdir (OCVSADM))
{
char *repos;
char tmp[PATH_MAX];
repos = Name_Repository ((char *) NULL, update_dir);
(void) sprintf (tmp, "%s/%s", repos, argv[i]);
if (isdir (tmp))
addlist (&dirlist, argv[i]);
else
addlist (&filelist, argv[i]);
free (repos);
}
else
addlist (&filelist, argv[i]);
}
}
/* we aren't recursive if no directories were specified */
if (dirlist == NULL)
local = 1;
/* process the lists */
err += do_recursion (fileproc, filesdoneproc, direntproc,
dirleaveproc, flags, which, aflag,
readlock, dosrcs);
}
/* otherwise - do it the hard way */
else
{
char *cp;
char *dir = (char *) NULL;
char *comp = (char *) NULL;
char *oldupdate = (char *) NULL;
char savewd[PATH_MAX];
if (getwd (savewd) == NULL)
error (1, 0, "could not get working directory: %s", savewd);
for (i = 0; i < argc; i++)
{
/* split the arg into the dir and component parts */
dir = xstrdup (argv[i]);
if ((cp = rindex (dir, '/')) != NULL)
{
*cp = '\0';
comp = xstrdup (cp + 1);
oldupdate = xstrdup (update_dir);
if (update_dir[0] != '\0')
(void) strcat (update_dir, "/");
(void) strcat (update_dir, dir);
}
else
{
comp = xstrdup (dir);
if (dir)
free (dir);
dir = (char *) NULL;
}
/* chdir to the appropriate place if necessary */
if (dir && chdir (dir) < 0)
error (1, errno, "could not chdir to %s", dir);
/* set up the list */
if (isdir (comp))
addlist (&dirlist, comp);
else
{
if (isdir (CVSADM) || isdir (OCVSADM))
{
char *repos;
char tmp[PATH_MAX];
repos = Name_Repository ((char *) NULL, update_dir);
(void) sprintf (tmp, "%s/%s", repos, comp);
if (isdir (tmp))
addlist (&dirlist, comp);
else
addlist (&filelist, comp);
free (repos);
}
else
addlist (&filelist, comp);
}
/* do the recursion */
err += do_recursion (fileproc, filesdoneproc, direntproc,
dirleaveproc, flags, which,
aflag, readlock, dosrcs);
/* chdir back and fix update_dir if necessary */
if (dir && chdir (savewd) < 0)
error (1, errno, "could not chdir to %s", dir);
if (oldupdate)
{
(void) strcpy (update_dir, oldupdate);
free (oldupdate);
}
}
if (dir)
free (dir);
if (comp)
free (comp);
}
}
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) || isdir (OCVSADM))
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 = ParseEntries (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);
/* unlock it */
if (readlock)
Lock_Cleanup ();
/* clean up */
dellist (&filelist);
dellist (&srcfiles);
dellist (&entries);
}
/* 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);
#ifdef notdef
else if (dirleaveproc != NULL)
err += dirleaveproc(".", err, ".");
#endif
dellist (&dirlist);
/* free the saved copy of the pointer if necessary */
if (srepository)
{
(void) 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)
Node *p;
{
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)
Node *p;
{
char *dir = p->key;
char savewd[PATH_MAX];
char newrepos[PATH_MAX];
List *sdirlist;
char *srepository;
char *cp;
Dtype dir_return = R_PROCESS;
int stripped_dot = 0;
int err = 0;
/* 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 (getwd (savewd) == NULL)
error (1, 0, "could not get working directory: %s", savewd);
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 (chdir (savewd) < 0)
error (1, errno, "could not chdir to %s", savewd);
dirlist = sdirlist;
repository = srepository;
}
/* put back update_dir */
if ((cp = rindex (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);
(void) addnode (*listp, p);
}