117721Speter/* 2177404Sobrien * Copyright (C) 1986-2008 The Free Software Foundation, Inc. 3175282Sobrien * 4175282Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175282Sobrien * and others. 6175282Sobrien * 7175282Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175282Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 917721Speter * 1017721Speter * You may distribute under the terms of the GNU General Public License as 1132788Speter * specified in the README file that comes with the CVS source distribution. 1217721Speter * 1317721Speter * General recursion handler 1417721Speter * 1517721Speter */ 1617721Speter 1717721Speter#include "cvs.h" 1817721Speter#include "savecwd.h" 1917721Speter#include "fileattr.h" 2017721Speter#include "edit.h" 21128269Speter#include <assert.h> 2217721Speter 2317721Speterstatic int do_dir_proc PROTO((Node * p, void *closure)); 2417721Speterstatic int do_file_proc PROTO((Node * p, void *closure)); 2517721Speterstatic void addlist PROTO((List ** listp, char *key)); 2617721Speterstatic int unroll_files_proc PROTO((Node *p, void *closure)); 2717721Speterstatic void addfile PROTO((List **listp, char *dir, char *file)); 2817721Speter 2925839Speterstatic char *update_dir; 3017721Speterstatic char *repository = NULL; 3117721Speterstatic List *filelist = NULL; /* holds list of files on which to operate */ 3217721Speterstatic List *dirlist = NULL; /* holds list of directories on which to operate */ 3317721Speter 3417721Speterstruct recursion_frame { 3525839Speter FILEPROC fileproc; 3625839Speter FILESDONEPROC filesdoneproc; 3725839Speter DIRENTPROC direntproc; 3825839Speter DIRLEAVEPROC dirleaveproc; 3925839Speter void *callerdat; 4025839Speter Dtype flags; 4125839Speter int which; 4225839Speter int aflag; 43107487Speter int locktype; 4425839Speter int dosrcs; 45128269Speter char *repository; /* Keep track of repository for rtag */ 4617721Speter}; 4717721Speter 4825839Speterstatic int do_recursion PROTO ((struct recursion_frame *frame)); 4925839Speter 5025839Speter/* I am half tempted to shove a struct file_info * into the struct 5125839Speter recursion_frame (but then we would need to modify or create a 5225839Speter recursion_frame for each file), or shove a struct recursion_frame * 5325839Speter into the struct file_info (more tempting, although it isn't completely 5425839Speter clear that the struct file_info should contain info about recursion 5525839Speter processor internals). So instead use this struct. */ 5625839Speter 5725839Speterstruct frame_and_file { 5825839Speter struct recursion_frame *frame; 5925839Speter struct file_info *finfo; 6025839Speter}; 6125839Speter 6225839Speter/* Similarly, we need to pass the entries list to do_dir_proc. */ 6325839Speter 6425839Speterstruct frame_and_entries { 6525839Speter struct recursion_frame *frame; 6625839Speter List *entries; 6725839Speter}; 6825839Speter 6944856Speter 7025839Speter/* Start a recursive command. 7125839Speter 7225839Speter Command line arguments (ARGC, ARGV) dictate the directories and 7325839Speter files on which we operate. In the special case of no arguments, we 7425839Speter default to ".". */ 7517721Speterint 7625839Speterstart_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, 77107487Speter argc, argv, local, which, aflag, locktype, 78128269Speter update_preload, dosrcs, repository_in) 7917721Speter FILEPROC fileproc; 8017721Speter FILESDONEPROC filesdoneproc; 8117721Speter DIRENTPROC direntproc; 8217721Speter DIRLEAVEPROC dirleaveproc; 8325839Speter void *callerdat; 8425839Speter 8517721Speter int argc; 8617721Speter char **argv; 8717721Speter int local; 8825839Speter 8925839Speter /* This specifies the kind of recursion. There are several cases: 9025839Speter 9125839Speter 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current 9225839Speter directory when we are called must be the repository and 9325839Speter recursion proceeds according to what exists in the repository. 9425839Speter 9525839Speter 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The 9625839Speter current directory when we are called must be the working 9725839Speter directory. Recursion proceeds according to what exists in the 9825839Speter working directory, never (I think) consulting any part of the 9925839Speter repository which does not correspond to the working directory 10025839Speter ("correspond" == Name_Repository). 10125839Speter 10225839Speter 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the 10325839Speter weird one. The current directory when we are called must be 10425839Speter the working directory. We recurse through working directories, 10525839Speter but we recurse into a directory if it is exists in the working 10625839Speter directory *or* it exists in the repository. If a directory 10725839Speter does not exist in the working directory, the direntproc must 10825839Speter either tell us to skip it (R_SKIP_ALL), or must create it (I 10925839Speter think those are the only two cases). */ 11017721Speter int which; 11125839Speter 11217721Speter int aflag; 113107487Speter int locktype; 11417721Speter char *update_preload; 11517721Speter int dosrcs; 116128269Speter /* Keep track of the repository string. This is only for the remote mode, 117128269Speter * specifically, r* commands (rtag, rdiff, co, ...) where xgetwd() was 118128269Speter * used to locate the repository. Things would break when xgetwd() was 119128269Speter * used with a symlinked repository because xgetwd() would return the true 120128269Speter * path and in some cases this would cause the path to be printed as other 121128269Speter * than the user specified in error messages and in other cases some of 122128269Speter * CVS's security assertions would fail. 123128269Speter */ 124128269Speter char *repository_in; 12517721Speter{ 12617721Speter int i, err = 0; 12754431Speter#ifdef CLIENT_SUPPORT 12854431Speter List *args_to_send_when_finished = NULL; 12954431Speter#endif 13017721Speter List *files_by_dir = NULL; 13117721Speter struct recursion_frame frame; 13217721Speter 13325839Speter frame.fileproc = fileproc; 13425839Speter frame.filesdoneproc = filesdoneproc; 13525839Speter frame.direntproc = direntproc; 13625839Speter frame.dirleaveproc = dirleaveproc; 13725839Speter frame.callerdat = callerdat; 13825839Speter frame.flags = local ? R_SKIP_DIRS : R_PROCESS; 13925839Speter frame.which = which; 14025839Speter frame.aflag = aflag; 141107487Speter frame.locktype = locktype; 14225839Speter frame.dosrcs = dosrcs; 143175282Sobrien 144175282Sobrien /* If our repository_in has a trailing "/.", remove it before storing it 145175282Sobrien * for do_recursion(). 146175282Sobrien * 147175282Sobrien * FIXME: This is somewhat of a hack in the sense that many of our callers 148175282Sobrien * painstakingly compute and add the trailing '.' we now remove. 149175282Sobrien */ 150175282Sobrien while (repository_in && strlen (repository_in) >= 2 151175282Sobrien && repository_in[strlen (repository_in) - 2] == '/' 152175282Sobrien && repository_in[strlen (repository_in) - 1] == '.') 153175282Sobrien { 154175282Sobrien /* Beware the case where the string is exactly "/." or "//.". 155175282Sobrien * Paths with a leading "//" are special on some early UNIXes. 156175282Sobrien */ 157175282Sobrien if (strlen (repository_in) == 2 || strlen (repository_in) == 3) 158175282Sobrien repository_in[strlen (repository_in) - 1] = '\0'; 159175282Sobrien else 160175282Sobrien repository_in[strlen (repository_in) - 2] = '\0'; 161175282Sobrien } 162128269Speter frame.repository = repository_in; 16325839Speter 16417721Speter expand_wild (argc, argv, &argc, &argv); 16517721Speter 16617721Speter if (update_preload == NULL) 16725839Speter update_dir = xstrdup (""); 16817721Speter else 16925839Speter update_dir = xstrdup (update_preload); 17017721Speter 17117721Speter /* clean up from any previous calls to start_recursion */ 17217721Speter if (repository) 17317721Speter { 17417721Speter free (repository); 17517721Speter repository = (char *) NULL; 17617721Speter } 17717721Speter if (filelist) 17817721Speter dellist (&filelist); /* FIXME-krp: no longer correct. */ 17917721Speter if (dirlist) 18017721Speter dellist (&dirlist); 18117721Speter 18225839Speter#ifdef SERVER_SUPPORT 18325839Speter if (server_active) 18425839Speter { 18525839Speter for (i = 0; i < argc; ++i) 18625839Speter server_pathname_check (argv[i]); 18725839Speter } 18825839Speter#endif 18925839Speter 19017721Speter if (argc == 0) 19117721Speter { 19254431Speter int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM); 19317721Speter 19454431Speter#ifdef CLIENT_SUPPORT 19554431Speter if (!just_subdirs 19654431Speter && CVSroot_cmdline == NULL 19781407Speter && current_parsed_root->isremote) 19854431Speter { 199175282Sobrien cvsroot_t *root = Name_Root (NULL, update_dir); 200175282Sobrien if (root) 201175282Sobrien { 202175282Sobrien if (strcmp (root->original, current_parsed_root->original)) 203175282Sobrien /* We're skipping this directory because it is for 204175282Sobrien * a different root. Therefore, we just want to 205175282Sobrien * do the subdirectories only. Processing files would 206175282Sobrien * cause a working directory from one repository to be 207175282Sobrien * processed against a different repository, which could 208175282Sobrien * cause all kinds of spurious conflicts and such. 209175282Sobrien * 210175282Sobrien * Question: what about the case of "cvs update foo" 211175282Sobrien * where we process foo/bar and not foo itself? That 212175282Sobrien * seems to be handled somewhere (else) but why should 213175282Sobrien * it be a separate case? Needs investigation... */ 214175282Sobrien just_subdirs = 1; 215175282Sobrien free_cvsroot_t (root); 216175282Sobrien } 21754431Speter } 21854431Speter#endif 21954431Speter 22017721Speter /* 22117721Speter * There were no arguments, so we'll probably just recurse. The 22217721Speter * exception to the rule is when we are called from a directory 22317721Speter * without any CVS administration files. That has always meant to 22417721Speter * process each of the sub-directories, so we pretend like we were 22517721Speter * called with the list of sub-dirs of the current dir as args 22617721Speter */ 22754431Speter if (just_subdirs) 22832788Speter { 22925839Speter dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL); 23032788Speter /* If there are no sub-directories, there is a certain logic in 23132788Speter favor of doing nothing, but in fact probably the user is just 23232788Speter confused about what directory they are in, or whether they 23332788Speter cvs add'd a new directory. In the case of at least one 23432788Speter sub-directory, at least when we recurse into them we 23532788Speter notice (hopefully) whether they are under CVS control. */ 23632788Speter if (list_isempty (dirlist)) 23732788Speter { 23832788Speter if (update_dir[0] == '\0') 23932788Speter error (0, 0, "in directory .:"); 24032788Speter else 24132788Speter error (0, 0, "in directory %s:", update_dir); 24232788Speter error (1, 0, 24332788Speter "there is no version here; run '%s checkout' first", 24432788Speter program_name); 24532788Speter } 24644856Speter#ifdef CLIENT_SUPPORT 24781407Speter else if (current_parsed_root->isremote && server_started) 24844856Speter { 24944856Speter /* In the the case "cvs update foo bar baz", a call to 25044856Speter send_file_names in update.c will have sent the 25144856Speter appropriate "Argument" commands to the server. In 25244856Speter this case, that won't have happened, so we need to 25344856Speter do it here. While this example uses "update", this 25454431Speter generalizes to other commands. */ 25544856Speter 25654431Speter /* This is the same call to Find_Directories as above. 25754431Speter FIXME: perhaps it would be better to write a 25854431Speter function that duplicates a list. */ 25954431Speter args_to_send_when_finished = Find_Directories ((char *) NULL, 26054431Speter W_LOCAL, 26154431Speter (List *) NULL); 26244856Speter } 26344856Speter#endif 26432788Speter } 26517721Speter else 26617721Speter addlist (&dirlist, "."); 26717721Speter 26854431Speter goto do_the_work; 26917721Speter } 27017721Speter 27117721Speter 27217721Speter /* 27317721Speter * There were arguments, so we have to handle them by hand. To do 27417721Speter * that, we set up the filelist and dirlist with the arguments and 27517721Speter * call do_recursion. do_recursion recognizes the fact that the 27617721Speter * lists are non-null when it starts and doesn't update them. 27717721Speter * 27817721Speter * explicitly named directories are stored in dirlist. 27917721Speter * explicitly named files are stored in filelist. 28017721Speter * other possibility is named entities whicha are not currently in 28117721Speter * the working directory. 28217721Speter */ 28317721Speter 28417721Speter for (i = 0; i < argc; i++) 28517721Speter { 28617721Speter /* if this argument is a directory, then add it to the list of 28717721Speter directories. */ 28817721Speter 28917721Speter if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) 290102843Speter { 291102843Speter strip_trailing_slashes (argv[i]); 29217721Speter addlist (&dirlist, argv[i]); 293102843Speter } 29417721Speter else 29517721Speter { 29617721Speter /* otherwise, split argument into directory and component names. */ 29717721Speter char *dir; 29817721Speter char *comp; 29917721Speter char *file_to_try; 30017721Speter 30117721Speter /* Now break out argv[i] into directory part (DIR) and file part (COMP). 30217721Speter DIR and COMP will each point to a newly malloc'd string. */ 30317721Speter dir = xstrdup (argv[i]); 304128269Speter /* Its okay to discard the const below - we know we just allocated 305128269Speter * dir ourselves. 306128269Speter */ 307128269Speter comp = (char *)last_component (dir); 30817721Speter if (comp == dir) 30917721Speter { 31017721Speter /* no dir component. What we have is an implied "./" */ 31117721Speter dir = xstrdup("."); 31217721Speter } 31317721Speter else 31417721Speter { 31517721Speter char *p = comp; 31617721Speter 31717721Speter p[-1] = '\0'; 31817721Speter comp = xstrdup (p); 31917721Speter } 32017721Speter 32117721Speter /* if this argument exists as a file in the current 32217721Speter working directory tree, then add it to the files list. */ 32317721Speter 32425839Speter if (!(which & W_LOCAL)) 32517721Speter { 32617721Speter /* If doing rtag, we've done a chdir to the repository. */ 32725839Speter file_to_try = xmalloc (strlen (argv[i]) + sizeof (RCSEXT) + 5); 32825839Speter sprintf (file_to_try, "%s%s", argv[i], RCSEXT); 32917721Speter } 33017721Speter else 33125839Speter file_to_try = xstrdup (argv[i]); 33217721Speter 33325839Speter if (isfile (file_to_try)) 33417721Speter addfile (&files_by_dir, dir, comp); 33517721Speter else if (isdir (dir)) 33617721Speter { 337175282Sobrien if ((which & W_LOCAL) && isdir (CVSADM) && 338175282Sobrien !current_parsed_root->isremote) 33917721Speter { 34017721Speter /* otherwise, look for it in the repository. */ 34125839Speter char *tmp_update_dir; 34217721Speter char *repos; 34325839Speter char *reposfile; 34417721Speter 34525839Speter tmp_update_dir = xmalloc (strlen (update_dir) 34625839Speter + strlen (dir) 34725839Speter + 5); 34825839Speter strcpy (tmp_update_dir, update_dir); 34917721Speter 35025839Speter if (*tmp_update_dir != '\0') 35125839Speter (void) strcat (tmp_update_dir, "/"); 35225839Speter 35325839Speter (void) strcat (tmp_update_dir, dir); 35425839Speter 35517721Speter /* look for it in the repository. */ 35625839Speter repos = Name_Repository (dir, tmp_update_dir); 35725839Speter reposfile = xmalloc (strlen (repos) 35825839Speter + strlen (comp) 35925839Speter + 5); 36025839Speter (void) sprintf (reposfile, "%s/%s", repos, comp); 36117721Speter free (repos); 36217721Speter 36325839Speter if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) 36417721Speter addlist (&dirlist, argv[i]); 36517721Speter else 36617721Speter addfile (&files_by_dir, dir, comp); 36717721Speter 36825839Speter free (tmp_update_dir); 36925839Speter free (reposfile); 37017721Speter } 37117721Speter else 37217721Speter addfile (&files_by_dir, dir, comp); 37317721Speter } 37417721Speter else 37517721Speter error (1, 0, "no such directory `%s'", dir); 37617721Speter 37725839Speter free (file_to_try); 37817721Speter free (dir); 37917721Speter free (comp); 38017721Speter } 38117721Speter } 38217721Speter 38317721Speter /* At this point we have looped over all named arguments and built 38417721Speter a coupla lists. Now we unroll the lists, setting up and 38517721Speter calling do_recursion. */ 38617721Speter 38717721Speter err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); 38825839Speter dellist(&files_by_dir); 38917721Speter 39017721Speter /* then do_recursion on the dirlist. */ 39117721Speter if (dirlist != NULL) 39254431Speter { 39354431Speter do_the_work: 39425839Speter err += do_recursion (&frame); 39554431Speter } 39654431Speter 39717721Speter /* Free the data which expand_wild allocated. */ 39826065Speter free_names (&argc, argv); 39917721Speter 40025839Speter free (update_dir); 40125839Speter update_dir = NULL; 40254431Speter 40354431Speter#ifdef CLIENT_SUPPORT 40454431Speter if (args_to_send_when_finished != NULL) 40554431Speter { 40654431Speter /* FIXME (njc): in the multiroot case, we don't want to send 40754431Speter argument commands for those top-level directories which do 40854431Speter not contain any subdirectories which have files checked out 40981407Speter from current_parsed_root->original. If we do, and two repositories 41081407Speter have a module with the same name, nasty things could happen. 41154431Speter 41254431Speter This is hard. Perhaps we should send the Argument commands 41354431Speter later in this procedure, after we've had a chance to notice 41454431Speter which directores we're using (after do_recursion has been 41554431Speter called once). This means a _lot_ of rewriting, however. 41654431Speter 41754431Speter What we need to do for that to happen is descend the tree 41854431Speter and construct a list of directories which are checked out 41954431Speter from current_cvsroot. Now, we eliminate from the list all 42054431Speter of those directories which are immediate subdirectories of 42154431Speter another directory in the list. To say that the opposite 42254431Speter way, we keep the directories which are not immediate 42354431Speter subdirectories of any other in the list. Here's a picture: 42454431Speter 42554431Speter a 42654431Speter / \ 42754431Speter B C 42854431Speter / \ 42954431Speter D e 43054431Speter / \ 43154431Speter F G 43254431Speter / \ 43354431Speter H I 43454431Speter 43554431Speter The node in capitals are those directories which are 43654431Speter checked out from current_cvsroot. We want the list to 43754431Speter contain B, C, F, and G. D, H, and I are not included, 43854431Speter because their parents are also checked out from 43954431Speter current_cvsroot. 44054431Speter 44154431Speter The algorithm should be: 44254431Speter 44354431Speter 1) construct a tree of all directory names where each 44454431Speter element contains a directory name and a flag which notes if 44554431Speter that directory is checked out from current_cvsroot 44654431Speter 44754431Speter a0 44854431Speter / \ 44954431Speter B1 C1 45054431Speter / \ 45154431Speter D1 e0 45254431Speter / \ 45354431Speter F1 G1 45454431Speter / \ 45554431Speter H1 I1 45654431Speter 45754431Speter 2) Recursively descend the tree. For each node, recurse 45854431Speter before processing the node. If the flag is zero, do 45954431Speter nothing. If the flag is 1, check the node's parent. If 46054431Speter the parent's flag is one, change the current entry's flag 46154431Speter to zero. 46254431Speter 46354431Speter a0 46454431Speter / \ 46554431Speter B1 C1 46654431Speter / \ 46754431Speter D0 e0 46854431Speter / \ 46954431Speter F1 G1 47054431Speter / \ 47154431Speter H0 I0 47254431Speter 47354431Speter 3) Walk the tree and spit out "Argument" commands to tell 47454431Speter the server which directories to munge. 47554431Speter 47654431Speter Yuck. It's not clear this is worth spending time on, since 47754431Speter we might want to disable cvs commands entirely from 47854431Speter directories that do not have CVSADM files... 47954431Speter 48054431Speter Anyways, the solution as it stands has modified server.c 48154431Speter (dirswitch) to create admin files [via server.c 48254431Speter (create_adm_p)] in all path elements for a client's 48354431Speter "Directory xxx" command, which forces the server to descend 48454431Speter and serve the files there. client.c (send_file_names) has 48554431Speter also been modified to send only those arguments which are 48681407Speter appropriate to current_parsed_root->original. 48754431Speter 48854431Speter */ 48954431Speter 49054431Speter /* Construct a fake argc/argv pair. */ 49154431Speter 49254431Speter int our_argc = 0, i; 49354431Speter char **our_argv = NULL; 49454431Speter 49554431Speter if (! list_isempty (args_to_send_when_finished)) 49654431Speter { 49754431Speter Node *head, *p; 49854431Speter 49954431Speter head = args_to_send_when_finished->list; 50054431Speter 50154431Speter /* count the number of nodes */ 50254431Speter i = 0; 50354431Speter for (p = head->next; p != head; p = p->next) 50454431Speter i++; 50554431Speter our_argc = i; 50654431Speter 50754431Speter /* create the argument vector */ 50854431Speter our_argv = (char **) xmalloc (sizeof (char *) * our_argc); 50954431Speter 51054431Speter /* populate it */ 51154431Speter i = 0; 51254431Speter for (p = head->next; p != head; p = p->next) 51354431Speter our_argv[i++] = xstrdup (p->key); 51454431Speter } 51554431Speter 51654431Speter /* We don't want to expand widcards, since we've just created 51754431Speter a list of directories directly from the filesystem. */ 51854431Speter send_file_names (our_argc, our_argv, 0); 51954431Speter 52054431Speter /* Free our argc/argv. */ 52154431Speter if (our_argv != NULL) 52254431Speter { 52354431Speter for (i = 0; i < our_argc; i++) 52454431Speter free (our_argv[i]); 52554431Speter free (our_argv); 52654431Speter } 52754431Speter 52854431Speter dellist (&args_to_send_when_finished); 52954431Speter } 53054431Speter#endif 53154431Speter 53217721Speter return (err); 53317721Speter} 53417721Speter 53517721Speter/* 53617721Speter * Implement the recursive policies on the local directory. This may be 53717721Speter * called directly, or may be called by start_recursion 53817721Speter */ 53925839Speterstatic int 54025839Speterdo_recursion (frame) 54125839Speter struct recursion_frame *frame; 54217721Speter{ 54317721Speter int err = 0; 54417721Speter int dodoneproc = 1; 545128269Speter char *srepository = NULL; 54617721Speter List *entries = NULL; 547107487Speter int locktype; 54854431Speter int process_this_directory = 1; 54917721Speter 55017721Speter /* do nothing if told */ 55125839Speter if (frame->flags == R_SKIP_ALL) 55217721Speter return (0); 55317721Speter 554109660Speter locktype = noexec ? CVS_LOCK_NONE : frame->locktype; 55517721Speter 55617721Speter /* The fact that locks are not active here is what makes us fail to have 55717721Speter the 55817721Speter 55917721Speter If someone commits some changes in one cvs command, 56017721Speter then an update by someone else will either get all the 56117721Speter changes, or none of them. 56217721Speter 56317721Speter property (see node Concurrency in cvs.texinfo). 56417721Speter 56517721Speter The most straightforward fix would just to readlock the whole 56617721Speter tree before starting an update, but that means that if a commit 56717721Speter gets blocked on a big update, it might need to wait a *long* 56817721Speter time. 56917721Speter 57017721Speter A more adequate fix would be a two-pass design for update, 57117721Speter checkout, etc. The first pass would go through the repository, 57217721Speter with the whole tree readlocked, noting what versions of each 57317721Speter file we want to get. The second pass would release all locks 57417721Speter (except perhaps short-term locks on one file at a 57517721Speter time--although I think RCS already deals with this) and 57617721Speter actually get the files, specifying the particular versions it wants. 57717721Speter 57817721Speter This could be sped up by separating out the data needed for the 57917721Speter first pass into a separate file(s)--for example a file 58017721Speter attribute for each file whose value contains the head revision 58117721Speter for each branch. The structure should be designed so that 58217721Speter commit can relatively quickly update the information for a 58317721Speter single file or a handful of files (file attributes, as 58417721Speter implemented in Jan 96, are probably acceptable; improvements 58517721Speter would be possible such as branch attributes which are in 58617721Speter separate files for each branch). */ 58717721Speter 58817721Speter#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) 58917721Speter /* 59017721Speter * Now would be a good time to check to see if we need to stop 59117721Speter * generating data, to give the buffers a chance to drain to the 592107487Speter * remote client. We should not have locks active at this point, 593107487Speter * but if there are writelocks around, we cannot pause here. */ 594175282Sobrien if (server_active && locktype != CVS_LOCK_WRITE) 59517721Speter server_pause_check(); 59617721Speter#endif 59717721Speter 59854431Speter /* Check the value in CVSADM_ROOT and see if it's in the list. If 59954431Speter not, add it to our lists of CVS/Root directories and do not 60054431Speter process the files in this directory. Otherwise, continue as 60154431Speter usual. THIS_ROOT might be NULL if we're doing an initial 60254431Speter checkout -- check before using it. The default should be that 60354431Speter we process a directory's contents and only skip those contents 60454431Speter if a CVS/Root file exists. 60554431Speter 60654431Speter If we're running the server, we want to process all 60754431Speter directories, since we're guaranteed to have only one CVSROOT -- 60854431Speter our own. */ 60954431Speter 610175282Sobrien /* If -d was specified, it should override CVS/Root. 61154431Speter 612175282Sobrien In the single-repository case, it is long-standing CVS behavior 613175282Sobrien and makes sense - the user might want another access method, 614175282Sobrien another server (which mounts the same repository), &c. 61554431Speter 616175282Sobrien In the multiple-repository case, -d overrides all CVS/Root 617175282Sobrien files. That is the only plausible generalization I can 618175282Sobrien think of. */ 619175282Sobrien if (CVSroot_cmdline == NULL && !server_active) 62054431Speter { 621175282Sobrien cvsroot_t *this_root = Name_Root ((char *) NULL, update_dir); 62254431Speter if (this_root != NULL) 62354431Speter { 624175282Sobrien if (findnode (root_directories, this_root->original)) 62554431Speter { 626175282Sobrien process_this_directory = !strcmp (current_parsed_root->original, 627175282Sobrien this_root->original); 628175282Sobrien free_cvsroot_t (this_root); 629175282Sobrien } 630175282Sobrien else 631175282Sobrien { 63254431Speter /* Add it to our list. */ 63354431Speter 63454431Speter Node *n = getnode (); 63566528Speter n->type = NT_UNKNOWN; 636175282Sobrien n->key = xstrdup (this_root->original); 637175282Sobrien n->data = this_root; 63854431Speter 63954431Speter if (addnode (root_directories, n)) 640175282Sobrien error (1, 0, "cannot add new CVSROOT %s", 641175282Sobrien this_root->original); 642175282Sobrien 643175282Sobrien process_this_directory = 0; 64454431Speter } 64554431Speter } 64654431Speter } 64754431Speter 64817721Speter /* 64917721Speter * Fill in repository with the current repository 65017721Speter */ 65125839Speter if (frame->which & W_LOCAL) 65217721Speter { 65317721Speter if (isdir (CVSADM)) 654128269Speter { 65517721Speter repository = Name_Repository ((char *) NULL, update_dir); 656128269Speter srepository = repository; /* remember what to free */ 657128269Speter } 65817721Speter else 65917721Speter repository = NULL; 66017721Speter } 66117721Speter else 66217721Speter { 663128269Speter repository = frame->repository; 664128269Speter assert (repository != NULL); 66517721Speter } 66617721Speter 66717721Speter fileattr_startdir (repository); 66817721Speter 66917721Speter /* 67017721Speter * The filesdoneproc needs to be called for each directory where files 67117721Speter * processed, or each directory that is processed by a call where no 67217721Speter * directories were passed in. In fact, the only time we don't want to 67317721Speter * call back the filesdoneproc is when we are processing directories that 67417721Speter * were passed in on the command line (or in the special case of `.' when 67517721Speter * we were called with no args 67617721Speter */ 67717721Speter if (dirlist != NULL && filelist == NULL) 67817721Speter dodoneproc = 0; 67917721Speter 68017721Speter /* 68117721Speter * If filelist or dirlist is already set, we don't look again. Otherwise, 68217721Speter * find the files and directories 68317721Speter */ 68417721Speter if (filelist == NULL && dirlist == NULL) 68517721Speter { 68617721Speter /* both lists were NULL, so start from scratch */ 68725839Speter if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) 68817721Speter { 68925839Speter int lwhich = frame->which; 69017721Speter 69117721Speter /* be sure to look in the attic if we have sticky tags/date */ 69217721Speter if ((lwhich & W_ATTIC) == 0) 69317721Speter if (isreadable (CVSADM_TAG)) 69417721Speter lwhich |= W_ATTIC; 69517721Speter 69625839Speter /* In the !(which & W_LOCAL) case, we filled in repository 69725839Speter earlier in the function. In the (which & W_LOCAL) case, 69825839Speter the Find_Names function is going to look through the 69925839Speter Entries file. If we do not have a repository, that 70025839Speter does not make sense, so we insist upon having a 70125839Speter repository at this point. Name_Repository will give a 70225839Speter reasonable error message. */ 70325839Speter if (repository == NULL) 704128269Speter { 705128269Speter Name_Repository ((char *) NULL, update_dir); 706175282Sobrien assert (!"Not reached. Please report this problem to <" 707175282Sobrien PACKAGE_BUGREPORT ">"); 708128269Speter } 70925839Speter 71017721Speter /* find the files and fill in entries if appropriate */ 71154431Speter if (process_this_directory) 71254431Speter { 71354431Speter filelist = Find_Names (repository, lwhich, frame->aflag, 71454431Speter &entries); 71554431Speter if (filelist == NULL) 71654431Speter { 71754431Speter error (0, 0, "skipping directory %s", update_dir); 71854431Speter /* Note that Find_Directories and the filesdoneproc 71954431Speter in particular would do bad things ("? foo.c" in 72054431Speter the case of some filesdoneproc's). */ 72154431Speter goto skip_directory; 72254431Speter } 72354431Speter } 72417721Speter } 72517721Speter 72617721Speter /* find sub-directories if we will recurse */ 72725839Speter if (frame->flags != R_SKIP_DIRS) 72854431Speter dirlist = Find_Directories ( 72954431Speter process_this_directory ? repository : NULL, 73054431Speter frame->which, entries); 73117721Speter } 73217721Speter else 73317721Speter { 73417721Speter /* something was passed on the command line */ 73525839Speter if (filelist != NULL && frame->fileproc != NULL) 73617721Speter { 73717721Speter /* we will process files, so pre-parse entries */ 73825839Speter if (frame->which & W_LOCAL) 73934467Speter entries = Entries_Open (frame->aflag, NULL); 74017721Speter } 74117721Speter } 74217721Speter 74317721Speter /* process the files (if any) */ 74454431Speter if (process_this_directory && filelist != NULL && frame->fileproc) 74517721Speter { 74617721Speter struct file_info finfo_struct; 74725839Speter struct frame_and_file frfile; 74817721Speter 74917721Speter /* read lock it if necessary */ 750107487Speter if (repository) 751107487Speter { 752109660Speter if (locktype == CVS_LOCK_READ) 753107487Speter { 754107487Speter if (Reader_Lock (repository) != 0) 755107487Speter error (1, 0, "read lock failed - giving up"); 756107487Speter } 757109660Speter else if (locktype == CVS_LOCK_WRITE) 758107487Speter lock_dir_for_write (repository); 759107487Speter } 76017721Speter 76117721Speter#ifdef CLIENT_SUPPORT 76217721Speter /* For the server, we handle notifications in a completely different 76317721Speter place (server_notify). For local, we can't do them here--we don't 76417721Speter have writelocks in place, and there is no way to get writelocks 76517721Speter here. */ 76681407Speter if (current_parsed_root->isremote) 767177404Sobrien cvs_notify_check (repository, update_dir); 76817721Speter#endif /* CLIENT_SUPPORT */ 76917721Speter 77017721Speter finfo_struct.repository = repository; 77117721Speter finfo_struct.update_dir = update_dir; 77217721Speter finfo_struct.entries = entries; 77317721Speter /* do_file_proc will fill in finfo_struct.file. */ 77417721Speter 77525839Speter frfile.finfo = &finfo_struct; 77625839Speter frfile.frame = frame; 77725839Speter 77817721Speter /* process the files */ 77925839Speter err += walklist (filelist, do_file_proc, &frfile); 78017721Speter 78117721Speter /* unlock it */ 782128269Speter if (/* We only lock the repository above when repository is set */ 783128269Speter repository 784128269Speter /* and when asked for a read or write lock. */ 785128269Speter && locktype != CVS_LOCK_NONE) 78617721Speter Lock_Cleanup (); 78717721Speter 78817721Speter /* clean up */ 78917721Speter dellist (&filelist); 79017721Speter } 79117721Speter 79217721Speter /* call-back files done proc (if any) */ 79354431Speter if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) 79425839Speter err = frame->filesdoneproc (frame->callerdat, err, repository, 79525839Speter update_dir[0] ? update_dir : ".", 79625839Speter entries); 79717721Speter 79854431Speter skip_directory: 79917721Speter fileattr_write (); 80017721Speter fileattr_free (); 80117721Speter 80217721Speter /* process the directories (if necessary) */ 80317721Speter if (dirlist != NULL) 80425839Speter { 80525839Speter struct frame_and_entries frent; 80625839Speter 80725839Speter frent.frame = frame; 80825839Speter frent.entries = entries; 80925839Speter err += walklist (dirlist, do_dir_proc, (void *) &frent); 81025839Speter } 81125839Speter#if 0 81225839Speter else if (frame->dirleaveproc != NULL) 81325839Speter err += frame->dirleaveproc (frame->callerdat, ".", err, "."); 81417721Speter#endif 81517721Speter dellist (&dirlist); 81617721Speter 81725839Speter if (entries) 81825839Speter { 81925839Speter Entries_Close (entries); 82025839Speter entries = NULL; 82125839Speter } 82225839Speter 82317721Speter /* free the saved copy of the pointer if necessary */ 82417721Speter if (srepository) 82517721Speter { 82617721Speter free (srepository); 82717721Speter } 828128269Speter repository = (char *) NULL; 82917721Speter 830128269Speter return err; 83117721Speter} 83217721Speter 833128269Speter 834128269Speter 83517721Speter/* 83617721Speter * Process each of the files in the list with the callback proc 83717721Speter */ 83817721Speterstatic int 83917721Speterdo_file_proc (p, closure) 84017721Speter Node *p; 84117721Speter void *closure; 84217721Speter{ 84325839Speter struct frame_and_file *frfile = (struct frame_and_file *)closure; 84425839Speter struct file_info *finfo = frfile->finfo; 84517721Speter int ret; 846128269Speter char *tmp; 84717721Speter 84817721Speter finfo->file = p->key; 849128269Speter tmp = xmalloc (strlen (finfo->file) 85017721Speter + strlen (finfo->update_dir) 85117721Speter + 2); 852128269Speter tmp[0] = '\0'; 85317721Speter if (finfo->update_dir[0] != '\0') 85417721Speter { 855128269Speter strcat (tmp, finfo->update_dir); 856128269Speter strcat (tmp, "/"); 85717721Speter } 858128269Speter strcat (tmp, finfo->file); 85917721Speter 86025839Speter if (frfile->frame->dosrcs && repository) 86154431Speter { 86217721Speter finfo->rcs = RCS_parse (finfo->file, repository); 86354431Speter 86454431Speter /* OK, without W_LOCAL the error handling becomes relatively 86554431Speter simple. The file names came from readdir() on the 86654431Speter repository and so we know any ENOENT is an error 86754431Speter (e.g. symlink pointing to nothing). Now, the logic could 86854431Speter be simpler - since we got the name from readdir, we could 86954431Speter just be calling RCS_parsercsfile. */ 87054431Speter if (finfo->rcs == NULL 87154431Speter && !(frfile->frame->which & W_LOCAL)) 87254431Speter { 873128269Speter error (0, 0, "could not read RCS file for %s", tmp); 874128269Speter free (tmp); 87554431Speter cvs_flushout (); 87654431Speter return 0; 87754431Speter } 87854431Speter } 87917721Speter else 88017721Speter finfo->rcs = (RCSNode *) NULL; 881128269Speter finfo->fullname = tmp; 88225839Speter ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); 88317721Speter 88417721Speter freercsnode(&finfo->rcs); 885128269Speter free (tmp); 88617721Speter 88725839Speter /* Allow the user to monitor progress with tail -f. Doing this once 88825839Speter per file should be no big deal, but we don't want the performance 88925839Speter hit of flushing on every line like previous versions of CVS. */ 89025839Speter cvs_flushout (); 89125839Speter 892128269Speter return ret; 89317721Speter} 89417721Speter 895128269Speter 896128269Speter 89717721Speter/* 89817721Speter * Process each of the directories in the list (recursing as we go) 89917721Speter */ 90017721Speterstatic int 90117721Speterdo_dir_proc (p, closure) 90217721Speter Node *p; 90317721Speter void *closure; 90417721Speter{ 90525839Speter struct frame_and_entries *frent = (struct frame_and_entries *) closure; 90625839Speter struct recursion_frame *frame = frent->frame; 90725839Speter struct recursion_frame xframe; 90817721Speter char *dir = p->key; 90925839Speter char *newrepos; 91017721Speter List *sdirlist; 91117721Speter char *srepository; 91217721Speter Dtype dir_return = R_PROCESS; 91317721Speter int stripped_dot = 0; 91417721Speter int err = 0; 91517721Speter struct saved_cwd cwd; 91625839Speter char *saved_update_dir; 91754431Speter int process_this_directory = 1; 91817721Speter 91925839Speter if (fncmp (dir, CVSADM) == 0) 92025839Speter { 92125839Speter /* This seems to most often happen when users (beginning users, 92225839Speter generally), try "cvs ci *" or something similar. On that 92325839Speter theory, it is possible that we should just silently skip the 92425839Speter CVSADM directories, but on the other hand, using a wildcard 92525839Speter like this isn't necessarily a practice to encourage (it operates 92625839Speter only on files which exist in the working directory, unlike 92725839Speter regular CVS recursion). */ 92825839Speter 92925839Speter /* FIXME-reentrancy: printed_cvs_msg should be in a "command 93025839Speter struct" or some such, so that it gets cleared for each new 93125839Speter command (this is possible using the remote protocol and a 93225839Speter custom-written client). The struct recursion_frame is not 93325839Speter far back enough though, some commands (commit at least) 93425839Speter will call start_recursion several times. An alternate solution 93525839Speter would be to take this whole check and move it to a new function 93625839Speter validate_arguments or some such that all the commands call 93725839Speter and which snips the offending directory from the argc,argv 93825839Speter vector. */ 93925839Speter static int printed_cvs_msg = 0; 94025839Speter if (!printed_cvs_msg) 94125839Speter { 94225839Speter error (0, 0, "warning: directory %s specified in argument", 94325839Speter dir); 94425839Speter error (0, 0, "\ 94525839Speterbut CVS uses %s for its own purposes; skipping %s directory", 94625839Speter CVSADM, dir); 94725839Speter printed_cvs_msg = 1; 94825839Speter } 94925839Speter return 0; 95025839Speter } 95125839Speter 95225839Speter saved_update_dir = update_dir; 95325839Speter update_dir = xmalloc (strlen (saved_update_dir) 95425839Speter + strlen (dir) 95525839Speter + 5); 95625839Speter strcpy (update_dir, saved_update_dir); 95725839Speter 95817721Speter /* set up update_dir - skip dots if not at start */ 95917721Speter if (strcmp (dir, ".") != 0) 96017721Speter { 96117721Speter if (update_dir[0] != '\0') 96217721Speter { 96317721Speter (void) strcat (update_dir, "/"); 96417721Speter (void) strcat (update_dir, dir); 96517721Speter } 96617721Speter else 96717721Speter (void) strcpy (update_dir, dir); 96817721Speter 96917721Speter /* 97017721Speter * Here we need a plausible repository name for the sub-directory. We 97117721Speter * create one by concatenating the new directory name onto the 97217721Speter * previous repository name. The only case where the name should be 97317721Speter * used is in the case where we are creating a new sub-directory for 97417721Speter * update -d and in that case the generated name will be correct. 97517721Speter */ 97617721Speter if (repository == NULL) 97725839Speter newrepos = xstrdup (""); 97817721Speter else 97925839Speter { 98025839Speter newrepos = xmalloc (strlen (repository) + strlen (dir) + 5); 98132788Speter sprintf (newrepos, "%s/%s", repository, dir); 98225839Speter } 98317721Speter } 98417721Speter else 98517721Speter { 98617721Speter if (update_dir[0] == '\0') 98717721Speter (void) strcpy (update_dir, dir); 98817721Speter 98917721Speter if (repository == NULL) 99025839Speter newrepos = xstrdup (""); 99117721Speter else 99225839Speter newrepos = xstrdup (repository); 99317721Speter } 99417721Speter 99532788Speter /* Check to see that the CVSADM directory, if it exists, seems to be 99632788Speter well-formed. It can be missing files if the user hit ^C in the 99732788Speter middle of a previous run. We want to (a) make this a nonfatal 99832788Speter error, and (b) make sure we print which directory has the 99932788Speter problem. 100032788Speter 100132788Speter Do this before the direntproc, so that (1) the direntproc 100232788Speter doesn't have to guess/deduce whether we will skip the directory 100332788Speter (e.g. send_dirent_proc and whether to send the directory), and 100432788Speter (2) so that the warm fuzzy doesn't get printed if we skip the 100532788Speter directory. */ 100632788Speter if (frame->which & W_LOCAL) 100732788Speter { 100832788Speter char *cvsadmdir; 100932788Speter 101032788Speter cvsadmdir = xmalloc (strlen (dir) 101132788Speter + sizeof (CVSADM_REP) 101232788Speter + sizeof (CVSADM_ENT) 101332788Speter + 80); 101432788Speter 101532788Speter strcpy (cvsadmdir, dir); 101632788Speter strcat (cvsadmdir, "/"); 101732788Speter strcat (cvsadmdir, CVSADM); 101832788Speter if (isdir (cvsadmdir)) 101932788Speter { 102032788Speter strcpy (cvsadmdir, dir); 102132788Speter strcat (cvsadmdir, "/"); 102232788Speter strcat (cvsadmdir, CVSADM_REP); 102332788Speter if (!isfile (cvsadmdir)) 102432788Speter { 102532788Speter /* Some commands like update may have printed "? foo" but 102632788Speter if we were planning to recurse, and don't on account of 102732788Speter CVS/Repository, we want to say why. */ 102832788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 102932788Speter CVSADM_REP); 103032788Speter dir_return = R_SKIP_ALL; 103132788Speter } 103232788Speter 103332788Speter /* Likewise for CVS/Entries. */ 103432788Speter if (dir_return != R_SKIP_ALL) 103532788Speter { 103632788Speter strcpy (cvsadmdir, dir); 103732788Speter strcat (cvsadmdir, "/"); 103832788Speter strcat (cvsadmdir, CVSADM_ENT); 103932788Speter if (!isfile (cvsadmdir)) 104032788Speter { 104132788Speter /* Some commands like update may have printed "? foo" but 104232788Speter if we were planning to recurse, and don't on account of 104332788Speter CVS/Repository, we want to say why. */ 104432788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 104532788Speter CVSADM_ENT); 104632788Speter dir_return = R_SKIP_ALL; 104732788Speter } 104832788Speter } 104932788Speter } 105032788Speter free (cvsadmdir); 105132788Speter } 105232788Speter 105354431Speter /* Only process this directory if the root matches. This nearly 105454431Speter duplicates code in do_recursion. */ 105554431Speter 1056175282Sobrien /* If -d was specified, it should override CVS/Root. 105754431Speter 1058175282Sobrien In the single-repository case, it is long-standing CVS behavior 1059175282Sobrien and makes sense - the user might want another access method, 1060175282Sobrien another server (which mounts the same repository), &c. 106154431Speter 1062175282Sobrien In the multiple-repository case, -d overrides all CVS/Root 1063175282Sobrien files. That is the only plausible generalization I can 1064175282Sobrien think of. */ 1065175282Sobrien if (CVSroot_cmdline == NULL && !server_active) 106654431Speter { 1067175282Sobrien cvsroot_t *this_root = Name_Root (dir, update_dir); 106854431Speter if (this_root != NULL) 106954431Speter { 1070175282Sobrien if (findnode (root_directories, this_root->original)) 107154431Speter { 1072175282Sobrien process_this_directory = !strcmp (current_parsed_root->original, 1073175282Sobrien this_root->original); 1074175282Sobrien free_cvsroot_t (this_root); 1075175282Sobrien } 1076175282Sobrien else 1077175282Sobrien { 107854431Speter /* Add it to our list. */ 107954431Speter 108054431Speter Node *n = getnode (); 108166528Speter n->type = NT_UNKNOWN; 1082175282Sobrien n->key = xstrdup (this_root->original); 1083175282Sobrien n->data = this_root; 108454431Speter 108554431Speter if (addnode (root_directories, n)) 1086175282Sobrien error (1, 0, "cannot add new CVSROOT %s", 1087175282Sobrien this_root->original); 108854431Speter 1089175282Sobrien process_this_directory = 0; 109054431Speter } 109154431Speter } 109254431Speter } 109354431Speter 109417721Speter /* call-back dir entry proc (if any) */ 109532788Speter if (dir_return == R_SKIP_ALL) 109632788Speter ; 109732788Speter else if (frame->direntproc != NULL) 109854431Speter { 109954431Speter /* If we're doing the actual processing, call direntproc. 110054431Speter Otherwise, assume that we need to process this directory 110154431Speter and recurse. FIXME. */ 110254431Speter 110354431Speter if (process_this_directory) 110454431Speter dir_return = frame->direntproc (frame->callerdat, dir, newrepos, 110554431Speter update_dir, frent->entries); 110654431Speter else 110754431Speter dir_return = R_PROCESS; 110854431Speter } 110932788Speter else 111032788Speter { 111132788Speter /* Generic behavior. I don't see a reason to make the caller specify 111232788Speter a direntproc just to get this. */ 111332788Speter if ((frame->which & W_LOCAL) && !isdir (dir)) 111432788Speter dir_return = R_SKIP_ALL; 111532788Speter } 111632788Speter 111725839Speter free (newrepos); 111817721Speter 111917721Speter /* only process the dir if the return code was 0 */ 112017721Speter if (dir_return != R_SKIP_ALL) 112117721Speter { 112217721Speter /* save our current directory and static vars */ 112317721Speter if (save_cwd (&cwd)) 112425839Speter error_exit (); 112517721Speter sdirlist = dirlist; 112617721Speter srepository = repository; 112717721Speter dirlist = NULL; 112817721Speter 112917721Speter /* cd to the sub-directory */ 1130128269Speter if (CVS_CHDIR (dir) < 0) 113117721Speter error (1, errno, "could not chdir to %s", dir); 113217721Speter 113317721Speter /* honor the global SKIP_DIRS (a.k.a. local) */ 113425839Speter if (frame->flags == R_SKIP_DIRS) 113517721Speter dir_return = R_SKIP_DIRS; 113617721Speter 113717721Speter /* remember if the `.' will be stripped for subsequent dirs */ 113817721Speter if (strcmp (update_dir, ".") == 0) 113917721Speter { 114017721Speter update_dir[0] = '\0'; 114117721Speter stripped_dot = 1; 114217721Speter } 114317721Speter 114417721Speter /* make the recursive call */ 114525839Speter xframe = *frame; 114625839Speter xframe.flags = dir_return; 1147128269Speter /* Keep track of repository, really just for r* commands (rtag, rdiff, 1148128269Speter * co, ...) to tag_check_valid, since all the other commands use 1149128269Speter * CVS/Repository to figure it out per directory. 1150128269Speter */ 1151128269Speter if (repository) 1152128269Speter { 1153128269Speter if (strcmp (dir, ".") == 0) 1154128269Speter xframe.repository = xstrdup (repository); 1155128269Speter else 1156128269Speter { 1157128269Speter xframe.repository = xmalloc (strlen (repository) 1158128269Speter + strlen (dir) 1159128269Speter + 2); 1160128269Speter sprintf (xframe.repository, "%s/%s", repository, dir); 1161128269Speter } 1162128269Speter } 1163128269Speter else 1164128269Speter xframe.repository = NULL; 116525839Speter err += do_recursion (&xframe); 1166128269Speter if (xframe.repository) 1167128269Speter { 1168128269Speter free (xframe.repository); 1169128269Speter xframe.repository = NULL; 1170128269Speter } 117117721Speter 117217721Speter /* put the `.' back if necessary */ 117317721Speter if (stripped_dot) 117417721Speter (void) strcpy (update_dir, "."); 117517721Speter 117617721Speter /* call-back dir leave proc (if any) */ 117754431Speter if (process_this_directory && frame->dirleaveproc != NULL) 117825839Speter err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, 117925839Speter frent->entries); 118017721Speter 118117721Speter /* get back to where we started and restore state vars */ 118217721Speter if (restore_cwd (&cwd, NULL)) 118325839Speter error_exit (); 118417721Speter free_cwd (&cwd); 118517721Speter dirlist = sdirlist; 118617721Speter repository = srepository; 118717721Speter } 118817721Speter 118930337Speter free (update_dir); 119030337Speter update_dir = saved_update_dir; 119117721Speter 1192128269Speter return err; 119317721Speter} 119417721Speter 119517721Speter/* 119617721Speter * Add a node to a list allocating the list if necessary. 119717721Speter */ 119817721Speterstatic void 119917721Speteraddlist (listp, key) 120017721Speter List **listp; 120117721Speter char *key; 120217721Speter{ 120317721Speter Node *p; 120417721Speter 120517721Speter if (*listp == NULL) 120617721Speter *listp = getlist (); 120717721Speter p = getnode (); 120817721Speter p->type = FILES; 120917721Speter p->key = xstrdup (key); 121017721Speter if (addnode (*listp, p) != 0) 121117721Speter freenode (p); 121217721Speter} 121317721Speter 121417721Speterstatic void 121517721Speteraddfile (listp, dir, file) 121617721Speter List **listp; 121717721Speter char *dir; 121817721Speter char *file; 121917721Speter{ 122017721Speter Node *n; 122166528Speter List *fl; 122217721Speter 122317721Speter /* add this dir. */ 122417721Speter addlist (listp, dir); 122517721Speter 122617721Speter n = findnode (*listp, dir); 122717721Speter if (n == NULL) 122817721Speter { 122917721Speter error (1, 0, "can't find recently added dir node `%s' in start_recursion.", 123017721Speter dir); 123117721Speter } 123217721Speter 123317721Speter n->type = DIRS; 1234128269Speter fl = n->data; 123566528Speter addlist (&fl, file); 1236128269Speter n->data = fl; 123717721Speter return; 123817721Speter} 123917721Speter 124017721Speterstatic int 124117721Speterunroll_files_proc (p, closure) 124217721Speter Node *p; 124317721Speter void *closure; 124417721Speter{ 124517721Speter Node *n; 124617721Speter struct recursion_frame *frame = (struct recursion_frame *) closure; 124717721Speter int err = 0; 124817721Speter List *save_dirlist; 124917721Speter char *save_update_dir = NULL; 125017721Speter struct saved_cwd cwd; 125117721Speter 125217721Speter /* if this dir was also an explicitly named argument, then skip 125317721Speter it. We'll catch it later when we do dirs. */ 125417721Speter n = findnode (dirlist, p->key); 125517721Speter if (n != NULL) 125617721Speter return (0); 125717721Speter 125817721Speter /* otherwise, call dorecusion for this list of files. */ 1259128269Speter filelist = p->data; 126025839Speter p->data = NULL; 126117721Speter save_dirlist = dirlist; 126217721Speter dirlist = NULL; 126317721Speter 126417721Speter if (strcmp(p->key, ".") != 0) 126517721Speter { 126617721Speter if (save_cwd (&cwd)) 126725839Speter error_exit (); 126825839Speter if ( CVS_CHDIR (p->key) < 0) 126917721Speter error (1, errno, "could not chdir to %s", p->key); 127017721Speter 127125839Speter save_update_dir = update_dir; 127225839Speter update_dir = xmalloc (strlen (save_update_dir) 127325839Speter + strlen (p->key) 127425839Speter + 5); 127525839Speter strcpy (update_dir, save_update_dir); 127617721Speter 127717721Speter if (*update_dir != '\0') 127817721Speter (void) strcat (update_dir, "/"); 127917721Speter 128017721Speter (void) strcat (update_dir, p->key); 128117721Speter } 128217721Speter 128325839Speter err += do_recursion (frame); 128417721Speter 128517721Speter if (save_update_dir != NULL) 128617721Speter { 128725839Speter free (update_dir); 128825839Speter update_dir = save_update_dir; 128917721Speter 129017721Speter if (restore_cwd (&cwd, NULL)) 129125839Speter error_exit (); 129217721Speter free_cwd (&cwd); 129317721Speter } 129417721Speter 129517721Speter dirlist = save_dirlist; 129666528Speter if (filelist) 129766528Speter dellist (&filelist); 129817721Speter return(err); 129917721Speter} 1300