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