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.3 2016/05/17 14:00:09 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 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 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 /* 752 * If filelist or dirlist is already set, we don't look again. Otherwise, 753 * find the files and directories 754 */ 755 if (filelist == NULL && dirlist == NULL) 756 { 757 /* both lists were NULL, so start from scratch */ 758 if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) 759 { 760 int lwhich = frame->which; 761 762 /* be sure to look in the attic if we have sticky tags/date */ 763 if ((lwhich & W_ATTIC) == 0) 764 if (isreadable (CVSADM_TAG)) 765 lwhich |= W_ATTIC; 766 767 /* In the !(which & W_LOCAL) case, we filled in repository 768 earlier in the function. In the (which & W_LOCAL) case, 769 the Find_Names function is going to look through the 770 Entries file. If we do not have a repository, that 771 does not make sense, so we insist upon having a 772 repository at this point. Name_Repository will give a 773 reasonable error message. */ 774 if (repository == NULL) 775 { 776 Name_Repository (NULL, update_dir); 777 assert (!"Not reached. Please report this problem to <" 778 PACKAGE_BUGREPORT ">"); 779 } 780 /* find the files and fill in entries if appropriate */ 781 if (process_this_directory) 782 { 783 filelist = Find_Names (repository, lwhich, frame->aflag, 784 &entries); 785 if (filelist == NULL) 786 { 787 error (0, 0, "skipping directory %s", update_dir); 788 /* Note that Find_Directories and the filesdoneproc 789 in particular would do bad things ("? foo.c" in 790 the case of some filesdoneproc's). */ 791 goto skip_directory; 792 } 793 } 794 } 795 796 /* find sub-directories if we will recurse */ 797 if (frame->flags != R_SKIP_DIRS) 798 dirlist = Find_Directories ( 799 process_this_directory ? repository : NULL, 800 frame->which, entries); 801 } 802 else 803 { 804 /* something was passed on the command line */ 805 if (filelist != NULL && frame->fileproc != NULL) 806 { 807 /* we will process files, so pre-parse entries */ 808 if (frame->which & W_LOCAL) 809 entries = Entries_Open (frame->aflag, NULL); 810 } 811 } 812 813 /* process the files (if any) */ 814 if (process_this_directory && filelist != NULL && frame->fileproc) 815 { 816 struct file_info finfo_struct; 817 struct frame_and_file frfile; 818 819 /* Lock the repository, if necessary. */ 820 if (repository) 821 { 822 if (locktype == CVS_LOCK_READ) 823 { 824 if (Reader_Lock (repository) != 0) 825 error (1, 0, "read lock failed - giving up"); 826 } 827 else if (locktype == CVS_LOCK_WRITE) 828 lock_dir_for_write (repository); 829 } 830 831 #ifdef CLIENT_SUPPORT 832 /* For the server, we handle notifications in a completely different 833 place (server_notify). For local, we can't do them here--we don't 834 have writelocks in place, and there is no way to get writelocks 835 here. */ 836 if (current_parsed_root->isremote) 837 notify_check (repository, update_dir); 838 #endif /* CLIENT_SUPPORT */ 839 840 finfo_struct.repository = repository; 841 finfo_struct.update_dir = update_dir; 842 finfo_struct.entries = entries; 843 /* do_file_proc will fill in finfo_struct.file. */ 844 845 frfile.finfo = &finfo_struct; 846 frfile.frame = frame; 847 848 /* process the files */ 849 err += walklist (filelist, do_file_proc, &frfile); 850 851 /* unlock it */ 852 if (/* We only lock the repository above when repository is set */ 853 repository 854 /* and when asked for a read or write lock. */ 855 && locktype != CVS_LOCK_NONE) 856 Simple_Lock_Cleanup (); 857 858 /* clean up */ 859 dellist (&filelist); 860 } 861 862 /* call-back files done proc (if any) */ 863 if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) 864 err = frame->filesdoneproc (frame->callerdat, err, repository, 865 update_dir[0] ? update_dir : ".", 866 entries); 867 868 skip_directory: 869 fileattr_write (); 870 fileattr_free (); 871 872 /* process the directories (if necessary) */ 873 if (dirlist != NULL) 874 { 875 struct frame_and_entries frent; 876 877 frent.frame = frame; 878 frent.entries = entries; 879 err += walklist (dirlist, do_dir_proc, &frent); 880 } 881 #if 0 882 else if (frame->dirleaveproc != NULL) 883 err += frame->dirleaveproc (frame->callerdat, ".", err, "."); 884 #endif 885 dellist (&dirlist); 886 887 if (entries) 888 { 889 Entries_Close (entries); 890 entries = NULL; 891 } 892 893 /* free the saved copy of the pointer if necessary */ 894 if (srepository) 895 free (srepository); 896 repository = NULL; 897 898 #ifdef HAVE_PRINT_PTR 899 TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%p)", (void *)frame); 900 #else 901 TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%lx)", 902 (unsigned long)frame); 903 #endif 904 905 return err; 906 } 907 908 909 910 /* 911 * Process each of the files in the list with the callback proc 912 * 913 * NOTES 914 * Fills in FINFO->fullname, and sometimes FINFO->rcs before 915 * calling the callback proc (FRFILE->frame->fileproc), but frees them 916 * before return. 917 * 918 * OUTPUTS 919 * Fills in FINFO->file. 920 * 921 * RETURNS 922 * 0 if we were supposed to find an RCS file but couldn't. 923 * Otherwise, returns the error code returned by the callback function. 924 */ 925 static int 926 do_file_proc (Node *p, void *closure) 927 { 928 struct frame_and_file *frfile = closure; 929 struct file_info *finfo = frfile->finfo; 930 int ret; 931 char *tmp; 932 933 finfo->file = p->key; 934 if (finfo->update_dir[0] != '\0') 935 tmp = Xasprintf ("%s/%s", finfo->update_dir, finfo->file); 936 else 937 tmp = xstrdup (finfo->file); 938 939 if (frfile->frame->dosrcs && repository) 940 { 941 finfo->rcs = RCS_parse (finfo->file, repository); 942 943 /* OK, without W_LOCAL the error handling becomes relatively 944 simple. The file names came from readdir() on the 945 repository and so we know any ENOENT is an error 946 (e.g. symlink pointing to nothing). Now, the logic could 947 be simpler - since we got the name from readdir, we could 948 just be calling RCS_parsercsfile. */ 949 if (finfo->rcs == NULL 950 && !(frfile->frame->which & W_LOCAL)) 951 { 952 error (0, 0, "could not read RCS file for %s", tmp); 953 free (tmp); 954 cvs_flushout (); 955 return 0; 956 } 957 } 958 else 959 finfo->rcs = NULL; 960 finfo->fullname = tmp; 961 ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); 962 963 freercsnode (&finfo->rcs); 964 free (tmp); 965 966 /* Allow the user to monitor progress with tail -f. Doing this once 967 per file should be no big deal, but we don't want the performance 968 hit of flushing on every line like previous versions of CVS. */ 969 cvs_flushout (); 970 971 return ret; 972 } 973 974 975 976 /* 977 * Process each of the directories in the list (recursing as we go) 978 */ 979 static int 980 do_dir_proc (Node *p, void *closure) 981 { 982 struct frame_and_entries *frent = (struct frame_and_entries *) closure; 983 struct recursion_frame *frame = frent->frame; 984 struct recursion_frame xframe; 985 char *dir = p->key; 986 char *newrepos; 987 List *sdirlist; 988 char *srepository; 989 Dtype dir_return = R_PROCESS; 990 int stripped_dot = 0; 991 int err = 0; 992 struct saved_cwd cwd; 993 char *saved_update_dir; 994 bool process_this_directory = true; 995 996 if (fncmp (dir, CVSADM) == 0) 997 { 998 /* This seems to most often happen when users (beginning users, 999 generally), try "cvs ci *" or something similar. On that 1000 theory, it is possible that we should just silently skip the 1001 CVSADM directories, but on the other hand, using a wildcard 1002 like this isn't necessarily a practice to encourage (it operates 1003 only on files which exist in the working directory, unlike 1004 regular CVS recursion). */ 1005 1006 /* FIXME-reentrancy: printed_cvs_msg should be in a "command 1007 struct" or some such, so that it gets cleared for each new 1008 command (this is possible using the remote protocol and a 1009 custom-written client). The struct recursion_frame is not 1010 far back enough though, some commands (commit at least) 1011 will call start_recursion several times. An alternate solution 1012 would be to take this whole check and move it to a new function 1013 validate_arguments or some such that all the commands call 1014 and which snips the offending directory from the argc,argv 1015 vector. */ 1016 static int printed_cvs_msg = 0; 1017 if (!printed_cvs_msg) 1018 { 1019 error (0, 0, "warning: directory %s specified in argument", 1020 dir); 1021 error (0, 0, "\ 1022 but CVS uses %s for its own purposes; skipping %s directory", 1023 CVSADM, dir); 1024 printed_cvs_msg = 1; 1025 } 1026 return 0; 1027 } 1028 1029 saved_update_dir = update_dir; 1030 update_dir = xmalloc (strlen (saved_update_dir) 1031 + strlen (dir) 1032 + 5); 1033 strcpy (update_dir, saved_update_dir); 1034 1035 /* set up update_dir - skip dots if not at start */ 1036 if (strcmp (dir, ".") != 0) 1037 { 1038 if (update_dir[0] != '\0') 1039 { 1040 (void) strcat (update_dir, "/"); 1041 (void) strcat (update_dir, dir); 1042 } 1043 else 1044 (void) strcpy (update_dir, dir); 1045 1046 /* 1047 * Here we need a plausible repository name for the sub-directory. We 1048 * create one by concatenating the new directory name onto the 1049 * previous repository name. The only case where the name should be 1050 * used is in the case where we are creating a new sub-directory for 1051 * update -d and in that case the generated name will be correct. 1052 */ 1053 if (repository == NULL) 1054 newrepos = xstrdup (""); 1055 else 1056 newrepos = Xasprintf ("%s/%s", repository, dir); 1057 } 1058 else 1059 { 1060 if (update_dir[0] == '\0') 1061 (void) strcpy (update_dir, dir); 1062 1063 if (repository == NULL) 1064 newrepos = xstrdup (""); 1065 else 1066 newrepos = xstrdup (repository); 1067 } 1068 1069 /* Check to see that the CVSADM directory, if it exists, seems to be 1070 well-formed. It can be missing files if the user hit ^C in the 1071 middle of a previous run. We want to (a) make this a nonfatal 1072 error, and (b) make sure we print which directory has the 1073 problem. 1074 1075 Do this before the direntproc, so that (1) the direntproc 1076 doesn't have to guess/deduce whether we will skip the directory 1077 (e.g. send_dirent_proc and whether to send the directory), and 1078 (2) so that the warm fuzzy doesn't get printed if we skip the 1079 directory. */ 1080 if (frame->which & W_LOCAL) 1081 { 1082 char *cvsadmdir; 1083 1084 cvsadmdir = xmalloc (strlen (dir) 1085 + sizeof (CVSADM_REP) 1086 + sizeof (CVSADM_ENT) 1087 + 80); 1088 1089 strcpy (cvsadmdir, dir); 1090 strcat (cvsadmdir, "/"); 1091 strcat (cvsadmdir, CVSADM); 1092 if (isdir (cvsadmdir)) 1093 { 1094 strcpy (cvsadmdir, dir); 1095 strcat (cvsadmdir, "/"); 1096 strcat (cvsadmdir, CVSADM_REP); 1097 if (!isfile (cvsadmdir)) 1098 { 1099 /* Some commands like update may have printed "? foo" but 1100 if we were planning to recurse, and don't on account of 1101 CVS/Repository, we want to say why. */ 1102 error (0, 0, "ignoring %s (%s missing)", update_dir, 1103 CVSADM_REP); 1104 dir_return = R_SKIP_ALL; 1105 } 1106 1107 /* Likewise for CVS/Entries. */ 1108 if (dir_return != R_SKIP_ALL) 1109 { 1110 strcpy (cvsadmdir, dir); 1111 strcat (cvsadmdir, "/"); 1112 strcat (cvsadmdir, CVSADM_ENT); 1113 if (!isfile (cvsadmdir)) 1114 { 1115 /* Some commands like update may have printed "? foo" but 1116 if we were planning to recurse, and don't on account of 1117 CVS/Repository, we want to say why. */ 1118 error (0, 0, "ignoring %s (%s missing)", update_dir, 1119 CVSADM_ENT); 1120 dir_return = R_SKIP_ALL; 1121 } 1122 } 1123 } 1124 free (cvsadmdir); 1125 } 1126 1127 /* Only process this directory if the root matches. This nearly 1128 duplicates code in do_recursion. */ 1129 1130 /* If -d was specified, it should override CVS/Root. 1131 1132 In the single-repository case, it is long-standing CVS behavior 1133 and makes sense - the user might want another access method, 1134 another server (which mounts the same repository), &c. 1135 1136 In the multiple-repository case, -d overrides all CVS/Root 1137 files. That is the only plausible generalization I can 1138 think of. */ 1139 if (CVSroot_cmdline == NULL && !server_active) 1140 { 1141 cvsroot_t *this_root = Name_Root (dir, update_dir); 1142 if (this_root != NULL) 1143 { 1144 if (findnode (root_directories, this_root->original)) 1145 { 1146 process_this_directory = 1147 !strcmp (original_parsed_root->original, 1148 this_root->original); 1149 } 1150 else 1151 { 1152 /* Add it to our list. */ 1153 1154 Node *n = getnode (); 1155 n->type = NT_UNKNOWN; 1156 n->key = xstrdup (this_root->original); 1157 n->data = this_root; 1158 1159 if (addnode (root_directories, n)) 1160 error (1, 0, "cannot add new CVSROOT %s", 1161 this_root->original); 1162 1163 process_this_directory = false; 1164 } 1165 } 1166 } 1167 1168 /* call-back dir entry proc (if any) */ 1169 if (dir_return == R_SKIP_ALL) 1170 ; 1171 else if (frame->direntproc != NULL) 1172 { 1173 /* If we're doing the actual processing, call direntproc. 1174 Otherwise, assume that we need to process this directory 1175 and recurse. FIXME. */ 1176 1177 if (process_this_directory) 1178 dir_return = frame->direntproc (frame->callerdat, dir, newrepos, 1179 update_dir, frent->entries); 1180 else 1181 dir_return = R_PROCESS; 1182 } 1183 else 1184 { 1185 /* Generic behavior. I don't see a reason to make the caller specify 1186 a direntproc just to get this. */ 1187 if ((frame->which & W_LOCAL) && !isdir (dir)) 1188 dir_return = R_SKIP_ALL; 1189 } 1190 1191 free (newrepos); 1192 1193 /* only process the dir if the return code was 0 */ 1194 if (dir_return != R_SKIP_ALL) 1195 { 1196 /* save our current directory and static vars */ 1197 if (save_cwd (&cwd)) 1198 error (1, errno, "Failed to save current directory."); 1199 sdirlist = dirlist; 1200 srepository = repository; 1201 dirlist = NULL; 1202 1203 /* cd to the sub-directory */ 1204 if (CVS_CHDIR (dir) < 0) 1205 error (1, errno, "could not chdir to %s", dir); 1206 1207 /* honor the global SKIP_DIRS (a.k.a. local) */ 1208 if (frame->flags == R_SKIP_DIRS) 1209 dir_return = R_SKIP_DIRS; 1210 1211 /* remember if the `.' will be stripped for subsequent dirs */ 1212 if (strcmp (update_dir, ".") == 0) 1213 { 1214 update_dir[0] = '\0'; 1215 stripped_dot = 1; 1216 } 1217 1218 /* make the recursive call */ 1219 xframe = *frame; 1220 xframe.flags = dir_return; 1221 /* Keep track of repository, really just for r* commands (rtag, rdiff, 1222 * co, ...) to tag_check_valid, since all the other commands use 1223 * CVS/Repository to figure it out per directory. 1224 */ 1225 if (repository) 1226 { 1227 if (strcmp (dir, ".") == 0) 1228 xframe.repository = xstrdup (repository); 1229 else 1230 xframe.repository = Xasprintf ("%s/%s", repository, dir); 1231 } 1232 else 1233 xframe.repository = NULL; 1234 err += do_recursion (&xframe); 1235 if (xframe.repository) 1236 { 1237 free (xframe.repository); 1238 xframe.repository = NULL; 1239 } 1240 1241 /* put the `.' back if necessary */ 1242 if (stripped_dot) 1243 (void) strcpy (update_dir, "."); 1244 1245 /* call-back dir leave proc (if any) */ 1246 if (process_this_directory && frame->dirleaveproc != NULL) 1247 err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, 1248 frent->entries); 1249 1250 /* get back to where we started and restore state vars */ 1251 if (restore_cwd (&cwd)) 1252 error (1, errno, "Failed to restore current directory, `%s'.", 1253 cwd.name); 1254 free_cwd (&cwd); 1255 dirlist = sdirlist; 1256 repository = srepository; 1257 } 1258 1259 free (update_dir); 1260 update_dir = saved_update_dir; 1261 1262 return err; 1263 } 1264 1265 /* 1266 * Add a node to a list allocating the list if necessary. 1267 */ 1268 static void 1269 addlist (List **listp, char *key) 1270 { 1271 Node *p; 1272 1273 if (*listp == NULL) 1274 *listp = getlist (); 1275 p = getnode (); 1276 p->type = FILES; 1277 p->key = xstrdup (key); 1278 if (addnode (*listp, p) != 0) 1279 freenode (p); 1280 } 1281 1282 static void 1283 addfile (List **listp, char *dir, char *file) 1284 { 1285 Node *n; 1286 List *fl; 1287 1288 /* add this dir. */ 1289 addlist (listp, dir); 1290 1291 n = findnode (*listp, dir); 1292 if (n == NULL) 1293 { 1294 error (1, 0, "can't find recently added dir node `%s' in start_recursion.", 1295 dir); 1296 } 1297 1298 n->type = DIRS; 1299 fl = n->data; 1300 addlist (&fl, file); 1301 n->data = fl; 1302 return; 1303 } 1304 1305 static int 1306 unroll_files_proc (Node *p, void *closure) 1307 { 1308 Node *n; 1309 struct recursion_frame *frame = (struct recursion_frame *) closure; 1310 int err = 0; 1311 List *save_dirlist; 1312 char *save_update_dir = NULL; 1313 struct saved_cwd cwd; 1314 1315 /* if this dir was also an explicitly named argument, then skip 1316 it. We'll catch it later when we do dirs. */ 1317 n = findnode (dirlist, p->key); 1318 if (n != NULL) 1319 return (0); 1320 1321 /* otherwise, call dorecusion for this list of files. */ 1322 filelist = p->data; 1323 p->data = NULL; 1324 save_dirlist = dirlist; 1325 dirlist = NULL; 1326 1327 if (strcmp(p->key, ".") != 0) 1328 { 1329 if (save_cwd (&cwd)) 1330 error (1, errno, "Failed to save current directory."); 1331 if ( CVS_CHDIR (p->key) < 0) 1332 error (1, errno, "could not chdir to %s", p->key); 1333 1334 save_update_dir = update_dir; 1335 update_dir = xmalloc (strlen (save_update_dir) 1336 + strlen (p->key) 1337 + 5); 1338 strcpy (update_dir, save_update_dir); 1339 1340 if (*update_dir != '\0') 1341 (void) strcat (update_dir, "/"); 1342 1343 (void) strcat (update_dir, p->key); 1344 } 1345 1346 err += do_recursion (frame); 1347 1348 if (save_update_dir != NULL) 1349 { 1350 free (update_dir); 1351 update_dir = save_update_dir; 1352 1353 if (restore_cwd (&cwd)) 1354 error (1, errno, "Failed to restore current directory, `%s'.", 1355 cwd.name); 1356 free_cwd (&cwd); 1357 } 1358 1359 dirlist = save_dirlist; 1360 if (filelist) 1361 dellist (&filelist); 1362 return(err); 1363 } 1364 /* vim:tabstop=8:shiftwidth=4 1365 */ 1366