2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
4 * You may distribute under the terms of the GNU General Public License as
5 * specified in the README file that comes with the CVS 1.3 kit.
7 * General recursion handler
14 static char rcsid[] = "@(#)recurse.c 1.22 92/04/10";
18 static int do_dir_proc (Node * p);
19 static int do_file_proc (Node * p);
20 static void addlist (List ** listp, char *key);
22 static int do_file_proc ();
23 static int do_dir_proc ();
24 static void addlist ();
29 * Local static versions eliminates the need for globals
31 static int (*fileproc) ();
32 static int (*filesdoneproc) ();
33 static Dtype (*direntproc) ();
34 static int (*dirleaveproc) ();
40 static char update_dir[PATH_MAX];
41 static char *repository = NULL;
42 static List *entries = NULL;
43 static List *srcfiles = NULL;
44 static List *filelist = NULL;
45 static List *dirlist = NULL;
48 * Called to start a recursive command Command line arguments are processed
49 * if present, otherwise the local directory is processed.
52 start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
53 argc, argv, local, which, aflag, readlock,
54 update_preload, dosrcs)
56 int (*filesdoneproc) ();
57 Dtype (*direntproc) ();
58 int (*dirleaveproc) ();
71 if (update_preload == NULL)
74 (void) strcpy (update_dir, update_preload);
81 /* clean up from any previous calls to start_recursion */
85 repository = (char *) NULL;
100 * There were no arguments, so we'll probably just recurse. The
101 * exception to the rule is when we are called from a directory
102 * without any CVS administration files. That has always meant to
103 * process each of the sub-directories, so we pretend like we were
104 * called with the list of sub-dirs of the current dir as args
106 if ((which & W_LOCAL) && !isdir (CVSADM) && !isdir (OCVSADM))
107 dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
109 addlist (&dirlist, ".");
111 err += do_recursion (fileproc, filesdoneproc, direntproc,
112 dirleaveproc, flags, which, aflag,
119 * There were arguments, so we have to handle them by hand. To do
120 * that, we set up the filelist and dirlist with the arguments and
121 * call do_recursion. do_recursion recognizes the fact that the
122 * lists are non-null when it starts and doesn't update them
125 /* look for args with /-s in them */
126 for (i = 0; i < argc; i++)
127 if (index (argv[i], '/') != NULL)
130 /* if we didn't find any hard one's, do it the easy way */
133 /* set up the lists */
134 for (i = 0; i < argc; i++)
137 addlist (&dirlist, argv[i]);
140 if (isdir (CVSADM) || isdir (OCVSADM))
145 repos = Name_Repository ((char *) NULL, update_dir);
146 (void) sprintf (tmp, "%s/%s", repos, argv[i]);
148 addlist (&dirlist, argv[i]);
150 addlist (&filelist, argv[i]);
154 addlist (&filelist, argv[i]);
158 /* we aren't recursive if no directories were specified */
162 /* process the lists */
163 err += do_recursion (fileproc, filesdoneproc, direntproc,
164 dirleaveproc, flags, which, aflag,
167 /* otherwise - do it the hard way */
171 char *dir = (char *) NULL;
172 char *comp = (char *) NULL;
173 char *oldupdate = (char *) NULL;
174 char savewd[PATH_MAX];
176 if (getwd (savewd) == NULL)
177 error (1, 0, "could not get working directory: %s", savewd);
179 for (i = 0; i < argc; i++)
181 /* split the arg into the dir and component parts */
182 dir = xstrdup (argv[i]);
183 if ((cp = rindex (dir, '/')) != NULL)
186 comp = xstrdup (cp + 1);
187 oldupdate = xstrdup (update_dir);
188 if (update_dir[0] != '\0')
189 (void) strcat (update_dir, "/");
190 (void) strcat (update_dir, dir);
194 comp = xstrdup (dir);
200 /* chdir to the appropriate place if necessary */
201 if (dir && chdir (dir) < 0)
202 error (1, errno, "could not chdir to %s", dir);
204 /* set up the list */
206 addlist (&dirlist, comp);
209 if (isdir (CVSADM) || isdir (OCVSADM))
214 repos = Name_Repository ((char *) NULL, update_dir);
215 (void) sprintf (tmp, "%s/%s", repos, comp);
217 addlist (&dirlist, comp);
219 addlist (&filelist, comp);
223 addlist (&filelist, comp);
226 /* do the recursion */
227 err += do_recursion (fileproc, filesdoneproc, direntproc,
228 dirleaveproc, flags, which,
229 aflag, readlock, dosrcs);
231 /* chdir back and fix update_dir if necessary */
232 if (dir && chdir (savewd) < 0)
233 error (1, errno, "could not chdir to %s", dir);
236 (void) strcpy (update_dir, oldupdate);
251 * Implement the recursive policies on the local directory. This may be
252 * called directly, or may be called by start_recursion
255 do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
256 xflags, xwhich, xaflag, xreadlock, xdosrcs)
258 int (*xfilesdoneproc) ();
259 Dtype (*xdirentproc) ();
260 int (*xdirleaveproc) ();
271 /* do nothing if told */
272 if (xflags == R_SKIP_ALL)
275 /* set up the static vars */
276 fileproc = xfileproc;
277 filesdoneproc = xfilesdoneproc;
278 direntproc = xdirentproc;
279 dirleaveproc = xdirleaveproc;
283 readlock = noexec ? 0 : xreadlock;
287 * Fill in repository with the current repository
291 if (isdir (CVSADM) || isdir (OCVSADM))
292 repository = Name_Repository ((char *) NULL, update_dir);
298 repository = xmalloc (PATH_MAX);
299 (void) getwd (repository);
301 srepository = repository; /* remember what to free */
304 * The filesdoneproc needs to be called for each directory where files
305 * processed, or each directory that is processed by a call where no
306 * directories were passed in. In fact, the only time we don't want to
307 * call back the filesdoneproc is when we are processing directories that
308 * were passed in on the command line (or in the special case of `.' when
309 * we were called with no args
311 if (dirlist != NULL && filelist == NULL)
315 * If filelist or dirlist is already set, we don't look again. Otherwise,
316 * find the files and directories
318 if (filelist == NULL && dirlist == NULL)
320 /* both lists were NULL, so start from scratch */
321 if (fileproc != NULL && flags != R_SKIP_FILES)
325 /* be sure to look in the attic if we have sticky tags/date */
326 if ((lwhich & W_ATTIC) == 0)
327 if (isreadable (CVSADM_TAG))
330 /* find the files and fill in entries if appropriate */
331 filelist = Find_Names (repository, lwhich, aflag, &entries);
334 /* find sub-directories if we will recurse */
335 if (flags != R_SKIP_DIRS)
336 dirlist = Find_Dirs (repository, which);
340 /* something was passed on the command line */
341 if (filelist != NULL && fileproc != NULL)
343 /* we will process files, so pre-parse entries */
345 entries = ParseEntries (aflag);
349 /* process the files (if any) */
350 if (filelist != NULL)
352 /* read lock it if necessary */
353 if (readlock && repository && Reader_Lock (repository) != 0)
354 error (1, 0, "read lock failed - giving up");
356 /* pre-parse the source files */
357 if (dosrcs && repository)
358 srcfiles = RCS_parsefiles (filelist, repository);
360 srcfiles = (List *) NULL;
362 /* process the files */
363 err += walklist (filelist, do_file_proc);
375 /* call-back files done proc (if any) */
376 if (dodoneproc && filesdoneproc != NULL)
377 err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");
379 /* process the directories (if necessary) */
381 err += walklist (dirlist, do_dir_proc);
383 else if (dirleaveproc != NULL)
384 err += dirleaveproc(".", err, ".");
388 /* free the saved copy of the pointer if necessary */
391 (void) free (srepository);
392 repository = (char *) NULL;
399 * Process each of the files in the list with the callback proc
405 if (fileproc != NULL)
406 return (fileproc (p->key, update_dir, repository, entries, srcfiles));
412 * Process each of the directories in the list (recursing as we go)
419 char savewd[PATH_MAX];
420 char newrepos[PATH_MAX];
424 Dtype dir_return = R_PROCESS;
425 int stripped_dot = 0;
428 /* set up update_dir - skip dots if not at start */
429 if (strcmp (dir, ".") != 0)
431 if (update_dir[0] != '\0')
433 (void) strcat (update_dir, "/");
434 (void) strcat (update_dir, dir);
437 (void) strcpy (update_dir, dir);
440 * Here we need a plausible repository name for the sub-directory. We
441 * create one by concatenating the new directory name onto the
442 * previous repository name. The only case where the name should be
443 * used is in the case where we are creating a new sub-directory for
444 * update -d and in that case the generated name will be correct.
446 if (repository == NULL)
449 (void) sprintf (newrepos, "%s/%s", repository, dir);
453 if (update_dir[0] == '\0')
454 (void) strcpy (update_dir, dir);
456 if (repository == NULL)
459 (void) strcpy (newrepos, repository);
462 /* call-back dir entry proc (if any) */
463 if (direntproc != NULL)
464 dir_return = direntproc (dir, newrepos, update_dir);
466 /* only process the dir if the return code was 0 */
467 if (dir_return != R_SKIP_ALL)
469 /* save our current directory and static vars */
470 if (getwd (savewd) == NULL)
471 error (1, 0, "could not get working directory: %s", savewd);
473 srepository = repository;
476 /* cd to the sub-directory */
478 error (1, errno, "could not chdir to %s", dir);
480 /* honor the global SKIP_DIRS (a.k.a. local) */
481 if (flags == R_SKIP_DIRS)
482 dir_return = R_SKIP_DIRS;
484 /* remember if the `.' will be stripped for subsequent dirs */
485 if (strcmp (update_dir, ".") == 0)
487 update_dir[0] = '\0';
491 /* make the recursive call */
492 err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
493 dir_return, which, aflag, readlock, dosrcs);
495 /* put the `.' back if necessary */
497 (void) strcpy (update_dir, ".");
499 /* call-back dir leave proc (if any) */
500 if (dirleaveproc != NULL)
501 err = dirleaveproc (dir, err, update_dir);
503 /* get back to where we started and restore state vars */
504 if (chdir (savewd) < 0)
505 error (1, errno, "could not chdir to %s", savewd);
507 repository = srepository;
510 /* put back update_dir */
511 if ((cp = rindex (update_dir, '/')) != NULL)
514 update_dir[0] = '\0';
520 * Add a node to a list allocating the list if necessary
533 p->key = xstrdup (key);
534 (void) addnode (*listp, p);