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.