recurse.c revision 44856
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 1644856Speter#ifdef CLIENT_SUPPORT 1744856Speterstatic int do_argument_proc PROTO((Node * p, void *closure)); 1844856Speter#endif 1917721Speterstatic int do_dir_proc PROTO((Node * p, void *closure)); 2017721Speterstatic int do_file_proc PROTO((Node * p, void *closure)); 2117721Speterstatic void addlist PROTO((List ** listp, char *key)); 2217721Speterstatic int unroll_files_proc PROTO((Node *p, void *closure)); 2317721Speterstatic void addfile PROTO((List **listp, char *dir, char *file)); 2417721Speter 2525839Speterstatic char *update_dir; 2617721Speterstatic char *repository = NULL; 2717721Speterstatic List *filelist = NULL; /* holds list of files on which to operate */ 2817721Speterstatic List *dirlist = NULL; /* holds list of directories on which to operate */ 2917721Speter 3017721Speterstruct recursion_frame { 3125839Speter FILEPROC fileproc; 3225839Speter FILESDONEPROC filesdoneproc; 3325839Speter DIRENTPROC direntproc; 3425839Speter DIRLEAVEPROC dirleaveproc; 3525839Speter void *callerdat; 3625839Speter Dtype flags; 3725839Speter int which; 3825839Speter int aflag; 3925839Speter int readlock; 4025839Speter int dosrcs; 4117721Speter}; 4217721Speter 4325839Speterstatic int do_recursion PROTO ((struct recursion_frame *frame)); 4425839Speter 4525839Speter/* I am half tempted to shove a struct file_info * into the struct 4625839Speter recursion_frame (but then we would need to modify or create a 4725839Speter recursion_frame for each file), or shove a struct recursion_frame * 4825839Speter into the struct file_info (more tempting, although it isn't completely 4925839Speter clear that the struct file_info should contain info about recursion 5025839Speter processor internals). So instead use this struct. */ 5125839Speter 5225839Speterstruct frame_and_file { 5325839Speter struct recursion_frame *frame; 5425839Speter struct file_info *finfo; 5525839Speter}; 5625839Speter 5725839Speter/* Similarly, we need to pass the entries list to do_dir_proc. */ 5825839Speter 5925839Speterstruct frame_and_entries { 6025839Speter struct recursion_frame *frame; 6125839Speter List *entries; 6225839Speter}; 6325839Speter 6444856Speter#ifdef CLIENT_SUPPORT 6544856Speter/* This is a callback to send "Argument" commands to the server in the 6644856Speter case we've done a "cvs update" or "cvs commit" in a top-level 6744856Speter directory where there is no CVSADM directory. */ 6844856Speter 6944856Speterstatic int 7044856Speterdo_argument_proc (p, closure) 7144856Speter Node *p; 7244856Speter void *closure; 7344856Speter{ 7444856Speter char *dir = p->key; 7544856Speter send_to_server ("Argument ", 0); 7644856Speter send_to_server (dir, 0); 7744856Speter send_to_server ("\012", 1); 7844856Speter return 0; 7944856Speter} 8044856Speter#endif 8144856Speter 8225839Speter/* Start a recursive command. 8325839Speter 8425839Speter Command line arguments (ARGC, ARGV) dictate the directories and 8525839Speter files on which we operate. In the special case of no arguments, we 8625839Speter default to ".". */ 8717721Speterint 8825839Speterstart_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, 8917721Speter argc, argv, local, which, aflag, readlock, 9025839Speter update_preload, dosrcs) 9117721Speter FILEPROC fileproc; 9217721Speter FILESDONEPROC filesdoneproc; 9317721Speter DIRENTPROC direntproc; 9417721Speter DIRLEAVEPROC dirleaveproc; 9525839Speter void *callerdat; 9625839Speter 9717721Speter int argc; 9817721Speter char **argv; 9917721Speter int local; 10025839Speter 10125839Speter /* This specifies the kind of recursion. There are several cases: 10225839Speter 10325839Speter 1. W_LOCAL is not set but W_REPOS or W_ATTIC is. The current 10425839Speter directory when we are called must be the repository and 10525839Speter recursion proceeds according to what exists in the repository. 10625839Speter 10725839Speter 2a. W_LOCAL is set but W_REPOS and W_ATTIC are not. The 10825839Speter current directory when we are called must be the working 10925839Speter directory. Recursion proceeds according to what exists in the 11025839Speter working directory, never (I think) consulting any part of the 11125839Speter repository which does not correspond to the working directory 11225839Speter ("correspond" == Name_Repository). 11325839Speter 11425839Speter 2b. W_LOCAL is set and so is W_REPOS or W_ATTIC. This is the 11525839Speter weird one. The current directory when we are called must be 11625839Speter the working directory. We recurse through working directories, 11725839Speter but we recurse into a directory if it is exists in the working 11825839Speter directory *or* it exists in the repository. If a directory 11925839Speter does not exist in the working directory, the direntproc must 12025839Speter either tell us to skip it (R_SKIP_ALL), or must create it (I 12125839Speter think those are the only two cases). */ 12217721Speter int which; 12325839Speter 12417721Speter int aflag; 12517721Speter int readlock; 12617721Speter char *update_preload; 12717721Speter int dosrcs; 12817721Speter{ 12917721Speter int i, err = 0; 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; 14125839Speter frame.readlock = readlock; 14225839Speter frame.dosrcs = dosrcs; 14325839Speter 14417721Speter expand_wild (argc, argv, &argc, &argv); 14517721Speter 14617721Speter if (update_preload == NULL) 14725839Speter update_dir = xstrdup (""); 14817721Speter else 14925839Speter update_dir = xstrdup (update_preload); 15017721Speter 15117721Speter /* clean up from any previous calls to start_recursion */ 15217721Speter if (repository) 15317721Speter { 15417721Speter free (repository); 15517721Speter repository = (char *) NULL; 15617721Speter } 15717721Speter if (filelist) 15817721Speter dellist (&filelist); /* FIXME-krp: no longer correct. */ 15917721Speter if (dirlist) 16017721Speter dellist (&dirlist); 16117721Speter 16225839Speter#ifdef SERVER_SUPPORT 16325839Speter if (server_active) 16425839Speter { 16525839Speter for (i = 0; i < argc; ++i) 16625839Speter server_pathname_check (argv[i]); 16725839Speter } 16825839Speter#endif 16925839Speter 17017721Speter if (argc == 0) 17117721Speter { 17217721Speter 17317721Speter /* 17417721Speter * There were no arguments, so we'll probably just recurse. The 17517721Speter * exception to the rule is when we are called from a directory 17617721Speter * without any CVS administration files. That has always meant to 17717721Speter * process each of the sub-directories, so we pretend like we were 17817721Speter * called with the list of sub-dirs of the current dir as args 17917721Speter */ 18017721Speter if ((which & W_LOCAL) && !isdir (CVSADM)) 18132788Speter { 18225839Speter dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL); 18332788Speter /* If there are no sub-directories, there is a certain logic in 18432788Speter favor of doing nothing, but in fact probably the user is just 18532788Speter confused about what directory they are in, or whether they 18632788Speter cvs add'd a new directory. In the case of at least one 18732788Speter sub-directory, at least when we recurse into them we 18832788Speter notice (hopefully) whether they are under CVS control. */ 18932788Speter if (list_isempty (dirlist)) 19032788Speter { 19132788Speter if (update_dir[0] == '\0') 19232788Speter error (0, 0, "in directory .:"); 19332788Speter else 19432788Speter error (0, 0, "in directory %s:", update_dir); 19532788Speter error (1, 0, 19632788Speter "there is no version here; run '%s checkout' first", 19732788Speter program_name); 19832788Speter } 19944856Speter#ifdef CLIENT_SUPPORT 20044856Speter else if (client_active && server_started) 20144856Speter { 20244856Speter /* In the the case "cvs update foo bar baz", a call to 20344856Speter send_file_names in update.c will have sent the 20444856Speter appropriate "Argument" commands to the server. In 20544856Speter this case, that won't have happened, so we need to 20644856Speter do it here. While this example uses "update", this 20744856Speter generalizes to other commands. */ 20844856Speter 20944856Speter err += walklist (dirlist, do_argument_proc, NULL); 21044856Speter } 21144856Speter#endif 21232788Speter } 21317721Speter else 21417721Speter addlist (&dirlist, "."); 21517721Speter 21625839Speter err += do_recursion (&frame); 21725839Speter goto out; 21817721Speter } 21917721Speter 22017721Speter 22117721Speter /* 22217721Speter * There were arguments, so we have to handle them by hand. To do 22317721Speter * that, we set up the filelist and dirlist with the arguments and 22417721Speter * call do_recursion. do_recursion recognizes the fact that the 22517721Speter * lists are non-null when it starts and doesn't update them. 22617721Speter * 22717721Speter * explicitly named directories are stored in dirlist. 22817721Speter * explicitly named files are stored in filelist. 22917721Speter * other possibility is named entities whicha are not currently in 23017721Speter * the working directory. 23117721Speter */ 23217721Speter 23317721Speter for (i = 0; i < argc; i++) 23417721Speter { 23517721Speter /* if this argument is a directory, then add it to the list of 23617721Speter directories. */ 23717721Speter 23817721Speter if (!wrap_name_has (argv[i], WRAP_TOCVS) && isdir (argv[i])) 23917721Speter addlist (&dirlist, argv[i]); 24017721Speter else 24117721Speter { 24217721Speter /* otherwise, split argument into directory and component names. */ 24317721Speter char *dir; 24417721Speter char *comp; 24517721Speter char *file_to_try; 24617721Speter 24717721Speter /* Now break out argv[i] into directory part (DIR) and file part (COMP). 24817721Speter DIR and COMP will each point to a newly malloc'd string. */ 24917721Speter dir = xstrdup (argv[i]); 25017721Speter comp = last_component (dir); 25117721Speter if (comp == dir) 25217721Speter { 25317721Speter /* no dir component. What we have is an implied "./" */ 25417721Speter dir = xstrdup("."); 25517721Speter } 25617721Speter else 25717721Speter { 25817721Speter char *p = comp; 25917721Speter 26017721Speter p[-1] = '\0'; 26117721Speter comp = xstrdup (p); 26217721Speter } 26317721Speter 26417721Speter /* if this argument exists as a file in the current 26517721Speter working directory tree, then add it to the files list. */ 26617721Speter 26725839Speter if (!(which & W_LOCAL)) 26817721Speter { 26917721Speter /* If doing rtag, we've done a chdir to the repository. */ 27025839Speter file_to_try = xmalloc (strlen (argv[i]) + sizeof (RCSEXT) + 5); 27125839Speter sprintf (file_to_try, "%s%s", argv[i], RCSEXT); 27217721Speter } 27317721Speter else 27425839Speter file_to_try = xstrdup (argv[i]); 27517721Speter 27625839Speter if (isfile (file_to_try)) 27717721Speter addfile (&files_by_dir, dir, comp); 27817721Speter else if (isdir (dir)) 27917721Speter { 28025839Speter if ((which & W_LOCAL) && isdir (CVSADM) 28125839Speter#ifdef CLIENT_SUPPORT 28225839Speter && !client_active 28325839Speter#endif 28425839Speter ) 28517721Speter { 28617721Speter /* otherwise, look for it in the repository. */ 28725839Speter char *tmp_update_dir; 28817721Speter char *repos; 28925839Speter char *reposfile; 29017721Speter 29125839Speter tmp_update_dir = xmalloc (strlen (update_dir) 29225839Speter + strlen (dir) 29325839Speter + 5); 29425839Speter strcpy (tmp_update_dir, update_dir); 29517721Speter 29625839Speter if (*tmp_update_dir != '\0') 29725839Speter (void) strcat (tmp_update_dir, "/"); 29825839Speter 29925839Speter (void) strcat (tmp_update_dir, dir); 30025839Speter 30117721Speter /* look for it in the repository. */ 30225839Speter repos = Name_Repository (dir, tmp_update_dir); 30325839Speter reposfile = xmalloc (strlen (repos) 30425839Speter + strlen (comp) 30525839Speter + 5); 30625839Speter (void) sprintf (reposfile, "%s/%s", repos, comp); 30717721Speter free (repos); 30817721Speter 30925839Speter if (!wrap_name_has (comp, WRAP_TOCVS) && isdir (reposfile)) 31017721Speter addlist (&dirlist, argv[i]); 31117721Speter else 31217721Speter addfile (&files_by_dir, dir, comp); 31317721Speter 31425839Speter free (tmp_update_dir); 31525839Speter free (reposfile); 31617721Speter } 31717721Speter else 31817721Speter addfile (&files_by_dir, dir, comp); 31917721Speter } 32017721Speter else 32117721Speter error (1, 0, "no such directory `%s'", dir); 32217721Speter 32325839Speter free (file_to_try); 32417721Speter free (dir); 32517721Speter free (comp); 32617721Speter } 32717721Speter } 32817721Speter 32917721Speter /* At this point we have looped over all named arguments and built 33017721Speter a coupla lists. Now we unroll the lists, setting up and 33117721Speter calling do_recursion. */ 33217721Speter 33317721Speter err += walklist (files_by_dir, unroll_files_proc, (void *) &frame); 33425839Speter dellist(&files_by_dir); 33517721Speter 33617721Speter /* then do_recursion on the dirlist. */ 33717721Speter if (dirlist != NULL) 33825839Speter err += do_recursion (&frame); 33917721Speter 34017721Speter /* Free the data which expand_wild allocated. */ 34126065Speter free_names (&argc, argv); 34217721Speter 34325839Speter out: 34425839Speter free (update_dir); 34525839Speter update_dir = NULL; 34617721Speter return (err); 34717721Speter} 34817721Speter 34917721Speter/* 35017721Speter * Implement the recursive policies on the local directory. This may be 35117721Speter * called directly, or may be called by start_recursion 35217721Speter */ 35325839Speterstatic int 35425839Speterdo_recursion (frame) 35525839Speter struct recursion_frame *frame; 35617721Speter{ 35717721Speter int err = 0; 35817721Speter int dodoneproc = 1; 35917721Speter char *srepository; 36017721Speter List *entries = NULL; 36125839Speter int should_readlock; 36217721Speter 36317721Speter /* do nothing if told */ 36425839Speter if (frame->flags == R_SKIP_ALL) 36517721Speter return (0); 36617721Speter 36725839Speter should_readlock = noexec ? 0 : frame->readlock; 36817721Speter 36917721Speter /* The fact that locks are not active here is what makes us fail to have 37017721Speter the 37117721Speter 37217721Speter If someone commits some changes in one cvs command, 37317721Speter then an update by someone else will either get all the 37417721Speter changes, or none of them. 37517721Speter 37617721Speter property (see node Concurrency in cvs.texinfo). 37717721Speter 37817721Speter The most straightforward fix would just to readlock the whole 37917721Speter tree before starting an update, but that means that if a commit 38017721Speter gets blocked on a big update, it might need to wait a *long* 38117721Speter time. 38217721Speter 38317721Speter A more adequate fix would be a two-pass design for update, 38417721Speter checkout, etc. The first pass would go through the repository, 38517721Speter with the whole tree readlocked, noting what versions of each 38617721Speter file we want to get. The second pass would release all locks 38717721Speter (except perhaps short-term locks on one file at a 38817721Speter time--although I think RCS already deals with this) and 38917721Speter actually get the files, specifying the particular versions it wants. 39017721Speter 39117721Speter This could be sped up by separating out the data needed for the 39217721Speter first pass into a separate file(s)--for example a file 39317721Speter attribute for each file whose value contains the head revision 39417721Speter for each branch. The structure should be designed so that 39517721Speter commit can relatively quickly update the information for a 39617721Speter single file or a handful of files (file attributes, as 39717721Speter implemented in Jan 96, are probably acceptable; improvements 39817721Speter would be possible such as branch attributes which are in 39917721Speter separate files for each branch). */ 40017721Speter 40117721Speter#if defined(SERVER_SUPPORT) && defined(SERVER_FLOWCONTROL) 40217721Speter /* 40317721Speter * Now would be a good time to check to see if we need to stop 40417721Speter * generating data, to give the buffers a chance to drain to the 40517721Speter * remote client. We should not have locks active at this point. 40617721Speter */ 40717721Speter if (server_active 40817721Speter /* If there are writelocks around, we cannot pause here. */ 40925839Speter && (should_readlock || noexec)) 41017721Speter server_pause_check(); 41117721Speter#endif 41217721Speter 41317721Speter /* 41417721Speter * Fill in repository with the current repository 41517721Speter */ 41625839Speter if (frame->which & W_LOCAL) 41717721Speter { 41817721Speter if (isdir (CVSADM)) 41917721Speter repository = Name_Repository ((char *) NULL, update_dir); 42017721Speter else 42117721Speter repository = NULL; 42217721Speter } 42317721Speter else 42417721Speter { 42525839Speter repository = xgetwd (); 42625839Speter if (repository == NULL) 42725839Speter error (1, errno, "could not get working directory"); 42817721Speter } 42917721Speter srepository = repository; /* remember what to free */ 43017721Speter 43117721Speter fileattr_startdir (repository); 43217721Speter 43317721Speter /* 43417721Speter * The filesdoneproc needs to be called for each directory where files 43517721Speter * processed, or each directory that is processed by a call where no 43617721Speter * directories were passed in. In fact, the only time we don't want to 43717721Speter * call back the filesdoneproc is when we are processing directories that 43817721Speter * were passed in on the command line (or in the special case of `.' when 43917721Speter * we were called with no args 44017721Speter */ 44117721Speter if (dirlist != NULL && filelist == NULL) 44217721Speter dodoneproc = 0; 44317721Speter 44417721Speter /* 44517721Speter * If filelist or dirlist is already set, we don't look again. Otherwise, 44617721Speter * find the files and directories 44717721Speter */ 44817721Speter if (filelist == NULL && dirlist == NULL) 44917721Speter { 45017721Speter /* both lists were NULL, so start from scratch */ 45125839Speter if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) 45217721Speter { 45325839Speter int lwhich = frame->which; 45417721Speter 45517721Speter /* be sure to look in the attic if we have sticky tags/date */ 45617721Speter if ((lwhich & W_ATTIC) == 0) 45717721Speter if (isreadable (CVSADM_TAG)) 45817721Speter lwhich |= W_ATTIC; 45917721Speter 46025839Speter /* In the !(which & W_LOCAL) case, we filled in repository 46125839Speter earlier in the function. In the (which & W_LOCAL) case, 46225839Speter the Find_Names function is going to look through the 46325839Speter Entries file. If we do not have a repository, that 46425839Speter does not make sense, so we insist upon having a 46525839Speter repository at this point. Name_Repository will give a 46625839Speter reasonable error message. */ 46725839Speter if (repository == NULL) 46825839Speter repository = Name_Repository ((char *) NULL, update_dir); 46925839Speter 47017721Speter /* find the files and fill in entries if appropriate */ 47125839Speter filelist = Find_Names (repository, lwhich, frame->aflag, &entries); 47217721Speter } 47317721Speter 47417721Speter /* find sub-directories if we will recurse */ 47525839Speter if (frame->flags != R_SKIP_DIRS) 47625839Speter dirlist = Find_Directories (repository, frame->which, entries); 47717721Speter } 47817721Speter else 47917721Speter { 48017721Speter /* something was passed on the command line */ 48125839Speter if (filelist != NULL && frame->fileproc != NULL) 48217721Speter { 48317721Speter /* we will process files, so pre-parse entries */ 48425839Speter if (frame->which & W_LOCAL) 48534467Speter entries = Entries_Open (frame->aflag, NULL); 48617721Speter } 48717721Speter } 48817721Speter 48917721Speter /* process the files (if any) */ 49025839Speter if (filelist != NULL && frame->fileproc) 49117721Speter { 49217721Speter struct file_info finfo_struct; 49325839Speter struct frame_and_file frfile; 49417721Speter 49517721Speter /* read lock it if necessary */ 49625839Speter if (should_readlock && repository && Reader_Lock (repository) != 0) 49717721Speter error (1, 0, "read lock failed - giving up"); 49817721Speter 49917721Speter#ifdef CLIENT_SUPPORT 50017721Speter /* For the server, we handle notifications in a completely different 50117721Speter place (server_notify). For local, we can't do them here--we don't 50217721Speter have writelocks in place, and there is no way to get writelocks 50317721Speter here. */ 50417721Speter if (client_active) 50517721Speter notify_check (repository, update_dir); 50617721Speter#endif /* CLIENT_SUPPORT */ 50717721Speter 50817721Speter finfo_struct.repository = repository; 50917721Speter finfo_struct.update_dir = update_dir; 51017721Speter finfo_struct.entries = entries; 51117721Speter /* do_file_proc will fill in finfo_struct.file. */ 51217721Speter 51325839Speter frfile.finfo = &finfo_struct; 51425839Speter frfile.frame = frame; 51525839Speter 51617721Speter /* process the files */ 51725839Speter err += walklist (filelist, do_file_proc, &frfile); 51817721Speter 51917721Speter /* unlock it */ 52025839Speter if (should_readlock) 52117721Speter Lock_Cleanup (); 52217721Speter 52317721Speter /* clean up */ 52417721Speter dellist (&filelist); 52517721Speter } 52617721Speter 52717721Speter /* call-back files done proc (if any) */ 52825839Speter if (dodoneproc && frame->filesdoneproc != NULL) 52925839Speter err = frame->filesdoneproc (frame->callerdat, err, repository, 53025839Speter update_dir[0] ? update_dir : ".", 53125839Speter entries); 53217721Speter 53317721Speter fileattr_write (); 53417721Speter fileattr_free (); 53517721Speter 53617721Speter /* process the directories (if necessary) */ 53717721Speter if (dirlist != NULL) 53825839Speter { 53925839Speter struct frame_and_entries frent; 54025839Speter 54125839Speter frent.frame = frame; 54225839Speter frent.entries = entries; 54325839Speter err += walklist (dirlist, do_dir_proc, (void *) &frent); 54425839Speter } 54525839Speter#if 0 54625839Speter else if (frame->dirleaveproc != NULL) 54725839Speter err += frame->dirleaveproc (frame->callerdat, ".", err, "."); 54817721Speter#endif 54917721Speter dellist (&dirlist); 55017721Speter 55125839Speter if (entries) 55225839Speter { 55325839Speter Entries_Close (entries); 55425839Speter entries = NULL; 55525839Speter } 55625839Speter 55717721Speter /* free the saved copy of the pointer if necessary */ 55817721Speter if (srepository) 55917721Speter { 56017721Speter free (srepository); 56117721Speter repository = (char *) NULL; 56217721Speter } 56317721Speter 56417721Speter return (err); 56517721Speter} 56617721Speter 56717721Speter/* 56817721Speter * Process each of the files in the list with the callback proc 56917721Speter */ 57017721Speterstatic int 57117721Speterdo_file_proc (p, closure) 57217721Speter Node *p; 57317721Speter void *closure; 57417721Speter{ 57525839Speter struct frame_and_file *frfile = (struct frame_and_file *)closure; 57625839Speter struct file_info *finfo = frfile->finfo; 57717721Speter int ret; 57817721Speter 57917721Speter finfo->file = p->key; 58017721Speter finfo->fullname = xmalloc (strlen (finfo->file) 58117721Speter + strlen (finfo->update_dir) 58217721Speter + 2); 58317721Speter finfo->fullname[0] = '\0'; 58417721Speter if (finfo->update_dir[0] != '\0') 58517721Speter { 58617721Speter strcat (finfo->fullname, finfo->update_dir); 58717721Speter strcat (finfo->fullname, "/"); 58817721Speter } 58917721Speter strcat (finfo->fullname, finfo->file); 59017721Speter 59125839Speter if (frfile->frame->dosrcs && repository) 59217721Speter finfo->rcs = RCS_parse (finfo->file, repository); 59317721Speter else 59417721Speter finfo->rcs = (RCSNode *) NULL; 59525839Speter ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo); 59617721Speter 59717721Speter freercsnode(&finfo->rcs); 59817721Speter free (finfo->fullname); 59917721Speter 60025839Speter /* Allow the user to monitor progress with tail -f. Doing this once 60125839Speter per file should be no big deal, but we don't want the performance 60225839Speter hit of flushing on every line like previous versions of CVS. */ 60325839Speter cvs_flushout (); 60425839Speter 60517721Speter return (ret); 60617721Speter} 60717721Speter 60817721Speter/* 60917721Speter * Process each of the directories in the list (recursing as we go) 61017721Speter */ 61117721Speterstatic int 61217721Speterdo_dir_proc (p, closure) 61317721Speter Node *p; 61417721Speter void *closure; 61517721Speter{ 61625839Speter struct frame_and_entries *frent = (struct frame_and_entries *) closure; 61725839Speter struct recursion_frame *frame = frent->frame; 61825839Speter struct recursion_frame xframe; 61917721Speter char *dir = p->key; 62025839Speter char *newrepos; 62117721Speter List *sdirlist; 62217721Speter char *srepository; 62317721Speter Dtype dir_return = R_PROCESS; 62417721Speter int stripped_dot = 0; 62517721Speter int err = 0; 62617721Speter struct saved_cwd cwd; 62725839Speter char *saved_update_dir; 62817721Speter 62925839Speter if (fncmp (dir, CVSADM) == 0) 63025839Speter { 63125839Speter /* This seems to most often happen when users (beginning users, 63225839Speter generally), try "cvs ci *" or something similar. On that 63325839Speter theory, it is possible that we should just silently skip the 63425839Speter CVSADM directories, but on the other hand, using a wildcard 63525839Speter like this isn't necessarily a practice to encourage (it operates 63625839Speter only on files which exist in the working directory, unlike 63725839Speter regular CVS recursion). */ 63825839Speter 63925839Speter /* FIXME-reentrancy: printed_cvs_msg should be in a "command 64025839Speter struct" or some such, so that it gets cleared for each new 64125839Speter command (this is possible using the remote protocol and a 64225839Speter custom-written client). The struct recursion_frame is not 64325839Speter far back enough though, some commands (commit at least) 64425839Speter will call start_recursion several times. An alternate solution 64525839Speter would be to take this whole check and move it to a new function 64625839Speter validate_arguments or some such that all the commands call 64725839Speter and which snips the offending directory from the argc,argv 64825839Speter vector. */ 64925839Speter static int printed_cvs_msg = 0; 65025839Speter if (!printed_cvs_msg) 65125839Speter { 65225839Speter error (0, 0, "warning: directory %s specified in argument", 65325839Speter dir); 65425839Speter error (0, 0, "\ 65525839Speterbut CVS uses %s for its own purposes; skipping %s directory", 65625839Speter CVSADM, dir); 65725839Speter printed_cvs_msg = 1; 65825839Speter } 65925839Speter return 0; 66025839Speter } 66125839Speter 66225839Speter saved_update_dir = update_dir; 66325839Speter update_dir = xmalloc (strlen (saved_update_dir) 66425839Speter + strlen (dir) 66525839Speter + 5); 66625839Speter strcpy (update_dir, saved_update_dir); 66725839Speter 66817721Speter /* set up update_dir - skip dots if not at start */ 66917721Speter if (strcmp (dir, ".") != 0) 67017721Speter { 67117721Speter if (update_dir[0] != '\0') 67217721Speter { 67317721Speter (void) strcat (update_dir, "/"); 67417721Speter (void) strcat (update_dir, dir); 67517721Speter } 67617721Speter else 67717721Speter (void) strcpy (update_dir, dir); 67817721Speter 67917721Speter /* 68017721Speter * Here we need a plausible repository name for the sub-directory. We 68117721Speter * create one by concatenating the new directory name onto the 68217721Speter * previous repository name. The only case where the name should be 68317721Speter * used is in the case where we are creating a new sub-directory for 68417721Speter * update -d and in that case the generated name will be correct. 68517721Speter */ 68617721Speter if (repository == NULL) 68725839Speter newrepos = xstrdup (""); 68817721Speter else 68925839Speter { 69025839Speter newrepos = xmalloc (strlen (repository) + strlen (dir) + 5); 69132788Speter sprintf (newrepos, "%s/%s", repository, dir); 69225839Speter } 69317721Speter } 69417721Speter else 69517721Speter { 69617721Speter if (update_dir[0] == '\0') 69717721Speter (void) strcpy (update_dir, dir); 69817721Speter 69917721Speter if (repository == NULL) 70025839Speter newrepos = xstrdup (""); 70117721Speter else 70225839Speter newrepos = xstrdup (repository); 70317721Speter } 70417721Speter 70532788Speter /* Check to see that the CVSADM directory, if it exists, seems to be 70632788Speter well-formed. It can be missing files if the user hit ^C in the 70732788Speter middle of a previous run. We want to (a) make this a nonfatal 70832788Speter error, and (b) make sure we print which directory has the 70932788Speter problem. 71032788Speter 71132788Speter Do this before the direntproc, so that (1) the direntproc 71232788Speter doesn't have to guess/deduce whether we will skip the directory 71332788Speter (e.g. send_dirent_proc and whether to send the directory), and 71432788Speter (2) so that the warm fuzzy doesn't get printed if we skip the 71532788Speter directory. */ 71632788Speter if (frame->which & W_LOCAL) 71732788Speter { 71832788Speter char *cvsadmdir; 71932788Speter 72032788Speter cvsadmdir = xmalloc (strlen (dir) 72132788Speter + sizeof (CVSADM_REP) 72232788Speter + sizeof (CVSADM_ENT) 72332788Speter + 80); 72432788Speter 72532788Speter strcpy (cvsadmdir, dir); 72632788Speter strcat (cvsadmdir, "/"); 72732788Speter strcat (cvsadmdir, CVSADM); 72832788Speter if (isdir (cvsadmdir)) 72932788Speter { 73032788Speter strcpy (cvsadmdir, dir); 73132788Speter strcat (cvsadmdir, "/"); 73232788Speter strcat (cvsadmdir, CVSADM_REP); 73332788Speter if (!isfile (cvsadmdir)) 73432788Speter { 73532788Speter /* Some commands like update may have printed "? foo" but 73632788Speter if we were planning to recurse, and don't on account of 73732788Speter CVS/Repository, we want to say why. */ 73832788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 73932788Speter CVSADM_REP); 74032788Speter dir_return = R_SKIP_ALL; 74132788Speter } 74232788Speter 74332788Speter /* Likewise for CVS/Entries. */ 74432788Speter if (dir_return != R_SKIP_ALL) 74532788Speter { 74632788Speter strcpy (cvsadmdir, dir); 74732788Speter strcat (cvsadmdir, "/"); 74832788Speter strcat (cvsadmdir, CVSADM_ENT); 74932788Speter if (!isfile (cvsadmdir)) 75032788Speter { 75132788Speter /* Some commands like update may have printed "? foo" but 75232788Speter if we were planning to recurse, and don't on account of 75332788Speter CVS/Repository, we want to say why. */ 75432788Speter error (0, 0, "ignoring %s (%s missing)", update_dir, 75532788Speter CVSADM_ENT); 75632788Speter dir_return = R_SKIP_ALL; 75732788Speter } 75832788Speter } 75932788Speter } 76032788Speter free (cvsadmdir); 76132788Speter } 76232788Speter 76317721Speter /* call-back dir entry proc (if any) */ 76432788Speter if (dir_return == R_SKIP_ALL) 76532788Speter ; 76632788Speter else if (frame->direntproc != NULL) 76725839Speter dir_return = frame->direntproc (frame->callerdat, dir, newrepos, 76825839Speter update_dir, frent->entries); 76932788Speter else 77032788Speter { 77132788Speter /* Generic behavior. I don't see a reason to make the caller specify 77232788Speter a direntproc just to get this. */ 77332788Speter if ((frame->which & W_LOCAL) && !isdir (dir)) 77432788Speter dir_return = R_SKIP_ALL; 77532788Speter } 77632788Speter 77725839Speter free (newrepos); 77817721Speter 77917721Speter /* only process the dir if the return code was 0 */ 78017721Speter if (dir_return != R_SKIP_ALL) 78117721Speter { 78217721Speter /* save our current directory and static vars */ 78317721Speter if (save_cwd (&cwd)) 78425839Speter error_exit (); 78517721Speter sdirlist = dirlist; 78617721Speter srepository = repository; 78717721Speter dirlist = NULL; 78817721Speter 78917721Speter /* cd to the sub-directory */ 79025839Speter if ( CVS_CHDIR (dir) < 0) 79117721Speter error (1, errno, "could not chdir to %s", dir); 79217721Speter 79317721Speter /* honor the global SKIP_DIRS (a.k.a. local) */ 79425839Speter if (frame->flags == R_SKIP_DIRS) 79517721Speter dir_return = R_SKIP_DIRS; 79617721Speter 79717721Speter /* remember if the `.' will be stripped for subsequent dirs */ 79817721Speter if (strcmp (update_dir, ".") == 0) 79917721Speter { 80017721Speter update_dir[0] = '\0'; 80117721Speter stripped_dot = 1; 80217721Speter } 80317721Speter 80417721Speter /* make the recursive call */ 80525839Speter xframe = *frame; 80625839Speter xframe.flags = dir_return; 80725839Speter err += do_recursion (&xframe); 80817721Speter 80917721Speter /* put the `.' back if necessary */ 81017721Speter if (stripped_dot) 81117721Speter (void) strcpy (update_dir, "."); 81217721Speter 81317721Speter /* call-back dir leave proc (if any) */ 81425839Speter if (frame->dirleaveproc != NULL) 81525839Speter err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, 81625839Speter frent->entries); 81717721Speter 81817721Speter /* get back to where we started and restore state vars */ 81917721Speter if (restore_cwd (&cwd, NULL)) 82025839Speter error_exit (); 82117721Speter free_cwd (&cwd); 82217721Speter dirlist = sdirlist; 82317721Speter repository = srepository; 82417721Speter } 82517721Speter 82630337Speter free (update_dir); 82730337Speter update_dir = saved_update_dir; 82817721Speter 82917721Speter return (err); 83017721Speter} 83117721Speter 83217721Speter/* 83317721Speter * Add a node to a list allocating the list if necessary. 83417721Speter */ 83517721Speterstatic void 83617721Speteraddlist (listp, key) 83717721Speter List **listp; 83817721Speter char *key; 83917721Speter{ 84017721Speter Node *p; 84117721Speter 84217721Speter if (*listp == NULL) 84317721Speter *listp = getlist (); 84417721Speter p = getnode (); 84517721Speter p->type = FILES; 84617721Speter p->key = xstrdup (key); 84717721Speter if (addnode (*listp, p) != 0) 84817721Speter freenode (p); 84917721Speter} 85017721Speter 85117721Speterstatic void 85217721Speteraddfile (listp, dir, file) 85317721Speter List **listp; 85417721Speter char *dir; 85517721Speter char *file; 85617721Speter{ 85717721Speter Node *n; 85817721Speter 85917721Speter /* add this dir. */ 86017721Speter addlist (listp, dir); 86117721Speter 86217721Speter n = findnode (*listp, dir); 86317721Speter if (n == NULL) 86417721Speter { 86517721Speter error (1, 0, "can't find recently added dir node `%s' in start_recursion.", 86617721Speter dir); 86717721Speter } 86817721Speter 86917721Speter n->type = DIRS; 87017721Speter addlist ((List **) &n->data, file); 87117721Speter return; 87217721Speter} 87317721Speter 87417721Speterstatic int 87517721Speterunroll_files_proc (p, closure) 87617721Speter Node *p; 87717721Speter void *closure; 87817721Speter{ 87917721Speter Node *n; 88017721Speter struct recursion_frame *frame = (struct recursion_frame *) closure; 88117721Speter int err = 0; 88217721Speter List *save_dirlist; 88317721Speter char *save_update_dir = NULL; 88417721Speter struct saved_cwd cwd; 88517721Speter 88617721Speter /* if this dir was also an explicitly named argument, then skip 88717721Speter it. We'll catch it later when we do dirs. */ 88817721Speter n = findnode (dirlist, p->key); 88917721Speter if (n != NULL) 89017721Speter return (0); 89117721Speter 89217721Speter /* otherwise, call dorecusion for this list of files. */ 89317721Speter filelist = (List *) p->data; 89425839Speter p->data = NULL; 89517721Speter save_dirlist = dirlist; 89617721Speter dirlist = NULL; 89717721Speter 89817721Speter if (strcmp(p->key, ".") != 0) 89917721Speter { 90017721Speter if (save_cwd (&cwd)) 90125839Speter error_exit (); 90225839Speter if ( CVS_CHDIR (p->key) < 0) 90317721Speter error (1, errno, "could not chdir to %s", p->key); 90417721Speter 90525839Speter save_update_dir = update_dir; 90625839Speter update_dir = xmalloc (strlen (save_update_dir) 90725839Speter + strlen (p->key) 90825839Speter + 5); 90925839Speter strcpy (update_dir, save_update_dir); 91017721Speter 91117721Speter if (*update_dir != '\0') 91217721Speter (void) strcat (update_dir, "/"); 91317721Speter 91417721Speter (void) strcat (update_dir, p->key); 91517721Speter } 91617721Speter 91725839Speter err += do_recursion (frame); 91817721Speter 91917721Speter if (save_update_dir != NULL) 92017721Speter { 92125839Speter free (update_dir); 92225839Speter update_dir = save_update_dir; 92317721Speter 92417721Speter if (restore_cwd (&cwd, NULL)) 92525839Speter error_exit (); 92617721Speter free_cwd (&cwd); 92717721Speter } 92817721Speter 92917721Speter dirlist = save_dirlist; 93017721Speter filelist = NULL; 93117721Speter return(err); 93217721Speter} 933