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