xref: /openbsd-src/gnu/usr.bin/cvs/src/recurse.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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 
16 static int do_dir_proc PROTO((Node * p, void *closure));
17 static int do_file_proc PROTO((Node * p, void *closure));
18 static void addlist PROTO((List ** listp, char *key));
19 static int unroll_files_proc PROTO((Node *p, void *closure));
20 static void addfile PROTO((List **listp, char *dir, char *file));
21 
22 static char *update_dir;
23 static char *repository = NULL;
24 static List *filelist = NULL; /* holds list of files on which to operate */
25 static List *dirlist = NULL; /* holds list of directories on which to operate */
26 
27 struct 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 
40 static 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 
49 struct 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 
56 struct 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 ".".  */
67 int
68 start_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 	    && client_active)
161 	{
162 	    char *root = Name_Root (NULL, update_dir);
163 	    if (root && strcmp (root, current_root) != 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 (client_active && 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 		    && !client_active
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_root.  If we do, and two repositories have a
367 	   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_root.
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  */
496 static int
497 do_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 = (strcmp (current_root, this_root) == 0);
603 
604 	    free (this_root);
605 	}
606     }
607 
608     /*
609      * Fill in repository with the current repository
610      */
611     if (frame->which & W_LOCAL)
612     {
613 	if (isdir (CVSADM))
614 	    repository = Name_Repository ((char *) NULL, update_dir);
615 	else
616 	    repository = NULL;
617     }
618     else
619     {
620 	repository = xgetwd ();
621 	if (repository == NULL)
622 	    error (1, errno, "could not get working directory");
623     }
624     srepository = repository;		/* remember what to free */
625 
626     fileattr_startdir (repository);
627 
628     /*
629      * The filesdoneproc needs to be called for each directory where files
630      * processed, or each directory that is processed by a call where no
631      * directories were passed in.  In fact, the only time we don't want to
632      * call back the filesdoneproc is when we are processing directories that
633      * were passed in on the command line (or in the special case of `.' when
634      * we were called with no args
635      */
636     if (dirlist != NULL && filelist == NULL)
637 	dodoneproc = 0;
638 
639     /*
640      * If filelist or dirlist is already set, we don't look again. Otherwise,
641      * find the files and directories
642      */
643     if (filelist == NULL && dirlist == NULL)
644     {
645 	/* both lists were NULL, so start from scratch */
646 	if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES)
647 	{
648 	    int lwhich = frame->which;
649 
650 	    /* be sure to look in the attic if we have sticky tags/date */
651 	    if ((lwhich & W_ATTIC) == 0)
652 		if (isreadable (CVSADM_TAG))
653 		    lwhich |= W_ATTIC;
654 
655 	    /* In the !(which & W_LOCAL) case, we filled in repository
656 	       earlier in the function.  In the (which & W_LOCAL) case,
657 	       the Find_Names function is going to look through the
658 	       Entries file.  If we do not have a repository, that
659 	       does not make sense, so we insist upon having a
660 	       repository at this point.  Name_Repository will give a
661 	       reasonable error message.  */
662 	    if (repository == NULL)
663 		repository = Name_Repository ((char *) NULL, update_dir);
664 
665 	    /* find the files and fill in entries if appropriate */
666 	    if (process_this_directory)
667 	    {
668 		filelist = Find_Names (repository, lwhich, frame->aflag,
669 				       &entries);
670 		if (filelist == NULL)
671 		{
672 		    error (0, 0, "skipping directory %s", update_dir);
673 		    /* Note that Find_Directories and the filesdoneproc
674 		       in particular would do bad things ("? foo.c" in
675 		       the case of some filesdoneproc's).  */
676 		    goto skip_directory;
677 		}
678 	    }
679 	}
680 
681 	/* find sub-directories if we will recurse */
682 	if (frame->flags != R_SKIP_DIRS)
683 	    dirlist = Find_Directories (
684 		process_this_directory ? repository : NULL,
685 		frame->which, entries);
686     }
687     else
688     {
689 	/* something was passed on the command line */
690 	if (filelist != NULL && frame->fileproc != NULL)
691 	{
692 	    /* we will process files, so pre-parse entries */
693 	    if (frame->which & W_LOCAL)
694 		entries = Entries_Open (frame->aflag, NULL);
695 	}
696     }
697 
698     /* process the files (if any) */
699     if (process_this_directory && filelist != NULL && frame->fileproc)
700     {
701 	struct file_info finfo_struct;
702 	struct frame_and_file frfile;
703 
704 	/* read lock it if necessary */
705 	if (should_readlock && repository && Reader_Lock (repository) != 0)
706 	    error (1, 0, "read lock failed - giving up");
707 
708 #ifdef CLIENT_SUPPORT
709 	/* For the server, we handle notifications in a completely different
710 	   place (server_notify).  For local, we can't do them here--we don't
711 	   have writelocks in place, and there is no way to get writelocks
712 	   here.  */
713 	if (client_active)
714 	    notify_check (repository, update_dir);
715 #endif /* CLIENT_SUPPORT */
716 
717 	finfo_struct.repository = repository;
718 	finfo_struct.update_dir = update_dir;
719 	finfo_struct.entries = entries;
720 	/* do_file_proc will fill in finfo_struct.file.  */
721 
722 	frfile.finfo = &finfo_struct;
723 	frfile.frame = frame;
724 
725 	/* process the files */
726 	err += walklist (filelist, do_file_proc, &frfile);
727 
728 	/* unlock it */
729 	if (should_readlock)
730 	    Lock_Cleanup ();
731 
732 	/* clean up */
733 	dellist (&filelist);
734     }
735 
736     /* call-back files done proc (if any) */
737     if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL)
738 	err = frame->filesdoneproc (frame->callerdat, err, repository,
739 				    update_dir[0] ? update_dir : ".",
740 				    entries);
741 
742  skip_directory:
743     fileattr_write ();
744     fileattr_free ();
745 
746     /* process the directories (if necessary) */
747     if (dirlist != NULL)
748     {
749 	struct frame_and_entries frent;
750 
751 	frent.frame = frame;
752 	frent.entries = entries;
753 	err += walklist (dirlist, do_dir_proc, (void *) &frent);
754     }
755 #if 0
756     else if (frame->dirleaveproc != NULL)
757 	err += frame->dirleaveproc (frame->callerdat, ".", err, ".");
758 #endif
759     dellist (&dirlist);
760 
761     if (entries)
762     {
763 	Entries_Close (entries);
764 	entries = NULL;
765     }
766 
767     /* free the saved copy of the pointer if necessary */
768     if (srepository)
769     {
770 	free (srepository);
771 	repository = (char *) NULL;
772     }
773 
774     return (err);
775 }
776 
777 /*
778  * Process each of the files in the list with the callback proc
779  */
780 static int
781 do_file_proc (p, closure)
782     Node *p;
783     void *closure;
784 {
785     struct frame_and_file *frfile = (struct frame_and_file *)closure;
786     struct file_info *finfo = frfile->finfo;
787     int ret;
788 
789     finfo->file = p->key;
790     finfo->fullname = xmalloc (strlen (finfo->file)
791 			       + strlen (finfo->update_dir)
792 			       + 2);
793     finfo->fullname[0] = '\0';
794     if (finfo->update_dir[0] != '\0')
795     {
796 	strcat (finfo->fullname, finfo->update_dir);
797 	strcat (finfo->fullname, "/");
798     }
799     strcat (finfo->fullname, finfo->file);
800 
801     if (frfile->frame->dosrcs && repository)
802     {
803 	finfo->rcs = RCS_parse (finfo->file, repository);
804 
805 	/* OK, without W_LOCAL the error handling becomes relatively
806 	   simple.  The file names came from readdir() on the
807 	   repository and so we know any ENOENT is an error
808 	   (e.g. symlink pointing to nothing).  Now, the logic could
809 	   be simpler - since we got the name from readdir, we could
810 	   just be calling RCS_parsercsfile.  */
811 	if (finfo->rcs == NULL
812 	    && !(frfile->frame->which & W_LOCAL))
813 	{
814 	    error (0, 0, "could not read RCS file for %s", finfo->fullname);
815 	    free (finfo->fullname);
816 	    cvs_flushout ();
817 	    return 0;
818 	}
819     }
820     else
821         finfo->rcs = (RCSNode *) NULL;
822     ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo);
823 
824     freercsnode(&finfo->rcs);
825     free (finfo->fullname);
826 
827     /* Allow the user to monitor progress with tail -f.  Doing this once
828        per file should be no big deal, but we don't want the performance
829        hit of flushing on every line like previous versions of CVS.  */
830     cvs_flushout ();
831 
832     return (ret);
833 }
834 
835 /*
836  * Process each of the directories in the list (recursing as we go)
837  */
838 static int
839 do_dir_proc (p, closure)
840     Node *p;
841     void *closure;
842 {
843     struct frame_and_entries *frent = (struct frame_and_entries *) closure;
844     struct recursion_frame *frame = frent->frame;
845     struct recursion_frame xframe;
846     char *dir = p->key;
847     char *newrepos;
848     List *sdirlist;
849     char *srepository;
850     Dtype dir_return = R_PROCESS;
851     int stripped_dot = 0;
852     int err = 0;
853     struct saved_cwd cwd;
854     char *saved_update_dir;
855     int process_this_directory = 1;
856 
857     if (fncmp (dir, CVSADM) == 0)
858     {
859 	/* This seems to most often happen when users (beginning users,
860 	   generally), try "cvs ci *" or something similar.  On that
861 	   theory, it is possible that we should just silently skip the
862 	   CVSADM directories, but on the other hand, using a wildcard
863 	   like this isn't necessarily a practice to encourage (it operates
864 	   only on files which exist in the working directory, unlike
865 	   regular CVS recursion).  */
866 
867 	/* FIXME-reentrancy: printed_cvs_msg should be in a "command
868 	   struct" or some such, so that it gets cleared for each new
869 	   command (this is possible using the remote protocol and a
870 	   custom-written client).  The struct recursion_frame is not
871 	   far back enough though, some commands (commit at least)
872 	   will call start_recursion several times.  An alternate solution
873 	   would be to take this whole check and move it to a new function
874 	   validate_arguments or some such that all the commands call
875 	   and which snips the offending directory from the argc,argv
876 	   vector.  */
877 	static int printed_cvs_msg = 0;
878 	if (!printed_cvs_msg)
879 	{
880 	    error (0, 0, "warning: directory %s specified in argument",
881 		   dir);
882 	    error (0, 0, "\
883 but CVS uses %s for its own purposes; skipping %s directory",
884 		   CVSADM, dir);
885 	    printed_cvs_msg = 1;
886 	}
887 	return 0;
888     }
889 
890     saved_update_dir = update_dir;
891     update_dir = xmalloc (strlen (saved_update_dir)
892 			  + strlen (dir)
893 			  + 5);
894     strcpy (update_dir, saved_update_dir);
895 
896     /* set up update_dir - skip dots if not at start */
897     if (strcmp (dir, ".") != 0)
898     {
899 	if (update_dir[0] != '\0')
900 	{
901 	    (void) strcat (update_dir, "/");
902 	    (void) strcat (update_dir, dir);
903 	}
904 	else
905 	    (void) strcpy (update_dir, dir);
906 
907 	/*
908 	 * Here we need a plausible repository name for the sub-directory. We
909 	 * create one by concatenating the new directory name onto the
910 	 * previous repository name.  The only case where the name should be
911 	 * used is in the case where we are creating a new sub-directory for
912 	 * update -d and in that case the generated name will be correct.
913 	 */
914 	if (repository == NULL)
915 	    newrepos = xstrdup ("");
916 	else
917 	{
918 	    newrepos = xmalloc (strlen (repository) + strlen (dir) + 5);
919 	    sprintf (newrepos, "%s/%s", repository, dir);
920 	}
921     }
922     else
923     {
924 	if (update_dir[0] == '\0')
925 	    (void) strcpy (update_dir, dir);
926 
927 	if (repository == NULL)
928 	    newrepos = xstrdup ("");
929 	else
930 	    newrepos = xstrdup (repository);
931     }
932 
933     /* Check to see that the CVSADM directory, if it exists, seems to be
934        well-formed.  It can be missing files if the user hit ^C in the
935        middle of a previous run.  We want to (a) make this a nonfatal
936        error, and (b) make sure we print which directory has the
937        problem.
938 
939        Do this before the direntproc, so that (1) the direntproc
940        doesn't have to guess/deduce whether we will skip the directory
941        (e.g. send_dirent_proc and whether to send the directory), and
942        (2) so that the warm fuzzy doesn't get printed if we skip the
943        directory.  */
944     if (frame->which & W_LOCAL)
945     {
946 	char *cvsadmdir;
947 
948 	cvsadmdir = xmalloc (strlen (dir)
949 			     + sizeof (CVSADM_REP)
950 			     + sizeof (CVSADM_ENT)
951 			     + 80);
952 
953 	strcpy (cvsadmdir, dir);
954 	strcat (cvsadmdir, "/");
955 	strcat (cvsadmdir, CVSADM);
956 	if (isdir (cvsadmdir))
957 	{
958 	    strcpy (cvsadmdir, dir);
959 	    strcat (cvsadmdir, "/");
960 	    strcat (cvsadmdir, CVSADM_REP);
961 	    if (!isfile (cvsadmdir))
962 	    {
963 		/* Some commands like update may have printed "? foo" but
964 		   if we were planning to recurse, and don't on account of
965 		   CVS/Repository, we want to say why.  */
966 		error (0, 0, "ignoring %s (%s missing)", update_dir,
967 		       CVSADM_REP);
968 		dir_return = R_SKIP_ALL;
969 	    }
970 
971 	    /* Likewise for CVS/Entries.  */
972 	    if (dir_return != R_SKIP_ALL)
973 	    {
974 		strcpy (cvsadmdir, dir);
975 		strcat (cvsadmdir, "/");
976 		strcat (cvsadmdir, CVSADM_ENT);
977 		if (!isfile (cvsadmdir))
978 		{
979 		    /* Some commands like update may have printed "? foo" but
980 		       if we were planning to recurse, and don't on account of
981 		       CVS/Repository, we want to say why.  */
982 		    error (0, 0, "ignoring %s (%s missing)", update_dir,
983 			   CVSADM_ENT);
984 		    dir_return = R_SKIP_ALL;
985 		}
986 	    }
987 	}
988 	free (cvsadmdir);
989     }
990 
991     /* Only process this directory if the root matches.  This nearly
992        duplicates code in do_recursion. */
993 
994     if (
995 	/* If -d was specified, it should override CVS/Root.
996 
997 	   In the single-repository case, it is long-standing CVS behavior
998 	   and makes sense - the user might want another access method,
999 	   another server (which mounts the same repository), &c.
1000 
1001 	   In the multiple-repository case, -d overrides all CVS/Root
1002 	   files.  That is the only plausible generalization I can
1003 	   think of.  */
1004 	CVSroot_cmdline == NULL
1005 
1006 #ifdef SERVER_SUPPORT
1007 	&& ! server_active
1008 #endif
1009 	)
1010     {
1011 	char *this_root = Name_Root (dir, update_dir);
1012 	if (this_root != NULL)
1013 	{
1014 	    if (findnode (root_directories, this_root) == NULL)
1015 	    {
1016 		/* Add it to our list. */
1017 
1018 		Node *n = getnode ();
1019 		n->type = NT_UNKNOWN;
1020 		n->key = xstrdup (this_root);
1021 
1022 		if (addnode (root_directories, n))
1023 		    error (1, 0, "cannot add new CVSROOT %s", this_root);
1024 
1025 	    }
1026 
1027 	    process_this_directory = (strcmp (current_root, this_root) == 0);
1028 	    free (this_root);
1029 	}
1030     }
1031 
1032     /* call-back dir entry proc (if any) */
1033     if (dir_return == R_SKIP_ALL)
1034 	;
1035     else if (frame->direntproc != NULL)
1036     {
1037 	/* If we're doing the actual processing, call direntproc.
1038            Otherwise, assume that we need to process this directory
1039            and recurse. FIXME. */
1040 
1041 	if (process_this_directory)
1042 	    dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
1043 					    update_dir, frent->entries);
1044 	else
1045 	    dir_return = R_PROCESS;
1046     }
1047     else
1048     {
1049 	/* Generic behavior.  I don't see a reason to make the caller specify
1050 	   a direntproc just to get this.  */
1051 	if ((frame->which & W_LOCAL) && !isdir (dir))
1052 	    dir_return = R_SKIP_ALL;
1053     }
1054 
1055     free (newrepos);
1056 
1057     /* only process the dir if the return code was 0 */
1058     if (dir_return != R_SKIP_ALL)
1059     {
1060 	/* save our current directory and static vars */
1061         if (save_cwd (&cwd))
1062 	    error_exit ();
1063 	sdirlist = dirlist;
1064 	srepository = repository;
1065 	dirlist = NULL;
1066 
1067 	/* cd to the sub-directory */
1068 	if ( CVS_CHDIR (dir) < 0)
1069 	    error (1, errno, "could not chdir to %s", dir);
1070 
1071 	/* honor the global SKIP_DIRS (a.k.a. local) */
1072 	if (frame->flags == R_SKIP_DIRS)
1073 	    dir_return = R_SKIP_DIRS;
1074 
1075 	/* remember if the `.' will be stripped for subsequent dirs */
1076 	if (strcmp (update_dir, ".") == 0)
1077 	{
1078 	    update_dir[0] = '\0';
1079 	    stripped_dot = 1;
1080 	}
1081 
1082 	/* make the recursive call */
1083 	xframe = *frame;
1084 	xframe.flags = dir_return;
1085 	err += do_recursion (&xframe);
1086 
1087 	/* put the `.' back if necessary */
1088 	if (stripped_dot)
1089 	    (void) strcpy (update_dir, ".");
1090 
1091 	/* call-back dir leave proc (if any) */
1092 	if (process_this_directory && frame->dirleaveproc != NULL)
1093 	    err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir,
1094 				       frent->entries);
1095 
1096 	/* get back to where we started and restore state vars */
1097 	if (restore_cwd (&cwd, NULL))
1098 	    error_exit ();
1099 	free_cwd (&cwd);
1100 	dirlist = sdirlist;
1101 	repository = srepository;
1102     }
1103 
1104     free (update_dir);
1105     update_dir = saved_update_dir;
1106 
1107     return (err);
1108 }
1109 
1110 /*
1111  * Add a node to a list allocating the list if necessary.
1112  */
1113 static void
1114 addlist (listp, key)
1115     List **listp;
1116     char *key;
1117 {
1118     Node *p;
1119 
1120     if (*listp == NULL)
1121 	*listp = getlist ();
1122     p = getnode ();
1123     p->type = FILES;
1124     p->key = xstrdup (key);
1125     if (addnode (*listp, p) != 0)
1126 	freenode (p);
1127 }
1128 
1129 static void
1130 addfile (listp, dir, file)
1131     List **listp;
1132     char *dir;
1133     char *file;
1134 {
1135     Node *n;
1136     List *fl;
1137 
1138     /* add this dir. */
1139     addlist (listp, dir);
1140 
1141     n = findnode (*listp, dir);
1142     if (n == NULL)
1143     {
1144 	error (1, 0, "can't find recently added dir node `%s' in start_recursion.",
1145 	       dir);
1146     }
1147 
1148     n->type = DIRS;
1149     fl = (List *) n->data;
1150     addlist (&fl, file);
1151     n->data = (char *) fl;
1152     return;
1153 }
1154 
1155 static int
1156 unroll_files_proc (p, closure)
1157     Node *p;
1158     void *closure;
1159 {
1160     Node *n;
1161     struct recursion_frame *frame = (struct recursion_frame *) closure;
1162     int err = 0;
1163     List *save_dirlist;
1164     char *save_update_dir = NULL;
1165     struct saved_cwd cwd;
1166 
1167     /* if this dir was also an explicitly named argument, then skip
1168        it.  We'll catch it later when we do dirs. */
1169     n = findnode (dirlist, p->key);
1170     if (n != NULL)
1171 	return (0);
1172 
1173     /* otherwise, call dorecusion for this list of files. */
1174     filelist = (List *) p->data;
1175     p->data = NULL;
1176     save_dirlist = dirlist;
1177     dirlist = NULL;
1178 
1179     if (strcmp(p->key, ".") != 0)
1180     {
1181         if (save_cwd (&cwd))
1182 	    error_exit ();
1183 	if ( CVS_CHDIR (p->key) < 0)
1184 	    error (1, errno, "could not chdir to %s", p->key);
1185 
1186 	save_update_dir = update_dir;
1187 	update_dir = xmalloc (strlen (save_update_dir)
1188 				  + strlen (p->key)
1189 				  + 5);
1190 	strcpy (update_dir, save_update_dir);
1191 
1192 	if (*update_dir != '\0')
1193 	    (void) strcat (update_dir, "/");
1194 
1195 	(void) strcat (update_dir, p->key);
1196     }
1197 
1198     err += do_recursion (frame);
1199 
1200     if (save_update_dir != NULL)
1201     {
1202 	free (update_dir);
1203 	update_dir = save_update_dir;
1204 
1205 	if (restore_cwd (&cwd, NULL))
1206 	    error_exit ();
1207 	free_cwd (&cwd);
1208     }
1209 
1210     dirlist = save_dirlist;
1211     if (filelist)
1212 	dellist (&filelist);
1213     return(err);
1214 }
1215