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 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 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 931 do_file_proc (Node *p, void *closure) 932 { 933 struct frame_and_file *frfile = closure; 934 struct file_info *finfo = frfile->finfo; 935 int ret; 936 char *tmp; 937 938 finfo->file = p->key; 939 if (finfo->update_dir[0] != '\0') 940 tmp = Xasprintf ("%s/%s", finfo->update_dir, finfo->file); 941 else 942 tmp = xstrdup (finfo->file); 943 944 if (frfile->frame->dosrcs && repository) 945 { 946 finfo->rcs = RCS_parse (finfo->file, repository); 947 948 /* OK, without W_LOCAL the error handling becomes relatively 949 simple. The file names came from readdir() on the 950 repository and so we know any ENOENT is an error 951 (e.g. symlink pointing to nothing). Now, the logic could 952 be simpler - since we got the name from readdir, we could 953 just be calling RCS_parsercsfile. */ 954 if (finfo->rcs == NULL 955 && !(frfile->frame->which & W_LOCAL)) 956 { 957 error (0, 0, "could not read RCS file for %s", tmp); 958 free (tmp); 959 cvs_flushout (); 960 return 0; 961 } 962 } 963 else 964 finfo->rcs = NULL; 965 finfo->fullname = tmp; 966 ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); 967 968 freercsnode (&finfo->rcs); 969 free (tmp); 970 971 /* Allow the user to monitor progress with tail -f. Doing this once 972 per file should be no big deal, but we don't want the performance 973 hit of flushing on every line like previous versions of CVS. */ 974 cvs_flushout (); 975 976 return ret; 977 } 978 979 980 981 /* 982 * Process each of the directories in the list (recursing as we go) 983 */ 984 static int 985 do_dir_proc (Node *p, void *closure) 986 { 987 struct frame_and_entries *frent = (struct frame_and_entries *) closure; 988 struct recursion_frame *frame = frent->frame; 989 struct recursion_frame xframe; 990 char *dir = p->key; 991 char *newrepos; 992 List *sdirlist; 993 char *srepository; 994 Dtype dir_return = R_PROCESS; 995 int stripped_dot = 0; 996 int err = 0; 997 struct saved_cwd cwd; 998 char *saved_update_dir; 999 bool process_this_directory = true; 1000 1001 if (fncmp (dir, CVSADM) == 0) 1002 { 1003 /* This seems to most often happen when users (beginning users, 1004 generally), try "cvs ci *" or something similar. On that 1005 theory, it is possible that we should just silently skip the 1006 CVSADM directories, but on the other hand, using a wildcard 1007 like this isn't necessarily a practice to encourage (it operates 1008 only on files which exist in the working directory, unlike 1009 regular CVS recursion). */ 1010 1011 /* FIXME-reentrancy: printed_cvs_msg should be in a "command 1012 struct" or some such, so that it gets cleared for each new 1013 command (this is possible using the remote protocol and a 1014 custom-written client). The struct recursion_frame is not 1015 far back enough though, some commands (commit at least) 1016 will call start_recursion several times. An alternate solution 1017 would be to take this whole check and move it to a new function 1018 validate_arguments or some such that all the commands call 1019 and which snips the offending directory from the argc,argv 1020 vector. */ 1021 static int printed_cvs_msg = 0; 1022 if (!printed_cvs_msg) 1023 { 1024 error (0, 0, "warning: directory %s specified in argument", 1025 dir); 1026 error (0, 0, "\ 1027 but CVS uses %s for its own purposes; skipping %s directory", 1028 CVSADM, dir); 1029 printed_cvs_msg = 1; 1030 } 1031 return 0; 1032 } 1033 1034 saved_update_dir = update_dir; 1035 update_dir = xmalloc (strlen (saved_update_dir) 1036 + strlen (dir) 1037 + 5); 1038 strcpy (update_dir, saved_update_dir); 1039 1040 /* set up update_dir - skip dots if not at start */ 1041 if (strcmp (dir, ".") != 0) 1042 { 1043 if (update_dir[0] != '\0') 1044 { 1045 (void) strcat (update_dir, "/"); 1046 (void) strcat (update_dir, dir); 1047 } 1048 else 1049 (void) strcpy (update_dir, dir); 1050 1051 /* 1052 * Here we need a plausible repository name for the sub-directory. We 1053 * create one by concatenating the new directory name onto the 1054 * previous repository name. The only case where the name should be 1055 * used is in the case where we are creating a new sub-directory for 1056 * update -d and in that case the generated name will be correct. 1057 */ 1058 if (repository == NULL) 1059 newrepos = xstrdup (""); 1060 else 1061 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 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 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 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