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