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 */
16 #include <sys/cdefs.h>
17 __RCSID("$NetBSD: recurse.c,v 1.4 2024/02/04 20:47:25 christos Exp $");
18
19 #include "cvs.h"
20 #include "save-cwd.h"
21 #include "fileattr.h"
22 #include "edit.h"
23
24 static int do_dir_proc (Node * p, void *closure);
25 static int do_file_proc (Node * p, void *closure);
26 static void addlist (List ** listp, char *key);
27 static int unroll_files_proc (Node *p, void *closure);
28 static void addfile (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 (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 * INPUT
74 *
75 * fileproc
76 * Function called with each file as an argument.
77 *
78 * filesdoneproc
79 * Function called after all the files in a directory have been processed,
80 * before subdirectories have been processed.
81 *
82 * direntproc
83 * Function called immediately upon entering a directory, before processing
84 * any files or subdirectories.
85 *
86 * dirleaveproc
87 * Function called upon finishing a directory, immediately before leaving
88 * it and returning control to the function processing the parent
89 * directory.
90 *
91 * callerdat
92 * This void * is passed to the functions above.
93 *
94 * argc, argv
95 * The files on which to operate, interpreted as command line arguments.
96 * In the special case of no arguments, defaults to operating on the
97 * current directory (`.').
98 *
99 * local
100 *
101 * which
102 * specifies the kind of recursion. There are several cases:
103 *
104 * 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current
105 * directory when we are called must be the repository and
106 * recursion proceeds according to what exists in the repository.
107 *
108 * 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The
109 * current directory when we are called must be the working
110 * directory. Recursion proceeds according to what exists in the
111 * working directory, never (I think) consulting any part of the
112 * repository which does not correspond to the working directory
113 * ("correspond" == Name_Repository).
114 *
115 * 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the
116 * weird one. The current directory when we are called must be
117 * the working directory. We recurse through working directories,
118 * but we recurse into a directory if it is exists in the working
119 * directory *or* it exists in the repository. If a directory
120 * does not exist in the working directory, the direntproc must
121 * either tell us to skip it (R_SKIP_ALL), or must create it (I
122 * think those are the only two cases).
123 *
124 * aflag
125 * locktype
126 * update_preload
127 * dosrcs
128 *
129 * repository_in
130 * keeps track of the repository string. This is only for the remote mode,
131 * specifically, r* commands (rtag, rdiff, co, ...) where xgetcwd() was used
132 * to locate the repository. Things would break when xgetcwd() was used
133 * with a symlinked repository because xgetcwd() would return the true path
134 * and in some cases this would cause the path to be printed as other than
135 * the user specified in error messages and in other cases some of CVS's
136 * security assertions would fail.
137 *
138 * GLOBALS
139 * ???
140 *
141 * OUTPUT
142 *
143 * callerdat can be modified by the FILEPROC, FILESDONEPROC, DIRENTPROC, and
144 * DIRLEAVEPROC.
145 *
146 * RETURNS
147 * A count of errors counted by walking the argument list with
148 * unroll_files_proc() and do_recursion().
149 *
150 * ERRORS
151 * Fatal errors occur:
152 * 1. when there were no arguments and the current directory
153 * does not contain CVS metadata.
154 * 2. when all but the last path element from an argument from ARGV cannot
155 * be found to be a local directory.
156 */
157 int
start_recursion(FILEPROC fileproc,FILESDONEPROC filesdoneproc,DIRENTPROC direntproc,DIRLEAVEPROC dirleaveproc,void * callerdat,int argc,char ** argv,int local,int which,int aflag,int locktype,char * update_preload,int dosrcs,char * repository_in)158 start_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc,
159 DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc,
160 void *callerdat, int argc, char **argv, int local,
161 int which, int aflag, int locktype,
162 char *update_preload, int dosrcs, char *repository_in)
163 {
164 int i, err = 0;
165 #ifdef CLIENT_SUPPORT
166 List *args_to_send_when_finished = NULL;
167 #endif
168 List *files_by_dir = NULL;
169 struct recursion_frame frame;
170
171 #ifdef HAVE_PRINTF_PTR
172 TRACE ( TRACE_FLOW,
173 "start_recursion ( fileproc=%p, filesdoneproc=%p,\n"
174 " direntproc=%p, dirleavproc=%p,\n"
175 " callerdat=%p, argc=%d, argv=%p,\n"
176 " local=%d, which=%d, aflag=%d,\n"
177 " locktype=%d, update_preload=%s\n"
178 " dosrcs=%d, repository_in=%s )",
179 (void *) fileproc, (void *) filesdoneproc,
180 (void *) direntproc, (void *) dirleaveproc,
181 (void *) callerdat, argc, (void *) argv,
182 local, which, aflag, locktype,
183 update_preload ? update_preload : "(null)", dosrcs,
184 repository_in ? repository_in : "(null)");
185 #else
186 TRACE ( TRACE_FLOW,
187 "start_recursion ( fileproc=%lx, filesdoneproc=%lx,\n"
188 " direntproc=%lx, dirleavproc=%lx,\n"
189 " callerdat=%lx, argc=%d, argv=%lx,\n"
190 " local=%d, which=%d, aflag=%d,\n"
191 " locktype=%d, update_preload=%s\n"
192 " dosrcs=%d, repository_in=%s )",
193 (unsigned long) fileproc, (unsigned long) filesdoneproc,
194 (unsigned long) direntproc, (unsigned long) dirleaveproc,
195 (unsigned long) callerdat, argc, (unsigned long) argv,
196 local, which, aflag, locktype,
197 update_preload ? update_preload : "(null)", dosrcs,
198 repository_in ? repository_in : "(null)");
199 #endif
200
201 frame.fileproc = fileproc;
202 frame.filesdoneproc = filesdoneproc;
203 frame.direntproc = direntproc;
204 frame.dirleaveproc = dirleaveproc;
205 frame.callerdat = callerdat;
206 frame.flags = local ? R_SKIP_DIRS : R_PROCESS;
207 frame.which = which;
208 frame.aflag = aflag;
209 frame.locktype = locktype;
210 frame.dosrcs = dosrcs;
211
212 /* If our repository_in has a trailing "/.", remove it before storing it
213 * for do_recursion().
214 *
215 * FIXME: This is somewhat of a hack in the sense that many of our callers
216 * painstakingly compute and add the trailing '.' we now remove.
217 */
218 while (repository_in && strlen (repository_in) >= 2
219 && repository_in[strlen (repository_in) - 2] == '/'
220 && repository_in[strlen (repository_in) - 1] == '.')
221 {
222 /* Beware the case where the string is exactly "/." or "//.".
223 * Paths with a leading "//" are special on some early UNIXes.
224 */
225 if (strlen (repository_in) == 2 || strlen (repository_in) == 3)
226 repository_in[strlen (repository_in) - 1] = '\0';
227 else
228 repository_in[strlen (repository_in) - 2] = '\0';
229 }
230 frame.repository = repository_in;
231
232 expand_wild (argc, argv, &argc, &argv);
233
234 if (update_preload == NULL)
235 update_dir = xstrdup ("");
236 else
237 update_dir = xstrdup (update_preload);
238
239 /* clean up from any previous calls to start_recursion */
240 if (repository)
241 {
242 free (repository);
243 repository = NULL;
244 }
245 if (filelist)
246 dellist (&filelist); /* FIXME-krp: no longer correct. */
247 if (dirlist)
248 dellist (&dirlist);
249
250 #ifdef SERVER_SUPPORT
251 if (server_active)
252 {
253 for (i = 0; i < argc; ++i)
254 server_pathname_check (argv[i]);
255 }
256 #endif
257
258 if (argc == 0)
259 {
260 int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM);
261
262 #ifdef CLIENT_SUPPORT
263 if (!just_subdirs
264 && CVSroot_cmdline == NULL
265 && current_parsed_root->isremote)
266 {
267 cvsroot_t *root = Name_Root (NULL, update_dir);
268 if (root)
269 {
270 if (strcmp (root->original, original_parsed_root->original))
271 /* We're skipping this directory because it is for
272 * a different root. Therefore, we just want to
273 * do the subdirectories only. Processing files would
274 * cause a working directory from one repository to be
275 * processed against a different repository, which could
276 * cause all kinds of spurious conflicts and such.
277 *
278 * Question: what about the case of "cvs update foo"
279 * where we process foo/bar and not foo itself? That
280 * seems to be handled somewhere (else) but why should
281 * it be a separate case? Needs investigation... */
282 just_subdirs = 1;
283 }
284 }
285 #endif
286
287 /*
288 * There were no arguments, so we'll probably just recurse. The
289 * exception to the rule is when we are called from a directory
290 * without any CVS administration files. That has always meant to
291 * process each of the sub-directories, so we pretend like we were
292 * called with the list of sub-dirs of the current dir as args
293 */
294 if (just_subdirs)
295 {
296 dirlist = Find_Directories (NULL, W_LOCAL, NULL);
297 /* If there are no sub-directories, there is a certain logic in
298 favor of doing nothing, but in fact probably the user is just
299 confused about what directory they are in, or whether they
300 cvs add'd a new directory. In the case of at least one
301 sub-directory, at least when we recurse into them we
302 notice (hopefully) whether they are under CVS control. */
303 if (list_isempty (dirlist))
304 {
305 if (update_dir[0] == '\0')
306 error (0, 0, "in directory .:");
307 else
308 error (0, 0, "in directory %s:", update_dir);
309 error (1, 0,
310 "there is no version here; run '%s checkout' first",
311 program_name);
312 }
313 #ifdef CLIENT_SUPPORT
314 else if (current_parsed_root->isremote && server_started)
315 {
316 /* In the the case "cvs update foo bar baz", a call to
317 send_file_names in update.c will have sent the
318 appropriate "Argument" commands to the server. In
319 this case, that won't have happened, so we need to
320 do it here. While this example uses "update", this
321 generalizes to other commands. */
322
323 /* This is the same call to Find_Directories as above.
324 FIXME: perhaps it would be better to write a
325 function that duplicates a list. */
326 args_to_send_when_finished = Find_Directories (NULL,
327 W_LOCAL,
328 NULL);
329 }
330 #endif
331 }
332 else
333 addlist (&dirlist, ".");
334
335 goto do_the_work;
336 }
337
338
339 /*
340 * There were arguments, so we have to handle them by hand. To do
341 * that, we set up the filelist and dirlist with the arguments and
342 * call do_recursion. do_recursion recognizes the fact that the
343 * lists are non-null when it starts and doesn't update them.
344 *
345 * explicitly named directories are stored in dirlist.
346 * explicitly named files are stored in filelist.
347 * other possibility is named entities whicha are not currently in
348 * the working directory.
349 */
350
351 for (i = 0; i < argc; i++)
352 {
353 /* if this argument is a directory, then add it to the list of
354 directories. */
355
356 if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i]))
357 {
358 strip_trailing_slashes (argv[i]);
359 addlist (&dirlist, argv[i]);
360 }
361 else
362 {
363 /* otherwise, split argument into directory and component names. */
364 char *dir;
365 char *comp;
366 char *file_to_try;
367
368 /* Now break out argv[i] into directory part (DIR) and file part
369 * (COMP). DIR and COMP will each point to a newly malloc'd
370 * string.
371 */
372 dir = xstrdup (argv[i]);
373 /* It's okay to cast out last_component's const below since we know
374 * we just allocated dir here and have control of it.
375 */
376 comp = (char *)last_component (dir);
377 if (comp == dir)
378 {
379 /* no dir component. What we have is an implied "./" */
380 dir = xstrdup(".");
381 }
382 else
383 {
384 comp[-1] = '\0';
385 comp = xstrdup (comp);
386 }
387
388 /* if this argument exists as a file in the current
389 working directory tree, then add it to the files list. */
390
391 if (!(which & W_LOCAL))
392 {
393 /* If doing rtag, we've done a chdir to the repository. */
394 file_to_try = Xasprintf ("%s%s", argv[i], RCSEXT);
395 }
396 else
397 file_to_try = xstrdup (argv[i]);
398
399 if (isfile (file_to_try))
400 addfile (&files_by_dir, dir, comp);
401 else if (isdir (dir))
402 {
403 if ((which & W_LOCAL) && isdir (CVSADM) &&
404 !current_parsed_root->isremote)
405 {
406 /* otherwise, look for it in the repository. */
407 char *tmp_update_dir;
408 char *repos;
409 char *reposfile;
410
411 tmp_update_dir = xmalloc (strlen (update_dir)
412 + strlen (dir)
413 + 5);
414 strcpy (tmp_update_dir, update_dir);
415
416 if (*tmp_update_dir != '\0')
417 strcat (tmp_update_dir, "/");
418
419 strcat (tmp_update_dir, dir);
420
421 /* look for it in the repository. */
422 repos = Name_Repository (dir, tmp_update_dir);
423 reposfile = Xasprintf ("%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
do_recursion(struct recursion_frame * frame)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 = (nolock || 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 -d was specified, it should override CVS/Root.
681
682 In the single-repository case, it is long-standing CVS behavior
683 and makes sense - the user might want another access method,
684 another server (which mounts the same repository), &c.
685
686 In the multiple-repository case, -d overrides all CVS/Root
687 files. That is the only plausible generalization I can
688 think of. */
689 if (CVSroot_cmdline == NULL && !server_active)
690 {
691 cvsroot_t *this_root = Name_Root (NULL, update_dir);
692 if (this_root != NULL)
693 {
694 if (findnode (root_directories, this_root->original))
695 {
696 process_this_directory =
697 !strcmp (original_parsed_root->original,
698 this_root->original);
699 }
700 else
701 {
702 /* Add it to our list. */
703
704 Node *n = getnode ();
705 n->type = NT_UNKNOWN;
706 n->key = xstrdup (this_root->original);
707 n->data = this_root;
708
709 if (addnode (root_directories, n))
710 error (1, 0, "cannot add new CVSROOT %s",
711 this_root->original);
712
713 process_this_directory = false;
714 }
715 }
716 }
717
718 /*
719 * Fill in repository with the current repository
720 */
721 if (frame->which & W_LOCAL)
722 {
723 if (isdir (CVSADM))
724 {
725 repository = Name_Repository (NULL, update_dir);
726 srepository = repository; /* remember what to free */
727 }
728 else
729 repository = NULL;
730 }
731 else
732 {
733 repository = frame->repository;
734 assert (repository != NULL);
735 assert (strstr (repository, "/./") == NULL);
736 }
737
738 fileattr_startdir (repository);
739
740 /*
741 * The filesdoneproc needs to be called for each directory where files
742 * processed, or each directory that is processed by a call where no
743 * directories were passed in. In fact, the only time we don't want to
744 * call back the filesdoneproc is when we are processing directories that
745 * were passed in on the command line (or in the special case of `.' when
746 * we were called with no args
747 */
748 if (dirlist != NULL && filelist == NULL)
749 dodoneproc = 0;
750
751 processing = "scan";
752 /*
753 * If filelist or dirlist is already set, we don't look again. Otherwise,
754 * find the files and directories
755 */
756 if (filelist == NULL && dirlist == NULL)
757 {
758 /* both lists were NULL, so start from scratch */
759 if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES)
760 {
761 int lwhich = frame->which;
762
763 /* be sure to look in the attic if we have sticky tags/date */
764 if ((lwhich & W_ATTIC) == 0)
765 if (isreadable (CVSADM_TAG))
766 lwhich |= W_ATTIC;
767
768 /* In the !(which & W_LOCAL) case, we filled in repository
769 earlier in the function. In the (which & W_LOCAL) case,
770 the Find_Names function is going to look through the
771 Entries file. If we do not have a repository, that
772 does not make sense, so we insist upon having a
773 repository at this point. Name_Repository will give a
774 reasonable error message. */
775 if (repository == NULL)
776 {
777 Name_Repository (NULL, update_dir);
778 assert (!"Not reached. Please report this problem to <"
779 PACKAGE_BUGREPORT ">");
780 }
781 /* find the files and fill in entries if appropriate */
782 if (process_this_directory)
783 {
784 filelist = Find_Names (repository, lwhich, frame->aflag,
785 &entries);
786 if (filelist == NULL)
787 {
788 error (0, 0, "skipping directory %s", update_dir);
789 /* Note that Find_Directories and the filesdoneproc
790 in particular would do bad things ("? foo.c" in
791 the case of some filesdoneproc's). */
792 goto skip_directory;
793 }
794 }
795 }
796
797 /* find sub-directories if we will recurse */
798 if (frame->flags != R_SKIP_DIRS)
799 dirlist = Find_Directories (
800 process_this_directory ? repository : NULL,
801 frame->which, entries);
802 }
803 else
804 {
805 /* something was passed on the command line */
806 if (filelist != NULL && frame->fileproc != NULL)
807 {
808 /* we will process files, so pre-parse entries */
809 if (frame->which & W_LOCAL)
810 entries = Entries_Open (frame->aflag, NULL);
811 }
812 }
813
814 processing = "process";
815
816 /* process the files (if any) */
817 if (process_this_directory && filelist != NULL && frame->fileproc)
818 {
819 struct file_info finfo_struct;
820 struct frame_and_file frfile;
821
822 /* Lock the repository, if necessary. */
823 if (repository)
824 {
825 if (locktype == CVS_LOCK_READ)
826 {
827 if (Reader_Lock (repository) != 0)
828 error (1, 0, "read lock failed - giving up");
829 }
830 else if (locktype == CVS_LOCK_WRITE)
831 lock_dir_for_write (repository);
832 }
833
834 #ifdef CLIENT_SUPPORT
835 /* For the server, we handle notifications in a completely different
836 place (server_notify). For local, we can't do them here--we don't
837 have writelocks in place, and there is no way to get writelocks
838 here. */
839 if (current_parsed_root->isremote)
840 notify_check (repository, update_dir);
841 #endif /* CLIENT_SUPPORT */
842
843 finfo_struct.repository = repository;
844 finfo_struct.update_dir = update_dir;
845 finfo_struct.entries = entries;
846 /* do_file_proc will fill in finfo_struct.file. */
847
848 frfile.finfo = &finfo_struct;
849 frfile.frame = frame;
850
851 /* process the files */
852 err += walklist (filelist, do_file_proc, &frfile);
853
854 /* unlock it */
855 if (/* We only lock the repository above when repository is set */
856 repository
857 /* and when asked for a read or write lock. */
858 && locktype != CVS_LOCK_NONE)
859 Simple_Lock_Cleanup ();
860
861 /* clean up */
862 dellist (&filelist);
863 }
864
865 processing = "cleanup";
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
do_file_proc(Node * p,void * closure)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
do_dir_proc(Node * p,void * closure)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 newrepos = Xasprintf ("%s/%s", repository, dir);
1062 }
1063 else
1064 {
1065 if (update_dir[0] == '\0')
1066 (void) strcpy (update_dir, dir);
1067
1068 if (repository == NULL)
1069 newrepos = xstrdup ("");
1070 else
1071 newrepos = xstrdup (repository);
1072 }
1073
1074 /* Check to see that the CVSADM directory, if it exists, seems to be
1075 well-formed. It can be missing files if the user hit ^C in the
1076 middle of a previous run. We want to (a) make this a nonfatal
1077 error, and (b) make sure we print which directory has the
1078 problem.
1079
1080 Do this before the direntproc, so that (1) the direntproc
1081 doesn't have to guess/deduce whether we will skip the directory
1082 (e.g. send_dirent_proc and whether to send the directory), and
1083 (2) so that the warm fuzzy doesn't get printed if we skip the
1084 directory. */
1085 if (frame->which & W_LOCAL)
1086 {
1087 char *cvsadmdir;
1088
1089 cvsadmdir = xmalloc (strlen (dir)
1090 + sizeof (CVSADM_REP)
1091 + sizeof (CVSADM_ENT)
1092 + 80);
1093
1094 strcpy (cvsadmdir, dir);
1095 strcat (cvsadmdir, "/");
1096 strcat (cvsadmdir, CVSADM);
1097 if (isdir (cvsadmdir))
1098 {
1099 strcpy (cvsadmdir, dir);
1100 strcat (cvsadmdir, "/");
1101 strcat (cvsadmdir, CVSADM_REP);
1102 if (!isfile (cvsadmdir))
1103 {
1104 /* Some commands like update may have printed "? foo" but
1105 if we were planning to recurse, and don't on account of
1106 CVS/Repository, we want to say why. */
1107 error (0, 0, "ignoring %s (%s missing)", update_dir,
1108 CVSADM_REP);
1109 dir_return = R_SKIP_ALL;
1110 }
1111
1112 /* Likewise for CVS/Entries. */
1113 if (dir_return != R_SKIP_ALL)
1114 {
1115 strcpy (cvsadmdir, dir);
1116 strcat (cvsadmdir, "/");
1117 strcat (cvsadmdir, CVSADM_ENT);
1118 if (!isfile (cvsadmdir))
1119 {
1120 /* Some commands like update may have printed "? foo" but
1121 if we were planning to recurse, and don't on account of
1122 CVS/Repository, we want to say why. */
1123 error (0, 0, "ignoring %s (%s missing)", update_dir,
1124 CVSADM_ENT);
1125 dir_return = R_SKIP_ALL;
1126 }
1127 }
1128 }
1129 free (cvsadmdir);
1130 }
1131
1132 /* Only process this directory if the root matches. This nearly
1133 duplicates code in do_recursion. */
1134
1135 /* If -d was specified, it should override CVS/Root.
1136
1137 In the single-repository case, it is long-standing CVS behavior
1138 and makes sense - the user might want another access method,
1139 another server (which mounts the same repository), &c.
1140
1141 In the multiple-repository case, -d overrides all CVS/Root
1142 files. That is the only plausible generalization I can
1143 think of. */
1144 if (CVSroot_cmdline == NULL && !server_active)
1145 {
1146 cvsroot_t *this_root = Name_Root (dir, update_dir);
1147 if (this_root != NULL)
1148 {
1149 if (findnode (root_directories, this_root->original))
1150 {
1151 process_this_directory =
1152 !strcmp (original_parsed_root->original,
1153 this_root->original);
1154 }
1155 else
1156 {
1157 /* Add it to our list. */
1158
1159 Node *n = getnode ();
1160 n->type = NT_UNKNOWN;
1161 n->key = xstrdup (this_root->original);
1162 n->data = this_root;
1163
1164 if (addnode (root_directories, n))
1165 error (1, 0, "cannot add new CVSROOT %s",
1166 this_root->original);
1167
1168 process_this_directory = false;
1169 }
1170 }
1171 }
1172
1173 /* call-back dir entry proc (if any) */
1174 if (dir_return == R_SKIP_ALL)
1175 ;
1176 else if (frame->direntproc != NULL)
1177 {
1178 /* If we're doing the actual processing, call direntproc.
1179 Otherwise, assume that we need to process this directory
1180 and recurse. FIXME. */
1181
1182 if (process_this_directory)
1183 dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
1184 update_dir, frent->entries);
1185 else
1186 dir_return = R_PROCESS;
1187 }
1188 else
1189 {
1190 /* Generic behavior. I don't see a reason to make the caller specify
1191 a direntproc just to get this. */
1192 if ((frame->which & W_LOCAL) && !isdir (dir))
1193 dir_return = R_SKIP_ALL;
1194 }
1195
1196 free (newrepos);
1197
1198 /* only process the dir if the return code was 0 */
1199 if (dir_return != R_SKIP_ALL)
1200 {
1201 /* save our current directory and static vars */
1202 if (save_cwd (&cwd))
1203 error (1, errno, "Failed to save current directory.");
1204 sdirlist = dirlist;
1205 srepository = repository;
1206 dirlist = NULL;
1207
1208 /* cd to the sub-directory */
1209 if (CVS_CHDIR (dir) < 0)
1210 error (1, errno, "could not chdir to %s", dir);
1211
1212 /* honor the global SKIP_DIRS (a.k.a. local) */
1213 if (frame->flags == R_SKIP_DIRS)
1214 dir_return = R_SKIP_DIRS;
1215
1216 /* remember if the `.' will be stripped for subsequent dirs */
1217 if (strcmp (update_dir, ".") == 0)
1218 {
1219 update_dir[0] = '\0';
1220 stripped_dot = 1;
1221 }
1222
1223 /* make the recursive call */
1224 xframe = *frame;
1225 xframe.flags = dir_return;
1226 /* Keep track of repository, really just for r* commands (rtag, rdiff,
1227 * co, ...) to tag_check_valid, since all the other commands use
1228 * CVS/Repository to figure it out per directory.
1229 */
1230 if (repository)
1231 {
1232 if (strcmp (dir, ".") == 0)
1233 xframe.repository = xstrdup (repository);
1234 else
1235 xframe.repository = Xasprintf ("%s/%s", repository, dir);
1236 }
1237 else
1238 xframe.repository = NULL;
1239 err += do_recursion (&xframe);
1240 if (xframe.repository)
1241 {
1242 free (xframe.repository);
1243 xframe.repository = NULL;
1244 }
1245
1246 /* put the `.' back if necessary */
1247 if (stripped_dot)
1248 (void) strcpy (update_dir, ".");
1249
1250 /* call-back dir leave proc (if any) */
1251 if (process_this_directory && frame->dirleaveproc != NULL)
1252 err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir,
1253 frent->entries);
1254
1255 /* get back to where we started and restore state vars */
1256 if (restore_cwd (&cwd))
1257 error (1, errno, "Failed to restore current directory, `%s'.",
1258 cwd.name);
1259 free_cwd (&cwd);
1260 dirlist = sdirlist;
1261 repository = srepository;
1262 }
1263
1264 free (update_dir);
1265 update_dir = saved_update_dir;
1266
1267 return err;
1268 }
1269
1270 /*
1271 * Add a node to a list allocating the list if necessary.
1272 */
1273 static void
addlist(List ** listp,char * key)1274 addlist (List **listp, char *key)
1275 {
1276 Node *p;
1277
1278 if (*listp == NULL)
1279 *listp = getlist ();
1280 p = getnode ();
1281 p->type = FILES;
1282 p->key = xstrdup (key);
1283 if (addnode (*listp, p) != 0)
1284 freenode (p);
1285 }
1286
1287 static void
addfile(List ** listp,char * dir,char * file)1288 addfile (List **listp, char *dir, char *file)
1289 {
1290 Node *n;
1291 List *fl;
1292
1293 /* add this dir. */
1294 addlist (listp, dir);
1295
1296 n = findnode (*listp, dir);
1297 if (n == NULL)
1298 {
1299 error (1, 0, "can't find recently added dir node `%s' in start_recursion.",
1300 dir);
1301 }
1302
1303 n->type = DIRS;
1304 fl = n->data;
1305 addlist (&fl, file);
1306 n->data = fl;
1307 return;
1308 }
1309
1310 static int
unroll_files_proc(Node * p,void * closure)1311 unroll_files_proc (Node *p, void *closure)
1312 {
1313 Node *n;
1314 struct recursion_frame *frame = (struct recursion_frame *) closure;
1315 int err = 0;
1316 List *save_dirlist;
1317 char *save_update_dir = NULL;
1318 struct saved_cwd cwd;
1319
1320 /* if this dir was also an explicitly named argument, then skip
1321 it. We'll catch it later when we do dirs. */
1322 n = findnode (dirlist, p->key);
1323 if (n != NULL)
1324 return (0);
1325
1326 /* otherwise, call dorecusion for this list of files. */
1327 filelist = p->data;
1328 p->data = NULL;
1329 save_dirlist = dirlist;
1330 dirlist = NULL;
1331
1332 if (strcmp(p->key, ".") != 0)
1333 {
1334 if (save_cwd (&cwd))
1335 error (1, errno, "Failed to save current directory.");
1336 if ( CVS_CHDIR (p->key) < 0)
1337 error (1, errno, "could not chdir to %s", p->key);
1338
1339 save_update_dir = update_dir;
1340 update_dir = xmalloc (strlen (save_update_dir)
1341 + strlen (p->key)
1342 + 5);
1343 strcpy (update_dir, save_update_dir);
1344
1345 if (*update_dir != '\0')
1346 (void) strcat (update_dir, "/");
1347
1348 (void) strcat (update_dir, p->key);
1349 }
1350
1351 err += do_recursion (frame);
1352
1353 if (save_update_dir != NULL)
1354 {
1355 free (update_dir);
1356 update_dir = save_update_dir;
1357
1358 if (restore_cwd (&cwd))
1359 error (1, errno, "Failed to restore current directory, `%s'.",
1360 cwd.name);
1361 free_cwd (&cwd);
1362 }
1363
1364 dirlist = save_dirlist;
1365 if (filelist)
1366 dellist (&filelist);
1367 return(err);
1368 }
1369 /* vim:tabstop=8:shiftwidth=4
1370 */
1371