recurse.c revision 32788
117721Speter/* 217721Speter * Copyright (c) 1992, Brian Berliner and Jeff Polk 317721Speter * 417721Speter * You may distribute under the terms of the GNU General Public License as 532788Speter * specified in the README file that comes with the CVS source distribution. 617721Speter * 717721Speter * General recursion handler 817721Speter * 917721Speter */ 1017721Speter 1117721Speter#include "cvs.h" 1217721Speter#include "savecwd.h" 1317721Speter#include "fileattr.h" 1417721Speter#include "edit.h" 1517721Speter 1617721Speterstatic int do_dir_proc PROTO((Node * p, void *closure)); 1717721Speterstatic int do_file_proc PROTO((Node * p, void *closure)); 1817721Speterstatic void addlist PROTO((List ** listp, char *key)); 1917721Speterstatic int unroll_files_proc PROTO((Node *p, void *closure)); 2017721Speterstatic void addfile PROTO((List **listp, char *dir, char *file)); 2117721Speter 2225839Speterstatic char *update_dir; 2317721Speterstatic char *repository = NULL; 2417721Speterstatic List *filelist = NULL; /* holds list of files on which to operate */ 2517721Speterstatic List *dirlist = NULL; /* holds list of directories on which to operate */ 2617721Speter 2717721Speterstruct recursion_frame { 2825839Speter FILEPROC fileproc; 2925839Speter FILESDONEPROC filesdoneproc; 3025839Speter DIRENTPROC direntproc; 3125839Speter DIRLEAVEPROC dirleaveproc; 3225839Speter void *callerdat; 3325839Speter Dtype flags; 3425839Speter int which; 3525839Speter int aflag; 3625839Speter int readlock; 3725839Speter int dosrcs; 3817721Speter}; 3917721Speter 4025839Speterstatic int do_recursion PROTO ((struct recursion_frame *frame)); 4125839Speter 4225839Speter/* I am half tempted to shove a struct file_info * into the struct 4325839Speter recursion_frame (but then we would need to modify or create a 4425839Speter recursion_frame for each file), or shove a struct recursion_frame * 4525839Speter into the struct file_info (more tempting, although it isn't completely 4625839Speter clear that the struct file_info should contain info about recursion 4725839Speter processor internals). So instead use this struct. */ 4825839Speter 4925839Speterstruct frame_and_file { 5025839Speter struct recursion_frame *frame; 5125839Speter struct file_info *finfo; 5225839Speter}; 5325839Speter 5425839Speter/* Similarly, we need to pass the entries list to do_dir_proc. */ 5525839Speter 5625839Speterstruct frame_and_entries { 5725839Speter struct recursion_frame *frame; 5825839Speter List *entries; 5925839Speter}; 6025839Speter 6125839Speter/* Start a recursive command. 6225839Speter 6325839Speter Command line arguments (ARGC, ARGV) dictate the directories and 6425839Speter files on which we operate. In the special case of no arguments, we 6525839Speter default to ".". */ 6617721Speterint 6725839Speterstart_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, 6817721Speter argc, argv, local, which, aflag, readlock, 6925839Speter update_preload, dosrcs) 7017721Speter FILEPROC fileproc; 7117721Speter FILESDONEPROC filesdoneproc; 7217721Speter DIRENTPROC direntproc; 7317721Speter DIRLEAVEPROC dirleaveproc; 7425839Speter void *callerdat; 7525839Speter 7617721Speter int argc; 7717721Speter char **argv; 7817721Speter int local; 7925839Speter 8025839Speter /* This specifies the kind of recursion. There are several cases: 8125839Speter 8225839Speter 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current 8325839Speter directory when we are called must be the repository and 8425839Speter recursion proceeds according to what exists in the repository. 8525839Speter 8625839Speter 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The 8725839Speter current directory when we are called must be the working 8825839Speter directory. Recursion proceeds according to what exists in the 8925839Speter working directory, never (I think) consulting any part of the 9025839Speter repository which does not correspond to the working directory 9125839Speter ("correspond" == Name_Repository). 9225839Speter 9325839Speter 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the 9425839Speter weird one. The current directory when we are called must be 9525839Speter the working directory. We recurse through working directories, 9625839Speter but we recurse into a directory if it is exists in the working 9725839Speter directory *or* it exists in the repository. If a directory 9825839Speter does not exist in the working directory, the direntproc must 9925839Speter either tell us to skip it (R_SKIP_ALL), or must create it (I 10025839Speter think those are the only two cases). */ 10117721Speter int which; 10225839Speter 10317721Speter int aflag; 10417721Speter int readlock; 10517721Speter char *update_preload; 10617721Speter int dosrcs; 10717721Speter{ 10817721Speter int i, err = 0; 10917721Speter List *files_by_dir = NULL; 11017721Speter struct recursion_frame frame; 11117721Speter 11225839Speter frame.fileproc = fileproc; 11325839Speter frame.filesdoneproc = filesdoneproc; 11425839Speter frame.direntproc = direntproc; 11525839Speter frame.dirleaveproc = dirleaveproc; 11625839Speter frame.callerdat = callerdat; 11725839Speter frame.flags = local ? R_SKIP_DIRS : R_PROCESS; 11825839Speter frame.which = which; 11925839Speter frame.aflag = aflag; 12025839Speter frame.readlock = readlock; 12125839Speter frame.dosrcs = dosrcs; 12225839Speter 12317721Speter expand_wild (argc, argv, &argc, &argv); 12417721Speter 12517721Speter if (update_preload == NULL) 12625839Speter update_dir = xstrdup (""); 12717721Speter else 12825839Speter update_dir = xstrdup (update_preload); 12917721Speter 13017721Speter /* clean up from any previous calls to start_recursion */ 13117721Speter if (repository) 13217721Speter { 13317721Speter free (repository); 13417721Speter repository = (char *) NULL; 13517721Speter } 13617721Speter if (filelist) 13717721Speter dellist (&filelist); /* FIXME-krp: no longer correct. */ 13817721Speter if (dirlist) 13917721Speter dellist (&dirlist); 14017721Speter 14125839Speter#ifdef SERVER_SUPPORT 14225839Speter if (server_active) 14325839Speter { 14425839Speter for (i = 0; i < argc; ++i) 14525839Speter server_pathname_check (argv[i]); 14625839Speter } 14725839Speter#endif 14825839Speter 14917721Speter if (argc == 0) 15017721Speter { 15117721Speter 15217721Speter /* 15317721Speter * There were no arguments, so we'll probably just recurse. The 15417721Speter * exception to the rule is when we are called from a directory 15517721Speter * without any CVS administration files. That has always meant to 15617721Speter * process each of the sub-directories, so we pretend like we were 15717721Speter * called with the list of sub-dirs of the current dir as args 15817721Speter */ 15917721Speter if ((which & W_LOCAL) && !isdir (CVSADM)) 16032788Speter { 16125839Speter dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL); 16232788Speter /* If there are no sub-directories, there is a certain logic in 16332788Speter favor of doing nothing, but in fact probably the user is just 16432788Speter confused about what directory they are in, or whether they 16532788Speter cvs add'd a new directory. In the case of at least one 16632788Speter sub-directory, at least when we recurse into them we 16732788Speter notice (hopefully) whether they are under CVS control. */ 16832788Speter if (list_isempty (dirlist)) 16932788Speter { 17032788Speter if (update_dir[0] == '\0') 17132788Speter error (0, 0, "in directory .:"); 17232788Speter else 17332788Speter error (0, 0, "in directory %s:", update_dir); 17432788Speter error (1, 0, 17532788Speter "there is no version here; run '%s checkout' first", 17632788Speter program_name); 17732788Speter } 17832788Speter } 17917721Speter else 18017721Speter addlist (&dirlist, "."); 18117721Speter 18225839Speter err += do_recursion (&frame); 18325839Speter goto out; 18417721Speter } 18517721Speter 18617721Speter 18717721Speter /* 18817721Speter * There were arguments, so we have to handle them by hand. To do 18917721Speter * that, we set up the filelist and dirlist with the arguments and 19017721Speter * call do_recursion. do_recursion recognizes the fact that the 19117721Speter * lists are non-null when it starts and doesn't update them. 19217721Speter * 19317721Speter * explicitly named directories are stored in dirlist. 19417721Speter * explicitly named files are stored in filelist. 19517721Speter * other possibility is named entities whicha are not currently in 19617721Speter * the working directory. 19717721Speter */ 19817721Speter 19917721Speter for (i = 0; i < argc; i++) 20017721Speter { 20117721Speter /* if this argument is a directory, then add it to the list of 20217721Speter directories. */ 20317721Speter 20417721Speter if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) 20517721Speter addlist (&dirlist, argv[i]); 20617721Speter else 20717721Speter { 20817721Speter /* otherwise, split argument into directory and component names. */ 20917721Speter char *dir; 21017721Speter char *comp; 21117721Speter char *file_to_try; 21217721Speter 21317721Speter /* Now break out argv[i] into directory part (DIR) and file part (COMP). 21417721Speter DIR and COMP will each point to a newly malloc'd string. */ 21517721Speter dir = xstrdup (argv[i]); 21617721Speter comp = last_component (dir); 21717721Speter if (comp == dir) 21817721Speter { 21917721Speter /* no dir component. What we have is an implied "./" */ 22017721Speter dir = xstrdup("."); 22117721Speter } 22217721Speter else 22317721Speter { 22417721Speter char *p = comp; 22517721Speter 22617721Speter p[-1] = '\0'; 22717721Speter comp = xstrdup (p); 22817721Speter } 22917721Speter 23017721Speter /* if this argument exists as a file in the current 23117721Speter working directory tree, then add it to the files list. */ 23217721Speter 23325839Speter if (!(which & W_LOCAL)) 23417721Speter { 23517721Speter /* If doing rtag, we've done a chdir to the repository. */ 23625839Speter file_to_try = xmalloc (strlen (argv[i]) + sizeof (RCSEXT) + 5); 23725839Speter sprintf (file_to_try, "%s%s", argv[i], RCSEXT); 23817721Speter } 23917721Speter else 24025839Speter file_to_try = xstrdup (argv[i]); 24117721Speter 24225839Speter if (isfile (file_to_try)) 24317721Speter addfile (&files_by_dir, dir, comp); 24417721Speter else if (isdir (dir)) 24517721Speter { 24625839Speter if ((which & W_LOCAL) && isdir (CVSADM) 24725839Speter#ifdef CLIENT_SUPPORT 24825839Speter && !client_active 24925839Speter#endif 25025839Speter ) 25117721Speter { 25217721Speter /* otherwise, look for it in the repository. */ 25325839Speter char *tmp_update_dir; 25417721Speter char *repos; 25525839Speter char *reposfile; 25617721Speter 25725839Speter tmp_update_dir = xmalloc (strlen (update_dir) 25825839Speter + strlen (dir) 25925839Speter + 5); 26025839Speter strcpy (tmp_update_dir, update_dir); 26117721Speter 26225839Speter if (*tmp_update_dir != '\0') 26325839Speter (void) strcat (tmp_update_dir, "/"); 26425839Speter 26525839Speter (void) strcat (tmp_update_dir, dir); 26625839Speter 26717721Speter /* look for it in the repository. */ 26825839Speter repos = Name_Repository (dir, tmp_update_dir); 26925839Speter reposfile = xmalloc (strlen (repos) 27025839Speter + strlen (comp) 27125839Speter + 5); 27225839Speter (void) sprintf (reposfile, "%s/%s", repos, comp); 27317721Speter free (repos); 27417721Speter 27525839Speter if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) 27617721Speter addlist (&dirlist, argv[i]); 27717721Speter else 27817721Speter addfile (&files_by_dir, dir, comp); 27917721Speter 28025839Speter free (tmp_update_dir); 28125839Speter free (reposfile); 28217721Speter } 28317721Speter else 28417721Speter addfile (&files_by_dir, dir, comp); 28517721Speter } 28617721Speter else 28717721Speter error (1, 0, "no such directory `%s'", dir); 28817721Speter 28925839Speter free (file_to_try); 29017721Speter free (dir); 29117721Speter free (comp); 29217721Speter } 29317721Speter } 29417721Speter 29517721Speter /* At this point we have looped over all named arguments and built 29617721Speter a coupla lists. Now we unroll the lists, setting up and 29717721Speter calling do_recursion. */ 29817721Speter 29917721Speter err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); 30025839Speter dellist(&files_by_dir); 30117721Speter 30217721Speter /* then do_recursion on the dirlist. */ 30317721Speter if (dirlist != NULL) 30425839Speter err += do_recursion (&frame); 30517721Speter 30617721Speter /* Free the data which expand_wild allocated. */ 30726065Speter free_names (&argc, argv); 30817721Speter 30925839Speter out: 31025839Speter free (update_dir); 31125839Speter update_dir = NULL; 31217721Speter return (err); 31317721Speter} 31417721Speter 31517721Speter/* 31617721Speter * Implement the recursive policies on the local directory. This may be 31717721Speter * called directly, or may be called by start_recursion 31817721Speter */ 31925839Speterstatic int 32025839Speterdo_recursion (frame) 32125839Speter struct recursion_frame *frame; 32217721Speter{ 32317721Speter int err = 0; 32417721Speter int dodoneproc = 1; 32517721Speter char *srepository; 32617721Speter List *entries = NULL; 32725839Speter int should_readlock; 32817721Speter 32917721Speter /* do nothing if told */ 33025839Speter if (frame->flags == R_SKIP_ALL) 33117721Speter return (0); 33217721Speter 33325839Speter should_readlock = noexec ? 0 : frame->readlock; 33417721Speter 33517721Speter /* The fact that locks are not active here is what makes us fail to have 33617721Speter the 33717721Speter 33817721Speter If someone commits some changes in one cvs command, 33917721Speter then an update by someone else will either get all the 34017721Speter changes, or none of them. 34117721Speter 34217721Speter property (see node Concurrency in cvs.texinfo). 34317721Speter 34417721Speter The most straightforward fix would just to readlock the whole 34517721Speter tree before starting an update, but that means that if a commit 34617721Speter gets blocked on a big update, it might need to wait a *long* 34717721Speter time. 34817721Speter 34917721Speter A more adequate fix would be a two-pass design for update, 35017721Speter checkout, etc. The first pass would go through the repository, 35117721Speter with the whole tree readlocked, noting what versions of each 35217721Speter file we want to get. The second pass would release all locks 35317721Speter (except perhaps short-term locks on one file at a 35417721Speter time--although I think RCS already deals with this) and 35517721Speter actually get the files, specifying the particular versions it wants. 35617721Speter 35717721Speter This could be sped up by separating out the data needed for the 35817721Speter first pass into a separate file(s)--for example a file 35917721Speter attribute for each file whose value contains the head revision 36017721Speter for each branch. The structure should be designed so that 36117721Speter commit can relatively quickly update the information for a 36217721Speter single file or a handful of files (file attributes, as 36317721Speter implemented in Jan 96, are probably acceptable; improvements 36417721Speter would be possible such as branch attributes which are in 36517721Speter separate files for each branch). */ 36617721Speter 36717721Speter#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) 36817721Speter /* 36917721Speter * Now would be a good time to check to see if we need to stop 37017721Speter * generating data, to give the buffers a chance to drain to the 37117721Speter * remote client. We should not have locks active at this point. 37217721Speter */ 37317721Speter if (server_active 37417721Speter /* If there are writelocks around, we cannot pause here. */ 37525839Speter && (should_readlock || noexec)) 37617721Speter server_pause_check(); 37717721Speter#endif 37817721Speter 37917721Speter /* 38017721Speter * Fill in repository with the current repository 38117721Speter */ 38225839Speter if (frame->which & W_LOCAL) 38317721Speter { 38417721Speter if (isdir (CVSADM)) 38517721Speter repository = Name_Repository ((char *) NULL, update_dir); 38617721Speter else 38717721Speter repository = NULL; 38817721Speter } 38917721Speter else 39017721Speter { 39125839Speter repository = xgetwd (); 39225839Speter if (repository == NULL) 39325839Speter error (1, errno, "could not get working directory"); 39417721Speter } 39517721Speter srepository = repository; /* remember what to free */ 39617721Speter 39717721Speter fileattr_startdir (repository); 39817721Speter 39917721Speter /* 40017721Speter * The filesdoneproc needs to be called for each directory where files 40117721Speter * processed, or each directory that is processed by a call where no 40217721Speter * directories were passed in. In fact, the only time we don't want to 40317721Speter * call back the filesdoneproc is when we are processing directories that 40417721Speter * were passed in on the command line (or in the special case of `.' when 40517721Speter * we were called with no args 40617721Speter */ 40717721Speter if (dirlist != NULL && filelist == NULL) 40817721Speter dodoneproc = 0; 40917721Speter 41017721Speter /* 41117721Speter * If filelist or dirlist is already set, we don't look again. Otherwise, 41217721Speter * find the files and directories 41317721Speter */ 41417721Speter if (filelist == NULL && dirlist == NULL) 41517721Speter { 41617721Speter /* both lists were NULL, so start from scratch */ 41725839Speter if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) 41817721Speter { 41925839Speter int lwhich = frame->which; 42017721Speter 42117721Speter /* be sure to look in the attic if we have sticky tags/date */ 42217721Speter if ((lwhich & W_ATTIC) == 0) 42317721Speter if (isreadable (CVSADM_TAG)) 42417721Speter lwhich |= W_ATTIC; 42517721Speter 42625839Speter /* In the !(which & W_LOCAL) case, we filled in repository 42725839Speter earlier in the function. In the (which & W_LOCAL) case, 42825839Speter the Find_Names function is going to look through the 42925839Speter Entries file. If we do not have a repository, that 43025839Speter does not make sense, so we insist upon having a 43125839Speter repository at this point. Name_Repository will give a 43225839Speter reasonable error message. */ 43325839Speter if (repository == NULL) 43425839Speter repository = Name_Repository ((char *) NULL, update_dir); 43525839Speter 43617721Speter /* find the files and fill in entries if appropriate */ 43725839Speter filelist = Find_Names (repository, lwhich, frame->aflag, &entries); 43817721Speter } 43917721Speter 44017721Speter /* find sub-directories if we will recurse */ 44125839Speter if (frame->flags != R_SKIP_DIRS) 44225839Speter dirlist = Find_Directories (repository, frame->which, entries); 44317721Speter } 44417721Speter else 44517721Speter { 44617721Speter /* something was passed on the command line */ 44725839Speter if (filelist != NULL && frame->fileproc != NULL) 44817721Speter { 44917721Speter /* we will process files, so pre-parse entries */ 45025839Speter if (frame->which & W_LOCAL) 45125839Speter entries = Entries_Open (frame->aflag); 45217721Speter } 45317721Speter } 45417721Speter 45517721Speter /* process the files (if any) */ 45625839Speter if (filelist != NULL && frame->fileproc) 45717721Speter { 45817721Speter struct file_info finfo_struct; 45925839Speter struct frame_and_file frfile; 46017721Speter 46117721Speter /* read lock it if necessary */ 46225839Speter if (should_readlock && repository && Reader_Lock (repository) != 0) 46317721Speter error (1, 0, "read lock failed - giving up"); 46417721Speter 46517721Speter#ifdef CLIENT_SUPPORT 46617721Speter /* For the server, we handle notifications in a completely different 46717721Speter place (server_notify). For local, we can't do them here--we don't 46817721Speter have writelocks in place, and there is no way to get writelocks 46917721Speter here. */ 47017721Speter if (client_active) 47117721Speter notify_check (repository, update_dir); 47217721Speter#endif /* CLIENT_SUPPORT */ 47317721Speter 47417721Speter finfo_struct.repository = repository; 47517721Speter finfo_struct.update_dir = update_dir; 47617721Speter finfo_struct.entries = entries; 47717721Speter /* do_file_proc will fill in finfo_struct.file. */ 47817721Speter 47925839Speter frfile.finfo = &finfo_struct; 48025839Speter frfile.frame = frame; 48125839Speter 48217721Speter /* process the files */ 48325839Speter err += walklist (filelist, do_file_proc, &frfile); 48417721Speter 48517721Speter /* unlock it */ 48625839Speter if (should_readlock) 48717721Speter Lock_Cleanup (); 48817721Speter 48917721Speter /* clean up */ 49017721Speter dellist (&filelist); 49117721Speter } 49217721Speter 49317721Speter /* call-back files done proc (if any) */ 49425839Speter if (dodoneproc && frame->filesdoneproc != NULL) 49525839Speter err = frame->filesdoneproc (frame->callerdat, err, repository, 49625839Speter update_dir[0] ? update_dir : ".", 49725839Speter entries); 49817721Speter 49917721Speter fileattr_write (); 50017721Speter fileattr_free (); 50117721Speter 50217721Speter /* process the directories (if necessary) */ 50317721Speter if (dirlist != NULL) 50425839Speter { 50525839Speter struct frame_and_entries frent; 50625839Speter 50725839Speter frent.frame = frame; 50825839Speter frent.entries = entries; 50925839Speter err += walklist (dirlist, do_dir_proc, (void *) &frent); 51025839Speter } 51125839Speter#if 0 51225839Speter else if (frame->dirleaveproc != NULL) 51325839Speter err += frame->dirleaveproc (frame->callerdat, ".", err, "."); 51417721Speter#endif 51517721Speter dellist (&dirlist); 51617721Speter 51725839Speter if (entries) 51825839Speter { 51925839Speter Entries_Close (entries); 52025839Speter entries = NULL; 52125839Speter } 52225839Speter 52317721Speter /* free the saved copy of the pointer if necessary */ 52417721Speter if (srepository) 52517721Speter { 52617721Speter free (srepository); 52717721Speter repository = (char *) NULL; 52817721Speter } 52917721Speter 53017721Speter return (err); 53117721Speter} 53217721Speter 53317721Speter/* 53417721Speter * Process each of the files in the list with the callback proc 53517721Speter */ 53617721Speterstatic int 53717721Speterdo_file_proc (p, closure) 53817721Speter Node *p; 53917721Speter void *closure; 54017721Speter{ 54125839Speter struct frame_and_file *frfile = (struct frame_and_file *)closure; 54225839Speter struct file_info *finfo = frfile->finfo; 54317721Speter int ret; 54417721Speter 54517721Speter finfo->file = p->key; 54617721Speter finfo->fullname = xmalloc (strlen (finfo->file) 54717721Speter + strlen (finfo->update_dir) 54817721Speter + 2); 54917721Speter finfo->fullname[0] = '\0'; 55017721Speter if (finfo->update_dir[0] != '\0') 55117721Speter { 55217721Speter strcat (finfo->fullname, finfo->update_dir); 55317721Speter strcat (finfo->fullname, "/"); 55417721Speter } 55517721Speter strcat (finfo->fullname, finfo->file); 55617721Speter 55725839Speter if (frfile->frame->dosrcs && repository) 55817721Speter finfo->rcs = RCS_parse (finfo->file, repository); 55917721Speter else 56017721Speter finfo->rcs = (RCSNode *) NULL; 56125839Speter ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); 56217721Speter 56317721Speter freercsnode(&finfo->rcs); 56417721Speter free (finfo->fullname); 56517721Speter 56625839Speter /* Allow the user to monitor progress with tail -f. Doing this once 56725839Speter per file should be no big deal, but we don't want the performance 56825839Speter hit of flushing on every line like previous versions of CVS. */ 56925839Speter cvs_flushout (); 57025839Speter 57117721Speter return (ret); 57217721Speter} 57317721Speter 57417721Speter/* 57517721Speter * Process each of the directories in the list (recursing as we go) 57617721Speter */ 57717721Speterstatic int 57817721Speterdo_dir_proc (p, closure) 57917721Speter Node *p; 58017721Speter void *closure; 58117721Speter{ 58225839Speter struct frame_and_entries *frent = (struct frame_and_entries *) closure; 58325839Speter struct recursion_frame *frame = frent->frame; 58425839Speter struct recursion_frame xframe; 58517721Speter char *dir = p->key; 58625839Speter char *newrepos; 58717721Speter List *sdirlist; 58817721Speter char *srepository; 58917721Speter char *cp; 59017721Speter Dtype dir_return = R_PROCESS; 59117721Speter int stripped_dot = 0; 59217721Speter int err = 0; 59317721Speter struct saved_cwd cwd; 59425839Speter char *saved_update_dir; 59517721Speter 59625839Speter if (fncmp (dir, CVSADM) == 0) 59725839Speter { 59825839Speter /* This seems to most often happen when users (beginning users, 59925839Speter generally), try "cvs ci *" or something similar. On that 60025839Speter theory, it is possible that we should just silently skip the 60125839Speter CVSADM directories, but on the other hand, using a wildcard 60225839Speter like this isn't necessarily a practice to encourage (it operates 60325839Speter only on files which exist in the working directory, unlike 60425839Speter regular CVS recursion). */ 60525839Speter 60625839Speter /* FIXME-reentrancy: printed_cvs_msg should be in a "command 60725839Speter struct" or some such, so that it gets cleared for each new 60825839Speter command (this is possible using the remote protocol and a 60925839Speter custom-written client). The struct recursion_frame is not 61025839Speter far back enough though, some commands (commit at least) 61125839Speter will call start_recursion several times. An alternate solution 61225839Speter would be to take this whole check and move it to a new function 61325839Speter validate_arguments or some such that all the commands call 61425839Speter and which snips the offending directory from the argc,argv 61525839Speter vector. */ 61625839Speter static int printed_cvs_msg = 0; 61725839Speter if (!printed_cvs_msg) 61825839Speter { 61925839Speter error (0, 0, "warning: directory %s specified in argument", 62025839Speter dir); 62125839Speter error (0, 0, "\ 62225839Speterbut CVS uses %s for its own purposes; skipping %s directory", 62325839Speter CVSADM, dir); 62425839Speter printed_cvs_msg = 1; 62525839Speter } 62625839Speter return 0; 62725839Speter } 62825839Speter 62925839Speter saved_update_dir = update_dir; 63025839Speter update_dir = xmalloc (strlen (saved_update_dir) 63125839Speter + strlen (dir) 63225839Speter + 5); 63325839Speter strcpy (update_dir, saved_update_dir); 63425839Speter 63517721Speter /* set up update_dir - skip dots if not at start */ 63617721Speter if (strcmp (dir, ".") != 0) 63717721Speter { 63817721Speter if (update_dir[0] != '\0') 63917721Speter { 64017721Speter (void) strcat (update_dir, "/"); 64117721Speter (void) strcat (update_dir, dir); 64217721Speter } 64317721Speter else 64417721Speter (void) strcpy (update_dir, dir); 64517721Speter 64617721Speter /* 64717721Speter * Here we need a plausible repository name for the sub-directory. We 64817721Speter * create one by concatenating the new directory name onto the 64917721Speter * previous repository name. The only case where the name should be 65017721Speter * used is in the case where we are creating a new sub-directory for 65117721Speter * update -d and in that case the generated name will be correct. 65217721Speter */ 65317721Speter if (repository == NULL) 65425839Speter newrepos = xstrdup (""); 65517721Speter else 65625839Speter { 65725839Speter newrepos = xmalloc (strlen (repository) + strlen (dir) + 5); 65832788Speter sprintf (newrepos, "%s/%s", repository, dir); 65925839Speter } 66017721Speter } 66117721Speter else 66217721Speter { 66317721Speter if (update_dir[0] == '\0') 66417721Speter (void) strcpy (update_dir, dir); 66517721Speter 66617721Speter if (repository == NULL) 66725839Speter newrepos = xstrdup (""); 66817721Speter else 66925839Speter newrepos = xstrdup (repository); 67017721Speter } 67117721Speter 67232788Speter /* Check to see that the CVSADM directory, if it exists, seems to be 67332788Speter well-formed. It can be missing files if the user hit ^C in the 67432788Speter middle of a previous run. We want to (a) make this a nonfatal 67532788Speter error, and (b) make sure we print which directory has the 67632788Speter problem. 67732788Speter 67832788Speter Do this before the direntproc, so that (1) the direntproc 67932788Speter doesn't have to guess/deduce whether we will skip the directory 68032788Speter (e.g. send_dirent_proc and whether to send the directory), and 68132788Speter (2) so that the warm fuzzy doesn't get printed if we skip the 68232788Speter directory. */ 68332788Speter if (frame->which & W_LOCAL) 68432788Speter { 68532788Speter char *cvsadmdir; 68632788Speter 68732788Speter cvsadmdir = xmalloc (strlen (dir) 68832788Speter + sizeof (CVSADM_REP) 68932788Speter + sizeof (CVSADM_ENT) 69032788Speter + 80); 69132788Speter 69232788Speter strcpy (cvsadmdir, dir); 69332788Speter strcat (cvsadmdir, "/"); 69432788Speter strcat (cvsadmdir, CVSADM); 69532788Speter if (isdir (cvsadmdir)) 69632788Speter { 69732788Speter strcpy (cvsadmdir, dir); 69832788Speter strcat (cvsadmdir, "/"); 69932788Speter strcat (cvsadmdir, CVSADM_REP); 70032788Speter if (!isfile (cvsadmdir)) 70132788Speter { 70232788Speter /* Some commands like update may have printed "? foo" but 70332788Speter if we were planning to recurse, and don't on account of 70432788Speter CVS/Repository, we want to say why. */ 70532788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 70632788Speter CVSADM_REP); 70732788Speter dir_return = R_SKIP_ALL; 70832788Speter } 70932788Speter 71032788Speter /* Likewise for CVS/Entries. */ 71132788Speter if (dir_return != R_SKIP_ALL) 71232788Speter { 71332788Speter strcpy (cvsadmdir, dir); 71432788Speter strcat (cvsadmdir, "/"); 71532788Speter strcat (cvsadmdir, CVSADM_ENT); 71632788Speter if (!isfile (cvsadmdir)) 71732788Speter { 71832788Speter /* Some commands like update may have printed "? foo" but 71932788Speter if we were planning to recurse, and don't on account of 72032788Speter CVS/Repository, we want to say why. */ 72132788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 72232788Speter CVSADM_ENT); 72332788Speter dir_return = R_SKIP_ALL; 72432788Speter } 72532788Speter } 72632788Speter } 72732788Speter free (cvsadmdir); 72832788Speter } 72932788Speter 73017721Speter /* call-back dir entry proc (if any) */ 73132788Speter if (dir_return == R_SKIP_ALL) 73232788Speter ; 73332788Speter else if (frame->direntproc != NULL) 73425839Speter dir_return = frame->direntproc (frame->callerdat, dir, newrepos, 73525839Speter update_dir, frent->entries); 73632788Speter else 73732788Speter { 73832788Speter /* Generic behavior. I don't see a reason to make the caller specify 73932788Speter a direntproc just to get this. */ 74032788Speter if ((frame->which & W_LOCAL) && !isdir (dir)) 74132788Speter dir_return = R_SKIP_ALL; 74232788Speter } 74332788Speter 74425839Speter free (newrepos); 74517721Speter 74617721Speter /* only process the dir if the return code was 0 */ 74717721Speter if (dir_return != R_SKIP_ALL) 74817721Speter { 74917721Speter /* save our current directory and static vars */ 75017721Speter if (save_cwd (&cwd)) 75125839Speter error_exit (); 75217721Speter sdirlist = dirlist; 75317721Speter srepository = repository; 75417721Speter dirlist = NULL; 75517721Speter 75617721Speter /* cd to the sub-directory */ 75725839Speter if ( CVS_CHDIR (dir) < 0) 75817721Speter error (1, errno, "could not chdir to %s", dir); 75917721Speter 76017721Speter /* honor the global SKIP_DIRS (a.k.a. local) */ 76125839Speter if (frame->flags == R_SKIP_DIRS) 76217721Speter dir_return = R_SKIP_DIRS; 76317721Speter 76417721Speter /* remember if the `.' will be stripped for subsequent dirs */ 76517721Speter if (strcmp (update_dir, ".") == 0) 76617721Speter { 76717721Speter update_dir[0] = '\0'; 76817721Speter stripped_dot = 1; 76917721Speter } 77017721Speter 77117721Speter /* make the recursive call */ 77225839Speter xframe = *frame; 77325839Speter xframe.flags = dir_return; 77425839Speter err += do_recursion (&xframe); 77517721Speter 77617721Speter /* put the `.' back if necessary */ 77717721Speter if (stripped_dot) 77817721Speter (void) strcpy (update_dir, "."); 77917721Speter 78017721Speter /* call-back dir leave proc (if any) */ 78125839Speter if (frame->dirleaveproc != NULL) 78225839Speter err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, 78325839Speter frent->entries); 78417721Speter 78517721Speter /* get back to where we started and restore state vars */ 78617721Speter if (restore_cwd (&cwd, NULL)) 78725839Speter error_exit (); 78817721Speter free_cwd (&cwd); 78917721Speter dirlist = sdirlist; 79017721Speter repository = srepository; 79117721Speter } 79217721Speter 79330337Speter#if 0 79425839Speter /* Put back update_dir. I think this is the same as just setting 79525839Speter update_dir back to saved_update_dir, but there are a few cases I'm 79625839Speter not sure about (in particular, if DIR is "." and update_dir is 79725839Speter not ""), so for conservatism I'm leaving this here. */ 79817721Speter cp = last_component (update_dir); 79917721Speter if (cp > update_dir) 80017721Speter cp[-1] = '\0'; 80117721Speter else 80217721Speter update_dir[0] = '\0'; 80325839Speter free (saved_update_dir); 80430337Speter#else 80530337Speter /* The above code is cactus!!! - it doesn't handle descending 80630337Speter multiple directories at once! ie: it recurses down several 80730337Speter dirs and then back up one. This breaks 'diff', 'update', 80830337Speter 'commit', etc. */ 80930337Speter free (update_dir); 81030337Speter update_dir = saved_update_dir; 81130337Speter#endif 81217721Speter 81317721Speter return (err); 81417721Speter} 81517721Speter 81617721Speter/* 81717721Speter * Add a node to a list allocating the list if necessary. 81817721Speter */ 81917721Speterstatic void 82017721Speteraddlist (listp, key) 82117721Speter List **listp; 82217721Speter char *key; 82317721Speter{ 82417721Speter Node *p; 82517721Speter 82617721Speter if (*listp == NULL) 82717721Speter *listp = getlist (); 82817721Speter p = getnode (); 82917721Speter p->type = FILES; 83017721Speter p->key = xstrdup (key); 83117721Speter if (addnode (*listp, p) != 0) 83217721Speter freenode (p); 83317721Speter} 83417721Speter 83517721Speterstatic void 83617721Speteraddfile (listp, dir, file) 83717721Speter List **listp; 83817721Speter char *dir; 83917721Speter char *file; 84017721Speter{ 84117721Speter Node *n; 84217721Speter 84317721Speter /* add this dir. */ 84417721Speter addlist (listp, dir); 84517721Speter 84617721Speter n = findnode (*listp, dir); 84717721Speter if (n == NULL) 84817721Speter { 84917721Speter error (1, 0, "can't find recently added dir node `%s' in start_recursion.", 85017721Speter dir); 85117721Speter } 85217721Speter 85317721Speter n->type = DIRS; 85417721Speter addlist ((List **) &n->data, file); 85517721Speter return; 85617721Speter} 85717721Speter 85817721Speterstatic int 85917721Speterunroll_files_proc (p, closure) 86017721Speter Node *p; 86117721Speter void *closure; 86217721Speter{ 86317721Speter Node *n; 86417721Speter struct recursion_frame *frame = (struct recursion_frame *) closure; 86517721Speter int err = 0; 86617721Speter List *save_dirlist; 86717721Speter char *save_update_dir = NULL; 86817721Speter struct saved_cwd cwd; 86917721Speter 87017721Speter /* if this dir was also an explicitly named argument, then skip 87117721Speter it. We'll catch it later when we do dirs. */ 87217721Speter n = findnode (dirlist, p->key); 87317721Speter if (n != NULL) 87417721Speter return (0); 87517721Speter 87617721Speter /* otherwise, call dorecusion for this list of files. */ 87717721Speter filelist = (List *) p->data; 87825839Speter p->data = NULL; 87917721Speter save_dirlist = dirlist; 88017721Speter dirlist = NULL; 88117721Speter 88217721Speter if (strcmp(p->key, ".") != 0) 88317721Speter { 88417721Speter if (save_cwd (&cwd)) 88525839Speter error_exit (); 88625839Speter if ( CVS_CHDIR (p->key) < 0) 88717721Speter error (1, errno, "could not chdir to %s", p->key); 88817721Speter 88925839Speter save_update_dir = update_dir; 89025839Speter update_dir = xmalloc (strlen (save_update_dir) 89125839Speter + strlen (p->key) 89225839Speter + 5); 89325839Speter strcpy (update_dir, save_update_dir); 89417721Speter 89517721Speter if (*update_dir != '\0') 89617721Speter (void) strcat (update_dir, "/"); 89717721Speter 89817721Speter (void) strcat (update_dir, p->key); 89917721Speter } 90017721Speter 90125839Speter err += do_recursion (frame); 90217721Speter 90317721Speter if (save_update_dir != NULL) 90417721Speter { 90525839Speter free (update_dir); 90625839Speter update_dir = save_update_dir; 90717721Speter 90817721Speter if (restore_cwd (&cwd, NULL)) 90925839Speter error_exit (); 91017721Speter free_cwd (&cwd); 91117721Speter } 91217721Speter 91317721Speter dirlist = save_dirlist; 91417721Speter filelist = NULL; 91517721Speter return(err); 91617721Speter} 917