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