/* * Finds all the ,v files in the argument directory, and adds them to the * files list. Returns 0 for success and non-zero if the argument directory * cannot be opened, in which case errno is set to indicate the error. * In the error case LIST is left in some reasonable state (unchanged, or * containing the files which were found before the error occurred). */ static int find_rcs (const char *dir, List *list, const char *regex) { Node *p; struct dirent *dp; DIR *dirp; /* set up to read the dir */ if ((dirp = opendir (dir)) == NULL) return (1); /* read the dir, grabbing the ,v files */ errno = 0; while ((dp = readdir (dirp)) != NULL) { if (CVS_FNMATCH (RCSPAT, dp->d_name, CVS_CASEFOLD) == 0) { char *comma, *q; comma = strrchr (dp->d_name, ','); /* strip the ,v */ *comma = '\0'; if(!fncmp(dp->d_name,RCSREPOVERSION)) continue; q = map_fixed_rename(dir,dp->d_name); if(q && *q && (!regex || regex_filename_match(regex, q))) { p = getnode (); p->type = FILES; p->key = xstrdup (q); if(!findnode_fn(list,p->key)) { if(addnode (list, p) != 0) freenode (p); } } } errno = 0; } if (errno != 0) { int save_errno = errno; (void) closedir (dirp); errno = save_errno; return 1; } (void) closedir (dirp); return (0); }
static void selectAddCb(Widget w, FileWindowRec *fw, XtPointer call_data) { register int i; XtPopdown(popups.select); for(i=0; i<popups.fw->n_files; i++) if (popups.fw->files[i]->icon.toggle) { if (!popups.fw->files[i]->selected && (fncmp(popups.select_s, popups.fw->files[i]->name))) { popups.fw->files[i]->selected = True; popups.fw->n_selections++; XtVaSetValues(popups.fw->files[i]->icon.toggle, XtNstate, True, NULL); } } updateMenus(popups.fw); }
static void selectReplaceCb(Widget w, FileWindowRec *fw, XtPointer call_data) { register int i; XtPopdown(popups.select); popups.fw->n_selections = 0; for (i=0; i<popups.fw->n_files; i++) { if (popups.fw->files[i]->icon.toggle) { if (fncmp(popups.select_s, popups.fw->files[i]->name)) { popups.fw->files[i]->selected = True; popups.fw->n_selections++; } else popups.fw->files[i]->selected = False; XtVaSetValues(popups.fw->files[i]->icon.toggle, XtNstate, (XtArgVal) popups.fw->files[i]->selected, NULL); } } updateMenus(popups.fw); }
const T * BinaryLookupFromIndex (const int aIndex[], const T aTable[], int cElms, const char * name, int (*fncmp)(const char *, const char *)) { if (cElms <= 0) return NULL; int ixLower = 0; int ixUpper = cElms-1; for (;;) { if (ixLower > ixUpper) return NULL; // return null for "not found" int ix = (ixLower + ixUpper) / 2; int iMatch = fncmp(aTable[aIndex[ix]].name, name); if (iMatch < 0) ixLower = ix+1; else if (iMatch > 0) ixUpper = ix-1; else return &aTable[aIndex[ix]]; } }
const T * BinaryLookup (const T aTable[], int cElms, const char * key, int (*fncmp)(const char *, const char *)) { if (cElms <= 0) return NULL; int ixLower = 0; int ixUpper = cElms-1; for (;;) { if (ixLower > ixUpper) return NULL; // return null for "not found" int ix = (ixLower + ixUpper) / 2; int iMatch = fncmp(aTable[ix].key, key); if (iMatch < 0) ixLower = ix+1; else if (iMatch > 0) ixUpper = ix-1; else return &aTable[ix]; } }
/* match a pattern with a filename, returning True if the match was correct */ static int fncmp(char *pattern, char *fn) { for (;; fn++, pattern++) { switch (*pattern) { case '?': if (!*fn) return False; break; case '*': pattern++; do if (fncmp(pattern,fn)) return True; while (*fn++); return False; case '[': do { pattern++; if (*pattern == ']') return False; } while (*fn != *pattern); while (*pattern != ']') if (!*pattern++) return False; break; default: if (*fn != *pattern) return False; } if (!*fn) return True; }; }
int cvsrename(int argc, char **argv) { int c; int err = 0; char *repos_file1, *repos_file2; char *root1, *root2; const char *filename1, *filename2, *dir1, *dir2; int rootlen; List *ent,*ent2; Node *node; Entnode *entnode; const char *cwd; if (argc == -1) usage (rename_usage); quiet = 0; optind = 0; while ((c = getopt (argc, argv, "+q")) != -1) { switch (c) { case 'q': quiet = 1; break; case '?': default: usage (rename_usage); break; } } argc -= optind; argv += optind; if(argc!=2) { usage(rename_usage); }; error(0,0,"Warning: rename is still experimental and may not behave as you would expect"); if(current_parsed_root->isremote) { if(!supported_request("Rename")) error(1,0,"Remote server does not support rename"); if(!supported_request("Can-Rename")) error(1,0,"Renames are currently disabled"); } if(!strcmp(argv[0],argv[1])) return 0; rootlen = strlen(current_parsed_root->directory); if(!isfile(argv[0])) error(1,0,"%s does not exist",argv[0]); if(isfile(argv[1]) && fncmp(argv[0],argv[1])) /* We allow case renames (on Unix this is redundant) */ error(1,0,"%s already exists",argv[1]); if(isdir(argv[0])) error(1,0,"Directory renames are not currently supported"); validate_file(argv[0],&root1, &repos_file1, &filename1, &dir1, 1); validate_file(argv[1],&root2, &repos_file2, &filename2, &dir2, 0); if(strcmp(root1,root2) || strcmp(root1,current_parsed_root->original)) error(1,0,"%s and %s are in different repositories",argv[0],argv[1]); xfree(root1); xfree(root2); repos_file1 = (char*)xrealloc(repos_file1, strlen(filename1)+strlen(repos_file1)+rootlen+10); repos_file2 = (char*)xrealloc(repos_file2, strlen(filename2)+strlen(repos_file2)+rootlen+10); memmove(repos_file1+rootlen+1,repos_file1,strlen(repos_file1)+1); memmove(repos_file2+rootlen+1,repos_file2,strlen(repos_file2)+1); strcpy(repos_file1,current_parsed_root->directory); strcpy(repos_file2,current_parsed_root->directory); repos_file1[rootlen]='/'; repos_file2[rootlen]='/'; strcat(repos_file1,"/"); strcat(repos_file2,"/"); strcat(repos_file1,filename1); strcat(repos_file2,filename2); if(fncmp(argv[0],argv[1])) set_mapping(dir2,repos_file2+rootlen+1,""); /* Delete old file */ if(fncmp(dir1,dir2)) set_mapping(dir1,repos_file1+rootlen+1,""); set_mapping(dir2,repos_file1+rootlen+1,repos_file2+rootlen+1); /* Rename to new file */ cwd = xgetwd(); if(CVS_CHDIR(dir1)) error(1,errno,"Couldn't chdir to %s",dir1); ent = Entries_Open(0, NULL); node = findnode_fn(ent, filename1); entnode=(Entnode*)node->data; if(!node) { error(1,0,"%s is not listed in CVS/Entries",filename1); CVS_CHDIR(cwd); xfree(cwd); return 1; } if(!fncmp(dir1,dir2)) Rename_Entry(ent,filename1,filename2); else { if(CVS_CHDIR(cwd)) error(1,errno,"Couldn't chdir to %s",cwd); if(CVS_CHDIR(dir2)) error(1,errno,"Couldn't chdir to %s",dir2); ent2 = Entries_Open(0, NULL); if(entnode->type==ENT_FILE) Register(ent2,(char*)filename2,entnode->version,entnode->timestamp,entnode->options,entnode->tag,entnode->date,entnode->conflict,entnode->merge_from_tag_1,entnode->merge_from_tag_2,entnode->rcs_timestamp,entnode->edit_revision,entnode->edit_tag,entnode->edit_bugid,entnode->md5); else if(entnode->type==ENT_SUBDIR) Subdir_Register(ent2,NULL,filename2); else error(1,0,"Unknown entry type %d in entries file",node->type); Entries_Close(ent2); if(CVS_CHDIR(cwd)) error(1,errno,"Couldn't chdir to %s",cwd); if(CVS_CHDIR(dir1)) error(1,errno,"Couldn't chdir to %s",dir1); if(entnode->type==ENT_SUBDIR) Subdir_Deregister(ent,NULL,filename1); else if(entnode->type==ENT_FILE) Scratch_Entry(ent,filename1); else error(1,0,"Unknown entry type %d in entries file",node->type); } Entries_Close(ent); CVS_RENAME(argv[0],argv[1]); if(isdir(argv[1])) { char *tmp=(char*)xmalloc(strlen(argv[1])+strlen(CVSADM_VIRTREPOS)+10); FILE *fp; sprintf(tmp,"%s/%s",argv[1],CVSADM_VIRTREPOS); fp = fopen(tmp,"w"); if(!fp) error(0,errno,"Couldn't write %s",tmp); fprintf(fp,"%s\n",repos_file2+rootlen+1); fclose(fp); } xfree(repos_file1); xfree(repos_file2); xfree(dir1); xfree(dir2); CVS_CHDIR(cwd); xfree(cwd); return (err); }
/* * Finds all the subdirectories of the argument dir and adds them to * the specified list. Sub-directories without a CVS administration * directory are optionally ignored. If ENTRIES is not NULL, all * files on the list are ignored. Returns 0 for success or 1 on * error, in which case errno is set to indicate the error. */ static int find_dirs (const char *dir, List *list, int checkadm, List *entries, const char *regex) { Node *p; char *tmp = NULL; size_t tmp_size = 0; struct dirent *dp; DIR *dirp; int skip_emptydir = 0; /* First figure out whether we need to skip directories named Emptydir. Except in the CVSNULLREPOS case, Emptydir is just a normal directory name. */ if (isabsolute (dir) && fnncmp (dir, current_parsed_root->directory, strlen (current_parsed_root->directory)) == 0 && ISDIRSEP (dir[strlen (current_parsed_root->directory)]) && fncmp (dir + strlen (current_parsed_root->directory) + 1, CVSROOTADM) == 0) skip_emptydir = 1; /* set up to read the dir */ if ((dirp = opendir (dir)) == NULL) return (1); /* read the dir, grabbing sub-dirs */ errno = 0; while ((dp = readdir (dirp)) != NULL) { if (fncmp (dp->d_name, ".") == 0 || fncmp (dp->d_name, "..") == 0 || fncmp (dp->d_name, CVSATTIC) == 0 || fncmp (dp->d_name, CVSLCK) == 0 || fncmp (dp->d_name, CVSREP) == 0 || fncmp (dp->d_name, CVSDUMMY) == 0) goto do_it_again; /* findnode() is going to be significantly faster than stat() because it involves no system calls. That is why we bother with the entries argument, and why we check this first. */ if (entries != NULL && findnode_fn (entries, dp->d_name) != NULL) goto do_it_again; if (skip_emptydir && fncmp (dp->d_name, CVSNULLREPOS) == 0) goto do_it_again; /* don't bother stating ,v files */ if (CVS_FNMATCH (RCSPAT, dp->d_name, CVS_CASEFOLD) == 0) goto do_it_again; expand_string (&tmp, &tmp_size, strlen (dir) + strlen (dp->d_name) + 10); sprintf (tmp, "%s/%s", dir, dp->d_name); if (!isdir (tmp)) goto do_it_again; /* check for administration directories (if needed) */ if (checkadm) { /* blow off symbolic links to dirs in local dir */ /* Note that we only get here if we already set tmp above. */ if (islink (tmp)) goto do_it_again; /* check for new style */ expand_string (&tmp, &tmp_size, (strlen (dir) + strlen (dp->d_name) + sizeof (CVSADM) + 10)); (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM); if (!isdir (tmp)) goto do_it_again; } /* If there's a regex, test against the name with '/' on the end to signify a directory */ if(regex) { strcpy(tmp,dp->d_name); strcat(tmp,"/"); if(!regex_filename_match(regex,tmp)) goto do_it_again; } /* put it in the list */ p = getnode (); p->type = DIRS; p->key = xstrdup (dp->d_name); if (addnode (list, p) != 0) freenode (p); do_it_again: errno = 0; } if (errno != 0) { int save_errno = errno; (void) closedir (dirp); errno = save_errno; return 1; } (void) closedir (dirp); if (tmp != NULL) xfree (tmp); return (0); }
/** * mix a list of titles into a new order */ struct entry_t *shuffleTitles( struct entry_t *base ) { struct entry_t *list=NULL; struct entry_t *end=NULL; struct entry_t *runner=NULL; struct entry_t *guard=NULL; char *lastname=NULL; int i, num=0, artguard=-1; struct timeval tv; // improve 'randomization' gettimeofday(&tv,NULL); srand(getpid()*tv.tv_sec); base = rewindTitles( base ); num = countTitles(base)+1; // Stepping through every item while( base != NULL ) { int j, pos; runner=base; pos=RANDOM(num--); for(j=1; j<=pos; j++){ runner=runner->next; if( runner == NULL ) { runner=base; } } if( artguard && lastname ) { guard=runner; while( 75 < fncmp( runner->artist, lastname ) ) { runner=runner->next; if( runner == NULL ) { runner=base; } if( guard == runner ) { artguard=0; break; } } } lastname=runner->artist; // Remove entry from base if(runner==base) base=runner->next; if(runner->prev != NULL){ runner->prev->next=runner->next; } if(runner->next != NULL){ runner->next->prev=runner->prev; } // add entry to list runner->next=NULL; runner->prev=end; if( NULL != end ) { end->next=runner; } end=runner; activity("Shuffling "); } return rewindTitles( end ); }
/* * Process each of the directories in the list (recursing as we go) */ static int do_dir_proc (Node *p, void *closure) { struct frame_and_entries *frent = (struct frame_and_entries *) closure; struct recursion_frame *frame = frent->frame; struct recursion_frame xframe; char *dir = p->key; char *newrepos, *virtrepos; List *sdirlist; char *srepository; char *smapped_repository; Dtype dir_return = R_PROCESS; Dtype hint; int stripped_dot = 0; int err = 0; struct saved_cwd cwd; char *saved_update_dir; int process_this_directory = 1; int directory_opened = 0; char *old_update_repos = NULL; int directory_not_valid = 0; if (fncmp (dir, CVSADM) == 0 || fncmp (dir, CVSDUMMY) == 0) { /* This seems to most often happen when users (beginning users, generally), try "cvs ci *" or something similar. On that theory, it is possible that we should just silently skip the CVSADM directories, but on the other hand, using a wildcard like this isn't necessarily a practice to encourage (it operates only on files which exist in the working directory, unlike regular CVS recursion). */ /* FIXME-reentrancy: printed_cvs_msg should be in a "command struct" or some such, so that it gets cleared for each new command (this is possible using the remote protocol and a custom-written client). The struct recursion_frame is not far back enough though, some commands (commit at least) will call start_recursion several times. An alternate solution would be to take this whole check and move it to a new function validate_arguments or some such that all the commands call and which snips the offending directory from the argc,argv vector. */ static int printed_cvs_msg = 0; if (!printed_cvs_msg) { error (0, 0, "warning: directory %s specified in argument", dir); error (0, 0, "but CVS uses %s for its own purposes; skipping %s directory", dir, dir); printed_cvs_msg = 1; } return 0; } saved_update_dir = update_dir; update_dir = (char*)xmalloc (strlen (saved_update_dir) + strlen (dir) + 5); strcpy (update_dir, saved_update_dir); /* set up update_dir - skip dots if not at start */ if (strcmp (dir, ".") != 0) { if (update_dir[0] != '\0') { (void) strcat (update_dir, "/"); (void) strcat (update_dir, dir); } else (void) strcpy (update_dir, dir); /* * Here we need a plausible repository name for the sub-directory. We * create one by concatenating the new directory name onto the * previous repository name. The only case where the name should be * used is in the case where we are creating a new sub-directory for * update -d and in that case the generated name will be correct. */ if (repository == NULL) { newrepos = xstrdup (""); virtrepos = xstrdup (""); } else { if(frame->which&W_LOCAL) { char *d=(char*)xmalloc(strlen(dir)+strlen(CVSADM_REP)+32); sprintf(d,"%s/%s",dir,CVSADM_REP); if(isfile(d)) virtrepos = Name_Repository(dir,update_dir); else { virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5); sprintf (virtrepos, "%s/%s", repository, dir); } xfree(d); } else { virtrepos = (char*)xmalloc (strlen (repository) + strlen (dir) + 5); sprintf (virtrepos, "%s/%s", repository, dir); } if(!current_parsed_root->isremote) { newrepos = map_repository(virtrepos); if(!newrepos) error(1,0,"Internal error - couldn't map %s to anything",virtrepos); } else newrepos = xstrdup(virtrepos); } } else { if (update_dir[0] == '\0') (void) strcpy (update_dir, dir); if (repository == NULL) { newrepos = xstrdup (""); virtrepos = xstrdup (""); } else { newrepos = xstrdup (mapped_repository); virtrepos = xstrdup (repository); } } /* Check to see that the CVSADM directory, if it exists, seems to be well-formed. It can be missing files if the user hit ^C in the middle of a previous run. We want to (a) make this a nonfatal error, and (b) make sure we print which directory has the problem. Do this before the direntproc, so that (1) the direntproc doesn't have to guess/deduce whether we will skip the directory (e.g. send_dirent_proc and whether to send the directory), and (2) so that the warm fuzzy doesn't get printed if we skip the directory. */ if (frame->which & W_LOCAL) { char *cvsadmdir; cvsadmdir = (char*)xmalloc (strlen (dir) + sizeof (CVSADM_REP) + sizeof (CVSADM_ENT) + 80); strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM); if (isdir (cvsadmdir)) { strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM_REP); if (!isfile (cvsadmdir)) { /* Some commands like update may have printed "? foo" but if we were planning to recurse, and don't on account of CVS/Repository, we want to say why. */ error (0, 0, "ignoring %s (%s missing)", update_dir, CVSADM_REP); dir_return = R_SKIP_ALL; } /* Likewise for CVS/Entries. */ if (dir_return != R_SKIP_ALL) { strcpy (cvsadmdir, dir); strcat (cvsadmdir, "/"); strcat (cvsadmdir, CVSADM_ENT); if (!isfile (cvsadmdir)) { /* Some commands like update may have printed "? foo" but if we were planning to recurse, and don't on account of CVS/Repository, we want to say why. */ error (0, 0, "ignoring %s (%s missing)", update_dir, CVSADM_ENT); dir_return = R_SKIP_ALL; } } } xfree (cvsadmdir); } /* Only process this directory if the root matches. This nearly duplicates code in do_recursion. */ /* If -d was specified, it should override CVS/Root. In the single-repository case, it is long-standing CVS behavior and makes sense - the user might want another access method, another server (which mounts the same repository), &c. In the multiple-repository case, -d overrides all CVS/Root files. That is the only plausible generalization I can think of. */ if (!(frame->which&W_FAKE) && CVSroot_cmdline == NULL && !server_active) { char *this_root = Name_Root (dir, update_dir); if (this_root != NULL) { if (findnode_fn (root_directories, this_root) == NULL) { /* Add it to our list. */ Node *n = getnode (); n->type = NT_UNKNOWN; n->key = xstrdup (this_root); if (addnode (root_directories, n)) error (1, 0, "cannot add new CVSROOT %s", this_root); } process_this_directory = (fncmp (current_parsed_root->original, this_root) == 0); xfree (this_root); } } /* * Do we have access to this directory? */ if(!current_parsed_root->isremote) { const char *tag=NULL; const char *date=NULL; int nonbranch=0; const char *message; const char *v_msg; /* before we do anything else, see if we have any per-directory tags */ ParseTag (&tag, &date, &nonbranch, NULL); if (! verify_access (frame->permproc, newrepos, NULL, update_dir, frame->tag?frame->tag:tag,&message, &v_msg)) { if(frame->permproc!=verify_read) { if(tag) error (0, 0, "User '%s' cannot %s %s on tag/branch %s", CVS_Username, v_msg, fn_root(virtrepos), tag); else error (0, 0, "User '%s' cannot %s %s", CVS_Username, v_msg, fn_root(virtrepos)); if(message) error(0,0,"%s",message); } dir_return = R_SKIP_ALL; } xfree(tag); xfree(date); } if(dir_return!=R_SKIP_ALL) { /* Generic behavior. I don't see a reason to make the caller specify a direntproc just to get this. */ if ((frame->which & (W_LOCAL|W_FAKE))) { if(!isdir (dir)) hint = R_SKIP_ALL; else hint = R_PROCESS; } else if(!isdir(newrepos)) hint = R_SKIP_ALL; else hint = R_PROCESS; } if (dir_return == R_SKIP_ALL || dir_return == R_ERROR) ; else if(process_this_directory) { int directory_opened_status=-1; const char *dirversion=NULL; const char *dirtag=NULL; const char *dirdate=NULL; int dirnonbranch=0; if(frame->predirentproc != NULL) { frame->predirentproc (frame->callerdat, dir, newrepos, update_dir, frent->entries, virtrepos, hint); } /* before we do anything else, see if we have any per-directory tags */ ParseTag_Dir (dir, &dirtag, &dirdate, &dirnonbranch, &dirversion); directory_opened_status=open_directory(newrepos,dir,dirtag,dirdate,dirnonbranch,dirversion,current_parsed_root->isremote); if (directory_opened_status!=-1) directory_opened = 1; xfree(dirversion); xfree(dirtag); xfree(dirdate); if(!current_parsed_root->isremote) fileattr_startdir(newrepos); } /* call-back dir entry proc (if any) */ if (dir_return == R_SKIP_ALL || dir_return == R_ERROR) ; else if (frame->direntproc != NULL) { /* If we're doing the actual processing, call direntproc. Otherwise, assume that we need to process this directory and recurse. FIXME. */ if (process_this_directory) dir_return = frame->direntproc (frame->callerdat, dir, newrepos, update_dir, frent->entries, virtrepos, hint); else dir_return = R_PROCESS; } else { /* Generic behavior. I don't see a reason to make the caller specify a direntproc just to get this. */ dir_return = hint; } /* only process the dir if the return code was 0 */ if (dir_return != R_SKIP_ALL && dir_return !=R_ERROR) { /* save our current directory and static vars */ if (save_cwd (&cwd)) error_exit (); sdirlist = dirlist; srepository = repository; smapped_repository = mapped_repository; dirlist = NULL; /* cd to the sub-directory */ if (!(frame->which&(W_LOCAL|W_FAKE))) { if ( CVS_CHDIR (newrepos) < 0) { error (1, errno, "could not chdir to %s", fn_root(newrepos)); } } else { if ( !(frame->which&W_FAKE) && CVS_CHDIR (dir) < 0) { if(!noexec) error (1, errno, "could not chdir to %s", fn_root(update_dir)); else directory_not_valid=1; } } old_update_repos = update_repos; update_repos = xstrdup(newrepos); /* honor the global SKIP_DIRS (a.k.a. local) */ if (frame->flags == R_SKIP_DIRS) dir_return = R_SKIP_DIRS; /* remember if the `.' will be stripped for subsequent dirs */ if (strcmp (update_dir, ".") == 0) { update_dir[0] = '\0'; stripped_dot = 1; } /* make the recursive call */ xframe = *frame; xframe.flags = dir_return; if(directory_not_valid) { xframe.which &= ~W_LOCAL; xframe.which |= W_FAKE; } err += do_recursion (&xframe, 0); /* put the `.' back if necessary */ if (stripped_dot) (void) strcpy (update_dir, "."); /* call-back dir leave proc (if any) */ if (process_this_directory && frame->dirleaveproc != NULL) err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir, frent->entries); if(directory_opened) close_directory(); /* get back to where we started and restore state vars */ if (restore_cwd (&cwd, NULL)) error_exit (); xfree(update_repos); update_repos = old_update_repos; free_cwd (&cwd); dirlist = sdirlist; repository = srepository; mapped_repository = smapped_repository; } else { if(directory_opened) close_directory(); fileattr_write(); fileattr_free(); } xfree (newrepos); xfree (virtrepos); xfree (update_dir); update_dir = saved_update_dir; if(dir_return == R_ERROR) err++; return (err); }
/* * Implement the recursive policies on the local directory. This may be * called directly, or may be called by start_recursion */ static int do_recursion (struct recursion_frame *frame, int top_level) { int err = 0; int dodoneproc = 1; char *srepository; List *entries = NULL; int should_readlock; int process_this_directory = 1; /* do nothing if told */ if (frame->flags == R_SKIP_ALL) return (0); should_readlock = noexec ? 0 : frame->readlock; /* Check the value in CVSADM_ROOT and see if it's in the list. If not, add it to our lists of CVS/Root directories and do not process the files in this directory. Otherwise, continue as usual. THIS_ROOT might be NULL if we're doing an initial checkout -- check before using it. The default should be that we process a directory's contents and only skip those contents if a CVS/Root file exists. If we're running the server, we want to process all directories, since we're guaranteed to have only one CVSROOT -- our own. */ /* If -d was specified, it should override CVS/Root. In the single-repository case, it is long-standing CVS behavior and makes sense - the user might want another access method, another server (which mounts the same repository), &c. In the multiple-repository case, -d overrides all CVS/Root files. That is the only plausible generalization I can think of. */ if(!(frame->which&W_FAKE) && CVSroot_cmdline == NULL && ! server_active) { char *this_root = Name_Root ((char *) NULL, update_dir); if (this_root != NULL) { if (findnode_fn (root_directories, this_root) == NULL) { /* Add it to our list. */ Node *n = getnode (); n->type = NT_UNKNOWN; n->key = xstrdup (this_root); if (addnode (root_directories, n)) error (1, 0, "cannot add new CVSROOT %s", this_root); } process_this_directory = (fncmp (current_parsed_root->original, this_root) == 0); xfree (this_root); } } /* * Fill in repository with the current repository */ if (frame->which & W_LOCAL) { if (isdir (CVSADM)) repository = Name_Repository ((char *) NULL, update_dir); else repository = NULL; } else { if(update_repos) /* May have been preloaded by checkout */ repository = xstrdup(update_repos); else { repository = (char*)xmalloc(strlen(current_parsed_root->directory)+strlen(update_dir)+10); sprintf(repository,"%s/%s",current_parsed_root->directory,update_dir); } } if(repository && ISDIRSEP(repository[strlen(repository)-1])) repository[strlen(repository)-1]='\0'; srepository = repository; /* remember what to free */ if(repository && !current_parsed_root->isremote) { mapped_repository = map_repository(repository); } else { mapped_repository=xstrdup(repository); } xfree(last_repository); last_repository = xstrdup(mapped_repository); /* * Do we have access to this directory? */ /* Note that for a recursion this is done already in do_dir_proc... */ if(top_level) { if(repository && !current_parsed_root->isremote) { const char *tag; const char *message; const char *v_msg; ParseTag(&tag, NULL, NULL, NULL); if (! verify_access(frame->permproc, mapped_repository, NULL, update_dir,frame->tag?frame->tag:tag,&message, &v_msg)) { if(tag) error (0, 0, "User '%s' cannot %s %s on tag/branch %s", CVS_Username, v_msg, fn_root(repository), tag); else error (0, 0, "User '%s' cannot %s %s", CVS_Username, v_msg, fn_root(repository)); if(message) error (0, 0, "%s", message); return (1); } xfree(tag); fileattr_startdir (mapped_repository); } } /* * The filesdoneproc needs to be called for each directory where files * processed, or each directory that is processed by a call where no * directories were passed in. In fact, the only time we don't want to * call back the filesdoneproc is when we are processing directories that * were passed in on the command line (or in the special case of `.' when * we were called with no args */ if (dirlist != NULL && filelist == NULL) dodoneproc = 0; /* * If filelist or dirlist is already set, we don't look again. Otherwise, * find the files and directories */ if (filelist == NULL && dirlist == NULL) { /* both lists were NULL, so start from scratch */ if (frame->fileproc != NULL && frame->flags != R_SKIP_FILES) { int lwhich = frame->which; /* In the !(which & W_LOCAL) case, we filled in repository earlier in the function. In the (which & W_LOCAL) case, the Find_Names function is going to look through the Entries file. If we do not have a repository, that does not make sense, so we insist upon having a repository at this point. Name_Repository will give a reasonable error message. */ if (repository == NULL) repository = Name_Repository ((char *) NULL, update_dir); else if(mapped_repository == NULL) mapped_repository = map_repository(repository); /* find the files and fill in entries if appropriate */ if (process_this_directory) { filelist = Find_Names (mapped_repository, lwhich, frame->aflag, &entries, repository); if (filelist == NULL) { error (0, 0, "skipping directory %s", update_dir); /* Note that Find_Directories and the filesdoneproc in particular would do bad things ("? foo.c" in the case of some filesdoneproc's). */ goto skip_directory; } } } if (frame->flags == R_SKIP_DIRS && !(frame->which&W_LOCAL) && nonrecursive_module(repository)) frame->flags = R_SKIP_DIRS; /* find sub-directories if we will recurse */ if (frame->flags != R_SKIP_DIRS) dirlist = Find_Directories ( process_this_directory ? mapped_repository : NULL, frame->which, entries, repository); } else { /* something was passed on the command line */ if (filelist != NULL && frame->fileproc != NULL) { /* we will process files, so pre-parse entries */ if (frame->which & W_LOCAL) entries = Entries_Open (frame->aflag, NULL); } } /* process the files (if any) */ if (process_this_directory && filelist != NULL && frame->fileproc) { struct file_info finfo_struct; struct frame_and_file frfile; /* read lock it if necessary */ if (should_readlock && mapped_repository && Reader_Lock (mapped_repository) != 0) error (1, 0, "read lock failed - giving up"); /* For the server, we handle notifications in a completely different place (server_notify). For local, we can't do them here--we don't have writelocks in place, and there is no way to get writelocks here. */ if (current_parsed_root->isremote) client_notify_check (repository, update_dir); finfo_struct.repository = mapped_repository; finfo_struct.update_dir = update_dir; finfo_struct.entries = entries; finfo_struct.virtual_repository = repository; /* do_file_proc will fill in finfo_struct.file. */ frfile.finfo = &finfo_struct; frfile.frame = frame; /* process the files */ err += walklist (filelist, do_file_proc, &frfile); /* unlock it */ if (should_readlock) Lock_Cleanup_Directory(); /* clean up */ if (filelist) dellist (&filelist); } /* call-back files done proc (if any) */ if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL) { err = frame->filesdoneproc (frame->callerdat, err, (char*)mapped_repository, (char*)(update_dir[0] ? update_dir : "."), entries); } skip_directory: if(repository && !current_parsed_root->isremote) { fileattr_write (); fileattr_free (); } /* process the directories (if necessary) */ if (dirlist != NULL) { // BUGME!!! /* for some reason this code path is not entered some times when it SHOULD be, eg: after a rename! This means the .direcrory_history file is not created !!!! */ struct frame_and_entries frent; frent.frame = frame; frent.entries = entries; err += walklist (dirlist, do_dir_proc, (void *) &frent); } if (dirlist) dellist (&dirlist); if (entries) { Entries_Close (entries); entries = NULL; } /* free the saved copy of the pointer if necessary */ xfree (srepository); xfree (mapped_repository); repository = NULL; return (err); }
/* Rebuild the checked out administrative files in directory DIR. */ int mkmodules (char *dir) { struct saved_cwd cwd; char *temp; char *cp, *last, *fname; #ifdef MY_NDBM DBM *db; #endif FILE *fp; char *line = NULL; size_t line_allocated = 0; const struct admin_file *fileptr; mode_t mode; if (noexec) return 0; if (save_cwd (&cwd)) error_exit (); if ( CVS_CHDIR (dir) < 0) error (1, errno, "cannot chdir to %s", dir); /* * First, do the work necessary to update the "modules" database. */ temp = make_tempfile (); switch (checkout_file (CVSROOTADM_MODULES, dir, temp, NULL)) { case 0: /* everything ok */ #ifdef MY_NDBM /* open it, to generate any duplicate errors */ if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) dbm_close (db); #else write_dbmfile (temp); rename_dbmfile (temp); #endif rename_rcsfile (temp, CVSROOTADM_MODULES); break; default: error (0, 0, "'cvs checkout' is less functional without a %s file", CVSROOTADM_MODULES); break; } /* switch on checkout_file() */ if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); /* Checkout the files that need it in CVSROOT dir */ for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { if (fileptr->errormsg == NULL) continue; temp = make_tempfile (); if (checkout_file (fileptr->filename, dir, temp, NULL) == 0) rename_rcsfile (temp, fileptr->filename); if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); } fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); if (fp) { /* * File format: * [<whitespace>]<filename><whitespace><error message><end-of-line> * * comment lines begin with '#' */ while (getline (&line, &line_allocated, fp) >= 0) { /* skip lines starting with # */ if (line[0] == '#') continue; if ((last = strrchr (line, '\n')) != NULL) *last = '\0'; /* strip the newline */ /* Skip leading white space. */ for (fname = line; *fname && isspace ((unsigned char) *fname); fname++) ; /* Find end of filename. */ for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) ; *cp = '\0'; if(isabsolute(fname) || pathname_levels(fname)>0 || !fncmp(fname,"checkoutlist")) { error(0,0,"Invalid filename '%s' in checkoutlist", fname); continue; } temp = make_tempfile (); if (checkout_file (fname, dir, temp, &mode) == 0) { chmod(temp,mode); rename_rcsfile (temp, fname); } else { for (cp++; cp < last && *last && isspace ((unsigned char) *last); cp++) ; if (cp < last && *cp) error (0, 0, cp, fname); } if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); } if (line) xfree (line); if (ferror (fp)) error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); } else { /* Error from CVS_FOPEN. */ if (!existence_error (errno)) error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); } if (restore_cwd (&cwd, NULL)) error_exit (); free_cwd (&cwd); return (0); }