summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/recurse.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
committerpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
commit8982e501c77217c860f79bba431f46a62b607a21 (patch)
tree70187fdf5be4cbefd0baf46bddac7e5e32c13c24 /contrib/cvs/src/recurse.c
parent01ee40fd6a76f6ff7ef247fc1b2cf6e337f216c5 (diff)
downloadFreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.zip
FreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.tar.gz
Import of slightly trimmed cvs-1.8 distribution. Generated files
and non-unix code has been left out.
Diffstat (limited to 'contrib/cvs/src/recurse.c')
-rw-r--r--contrib/cvs/src/recurse.c714
1 files changed, 714 insertions, 0 deletions
diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c
new file mode 100644
index 0000000..ec51a98
--- /dev/null
+++ b/contrib/cvs/src/recurse.c
@@ -0,0 +1,714 @@
+/*
+ * 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 "savecwd.h"
+#include "fileattr.h"
+#include "edit.h"
+
+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 FILEPROC fileproc;
+static FILESDONEPROC filesdoneproc;
+static DIRENTPROC direntproc;
+static DIRLEAVEPROC 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 *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 {
+ FILEPROC fileproc;
+ FILESDONEPROC filesdoneproc;
+ DIRENTPROC direntproc;
+ DIRLEAVEPROC 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)
+ FILEPROC fileproc;
+ FILESDONEPROC filesdoneproc;
+ DIRENTPROC direntproc;
+ DIRLEAVEPROC 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;
+
+ expand_wild (argc, argv, &argc, &argv);
+
+ 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 (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_Directories ((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;
+
+ /* Now break out argv[i] into directory part (DIR) and file part (COMP).
+ DIR and COMP will each point to a newly malloc'd string. */
+ dir = xstrdup (argv[i]);
+ comp = last_component (dir);
+ if (comp == dir)
+ {
+ /* no dir component. What we have is an implied "./" */
+ dir = xstrdup(".");
+ }
+ else
+ {
+ char *p = comp;
+
+ p[-1] = '\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);
+ free (repos);
+
+ 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);
+
+ /* Free the data which expand_wild allocated. */
+ for (i = 0; i < argc; ++i)
+ free (argv[i]);
+ free (argv);
+
+ 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)
+ FILEPROC xfileproc;
+ FILESDONEPROC xfilesdoneproc;
+ DIRENTPROC xdirentproc;
+ DIRLEAVEPROC xdirleaveproc;
+ Dtype xflags;
+ int xwhich;
+ int xaflag;
+ int xreadlock;
+ int xdosrcs;
+{
+ int err = 0;
+ int dodoneproc = 1;
+ char *srepository;
+ List *entries = NULL;
+
+ /* 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;
+
+ /* The fact that locks are not active here is what makes us fail to have
+ the
+
+ If someone commits some changes in one cvs command,
+ then an update by someone else will either get all the
+ changes, or none of them.
+
+ property (see node Concurrency in cvs.texinfo).
+
+ The most straightforward fix would just to readlock the whole
+ tree before starting an update, but that means that if a commit
+ gets blocked on a big update, it might need to wait a *long*
+ time.
+
+ A more adequate fix would be a two-pass design for update,
+ checkout, etc. The first pass would go through the repository,
+ with the whole tree readlocked, noting what versions of each
+ file we want to get. The second pass would release all locks
+ (except perhaps short-term locks on one file at a
+ time--although I think RCS already deals with this) and
+ actually get the files, specifying the particular versions it wants.
+
+ This could be sped up by separating out the data needed for the
+ first pass into a separate file(s)--for example a file
+ attribute for each file whose value contains the head revision
+ for each branch. The structure should be designed so that
+ commit can relatively quickly update the information for a
+ single file or a handful of files (file attributes, as
+ implemented in Jan 96, are probably acceptable; improvements
+ would be possible such as branch attributes which are in
+ separate files for each branch). */
+
+#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL)
+ /*
+ * Now would be a good time to check to see if we need to stop
+ * generating data, to give the buffers a chance to drain to the
+ * remote client. We should not have locks active at this point.
+ */
+ if (server_active
+ /* If there are writelocks around, we cannot pause here. */
+ && (readlock || noexec))
+ server_pause_check();
+#endif
+
+ /*
+ * 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 */
+
+ fileattr_startdir (repository);
+
+ /*
+ * 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_Directories (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 && fileproc)
+ {
+ struct file_info finfo_struct;
+
+ /* read lock it if necessary */
+ if (readlock && repository && Reader_Lock (repository) != 0)
+ error (1, 0, "read lock failed - giving up");
+
+#ifdef CLIENT_SUPPORT
+ /* For the server, we handle notifications in a completely different
+ place (server_notify). For local, we can't do them here--we don't
+ have writelocks in place, and there is no way to get writelocks
+ here. */
+ if (client_active)
+ notify_check (repository, update_dir);
+#endif /* CLIENT_SUPPORT */
+
+ finfo_struct.repository = repository;
+ finfo_struct.update_dir = update_dir;
+ finfo_struct.entries = entries;
+ /* do_file_proc will fill in finfo_struct.file. */
+
+ /* process the files */
+ err += walklist (filelist, do_file_proc, &finfo_struct);
+
+ /* unlock it */
+ if (readlock)
+ Lock_Cleanup ();
+
+ /* clean up */
+ dellist (&filelist);
+ }
+
+ if (entries)
+ {
+ 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 : ".");
+
+ fileattr_write ();
+ fileattr_free ();
+
+ /* 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;
+{
+ struct file_info *finfo = (struct file_info *)closure;
+ int ret;
+
+ finfo->file = p->key;
+ finfo->fullname = xmalloc (strlen (finfo->file)
+ + strlen (finfo->update_dir)
+ + 2);
+ finfo->fullname[0] = '\0';
+ if (finfo->update_dir[0] != '\0')
+ {
+ strcat (finfo->fullname, finfo->update_dir);
+ strcat (finfo->fullname, "/");
+ }
+ strcat (finfo->fullname, finfo->file);
+
+ if (dosrcs && repository)
+ finfo->rcs = RCS_parse (finfo->file, repository);
+ else
+ finfo->rcs = (RCSNode *) NULL;
+ ret = fileproc (finfo);
+
+ freercsnode(&finfo->rcs);
+ free (finfo->fullname);
+
+ return (ret);
+}
+
+/*
+ * 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 (EXIT_FAILURE);
+ 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 (EXIT_FAILURE);
+ free_cwd (&cwd);
+ dirlist = sdirlist;
+ repository = srepository;
+ }
+
+ /* put back update_dir */
+ cp = last_component (update_dir);
+ if (cp > update_dir)
+ cp[-1] = '\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 (EXIT_FAILURE);
+ 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 (EXIT_FAILURE);
+ free_cwd (&cwd);
+ }
+
+ dirlist = save_dirlist;
+ filelist = NULL;
+ return(err);
+}
OpenPOWER on IntegriCloud