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