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