1263367Semaste/* 2254721Semaste * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3254721Semaste * 4254721Semaste * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5254721Semaste * and others. 6254721Semaste * 7254721Semaste * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8254721Semaste * Portions Copyright (C) 1989-1992, Brian Berliner 9254721Semaste * 10254721Semaste * You may distribute under the terms of the GNU General Public License as 11254721Semaste * specified in the README file that comes with the CVS source distribution. 12254721Semaste * 13254721Semaste * General recursion handler 14254721Semaste * 15254721Semaste */ 16269024Semaste#include <sys/cdefs.h> 17254721Semaste__RCSID("$NetBSD: recurse.c,v 1.4 2024/02/04 20:47:25 christos Exp $"); 18254721Semaste 19263363Semaste#include "cvs.h" 20263363Semaste#include "save-cwd.h" 21269024Semaste#include "fileattr.h" 22269024Semaste#include "edit.h" 23263363Semaste 24263363Semastestatic int do_dir_proc (Node * p, void *closure); 25254721Semastestatic int do_file_proc (Node * p, void *closure); 26254721Semastestatic void addlist (List ** listp, char *key); 27254721Semastestatic int unroll_files_proc (Node *p, void *closure); 28254721Semastestatic void addfile (List **listp, char *dir, char *file); 29254721Semaste 30254721Semastestatic char *update_dir; 31254721Semastestatic char *repository = NULL; 32254721Semastestatic List *filelist = NULL; /* holds list of files on which to operate */ 33254721Semastestatic List *dirlist = NULL; /* holds list of directories on which to operate */ 34254721Semaste 35254721Semastestruct recursion_frame { 36254721Semaste FILEPROC fileproc; 37254721Semaste FILESDONEPROC filesdoneproc; 38254721Semaste DIRENTPROC direntproc; 39254721Semaste DIRLEAVEPROC dirleaveproc; 40254721Semaste void *callerdat; 41254721Semaste Dtype flags; 42254721Semaste int which; 43254721Semaste int aflag; 44254721Semaste int locktype; 45254721Semaste int dosrcs; 46254721Semaste char *repository; /* Keep track of repository for rtag */ 47254721Semaste}; 48254721Semaste 49254721Semastestatic int do_recursion (struct recursion_frame *frame); 50254721Semaste 51254721Semaste/* I am half tempted to shove a struct file_info * into the struct 52254721Semaste recursion_frame (but then we would need to modify or create a 53254721Semaste recursion_frame for each file), or shove a struct recursion_frame * 54254721Semaste into the struct file_info (more tempting, although it isn't completely 55254721Semaste clear that the struct file_info should contain info about recursion 56254721Semaste processor internals). So instead use this struct. */ 57254721Semaste 58254721Semastestruct frame_and_file { 59254721Semaste struct recursion_frame *frame; 60254721Semaste struct file_info *finfo; 61254721Semaste}; 62254721Semaste 63254721Semaste/* Similarly, we need to pass the entries list to do_dir_proc. */ 64254721Semaste 65254721Semastestruct frame_and_entries { 66254721Semaste struct recursion_frame *frame; 67254721Semaste List *entries; 68254721Semaste}; 69254721Semaste 70254721Semaste 71254721Semaste/* Start a recursive command. 72254721Semaste * 73254721Semaste * INPUT 74254721Semaste * 75254721Semaste * fileproc 76254721Semaste * Function called with each file as an argument. 77254721Semaste * 78254721Semaste * filesdoneproc 79254721Semaste * Function called after all the files in a directory have been processed, 80254721Semaste * before subdirectories have been processed. 81254721Semaste * 82269024Semaste * direntproc 83269024Semaste * Function called immediately upon entering a directory, before processing 84269024Semaste * any files or subdirectories. 85269024Semaste * 86269024Semaste * dirleaveproc 87254721Semaste * Function called upon finishing a directory, immediately before leaving 88254721Semaste * it and returning control to the function processing the parent 89254721Semaste * directory. 90254721Semaste * 91263363Semaste * callerdat 92263363Semaste * This void * is passed to the functions above. 93263363Semaste * 94263363Semaste * argc, argv 95263363Semaste * The files on which to operate, interpreted as command line arguments. 96263363Semaste * In the special case of no arguments, defaults to operating on the 97269024Semaste * current directory (`.'). 98269024Semaste * 99269024Semaste * local 100269024Semaste * 101269024Semaste * which 102263363Semaste * specifies the kind of recursion. There are several cases: 103263363Semaste * 104263363Semaste * 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current 105263363Semaste * directory when we are called must be the repository and 106263363Semaste * recursion proceeds according to what exists in the repository. 107263363Semaste * 108263363Semaste * 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The 109254721Semaste * current directory when we are called must be the working 110254721Semaste * directory. Recursion proceeds according to what exists in the 111254721Semaste * working directory, never (I think) consulting any part of the 112254721Semaste * repository which does not correspond to the working directory 113269024Semaste * ("correspond" == Name_Repository). 114269024Semaste * 115269024Semaste * 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the 116269024Semaste * weird one. The current directory when we are called must be 117254721Semaste * the working directory. We recurse through working directories, 118254721Semaste * but we recurse into a directory if it is exists in the working 119254721Semaste * directory *or* it exists in the repository. If a directory 120254721Semaste * does not exist in the working directory, the direntproc must 121254721Semaste * either tell us to skip it (R_SKIP_ALL), or must create it (I 122254721Semaste * think those are the only two cases). 123254721Semaste * 124254721Semaste * aflag 125254721Semaste * locktype 126254721Semaste * update_preload 127254721Semaste * dosrcs 128254721Semaste * 129254721Semaste * repository_in 130254721Semaste * keeps track of the repository string. This is only for the remote mode, 131254721Semaste * specifically, r* commands (rtag, rdiff, co, ...) where xgetcwd() was used 132254721Semaste * to locate the repository. Things would break when xgetcwd() was used 133254721Semaste * with a symlinked repository because xgetcwd() would return the true path 134254721Semaste * and in some cases this would cause the path to be printed as other than 135254721Semaste * the user specified in error messages and in other cases some of CVS's 136254721Semaste * security assertions would fail. 137254721Semaste * 138254721Semaste * GLOBALS 139254721Semaste * ??? 140254721Semaste * 141254721Semaste * OUTPUT 142254721Semaste * 143254721Semaste * callerdat can be modified by the FILEPROC, FILESDONEPROC, DIRENTPROC, and 144254721Semaste * DIRLEAVEPROC. 145254721Semaste * 146254721Semaste * RETURNS 147254721Semaste * A count of errors counted by walking the argument list with 148254721Semaste * unroll_files_proc() and do_recursion(). 149254721Semaste * 150254721Semaste * ERRORS 151254721Semaste * Fatal errors occur: 152254721Semaste * 1. when there were no arguments and the current directory 153254721Semaste * does not contain CVS metadata. 154254721Semaste * 2. when all but the last path element from an argument from ARGV cannot 155254721Semaste * be found to be a local directory. 156254721Semaste */ 157269024Semasteint 158254721Semastestart_recursion (FILEPROC fileproc, FILESDONEPROC filesdoneproc, 159254721Semaste DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc, 160254721Semaste void *callerdat, int argc, char **argv, int local, 161254721Semaste int which, int aflag, int locktype, 162254721Semaste char *update_preload, int dosrcs, char *repository_in) 163254721Semaste{ 164254721Semaste int i, err = 0; 165254721Semaste#ifdef CLIENT_SUPPORT 166254721Semaste List *args_to_send_when_finished = NULL; 167254721Semaste#endif 168254721Semaste List *files_by_dir = NULL; 169254721Semaste struct recursion_frame frame; 170254721Semaste 171269024Semaste#ifdef HAVE_PRINTF_PTR 172269024Semaste TRACE ( TRACE_FLOW, 173269024Semaste "start_recursion ( fileproc=%p, filesdoneproc=%p,\n" 174269024Semaste " direntproc=%p, dirleavproc=%p,\n" 175269024Semaste " callerdat=%p, argc=%d, argv=%p,\n" 176269024Semaste " local=%d, which=%d, aflag=%d,\n" 177269024Semaste " locktype=%d, update_preload=%s\n" 178269024Semaste " dosrcs=%d, repository_in=%s )", 179269024Semaste (void *) fileproc, (void *) filesdoneproc, 180269024Semaste (void *) direntproc, (void *) dirleaveproc, 181269024Semaste (void *) callerdat, argc, (void *) argv, 182269024Semaste local, which, aflag, locktype, 183254721Semaste update_preload ? update_preload : "(null)", dosrcs, 184254721Semaste repository_in ? repository_in : "(null)"); 185254721Semaste#else 186254721Semaste TRACE ( TRACE_FLOW, 187269024Semaste "start_recursion ( fileproc=%lx, filesdoneproc=%lx,\n" 188269024Semaste " direntproc=%lx, dirleavproc=%lx,\n" 189269024Semaste " callerdat=%lx, argc=%d, argv=%lx,\n" 190269024Semaste " local=%d, which=%d, aflag=%d,\n" 191269024Semaste " locktype=%d, update_preload=%s\n" 192269024Semaste " dosrcs=%d, repository_in=%s )", 193269024Semaste (unsigned long) fileproc, (unsigned long) filesdoneproc, 194269024Semaste (unsigned long) direntproc, (unsigned long) dirleaveproc, 195269024Semaste (unsigned long) callerdat, argc, (unsigned long) argv, 196254721Semaste local, which, aflag, locktype, 197254721Semaste update_preload ? update_preload : "(null)", dosrcs, 198254721Semaste repository_in ? repository_in : "(null)"); 199254721Semaste#endif 200254721Semaste 201254721Semaste frame.fileproc = fileproc; 202254721Semaste frame.filesdoneproc = filesdoneproc; 203254721Semaste frame.direntproc = direntproc; 204254721Semaste frame.dirleaveproc = dirleaveproc; 205254721Semaste frame.callerdat = callerdat; 206254721Semaste frame.flags = local ? R_SKIP_DIRS : R_PROCESS; 207254721Semaste frame.which = which; 208254721Semaste frame.aflag = aflag; 209269024Semaste frame.locktype = locktype; 210254721Semaste frame.dosrcs = dosrcs; 211254721Semaste 212254721Semaste /* If our repository_in has a trailing "/.", remove it before storing it 213254721Semaste * for do_recursion(). 214254721Semaste * 215254721Semaste * FIXME: This is somewhat of a hack in the sense that many of our callers 216254721Semaste * painstakingly compute and add the trailing '.' we now remove. 217254721Semaste */ 218254721Semaste while (repository_in && strlen (repository_in) >= 2 219254721Semaste && repository_in[strlen (repository_in) - 2] == '/' 220254721Semaste && repository_in[strlen (repository_in) - 1] == '.') 221263363Semaste { 222263363Semaste /* Beware the case where the string is exactly "/." or "//.". 223263363Semaste * Paths with a leading "//" are special on some early UNIXes. 224254721Semaste */ 225263363Semaste if (strlen (repository_in) == 2 || strlen (repository_in) == 3) 226254721Semaste repository_in[strlen (repository_in) - 1] = '\0'; 227254721Semaste else 228254721Semaste repository_in[strlen (repository_in) - 2] = '\0'; 229254721Semaste } 230254721Semaste frame.repository = repository_in; 231269024Semaste 232254721Semaste expand_wild (argc, argv, &argc, &argv); 233254721Semaste 234254721Semaste if (update_preload == NULL) 235254721Semaste update_dir = xstrdup (""); 236254721Semaste else 237254721Semaste update_dir = xstrdup (update_preload); 238254721Semaste 239254721Semaste /* clean up from any previous calls to start_recursion */ 240254721Semaste if (repository) 241254721Semaste { 242254721Semaste free (repository); 243254721Semaste repository = NULL; 244254721Semaste } 245254721Semaste if (filelist) 246254721Semaste dellist (&filelist); /* FIXME-krp: no longer correct. */ 247254721Semaste if (dirlist) 248254721Semaste dellist (&dirlist); 249254721Semaste 250254721Semaste#ifdef SERVER_SUPPORT 251254721Semaste if (server_active) 252254721Semaste { 253254721Semaste for (i = 0; i < argc; ++i) 254254721Semaste server_pathname_check (argv[i]); 255254721Semaste } 256254721Semaste#endif 257254721Semaste 258254721Semaste if (argc == 0) 259254721Semaste { 260254721Semaste int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM); 261254721Semaste 262254721Semaste#ifdef CLIENT_SUPPORT 263254721Semaste if (!just_subdirs 264254721Semaste && CVSroot_cmdline == NULL 265254721Semaste && current_parsed_root->isremote) 266254721Semaste { 267254721Semaste cvsroot_t *root = Name_Root (NULL, update_dir); 268254721Semaste if (root) 269254721Semaste { 270254721Semaste if (strcmp (root->original, original_parsed_root->original)) 271254721Semaste /* We're skipping this directory because it is for 272254721Semaste * a different root. Therefore, we just want to 273263367Semaste * do the subdirectories only. Processing files would 274263367Semaste * cause a working directory from one repository to be 275263367Semaste * processed against a different repository, which could 276263367Semaste * cause all kinds of spurious conflicts and such. 277263367Semaste * 278254721Semaste * Question: what about the case of "cvs update foo" 279254721Semaste * where we process foo/bar and not foo itself? That 280263363Semaste * seems to be handled somewhere (else) but why should 281254721Semaste * it be a separate case? Needs investigation... */ 282254721Semaste just_subdirs = 1; 283263363Semaste } 284263363Semaste } 285263363Semaste#endif 286254721Semaste 287254721Semaste /* 288254721Semaste * There were no arguments, so we'll probably just recurse. The 289254721Semaste * exception to the rule is when we are called from a directory 290263367Semaste * without any CVS administration files. That has always meant to 291263367Semaste * process each of the sub-directories, so we pretend like we were 292263367Semaste * called with the list of sub-dirs of the current dir as args 293263367Semaste */ 294263367Semaste if (just_subdirs) 295263367Semaste { 296263367Semaste dirlist = Find_Directories (NULL, W_LOCAL, NULL); 297263367Semaste /* If there are no sub-directories, there is a certain logic in 298263367Semaste favor of doing nothing, but in fact probably the user is just 299254721Semaste confused about what directory they are in, or whether they 300254721Semaste cvs add'd a new directory. In the case of at least one 301254721Semaste sub-directory, at least when we recurse into them we 302254721Semaste notice (hopefully) whether they are under CVS control. */ 303254721Semaste if (list_isempty (dirlist)) 304254721Semaste { 305254721Semaste if (update_dir[0] == '\0') 306254721Semaste error (0, 0, "in directory .:"); 307254721Semaste else 308254721Semaste error (0, 0, "in directory %s:", update_dir); 309269024Semaste error (1, 0, 310269024Semaste "there is no version here; run '%s checkout' first", 311269024Semaste program_name); 312269024Semaste } 313254721Semaste#ifdef CLIENT_SUPPORT 314254721Semaste else if (current_parsed_root->isremote && server_started) 315254721Semaste { 316254721Semaste /* In the the case "cvs update foo bar baz", a call to 317263363Semaste send_file_names in update.c will have sent the 318263363Semaste appropriate "Argument" commands to the server. In 319263363Semaste this case, that won't have happened, so we need to 320263363Semaste do it here. While this example uses "update", this 321263363Semaste generalizes to other commands. */ 322263363Semaste 323263363Semaste /* This is the same call to Find_Directories as above. 324263363Semaste FIXME: perhaps it would be better to write a 325263363Semaste function that duplicates a list. */ 326263363Semaste args_to_send_when_finished = Find_Directories (NULL, 327263363Semaste W_LOCAL, 328263367Semaste NULL); 329263363Semaste } 330263363Semaste#endif 331263363Semaste } 332263363Semaste else 333263363Semaste addlist (&dirlist, "."); 334263363Semaste 335263363Semaste goto do_the_work; 336263363Semaste } 337263363Semaste 338263363Semaste 339263363Semaste /* 340263363Semaste * There were arguments, so we have to handle them by hand. To do 341263363Semaste * that, we set up the filelist and dirlist with the arguments and 342263363Semaste * call do_recursion. do_recursion recognizes the fact that the 343263363Semaste * lists are non-null when it starts and doesn't update them. 344263363Semaste * 345263363Semaste * explicitly named directories are stored in dirlist. 346263363Semaste * explicitly named files are stored in filelist. 347263363Semaste * other possibility is named entities whicha are not currently in 348263363Semaste * the working directory. 349263363Semaste */ 350263363Semaste 351263363Semaste for (i = 0; i < argc; i++) 352263363Semaste { 353263367Semaste /* if this argument is a directory, then add it to the list of 354263363Semaste directories. */ 355263363Semaste 356263363Semaste if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) 357263363Semaste { 358263363Semaste strip_trailing_slashes (argv[i]); 359263363Semaste addlist (&dirlist, argv[i]); 360263363Semaste } 361263363Semaste else 362263363Semaste { 363263363Semaste /* otherwise, split argument into directory and component names. */ 364254721Semaste char *dir; 365254721Semaste char *comp; 366254721Semaste char *file_to_try; 367254721Semaste 368269024Semaste /* Now break out argv[i] into directory part (DIR) and file part 369254721Semaste * (COMP). DIR and COMP will each point to a newly malloc'd 370269024Semaste * string. 371269024Semaste */ 372254721Semaste dir = xstrdup (argv[i]); 373269024Semaste /* It's okay to cast out last_component's const below since we know 374269024Semaste * we just allocated dir here and have control of it. 375269024Semaste */ 376269024Semaste comp = (char *)last_component (dir); 377269024Semaste if (comp == dir) 378269024Semaste { 379269024Semaste /* no dir component. What we have is an implied "./" */ 380269024Semaste dir = xstrdup("."); 381269024Semaste } 382269024Semaste else 383269024Semaste { 384269024Semaste comp[-1] = '\0'; 385269024Semaste comp = xstrdup (comp); 386254721Semaste } 387254721Semaste 388254721Semaste /* if this argument exists as a file in the current 389254721Semaste working directory tree, then add it to the files list. */ 390254721Semaste 391254721Semaste if (!(which & W_LOCAL)) 392254721Semaste { 393254721Semaste /* If doing rtag, we've done a chdir to the repository. */ 394254721Semaste file_to_try = Xasprintf ("%s%s", argv[i], RCSEXT); 395254721Semaste } 396254721Semaste else 397254721Semaste file_to_try = xstrdup (argv[i]); 398254721Semaste 399254721Semaste if (isfile (file_to_try)) 400254721Semaste addfile (&files_by_dir, dir, comp); 401254721Semaste else if (isdir (dir)) 402254721Semaste { 403254721Semaste if ((which & W_LOCAL) && isdir (CVSADM) && 404254721Semaste !current_parsed_root->isremote) 405254721Semaste { 406254721Semaste /* otherwise, look for it in the repository. */ 407254721Semaste char *tmp_update_dir; 408254721Semaste char *repos; 409254721Semaste char *reposfile; 410254721Semaste 411254721Semaste tmp_update_dir = xmalloc (strlen (update_dir) 412254721Semaste + strlen (dir) 413254721Semaste + 5); 414254721Semaste strcpy (tmp_update_dir, update_dir); 415254721Semaste 416254721Semaste if (*tmp_update_dir != '\0') 417254721Semaste strcat (tmp_update_dir, "/"); 418254721Semaste 419254721Semaste strcat (tmp_update_dir, dir); 420254721Semaste 421254721Semaste /* look for it in the repository. */ 422254721Semaste repos = Name_Repository (dir, tmp_update_dir); 423254721Semaste reposfile = Xasprintf ("%s/%s", repos, comp); 424254721Semaste free (repos); 425254721Semaste 426254721Semaste if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) 427254721Semaste addlist (&dirlist, argv[i]); 428254721Semaste else 429254721Semaste addfile (&files_by_dir, dir, comp); 430254721Semaste 431254721Semaste free (tmp_update_dir); 432254721Semaste free (reposfile); 433254721Semaste } 434254721Semaste else 435254721Semaste addfile (&files_by_dir, dir, comp); 436254721Semaste } 437254721Semaste else 438254721Semaste error (1, 0, "no such directory `%s'", dir); 439254721Semaste 440254721Semaste free (file_to_try); 441254721Semaste free (dir); 442254721Semaste free (comp); 443254721Semaste } 444254721Semaste } 445254721Semaste 446254721Semaste /* At this point we have looped over all named arguments and built 447254721Semaste a coupla lists. Now we unroll the lists, setting up and 448254721Semaste calling do_recursion. */ 449254721Semaste 450254721Semaste err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); 451254721Semaste dellist(&files_by_dir); 452254721Semaste 453254721Semaste /* then do_recursion on the dirlist. */ 454254721Semaste if (dirlist != NULL) 455254721Semaste { 456254721Semaste do_the_work: 457254721Semaste err += do_recursion (&frame); 458254721Semaste } 459254721Semaste 460254721Semaste /* Free the data which expand_wild allocated. */ 461254721Semaste free_names (&argc, argv); 462254721Semaste 463254721Semaste free (update_dir); 464254721Semaste update_dir = NULL; 465254721Semaste 466254721Semaste#ifdef CLIENT_SUPPORT 467254721Semaste if (args_to_send_when_finished != NULL) 468254721Semaste { 469254721Semaste /* FIXME (njc): in the multiroot case, we don't want to send 470254721Semaste argument commands for those top-level directories which do 471254721Semaste not contain any subdirectories which have files checked out 472254721Semaste from current_parsed_root. If we do, and two repositories 473254721Semaste have a module with the same name, nasty things could happen. 474254721Semaste 475254721Semaste This is hard. Perhaps we should send the Argument commands 476254721Semaste later in this procedure, after we've had a chance to notice 477254721Semaste which directores we're using (after do_recursion has been 478254721Semaste called once). This means a _lot_ of rewriting, however. 479254721Semaste 480254721Semaste What we need to do for that to happen is descend the tree 481254721Semaste and construct a list of directories which are checked out 482254721Semaste from current_cvsroot. Now, we eliminate from the list all 483254721Semaste of those directories which are immediate subdirectories of 484254721Semaste another directory in the list. To say that the opposite 485254721Semaste way, we keep the directories which are not immediate 486254721Semaste subdirectories of any other in the list. Here's a picture: 487254721Semaste 488254721Semaste a 489254721Semaste / \ 490254721Semaste B C 491254721Semaste / \ 492254721Semaste D e 493254721Semaste / \ 494254721Semaste F G 495254721Semaste / \ 496254721Semaste H I 497254721Semaste 498254721Semaste The node in capitals are those directories which are 499254721Semaste checked out from current_cvsroot. We want the list to 500254721Semaste contain B, C, F, and G. D, H, and I are not included, 501254721Semaste because their parents are also checked out from 502254721Semaste current_cvsroot. 503254721Semaste 504254721Semaste The algorithm should be: 505254721Semaste 506254721Semaste 1) construct a tree of all directory names where each 507254721Semaste element contains a directory name and a flag which notes if 508254721Semaste that directory is checked out from current_cvsroot 509254721Semaste 510254721Semaste a0 511254721Semaste / \ 512254721Semaste B1 C1 513254721Semaste / \ 514254721Semaste D1 e0 515254721Semaste / \ 516254721Semaste F1 G1 517254721Semaste / \ 518254721Semaste H1 I1 519254721Semaste 520254721Semaste 2) Recursively descend the tree. For each node, recurse 521254721Semaste before processing the node. If the flag is zero, do 522254721Semaste nothing. If the flag is 1, check the node's parent. If 523254721Semaste the parent's flag is one, change the current entry's flag 524254721Semaste to zero. 525254721Semaste 526254721Semaste a0 527254721Semaste / \ 528254721Semaste B1 C1 529254721Semaste / \ 530254721Semaste D0 e0 531254721Semaste / \ 532254721Semaste F1 G1 533254721Semaste / \ 534254721Semaste H0 I0 535254721Semaste 536254721Semaste 3) Walk the tree and spit out "Argument" commands to tell 537254721Semaste the server which directories to munge. 538254721Semaste 539254721Semaste Yuck. It's not clear this is worth spending time on, since 540254721Semaste we might want to disable cvs commands entirely from 541254721Semaste directories that do not have CVSADM files... 542254721Semaste 543254721Semaste Anyways, the solution as it stands has modified server.c 544254721Semaste (dirswitch) to create admin files [via server.c 545254721Semaste (create_adm_p)] in all path elements for a client's 546254721Semaste "Directory xxx" command, which forces the server to descend 547254721Semaste and serve the files there. client.c (send_file_names) has 548254721Semaste also been modified to send only those arguments which are 549254721Semaste appropriate to current_parsed_root. 550254721Semaste 551254721Semaste */ 552254721Semaste 553254721Semaste /* Construct a fake argc/argv pair. */ 554254721Semaste 555254721Semaste int our_argc = 0, i; 556254721Semaste char **our_argv = NULL; 557254721Semaste 558254721Semaste if (! list_isempty (args_to_send_when_finished)) 559254721Semaste { 560254721Semaste Node *head, *p; 561254721Semaste 562254721Semaste head = args_to_send_when_finished->list; 563254721Semaste 564254721Semaste /* count the number of nodes */ 565254721Semaste i = 0; 566263363Semaste for (p = head->next; p != head; p = p->next) 567263363Semaste i++; 568263363Semaste our_argc = i; 569263363Semaste 570263363Semaste /* create the argument vector */ 571254721Semaste our_argv = xmalloc (sizeof (char *) * our_argc); 572254721Semaste 573254721Semaste /* populate it */ 574254721Semaste i = 0; 575254721Semaste for (p = head->next; p != head; p = p->next) 576254721Semaste our_argv[i++] = xstrdup (p->key); 577254721Semaste } 578254721Semaste 579263363Semaste /* We don't want to expand widcards, since we've just created 580254721Semaste a list of directories directly from the filesystem. */ 581254721Semaste send_file_names (our_argc, our_argv, 0); 582254721Semaste 583254721Semaste /* Free our argc/argv. */ 584254721Semaste if (our_argv != NULL) 585254721Semaste { 586254721Semaste for (i = 0; i < our_argc; i++) 587254721Semaste free (our_argv[i]); 588254721Semaste free (our_argv); 589254721Semaste } 590254721Semaste 591254721Semaste dellist (&args_to_send_when_finished); 592254721Semaste } 593254721Semaste#endif 594254721Semaste 595254721Semaste return err; 596254721Semaste} 597254721Semaste 598254721Semaste 599254721Semaste 600254721Semaste/* 601254721Semaste * Implement the recursive policies on the local directory. This may be 602254721Semaste * called directly, or may be called by start_recursion. 603254721Semaste */ 604254721Semastestatic int 605254721Semastedo_recursion (struct recursion_frame *frame) 606254721Semaste{ 607254721Semaste int err = 0; 608254721Semaste int dodoneproc = 1; 609254721Semaste char *srepository = NULL; 610254721Semaste List *entries = NULL; 611254721Semaste int locktype; 612254721Semaste bool process_this_directory = true; 613254721Semaste 614254721Semaste#ifdef HAVE_PRINT_PTR 615254721Semaste TRACE (TRACE_FLOW, "do_recursion ( frame=%p )", (void *) frame); 616254721Semaste#else 617254721Semaste TRACE (TRACE_FLOW, "do_recursion ( frame=%lx )", (unsigned long) frame); 618254721Semaste#endif 619254721Semaste 620254721Semaste /* do nothing if told */ 621254721Semaste if (frame->flags == R_SKIP_ALL) 622254721Semaste return 0; 623254721Semaste 624254721Semaste locktype = (nolock || noexec) ? CVS_LOCK_NONE : frame->locktype; 625254721Semaste 626254721Semaste /* The fact that locks are not active here is what makes us fail to have 627254721Semaste the 628254721Semaste 629254721Semaste If someone commits some changes in one cvs command, 630254721Semaste then an update by someone else will either get all the 631254721Semaste changes, or none of them. 632254721Semaste 633254721Semaste property (see node Concurrency in cvs.texinfo). 634254721Semaste 635254721Semaste The most straightforward fix would just to readlock the whole 636254721Semaste tree before starting an update, but that means that if a commit 637254721Semaste gets blocked on a big update, it might need to wait a *long* 638254721Semaste time. 639254721Semaste 640254721Semaste A more adequate fix would be a two-pass design for update, 641254721Semaste checkout, etc. The first pass would go through the repository, 642254721Semaste with the whole tree readlocked, noting what versions of each 643254721Semaste file we want to get. The second pass would release all locks 644254721Semaste (except perhaps short-term locks on one file at a 645254721Semaste time--although I think RCS already deals with this) and 646254721Semaste actually get the files, specifying the particular versions it wants. 647254721Semaste 648254721Semaste This could be sped up by separating out the data needed for the 649254721Semaste first pass into a separate file(s)--for example a file 650254721Semaste attribute for each file whose value contains the head revision 651254721Semaste for each branch. The structure should be designed so that 652254721Semaste commit can relatively quickly update the information for a 653254721Semaste single file or a handful of files (file attributes, as 654254721Semaste implemented in Jan 96, are probably acceptable; improvements 655254721Semaste would be possible such as branch attributes which are in 656254721Semaste separate files for each branch). */ 657254721Semaste 658254721Semaste#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) 659254721Semaste /* 660254721Semaste * Now would be a good time to check to see if we need to stop 661254721Semaste * generating data, to give the buffers a chance to drain to the 662254721Semaste * remote client. We should not have locks active at this point, 663254721Semaste * but if there are writelocks around, we cannot pause here. */ 664254721Semaste if (server_active && locktype != CVS_LOCK_WRITE) 665254721Semaste server_pause_check(); 666254721Semaste#endif 667254721Semaste 668254721Semaste /* Check the value in CVSADM_ROOT and see if it's in the list. If 669254721Semaste not, add it to our lists of CVS/Root directories and do not 670254721Semaste process the files in this directory. Otherwise, continue as 671254721Semaste usual. THIS_ROOT might be NULL if we're doing an initial 672254721Semaste checkout -- check before using it. The default should be that 673254721Semaste we process a directory's contents and only skip those contents 674254721Semaste if a CVS/Root file exists. 675254721Semaste 676254721Semaste If we're running the server, we want to process all 677254721Semaste directories, since we're guaranteed to have only one CVSROOT -- 678254721Semaste our own. */ 679263363Semaste 680254721Semaste /* If -d was specified, it should override CVS/Root. 681254721Semaste 682254721Semaste In the single-repository case, it is long-standing CVS behavior 683254721Semaste and makes sense - the user might want another access method, 684254721Semaste another server (which mounts the same repository), &c. 685254721Semaste 686254721Semaste In the multiple-repository case, -d overrides all CVS/Root 687254721Semaste files. That is the only plausible generalization I can 688254721Semaste think of. */ 689254721Semaste if (CVSroot_cmdline == NULL && !server_active) 690254721Semaste { 691254721Semaste cvsroot_t *this_root = Name_Root (NULL, update_dir); 692254721Semaste if (this_root != NULL) 693254721Semaste { 694254721Semaste if (findnode (root_directories, this_root->original)) 695254721Semaste { 696254721Semaste process_this_directory = 697254721Semaste !strcmp (original_parsed_root->original, 698254721Semaste this_root->original); 699254721Semaste } 700254721Semaste else 701254721Semaste { 702254721Semaste /* Add it to our list. */ 703254721Semaste 704254721Semaste Node *n = getnode (); 705254721Semaste n->type = NT_UNKNOWN; 706254721Semaste n->key = xstrdup (this_root->original); 707263363Semaste n->data = this_root; 708263363Semaste 709263363Semaste if (addnode (root_directories, n)) 710263363Semaste error (1, 0, "cannot add new CVSROOT %s", 711263363Semaste this_root->original); 712263363Semaste 713263363Semaste process_this_directory = false; 714263363Semaste } 715254721Semaste } 716254721Semaste } 717254721Semaste 718254721Semaste /* 719254721Semaste * Fill in repository with the current repository 720254721Semaste */ 721254721Semaste if (frame->which & W_LOCAL) 722254721Semaste { 723254721Semaste if (isdir (CVSADM)) 724254721Semaste { 725254721Semaste repository = Name_Repository (NULL, update_dir); 726254721Semaste srepository = repository; /* remember what to free */ 727254721Semaste } 728254721Semaste else 729254721Semaste repository = NULL; 730254721Semaste } 731254721Semaste else 732254721Semaste { 733254721Semaste repository = frame->repository; 734254721Semaste assert (repository != NULL); 735254721Semaste assert (strstr (repository, "/./") == NULL); 736254721Semaste } 737254721Semaste 738254721Semaste fileattr_startdir (repository); 739254721Semaste 740254721Semaste /* 741254721Semaste * The filesdoneproc needs to be called for each directory where files 742254721Semaste * processed, or each directory that is processed by a call where no 743254721Semaste * directories were passed in. In fact, the only time we don't want to 744254721Semaste * call back the filesdoneproc is when we are processing directories that 745254721Semaste * were passed in on the command line (or in the special case of `.' when 746254721Semaste * we were called with no args 747254721Semaste */ 748254721Semaste if (dirlist != NULL && filelist == NULL) 749254721Semaste dodoneproc = 0; 750254721Semaste 751254721Semaste processing = "scan"; 752254721Semaste /* 753254721Semaste * If filelist or dirlist is already set, we don't look again. Otherwise, 754254721Semaste * find the files and directories 755254721Semaste */ 756254721Semaste if (filelist == NULL && dirlist == NULL) 757254721Semaste { 758254721Semaste /* both lists were NULL, so start from scratch */ 759254721Semaste if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) 760254721Semaste { 761254721Semaste int lwhich = frame->which; 762254721Semaste 763254721Semaste /* be sure to look in the attic if we have sticky tags/date */ 764254721Semaste if ((lwhich & W_ATTIC) == 0) 765254721Semaste if (isreadable (CVSADM_TAG)) 766254721Semaste lwhich |= W_ATTIC; 767254721Semaste 768254721Semaste /* In the !(which & W_LOCAL) case, we filled in repository 769254721Semaste earlier in the function. In the (which & W_LOCAL) case, 770254721Semaste the Find_Names function is going to look through the 771254721Semaste Entries file. If we do not have a repository, that 772254721Semaste does not make sense, so we insist upon having a 773254721Semaste repository at this point. Name_Repository will give a 774254721Semaste reasonable error message. */ 775254721Semaste if (repository == NULL) 776254721Semaste { 777263363Semaste Name_Repository (NULL, update_dir); 778254721Semaste assert (!"Not reached. Please report this problem to <" 779254721Semaste PACKAGE_BUGREPORT ">"); 780254721Semaste } 781254721Semaste /* find the files and fill in entries if appropriate */ 782254721Semaste if (process_this_directory) 783254721Semaste { 784254721Semaste filelist = Find_Names (repository, lwhich, frame->aflag, 785254721Semaste &entries); 786254721Semaste if (filelist == NULL) 787254721Semaste { 788254721Semaste error (0, 0, "skipping directory %s", update_dir); 789254721Semaste /* Note that Find_Directories and the filesdoneproc 790254721Semaste in particular would do bad things ("? foo.c" in 791254721Semaste the case of some filesdoneproc's). */ 792254721Semaste goto skip_directory; 793254721Semaste } 794263363Semaste } 795263363Semaste } 796263363Semaste 797263363Semaste /* find sub-directories if we will recurse */ 798263363Semaste if (frame->flags != R_SKIP_DIRS) 799263363Semaste dirlist = Find_Directories ( 800263363Semaste process_this_directory ? repository : NULL, 801263363Semaste frame->which, entries); 802263363Semaste } 803263363Semaste else 804263363Semaste { 805254721Semaste /* something was passed on the command line */ 806254721Semaste if (filelist != NULL && frame->fileproc != NULL) 807254721Semaste { 808254721Semaste /* we will process files, so pre-parse entries */ 809254721Semaste if (frame->which & W_LOCAL) 810254721Semaste entries = Entries_Open (frame->aflag, NULL); 811254721Semaste } 812254721Semaste } 813254721Semaste 814254721Semaste processing = "process"; 815254721Semaste 816254721Semaste /* process the files (if any) */ 817254721Semaste if (process_this_directory && filelist != NULL && frame->fileproc) 818254721Semaste { 819254721Semaste struct file_info finfo_struct; 820254721Semaste struct frame_and_file frfile; 821254721Semaste 822254721Semaste /* Lock the repository, if necessary. */ 823254721Semaste if (repository) 824254721Semaste { 825254721Semaste if (locktype == CVS_LOCK_READ) 826254721Semaste { 827254721Semaste if (Reader_Lock (repository) != 0) 828254721Semaste error (1, 0, "read lock failed - giving up"); 829254721Semaste } 830254721Semaste else if (locktype == CVS_LOCK_WRITE) 831254721Semaste lock_dir_for_write (repository); 832254721Semaste } 833254721Semaste 834254721Semaste#ifdef CLIENT_SUPPORT 835254721Semaste /* For the server, we handle notifications in a completely different 836254721Semaste place (server_notify). For local, we can't do them here--we don't 837254721Semaste have writelocks in place, and there is no way to get writelocks 838254721Semaste here. */ 839254721Semaste if (current_parsed_root->isremote) 840254721Semaste notify_check (repository, update_dir); 841254721Semaste#endif /* CLIENT_SUPPORT */ 842254721Semaste 843254721Semaste finfo_struct.repository = repository; 844254721Semaste finfo_struct.update_dir = update_dir; 845254721Semaste finfo_struct.entries = entries; 846254721Semaste /* do_file_proc will fill in finfo_struct.file. */ 847254721Semaste 848254721Semaste frfile.finfo = &finfo_struct; 849254721Semaste frfile.frame = frame; 850254721Semaste 851254721Semaste /* process the files */ 852254721Semaste err += walklist (filelist, do_file_proc, &frfile); 853254721Semaste 854254721Semaste /* unlock it */ 855263363Semaste if (/* We only lock the repository above when repository is set */ 856263363Semaste repository 857263363Semaste /* and when asked for a read or write lock. */ 858263363Semaste && locktype != CVS_LOCK_NONE) 859263363Semaste Simple_Lock_Cleanup (); 860263363Semaste 861263363Semaste /* clean up */ 862263363Semaste dellist (&filelist); 863263363Semaste } 864263363Semaste 865263363Semaste processing = "cleanup"; 866263363Semaste 867263363Semaste /* call-back files done proc (if any) */ 868263363Semaste if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) 869263363Semaste err = frame->filesdoneproc (frame->callerdat, err, repository, 870263363Semaste update_dir[0] ? update_dir : ".", 871263363Semaste entries); 872263363Semaste 873263363Semaste skip_directory: 874263363Semaste fileattr_write (); 875263363Semaste fileattr_free (); 876263363Semaste 877263363Semaste /* process the directories (if necessary) */ 878263363Semaste if (dirlist != NULL) 879263363Semaste { 880263363Semaste struct frame_and_entries frent; 881269024Semaste 882269024Semaste frent.frame = frame; 883269024Semaste frent.entries = entries; 884269024Semaste err += walklist (dirlist, do_dir_proc, &frent); 885269024Semaste } 886269024Semaste#if 0 887269024Semaste else if (frame->dirleaveproc != NULL) 888269024Semaste err += frame->dirleaveproc (frame->callerdat, ".", err, "."); 889269024Semaste#endif 890269024Semaste dellist (&dirlist); 891269024Semaste 892269024Semaste if (entries) 893269024Semaste { 894269024Semaste Entries_Close (entries); 895269024Semaste entries = NULL; 896269024Semaste } 897269024Semaste 898269024Semaste /* free the saved copy of the pointer if necessary */ 899269024Semaste if (srepository) 900269024Semaste free (srepository); 901269024Semaste repository = NULL; 902269024Semaste 903269024Semaste#ifdef HAVE_PRINT_PTR 904269024Semaste TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%p)", (void *)frame); 905269024Semaste#else 906269024Semaste TRACE (TRACE_FLOW, "Leaving do_recursion (frame=%lx)", 907269024Semaste (unsigned long)frame); 908269024Semaste#endif 909269024Semaste 910269024Semaste return err; 911269024Semaste} 912269024Semaste 913269024Semaste 914269024Semaste 915269024Semaste/* 916269024Semaste * Process each of the files in the list with the callback proc 917269024Semaste * 918269024Semaste * NOTES 919269024Semaste * Fills in FINFO->fullname, and sometimes FINFO->rcs before 920269024Semaste * 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 */ 930static int 931do_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 */ 984static int 985do_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, "\ 1027but 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 */ 1273static void 1274addlist (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 1287static void 1288addfile (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 1310static int 1311unroll_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