recurse.c revision 1.1.1.1
1/*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3 *
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 *                                  and others.
6 *
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
12 *
13 * General recursion handler
14 *
15 */
16
17#include "cvs.h"
18#include "save-cwd.h"
19#include "fileattr.h"
20#include "edit.h"
21
22static int do_dir_proc (Node * p, void *closure);
23static int do_file_proc (Node * p, void *closure);
24static void addlist (List ** listp, char *key);
25static int unroll_files_proc (Node *p, void *closure);
26static void addfile (List **listp, char *dir, char *file);
27
28static char *update_dir;
29static char *repository = NULL;
30static List *filelist = NULL; /* holds list of files on which to operate */
31static List *dirlist = NULL; /* holds list of directories on which to operate */
32
33struct recursion_frame {
34    FILEPROC fileproc;
35    FILESDONEPROC filesdoneproc;
36    DIRENTPROC direntproc;
37    DIRLEAVEPROC dirleaveproc;
38    void *callerdat;
39    Dtype flags;
40    int which;
41    int aflag;
42    int locktype;
43    int dosrcs;
44    char *repository;			/* Keep track of repository for rtag */
45};
46
47static int do_recursion (struct recursion_frame *frame);
48
49/* I am half tempted to shove a struct file_info * into the struct
50   recursion_frame (but then we would need to modify or create a
51   recursion_frame for each file), or shove a struct recursion_frame *
52   into the struct file_info (more tempting, although it isn't completely
53   clear that the struct file_info should contain info about recursion
54   processor internals).  So instead use this struct.  */
55
56struct frame_and_file {
57    struct recursion_frame *frame;
58    struct file_info *finfo;
59};
60
61/* Similarly, we need to pass the entries list to do_dir_proc.  */
62
63struct frame_and_entries {
64    struct recursion_frame *frame;
65    List *entries;
66};
67
68
69/* Start a recursive command.
70 *
71 * INPUT
72 *
73 *   fileproc
74 *     Function called with each file as an argument.
75 *
76 *   filesdoneproc
77 *     Function called after all the files in a directory have been processed,
78 *     before subdirectories have been processed.
79 *
80 *   direntproc
81 *     Function called immediately upon entering a directory, before processing
82 *     any files or subdirectories.
83 *
84 *   dirleaveproc
85 *     Function called upon finishing a directory, immediately before leaving
86 *     it and returning control to the function processing the parent
87 *     directory.
88 *
89 *   callerdat
90 *   	This void * is passed to the functions above.
91 *
92 *   argc, argv
93 *     The files on which to operate, interpreted as command line arguments.
94 *     In the special case of no arguments, defaults to operating on the
95 *     current directory (`.').
96 *
97 *   local
98 *
99 *   which
100 *     specifies the kind of recursion.  There are several cases:
101 *
102 *       1.  W_LOCAL is not set but W_REPOS or W_ATTIC is.  The current
103 *       directory when we are called must be the repository and
104 *       recursion proceeds according to what exists in the repository.
105 *
106 *       2a.  W_LOCAL is set but W_REPOS and W_ATTIC are not.  The
107 *       current directory when we are called must be the working
108 *       directory.  Recursion proceeds according to what exists in the
109 *       working directory, never (I think) consulting any part of the
110 *       repository which does not correspond to the working directory
111 *       ("correspond" == Name_Repository).
112 *
113 *       2b.  W_LOCAL is set and so is W_REPOS or W_ATTIC.  This is the
114 *       weird one.  The current directory when we are called must be
115 *       the working directory.  We recurse through working directories,
116 *       but we recurse into a directory if it is exists in the working
117 *       directory *or* it exists in the repository.  If a directory
118 *       does not exist in the working directory, the direntproc must
119 *       either tell us to skip it (R_SKIP_ALL), or must create it (I
120 *       think those are the only two cases).
121 *
122 *   aflag
123 *   locktype
124 *   update_preload
125 *   dosrcs
126 *
127 *   repository_in
128 *     keeps track of the repository string.  This is only for the remote mode,
129 *     specifically, r* commands (rtag, rdiff, co, ...) where xgetcwd() was used
130 *     to locate the repository.  Things would break when xgetcwd() was used
131 *     with a symlinked repository because xgetcwd() would return the true path
132 *     and in some cases this would cause the path to be printed as other than
133 *     the user specified in error messages and in other cases some of CVS's
134 *     security assertions would fail.
135 *
136 * GLOBALS
137 *   ???
138 *
139 * OUTPUT
140 *
141 *  callerdat can be modified by the FILEPROC, FILESDONEPROC, DIRENTPROC, and
142 *  DIRLEAVEPROC.
143 *
144 * RETURNS
145 *   A count of errors counted by walking the argument list with
146 *   unroll_files_proc() and do_recursion().
147 *
148 * ERRORS
149 *   Fatal errors occur:
150 *     1.  when there were no arguments and the current directory
151 *         does not contain CVS metadata.
152 *     2.  when all but the last path element from an argument from ARGV cannot
153 *         be found to be a local directory.
154 */
155int
156start_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc,
157                 DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc,
158                 void *callerdat, int argc, char **argv, int local,
159                 int which, int aflag, int locktype,
160                 char *update_preload, int dosrcs, char *repository_in)
161{
162    int i, err = 0;
163#ifdef CLIENT_SUPPORT
164    List *args_to_send_when_finished = NULL;
165#endif
166    List *files_by_dir = NULL;
167    struct recursion_frame frame;
168
169#ifdef HAVE_PRINTF_PTR
170    TRACE ( TRACE_FLOW,
171	    "start_recursion ( fileproc=%p, filesdoneproc=%p,\n"
172       "                       direntproc=%p, dirleavproc=%p,\n"
173       "                       callerdat=%p, argc=%d, argv=%p,\n"
174       "                       local=%d, which=%d, aflag=%d,\n"
175       "                       locktype=%d, update_preload=%s\n"
176       "                       dosrcs=%d, repository_in=%s )",
177	       (void *) fileproc, (void *) filesdoneproc,
178	       (void *) direntproc, (void *) dirleaveproc,
179	       (void *) callerdat, argc, (void *) argv,
180	       local, which, aflag, locktype,
181	       update_preload ? update_preload : "(null)", dosrcs,
182	       repository_in ? repository_in : "(null)");
183#else
184    TRACE ( TRACE_FLOW,
185	    "start_recursion ( fileproc=%lx, filesdoneproc=%lx,\n"
186       "                       direntproc=%lx, dirleavproc=%lx,\n"
187       "                       callerdat=%lx, argc=%d, argv=%lx,\n"
188       "                       local=%d, which=%d, aflag=%d,\n"
189       "                       locktype=%d, update_preload=%s\n"
190       "                       dosrcs=%d, repository_in=%s )",
191	       (unsigned long) fileproc, (unsigned long) filesdoneproc,
192	       (unsigned long) direntproc, (unsigned long) dirleaveproc,
193	       (unsigned long) callerdat, argc, (unsigned long) argv,
194	       local, which, aflag, locktype,
195	       update_preload ? update_preload : "(null)", dosrcs,
196	       repository_in ? repository_in : "(null)");
197#endif
198
199    frame.fileproc = fileproc;
200    frame.filesdoneproc = filesdoneproc;
201    frame.direntproc = direntproc;
202    frame.dirleaveproc = dirleaveproc;
203    frame.callerdat = callerdat;
204    frame.flags = local ? R_SKIP_DIRS : R_PROCESS;
205    frame.which = which;
206    frame.aflag = aflag;
207    frame.locktype = locktype;
208    frame.dosrcs = dosrcs;
209
210    /* If our repository_in has a trailing "/.", remove it before storing it
211     * for do_recursion().
212     *
213     * FIXME: This is somewhat of a hack in the sense that many of our callers
214     * painstakingly compute and add the trailing '.' we now remove.
215     */
216    while (repository_in && strlen (repository_in) >= 2
217           && repository_in[strlen (repository_in) - 2] == '/'
218           && repository_in[strlen (repository_in) - 1] == '.')
219    {
220	/* Beware the case where the string is exactly "/." or "//.".
221	 * Paths with a leading "//" are special on some early UNIXes.
222	 */
223	if (strlen (repository_in) == 2 || strlen (repository_in) == 3)
224	    repository_in[strlen (repository_in) - 1] = '\0';
225	else
226	    repository_in[strlen (repository_in) - 2] = '\0';
227    }
228    frame.repository = repository_in;
229
230    expand_wild (argc, argv, &argc, &argv);
231
232    if (update_preload == NULL)
233	update_dir = xstrdup ("");
234    else
235	update_dir = xstrdup (update_preload);
236
237    /* clean up from any previous calls to start_recursion */
238    if (repository)
239    {
240	free (repository);
241	repository = NULL;
242    }
243    if (filelist)
244	dellist (&filelist); /* FIXME-krp: no longer correct. */
245    if (dirlist)
246	dellist (&dirlist);
247
248#ifdef SERVER_SUPPORT
249    if (server_active)
250    {
251	for (i = 0; i < argc; ++i)
252	    server_pathname_check (argv[i]);
253    }
254#endif
255
256    if (argc == 0)
257    {
258	int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM);
259
260#ifdef CLIENT_SUPPORT
261	if (!just_subdirs
262	    && CVSroot_cmdline == NULL
263	    && current_parsed_root->isremote)
264	{
265	    cvsroot_t *root = Name_Root (NULL, update_dir);
266	    if (root)
267	    {
268		if (strcmp (root->original, original_parsed_root->original))
269		    /* We're skipping this directory because it is for
270		     * a different root.  Therefore, we just want to
271		     * do the subdirectories only.  Processing files would
272		     * cause a working directory from one repository to be
273		     * processed against a different repository, which could
274		     * cause all kinds of spurious conflicts and such.
275		     *
276		     * Question: what about the case of "cvs update foo"
277		     * where we process foo/bar and not foo itself?  That
278		     * seems to be handled somewhere (else) but why should
279		     * it be a separate case?  Needs investigation...  */
280		    just_subdirs = 1;
281	    }
282	}
283#endif
284
285	/*
286	 * There were no arguments, so we'll probably just recurse. The
287	 * exception to the rule is when we are called from a directory
288	 * without any CVS administration files.  That has always meant to
289	 * process each of the sub-directories, so we pretend like we were
290	 * called with the list of sub-dirs of the current dir as args
291	 */
292	if (just_subdirs)
293	{
294	    dirlist = Find_Directories (NULL, W_LOCAL, NULL);
295	    /* If there are no sub-directories, there is a certain logic in
296	       favor of doing nothing, but in fact probably the user is just
297	       confused about what directory they are in, or whether they
298	       cvs add'd a new directory.  In the case of at least one
299	       sub-directory, at least when we recurse into them we
300	       notice (hopefully) whether they are under CVS control.  */
301	    if (list_isempty (dirlist))
302	    {
303		if (update_dir[0] == '\0')
304		    error (0, 0, "in directory .:");
305		else
306		    error (0, 0, "in directory %s:", update_dir);
307		error (1, 0,
308		       "there is no version here; run '%s checkout' first",
309		       program_name);
310	    }
311#ifdef CLIENT_SUPPORT
312	    else if (current_parsed_root->isremote && server_started)
313	    {
314		/* In the the case "cvs update foo bar baz", a call to
315		   send_file_names in update.c will have sent the
316		   appropriate "Argument" commands to the server.  In
317		   this case, that won't have happened, so we need to
318		   do it here.  While this example uses "update", this
319		   generalizes to other commands.  */
320
321		/* This is the same call to Find_Directories as above.
322                   FIXME: perhaps it would be better to write a
323                   function that duplicates a list. */
324		args_to_send_when_finished = Find_Directories (NULL,
325							       W_LOCAL,
326							       NULL);
327	    }
328#endif
329	}
330	else
331	    addlist (&dirlist, ".");
332
333	goto do_the_work;
334    }
335
336
337    /*
338     * There were arguments, so we have to handle them by hand. To do
339     * that, we set up the filelist and dirlist with the arguments and
340     * call do_recursion.  do_recursion recognizes the fact that the
341     * lists are non-null when it starts and doesn't update them.
342     *
343     * explicitly named directories are stored in dirlist.
344     * explicitly named files are stored in filelist.
345     * other possibility is named entities whicha are not currently in
346     * the working directory.
347     */
348
349    for (i = 0; i < argc; i++)
350    {
351	/* if this argument is a directory, then add it to the list of
352	   directories. */
353
354	if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i]))
355	{
356	    strip_trailing_slashes (argv[i]);
357	    addlist (&dirlist, argv[i]);
358	}
359	else
360	{
361	    /* otherwise, split argument into directory and component names. */
362	    char *dir;
363	    char *comp;
364	    char *file_to_try;
365
366	    /* Now break out argv[i] into directory part (DIR) and file part
367	     * (COMP).  DIR and COMP will each point to a newly malloc'd
368	     * string.
369	     */
370	    dir = xstrdup (argv[i]);
371	    /* It's okay to cast out last_component's const below since we know
372	     * we just allocated dir here and have control of it.
373	     */
374	    comp = (char *)last_component (dir);
375	    if (comp == dir)
376	    {
377		/* no dir component.  What we have is an implied "./" */
378		dir = xstrdup(".");
379	    }
380	    else
381	    {
382		comp[-1] = '\0';
383		comp = xstrdup (comp);
384	    }
385
386	    /* if this argument exists as a file in the current
387	       working directory tree, then add it to the files list.  */
388
389	    if (!(which & W_LOCAL))
390	    {
391		/* If doing rtag, we've done a chdir to the repository. */
392		file_to_try = Xasprintf ("%s%s", argv[i], RCSEXT);
393	    }
394	    else
395		file_to_try = xstrdup (argv[i]);
396
397	    if (isfile (file_to_try))
398		addfile (&files_by_dir, dir, comp);
399	    else if (isdir (dir))
400	    {
401		if ((which & W_LOCAL) && isdir (CVSADM) &&
402		    !current_parsed_root->isremote)
403		{
404		    /* otherwise, look for it in the repository. */
405		    char *tmp_update_dir;
406		    char *repos;
407		    char *reposfile;
408
409		    tmp_update_dir = xmalloc (strlen (update_dir)
410					      + strlen (dir)
411					      + 5);
412		    strcpy (tmp_update_dir, update_dir);
413
414		    if (*tmp_update_dir != '\0')
415			strcat (tmp_update_dir, "/");
416
417		    strcat (tmp_update_dir, dir);
418
419		    /* look for it in the repository. */
420		    repos = Name_Repository (dir, tmp_update_dir);
421		    reposfile = Xasprintf ("%s/%s", repos, comp);
422		    free (repos);
423
424		    if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile))
425			addlist (&dirlist, argv[i]);
426		    else
427			addfile (&files_by_dir, dir, comp);
428
429		    free (tmp_update_dir);
430		    free (reposfile);
431		}
432		else
433		    addfile (&files_by_dir, dir, comp);
434	    }
435	    else
436		error (1, 0, "no such directory `%s'", dir);
437
438	    free (file_to_try);
439	    free (dir);
440	    free (comp);
441	}
442    }
443
444    /* At this point we have looped over all named arguments and built
445       a coupla lists.  Now we unroll the lists, setting up and
446       calling do_recursion. */
447
448    err += walklist (files_by_dir, unroll_files_proc, (void *) &frame);
449    dellist(&files_by_dir);
450
451    /* then do_recursion on the dirlist. */
452    if (dirlist != NULL)
453    {
454    do_the_work:
455	err += do_recursion (&frame);
456    }
457
458    /* Free the data which expand_wild allocated.  */
459    free_names (&argc, argv);
460
461    free (update_dir);
462    update_dir = NULL;
463
464#ifdef CLIENT_SUPPORT
465    if (args_to_send_when_finished != NULL)
466    {
467	/* FIXME (njc): in the multiroot case, we don't want to send
468	   argument commands for those top-level directories which do
469	   not contain any subdirectories which have files checked out
470	   from current_parsed_root.  If we do, and two repositories
471	   have a module with the same name, nasty things could happen.
472
473	   This is hard.  Perhaps we should send the Argument commands
474	   later in this procedure, after we've had a chance to notice
475	   which directores we're using (after do_recursion has been
476	   called once).  This means a _lot_ of rewriting, however.
477
478	   What we need to do for that to happen is descend the tree
479	   and construct a list of directories which are checked out
480	   from current_cvsroot.  Now, we eliminate from the list all
481	   of those directories which are immediate subdirectories of
482	   another directory in the list.  To say that the opposite
483	   way, we keep the directories which are not immediate
484	   subdirectories of any other in the list.  Here's a picture:
485
486			      a
487			     / \
488			    B   C
489			   / \
490			  D   e
491			     / \
492			    F   G
493			       / \
494			      H   I
495
496	   The node in capitals are those directories which are
497	   checked out from current_cvsroot.  We want the list to
498	   contain B, C, F, and G.  D, H, and I are not included,
499	   because their parents are also checked out from
500	   current_cvsroot.
501
502	   The algorithm should be:
503
504	   1) construct a tree of all directory names where each
505	   element contains a directory name and a flag which notes if
506	   that directory is checked out from current_cvsroot
507
508			      a0
509			     / \
510			    B1  C1
511			   / \
512			  D1  e0
513			     / \
514			    F1  G1
515			       / \
516			      H1  I1
517
518	   2) Recursively descend the tree.  For each node, recurse
519	   before processing the node.  If the flag is zero, do
520	   nothing.  If the flag is 1, check the node's parent.  If
521	   the parent's flag is one, change the current entry's flag
522	   to zero.
523
524			      a0
525			     / \
526			    B1  C1
527			   / \
528			  D0  e0
529			     / \
530			    F1  G1
531			       / \
532			      H0  I0
533
534	   3) Walk the tree and spit out "Argument" commands to tell
535	   the server which directories to munge.
536
537	   Yuck.  It's not clear this is worth spending time on, since
538	   we might want to disable cvs commands entirely from
539	   directories that do not have CVSADM files...
540
541	   Anyways, the solution as it stands has modified server.c
542	   (dirswitch) to create admin files [via server.c
543	   (create_adm_p)] in all path elements for a client's
544	   "Directory xxx" command, which forces the server to descend
545	   and serve the files there.  client.c (send_file_names) has
546	   also been modified to send only those arguments which are
547	   appropriate to current_parsed_root.
548
549	*/
550
551	/* Construct a fake argc/argv pair. */
552
553	int our_argc = 0, i;
554	char **our_argv = NULL;
555
556	if (! list_isempty (args_to_send_when_finished))
557	{
558	    Node *head, *p;
559
560	    head = args_to_send_when_finished->list;
561
562	    /* count the number of nodes */
563	    i = 0;
564	    for (p = head->next; p != head; p = p->next)
565		i++;
566	    our_argc = i;
567
568	    /* create the argument vector */
569	    our_argv = xmalloc (sizeof (char *) * our_argc);
570
571	    /* populate it */
572	    i = 0;
573	    for (p = head->next; p != head; p = p->next)
574		our_argv[i++] = xstrdup (p->key);
575	}
576
577	/* We don't want to expand widcards, since we've just created
578	   a list of directories directly from the filesystem. */
579	send_file_names (our_argc, our_argv, 0);
580
581	/* Free our argc/argv. */
582	if (our_argv != NULL)
583	{
584	    for (i = 0; i < our_argc; i++)
585		free (our_argv[i]);
586	    free (our_argv);
587	}
588
589	dellist (&args_to_send_when_finished);
590    }
591#endif
592
593    return err;
594}
595
596
597
598/*
599 * Implement the recursive policies on the local directory.  This may be
600 * called directly, or may be called by start_recursion.
601 */
602static int
603do_recursion (struct recursion_frame *frame)
604{
605    int err = 0;
606    int dodoneproc = 1;
607    char *srepository = NULL;
608    List *entries = NULL;
609    int locktype;
610    bool process_this_directory = true;
611
612#ifdef HAVE_PRINT_PTR
613    TRACE (TRACE_FLOW, "do_recursion ( frame=%p )", (void *) frame);
614#else
615    TRACE (TRACE_FLOW, "do_recursion ( frame=%lx )", (unsigned long) frame);
616#endif
617
618    /* do nothing if told */
619    if (frame->flags == R_SKIP_ALL)
620	return 0;
621
622    locktype = noexec ? CVS_LOCK_NONE : frame->locktype;
623
624    /* The fact that locks are not active here is what makes us fail to have
625       the
626
627           If someone commits some changes in one cvs command,
628	   then an update by someone else will either get all the
629	   changes, or none of them.
630
631       property (see node Concurrency in cvs.texinfo).
632
633       The most straightforward fix would just to readlock the whole
634       tree before starting an update, but that means that if a commit
635       gets blocked on a big update, it might need to wait a *long*
636       time.
637
638       A more adequate fix would be a two-pass design for update,
639       checkout, etc.  The first pass would go through the repository,
640       with the whole tree readlocked, noting what versions of each
641       file we want to get.  The second pass would release all locks
642       (except perhaps short-term locks on one file at a
643       time--although I think RCS already deals with this) and
644       actually get the files, specifying the particular versions it wants.
645
646       This could be sped up by separating out the data needed for the
647       first pass into a separate file(s)--for example a file
648       attribute for each file whose value contains the head revision
649       for each branch.  The structure should be designed so that
650       commit can relatively quickly update the information for a
651       single file or a handful of files (file attributes, as
652       implemented in Jan 96, are probably acceptable; improvements
653       would be possible such as branch attributes which are in
654       separate files for each branch).  */
655
656#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL)
657    /*
658     * Now would be a good time to check to see if we need to stop
659     * generating data, to give the buffers a chance to drain to the
660     * remote client.  We should not have locks active at this point,
661     * but if there are writelocks around, we cannot pause here.  */
662    if (server_active && locktype != CVS_LOCK_WRITE)
663	server_pause_check();
664#endif
665
666    /* Check the value in CVSADM_ROOT and see if it's in the list.  If
667       not, add it to our lists of CVS/Root directories and do not
668       process the files in this directory.  Otherwise, continue as
669       usual.  THIS_ROOT might be NULL if we're doing an initial
670       checkout -- check before using it.  The default should be that
671       we process a directory's contents and only skip those contents
672       if a CVS/Root file exists.
673
674       If we're running the server, we want to process all
675       directories, since we're guaranteed to have only one CVSROOT --
676       our own.  */
677
678    /* If -d was specified, it should override CVS/Root.
679
680       In the single-repository case, it is long-standing CVS behavior
681       and makes sense - the user might want another access method,
682       another server (which mounts the same repository), &c.
683
684       In the multiple-repository case, -d overrides all CVS/Root
685       files.  That is the only plausible generalization I can
686       think of.  */
687    if (CVSroot_cmdline == NULL && !server_active)
688    {
689	cvsroot_t *this_root = Name_Root (NULL, update_dir);
690	if (this_root != NULL)
691	{
692	    if (findnode (root_directories, this_root->original))
693	    {
694		process_this_directory =
695		    !strcmp (original_parsed_root->original,
696			     this_root->original);
697	    }
698	    else
699	    {
700		/* Add it to our list. */
701
702		Node *n = getnode ();
703		n->type = NT_UNKNOWN;
704		n->key = xstrdup (this_root->original);
705		n->data = this_root;
706
707		if (addnode (root_directories, n))
708		    error (1, 0, "cannot add new CVSROOT %s",
709			   this_root->original);
710
711		process_this_directory = false;
712	    }
713	}
714    }
715
716    /*
717     * Fill in repository with the current repository
718     */
719    if (frame->which & W_LOCAL)
720    {
721	if (isdir (CVSADM))
722	{
723	    repository = Name_Repository (NULL, update_dir);
724	    srepository = repository;		/* remember what to free */
725	}
726	else
727	    repository = NULL;
728    }
729    else
730    {
731	repository = frame->repository;
732	assert (repository != NULL);
733	assert (strstr (repository, "/./") == NULL);
734    }
735
736    fileattr_startdir (repository);
737
738    /*
739     * The filesdoneproc needs to be called for each directory where files
740     * processed, or each directory that is processed by a call where no
741     * directories were passed in.  In fact, the only time we don't want to
742     * call back the filesdoneproc is when we are processing directories that
743     * were passed in on the command line (or in the special case of `.' when
744     * we were called with no args
745     */
746    if (dirlist != NULL && filelist == NULL)
747	dodoneproc = 0;
748
749    /*
750     * If filelist or dirlist is already set, we don't look again. Otherwise,
751     * find the files and directories
752     */
753    if (filelist == NULL && dirlist == NULL)
754    {
755	/* both lists were NULL, so start from scratch */
756	if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES)
757	{
758	    int lwhich = frame->which;
759
760	    /* be sure to look in the attic if we have sticky tags/date */
761	    if ((lwhich & W_ATTIC) == 0)
762		if (isreadable (CVSADM_TAG))
763		    lwhich |= W_ATTIC;
764
765	    /* In the !(which & W_LOCAL) case, we filled in repository
766	       earlier in the function.  In the (which & W_LOCAL) case,
767	       the Find_Names function is going to look through the
768	       Entries file.  If we do not have a repository, that
769	       does not make sense, so we insist upon having a
770	       repository at this point.  Name_Repository will give a
771	       reasonable error message.  */
772	    if (repository == NULL)
773	    {
774		Name_Repository (NULL, update_dir);
775	        assert (!"Not reached.  Please report this problem to <"
776			PACKAGE_BUGREPORT ">");
777	    }
778	    /* find the files and fill in entries if appropriate */
779	    if (process_this_directory)
780	    {
781		filelist = Find_Names (repository, lwhich, frame->aflag,
782				       &entries);
783		if (filelist == NULL)
784		{
785		    error (0, 0, "skipping directory %s", update_dir);
786		    /* Note that Find_Directories and the filesdoneproc
787		       in particular would do bad things ("? foo.c" in
788		       the case of some filesdoneproc's).  */
789		    goto skip_directory;
790		}
791	    }
792	}
793
794	/* find sub-directories if we will recurse */
795	if (frame->flags != R_SKIP_DIRS)
796	    dirlist = Find_Directories (
797		process_this_directory ? repository : NULL,
798		frame->which, entries);
799    }
800    else
801    {
802	/* something was passed on the command line */
803	if (filelist != NULL && frame->fileproc != NULL)
804	{
805	    /* we will process files, so pre-parse entries */
806	    if (frame->which & W_LOCAL)
807		entries = Entries_Open (frame->aflag, NULL);
808	}
809    }
810
811    /* process the files (if any) */
812    if (process_this_directory && filelist != NULL && frame->fileproc)
813    {
814	struct file_info finfo_struct;
815	struct frame_and_file frfile;
816
817	/* Lock the repository, if necessary. */
818	if (repository)
819	{
820	    if (locktype == CVS_LOCK_READ)
821	    {
822		if (Reader_Lock (repository) != 0)
823		    error (1, 0, "read lock failed - giving up");
824	    }
825	    else if (locktype == CVS_LOCK_WRITE)
826		lock_dir_for_write (repository);
827	}
828
829#ifdef CLIENT_SUPPORT
830	/* For the server, we handle notifications in a completely different
831	   place (server_notify).  For local, we can't do them here--we don't
832	   have writelocks in place, and there is no way to get writelocks
833	   here.  */
834	if (current_parsed_root->isremote)
835	    notify_check (repository, update_dir);
836#endif /* CLIENT_SUPPORT */
837
838	finfo_struct.repository = repository;
839	finfo_struct.update_dir = update_dir;
840	finfo_struct.entries = entries;
841	/* do_file_proc will fill in finfo_struct.file.  */
842
843	frfile.finfo = &finfo_struct;
844	frfile.frame = frame;
845
846	/* process the files */
847	err += walklist (filelist, do_file_proc, &frfile);
848
849	/* unlock it */
850	if (/* We only lock the repository above when repository is set */
851	    repository
852	    /* and when asked for a read or write lock. */
853	    && locktype != CVS_LOCK_NONE)
854	    Simple_Lock_Cleanup ();
855
856	/* clean up */
857	dellist (&filelist);
858    }
859
860    /* call-back files done proc (if any) */
861    if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL)
862	err = frame->filesdoneproc (frame->callerdat, err, repository,
863				    update_dir[0] ? update_dir : ".",
864				    entries);
865
866 skip_directory:
867    fileattr_write ();
868    fileattr_free ();
869
870    /* process the directories (if necessary) */
871    if (dirlist != NULL)
872    {
873	struct frame_and_entries frent;
874
875	frent.frame = frame;
876	frent.entries = entries;
877	err += walklist (dirlist, do_dir_proc, &frent);
878    }
879#if 0
880    else if (frame->dirleaveproc != NULL)
881	err += frame->dirleaveproc (frame->callerdat, ".", err, ".");
882#endif
883    dellist (&dirlist);
884
885    if (entries)
886    {
887	Entries_Close (entries);
888	entries = NULL;
889    }
890
891    /* free the saved copy of the pointer if necessary */
892    if (srepository)
893	free (srepository);
894    repository = NULL;
895
896#ifdef HAVE_PRINT_PTR
897    TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%p)", (void *)frame);
898#else
899    TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%lx)",
900	   (unsigned long)frame);
901#endif
902
903    return err;
904}
905
906
907
908/*
909 * Process each of the files in the list with the callback proc
910 *
911 * NOTES
912 *    Fills in FINFO->fullname, and sometimes FINFO->rcs before
913 *    calling the callback proc (FRFILE->frame->fileproc), but frees them
914 *    before return.
915 *
916 * OUTPUTS
917 *    Fills in FINFO->file.
918 *
919 * RETURNS
920 *    0 if we were supposed to find an RCS file but couldn't.
921 *    Otherwise, returns the error code returned by the callback function.
922 */
923static int
924do_file_proc (Node *p, void *closure)
925{
926    struct frame_and_file *frfile = closure;
927    struct file_info *finfo = frfile->finfo;
928    int ret;
929    char *tmp;
930
931    finfo->file = p->key;
932    if (finfo->update_dir[0] != '\0')
933	tmp = Xasprintf ("%s/%s", finfo->update_dir, finfo->file);
934    else
935	tmp = xstrdup (finfo->file);
936
937    if (frfile->frame->dosrcs && repository)
938    {
939	finfo->rcs = RCS_parse (finfo->file, repository);
940
941	/* OK, without W_LOCAL the error handling becomes relatively
942	   simple.  The file names came from readdir() on the
943	   repository and so we know any ENOENT is an error
944	   (e.g. symlink pointing to nothing).  Now, the logic could
945	   be simpler - since we got the name from readdir, we could
946	   just be calling RCS_parsercsfile.  */
947	if (finfo->rcs == NULL
948	    && !(frfile->frame->which & W_LOCAL))
949	{
950	    error (0, 0, "could not read RCS file for %s", tmp);
951	    free (tmp);
952	    cvs_flushout ();
953	    return 0;
954	}
955    }
956    else
957        finfo->rcs = NULL;
958    finfo->fullname = tmp;
959    ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo);
960
961    freercsnode (&finfo->rcs);
962    free (tmp);
963
964    /* Allow the user to monitor progress with tail -f.  Doing this once
965       per file should be no big deal, but we don't want the performance
966       hit of flushing on every line like previous versions of CVS.  */
967    cvs_flushout ();
968
969    return ret;
970}
971
972
973
974/*
975 * Process each of the directories in the list (recursing as we go)
976 */
977static int
978do_dir_proc (Node *p, void *closure)
979{
980    struct frame_and_entries *frent = (struct frame_and_entries *) closure;
981    struct recursion_frame *frame = frent->frame;
982    struct recursion_frame xframe;
983    char *dir = p->key;
984    char *newrepos;
985    List *sdirlist;
986    char *srepository;
987    Dtype dir_return = R_PROCESS;
988    int stripped_dot = 0;
989    int err = 0;
990    struct saved_cwd cwd;
991    char *saved_update_dir;
992    bool process_this_directory = true;
993
994    if (fncmp (dir, CVSADM) == 0)
995    {
996	/* This seems to most often happen when users (beginning users,
997	   generally), try "cvs ci *" or something similar.  On that
998	   theory, it is possible that we should just silently skip the
999	   CVSADM directories, but on the other hand, using a wildcard
1000	   like this isn't necessarily a practice to encourage (it operates
1001	   only on files which exist in the working directory, unlike
1002	   regular CVS recursion).  */
1003
1004	/* FIXME-reentrancy: printed_cvs_msg should be in a "command
1005	   struct" or some such, so that it gets cleared for each new
1006	   command (this is possible using the remote protocol and a
1007	   custom-written client).  The struct recursion_frame is not
1008	   far back enough though, some commands (commit at least)
1009	   will call start_recursion several times.  An alternate solution
1010	   would be to take this whole check and move it to a new function
1011	   validate_arguments or some such that all the commands call
1012	   and which snips the offending directory from the argc,argv
1013	   vector.  */
1014	static int printed_cvs_msg = 0;
1015	if (!printed_cvs_msg)
1016	{
1017	    error (0, 0, "warning: directory %s specified in argument",
1018		   dir);
1019	    error (0, 0, "\
1020but CVS uses %s for its own purposes; skipping %s directory",
1021		   CVSADM, dir);
1022	    printed_cvs_msg = 1;
1023	}
1024	return 0;
1025    }
1026
1027    saved_update_dir = update_dir;
1028    update_dir = xmalloc (strlen (saved_update_dir)
1029			  + strlen (dir)
1030			  + 5);
1031    strcpy (update_dir, saved_update_dir);
1032
1033    /* set up update_dir - skip dots if not at start */
1034    if (strcmp (dir, ".") != 0)
1035    {
1036	if (update_dir[0] != '\0')
1037	{
1038	    (void) strcat (update_dir, "/");
1039	    (void) strcat (update_dir, dir);
1040	}
1041	else
1042	    (void) strcpy (update_dir, dir);
1043
1044	/*
1045	 * Here we need a plausible repository name for the sub-directory. We
1046	 * create one by concatenating the new directory name onto the
1047	 * previous repository name.  The only case where the name should be
1048	 * used is in the case where we are creating a new sub-directory for
1049	 * update -d and in that case the generated name will be correct.
1050	 */
1051	if (repository == NULL)
1052	    newrepos = xstrdup ("");
1053	else
1054	    newrepos = Xasprintf ("%s/%s", repository, dir);
1055    }
1056    else
1057    {
1058	if (update_dir[0] == '\0')
1059	    (void) strcpy (update_dir, dir);
1060
1061	if (repository == NULL)
1062	    newrepos = xstrdup ("");
1063	else
1064	    newrepos = xstrdup (repository);
1065    }
1066
1067    /* Check to see that the CVSADM directory, if it exists, seems to be
1068       well-formed.  It can be missing files if the user hit ^C in the
1069       middle of a previous run.  We want to (a) make this a nonfatal
1070       error, and (b) make sure we print which directory has the
1071       problem.
1072
1073       Do this before the direntproc, so that (1) the direntproc
1074       doesn't have to guess/deduce whether we will skip the directory
1075       (e.g. send_dirent_proc and whether to send the directory), and
1076       (2) so that the warm fuzzy doesn't get printed if we skip the
1077       directory.  */
1078    if (frame->which & W_LOCAL)
1079    {
1080	char *cvsadmdir;
1081
1082	cvsadmdir = xmalloc (strlen (dir)
1083			     + sizeof (CVSADM_REP)
1084			     + sizeof (CVSADM_ENT)
1085			     + 80);
1086
1087	strcpy (cvsadmdir, dir);
1088	strcat (cvsadmdir, "/");
1089	strcat (cvsadmdir, CVSADM);
1090	if (isdir (cvsadmdir))
1091	{
1092	    strcpy (cvsadmdir, dir);
1093	    strcat (cvsadmdir, "/");
1094	    strcat (cvsadmdir, CVSADM_REP);
1095	    if (!isfile (cvsadmdir))
1096	    {
1097		/* Some commands like update may have printed "? foo" but
1098		   if we were planning to recurse, and don't on account of
1099		   CVS/Repository, we want to say why.  */
1100		error (0, 0, "ignoring %s (%s missing)", update_dir,
1101		       CVSADM_REP);
1102		dir_return = R_SKIP_ALL;
1103	    }
1104
1105	    /* Likewise for CVS/Entries.  */
1106	    if (dir_return != R_SKIP_ALL)
1107	    {
1108		strcpy (cvsadmdir, dir);
1109		strcat (cvsadmdir, "/");
1110		strcat (cvsadmdir, CVSADM_ENT);
1111		if (!isfile (cvsadmdir))
1112		{
1113		    /* Some commands like update may have printed "? foo" but
1114		       if we were planning to recurse, and don't on account of
1115		       CVS/Repository, we want to say why.  */
1116		    error (0, 0, "ignoring %s (%s missing)", update_dir,
1117			   CVSADM_ENT);
1118		    dir_return = R_SKIP_ALL;
1119		}
1120	    }
1121	}
1122	free (cvsadmdir);
1123    }
1124
1125    /* Only process this directory if the root matches.  This nearly
1126       duplicates code in do_recursion. */
1127
1128    /* If -d was specified, it should override CVS/Root.
1129
1130       In the single-repository case, it is long-standing CVS behavior
1131       and makes sense - the user might want another access method,
1132       another server (which mounts the same repository), &c.
1133
1134       In the multiple-repository case, -d overrides all CVS/Root
1135       files.  That is the only plausible generalization I can
1136       think of.  */
1137    if (CVSroot_cmdline == NULL && !server_active)
1138    {
1139	cvsroot_t *this_root = Name_Root (dir, update_dir);
1140	if (this_root != NULL)
1141	{
1142	    if (findnode (root_directories, this_root->original))
1143	    {
1144		process_this_directory =
1145		    !strcmp (original_parsed_root->original,
1146			     this_root->original);
1147	    }
1148	    else
1149	    {
1150		/* Add it to our list. */
1151
1152		Node *n = getnode ();
1153		n->type = NT_UNKNOWN;
1154		n->key = xstrdup (this_root->original);
1155		n->data = this_root;
1156
1157		if (addnode (root_directories, n))
1158		    error (1, 0, "cannot add new CVSROOT %s",
1159			   this_root->original);
1160
1161		process_this_directory = false;
1162	    }
1163	}
1164    }
1165
1166    /* call-back dir entry proc (if any) */
1167    if (dir_return == R_SKIP_ALL)
1168	;
1169    else if (frame->direntproc != NULL)
1170    {
1171	/* If we're doing the actual processing, call direntproc.
1172           Otherwise, assume that we need to process this directory
1173           and recurse. FIXME. */
1174
1175	if (process_this_directory)
1176	    dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
1177					    update_dir, frent->entries);
1178	else
1179	    dir_return = R_PROCESS;
1180    }
1181    else
1182    {
1183	/* Generic behavior.  I don't see a reason to make the caller specify
1184	   a direntproc just to get this.  */
1185	if ((frame->which & W_LOCAL) && !isdir (dir))
1186	    dir_return = R_SKIP_ALL;
1187    }
1188
1189    free (newrepos);
1190
1191    /* only process the dir if the return code was 0 */
1192    if (dir_return != R_SKIP_ALL)
1193    {
1194	/* save our current directory and static vars */
1195        if (save_cwd (&cwd))
1196	    error (1, errno, "Failed to save current directory.");
1197	sdirlist = dirlist;
1198	srepository = repository;
1199	dirlist = NULL;
1200
1201	/* cd to the sub-directory */
1202	if (CVS_CHDIR (dir) < 0)
1203	    error (1, errno, "could not chdir to %s", dir);
1204
1205	/* honor the global SKIP_DIRS (a.k.a. local) */
1206	if (frame->flags == R_SKIP_DIRS)
1207	    dir_return = R_SKIP_DIRS;
1208
1209	/* remember if the `.' will be stripped for subsequent dirs */
1210	if (strcmp (update_dir, ".") == 0)
1211	{
1212	    update_dir[0] = '\0';
1213	    stripped_dot = 1;
1214	}
1215
1216	/* make the recursive call */
1217	xframe = *frame;
1218	xframe.flags = dir_return;
1219	/* Keep track of repository, really just for r* commands (rtag, rdiff,
1220	 * co, ...) to tag_check_valid, since all the other commands use
1221	 * CVS/Repository to figure it out per directory.
1222	 */
1223	if (repository)
1224	{
1225	    if (strcmp (dir, ".") == 0)
1226		xframe.repository = xstrdup (repository);
1227	    else
1228		xframe.repository = Xasprintf ("%s/%s", repository, dir);
1229	}
1230	else
1231	    xframe.repository = NULL;
1232	err += do_recursion (&xframe);
1233	if (xframe.repository)
1234	{
1235	    free (xframe.repository);
1236	    xframe.repository = NULL;
1237	}
1238
1239	/* put the `.' back if necessary */
1240	if (stripped_dot)
1241	    (void) strcpy (update_dir, ".");
1242
1243	/* call-back dir leave proc (if any) */
1244	if (process_this_directory && frame->dirleaveproc != NULL)
1245	    err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir,
1246				       frent->entries);
1247
1248	/* get back to where we started and restore state vars */
1249	if (restore_cwd (&cwd))
1250	    error (1, errno, "Failed to restore current directory, `%s'.",
1251	           cwd.name);
1252	free_cwd (&cwd);
1253	dirlist = sdirlist;
1254	repository = srepository;
1255    }
1256
1257    free (update_dir);
1258    update_dir = saved_update_dir;
1259
1260    return err;
1261}
1262
1263/*
1264 * Add a node to a list allocating the list if necessary.
1265 */
1266static void
1267addlist (List **listp, char *key)
1268{
1269    Node *p;
1270
1271    if (*listp == NULL)
1272	*listp = getlist ();
1273    p = getnode ();
1274    p->type = FILES;
1275    p->key = xstrdup (key);
1276    if (addnode (*listp, p) != 0)
1277	freenode (p);
1278}
1279
1280static void
1281addfile (List **listp, char *dir, char *file)
1282{
1283    Node *n;
1284    List *fl;
1285
1286    /* add this dir. */
1287    addlist (listp, dir);
1288
1289    n = findnode (*listp, dir);
1290    if (n == NULL)
1291    {
1292	error (1, 0, "can't find recently added dir node `%s' in start_recursion.",
1293	       dir);
1294    }
1295
1296    n->type = DIRS;
1297    fl = n->data;
1298    addlist (&fl, file);
1299    n->data = fl;
1300    return;
1301}
1302
1303static int
1304unroll_files_proc (Node *p, void *closure)
1305{
1306    Node *n;
1307    struct recursion_frame *frame = (struct recursion_frame *) closure;
1308    int err = 0;
1309    List *save_dirlist;
1310    char *save_update_dir = NULL;
1311    struct saved_cwd cwd;
1312
1313    /* if this dir was also an explicitly named argument, then skip
1314       it.  We'll catch it later when we do dirs. */
1315    n = findnode (dirlist, p->key);
1316    if (n != NULL)
1317	return (0);
1318
1319    /* otherwise, call dorecusion for this list of files. */
1320    filelist = p->data;
1321    p->data = NULL;
1322    save_dirlist = dirlist;
1323    dirlist = NULL;
1324
1325    if (strcmp(p->key, ".") != 0)
1326    {
1327        if (save_cwd (&cwd))
1328	    error (1, errno, "Failed to save current directory.");
1329	if ( CVS_CHDIR (p->key) < 0)
1330	    error (1, errno, "could not chdir to %s", p->key);
1331
1332	save_update_dir = update_dir;
1333	update_dir = xmalloc (strlen (save_update_dir)
1334				  + strlen (p->key)
1335				  + 5);
1336	strcpy (update_dir, save_update_dir);
1337
1338	if (*update_dir != '\0')
1339	    (void) strcat (update_dir, "/");
1340
1341	(void) strcat (update_dir, p->key);
1342    }
1343
1344    err += do_recursion (frame);
1345
1346    if (save_update_dir != NULL)
1347    {
1348	free (update_dir);
1349	update_dir = save_update_dir;
1350
1351	if (restore_cwd (&cwd))
1352	    error (1, errno, "Failed to restore current directory, `%s'.",
1353	           cwd.name);
1354	free_cwd (&cwd);
1355    }
1356
1357    dirlist = save_dirlist;
1358    if (filelist)
1359	dellist (&filelist);
1360    return(err);
1361}
1362/* vim:tabstop=8:shiftwidth=4
1363 */
1364