Lisp_Object directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, bool attrs, Lisp_Object id_format) { ptrdiff_t directory_nbytes; Lisp_Object list, dirfilename, encoded_directory; struct re_pattern_buffer *bufp = NULL; bool needsep = 0; ptrdiff_t count = SPECPDL_INDEX (); #ifdef WINDOWSNT Lisp_Object w32_save = Qnil; #endif /* Don't let the compiler optimize away all copies of DIRECTORY, which would break GC; see Bug#16986. */ Lisp_Object volatile directory_volatile = directory; /* Because of file name handlers, these functions might call Ffuncall, and cause a GC. */ list = encoded_directory = dirfilename = Qnil; dirfilename = Fdirectory_file_name (directory); if (!NILP (match)) { CHECK_STRING (match); /* MATCH might be a flawed regular expression. Rather than catching and signaling our own errors, we just call compile_pattern to do the work for us. */ /* Pass 1 for the MULTIBYTE arg because we do make multibyte strings if the contents warrant. */ # ifdef WINDOWSNT /* Windows users want case-insensitive wildcards. */ bufp = compile_pattern (match, 0, BVAR (&buffer_defaults, case_canon_table), 0, 1); # else /* !WINDOWSNT */ bufp = compile_pattern (match, 0, Qnil, 0, 1); # endif /* !WINDOWSNT */ } /* Note: ENCODE_FILE and DECODE_FILE can GC because they can run run_pre_post_conversion_on_str which calls Lisp directly and indirectly. */ dirfilename = ENCODE_FILE (dirfilename); encoded_directory = ENCODE_FILE (directory); /* Now *bufp is the compiled form of MATCH; don't call anything which might compile a new regexp until we're done with the loop! */ int fd; DIR *d = open_directory (dirfilename, &fd); /* Unfortunately, we can now invoke expand-file-name and file-attributes on filenames, both of which can throw, so we must do a proper unwind-protect. */ record_unwind_protect_ptr (directory_files_internal_unwind, d); #ifdef WINDOWSNT if (attrs) { /* Do this only once to avoid doing it (in w32.c:stat) for each file in the directory, when we call file_attributes below. */ record_unwind_protect (directory_files_internal_w32_unwind, Vw32_get_true_file_attributes); w32_save = Vw32_get_true_file_attributes; if (EQ (Vw32_get_true_file_attributes, Qlocal)) { /* w32.c:stat will notice these bindings and avoid calling GetDriveType for each file. */ if (is_slow_fs (SSDATA (dirfilename))) Vw32_get_true_file_attributes = Qnil; else Vw32_get_true_file_attributes = Qt; } } #endif directory_nbytes = SBYTES (directory); re_match_object = Qt; /* Decide whether we need to add a directory separator. */ if (directory_nbytes == 0 || !IS_ANY_SEP (SREF (directory, directory_nbytes - 1))) needsep = 1; /* Loop reading directory entries. */ for (struct dirent *dp; (dp = read_dirent (d, directory)); ) { ptrdiff_t len = dirent_namelen (dp); Lisp_Object name = make_unibyte_string (dp->d_name, len); Lisp_Object finalname = name; /* Note: DECODE_FILE can GC; it should protect its argument, though. */ name = DECODE_FILE (name); len = SBYTES (name); /* Now that we have unwind_protect in place, we might as well allow matching to be interrupted. */ maybe_quit (); bool wanted = (NILP (match) || re_search (bufp, SSDATA (name), len, 0, len, 0) >= 0); if (wanted) { if (!NILP (full)) { Lisp_Object fullname; ptrdiff_t nbytes = len + directory_nbytes + needsep; ptrdiff_t nchars; fullname = make_uninit_multibyte_string (nbytes, nbytes); memcpy (SDATA (fullname), SDATA (directory), directory_nbytes); if (needsep) SSET (fullname, directory_nbytes, DIRECTORY_SEP); memcpy (SDATA (fullname) + directory_nbytes + needsep, SDATA (name), len); nchars = multibyte_chars_in_text (SDATA (fullname), nbytes); /* Some bug somewhere. */ if (nchars > nbytes) emacs_abort (); STRING_SET_CHARS (fullname, nchars); if (nchars == nbytes) STRING_SET_UNIBYTE (fullname); finalname = fullname; } else finalname = name; if (attrs) { Lisp_Object fileattrs = file_attributes (fd, dp->d_name, directory, name, id_format); list = Fcons (Fcons (finalname, fileattrs), list); } else list = Fcons (finalname, list); } } closedir (d); #ifdef WINDOWSNT if (attrs) Vw32_get_true_file_attributes = w32_save; #endif /* Discard the unwind protect. */ specpdl_ptr = specpdl + count; if (NILP (nosort)) list = Fsort (Fnreverse (list), attrs ? Qfile_attributes_lessp : Qstring_lessp); (void) directory_volatile; return list; }
/* * 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); }
CL_VirtualDirectory CL_VirtualFileSystem::get_root_directory() { return open_directory(CL_String()); }
static int unroll_files_proc (Node *p, void *closure) { int directory_opened = 0; Node *n; struct recursion_frame *frame = (struct recursion_frame *) closure; int err = 0; List *save_dirlist; char *save_update_dir = NULL; struct saved_cwd cwd; char *dir = p->key; /* if this dir was also an explicitly named argument, then skip it. We'll catch it later when we do dirs. */ n = findnode_fn (dirlist, dir); if (n != NULL) return (0); /* otherwise, call dorecusion for this list of files. */ filelist = (List *) p->data; p->data = NULL; save_dirlist = dirlist; dirlist = NULL; if (strcmp(dir, ".") != 0) { if (save_cwd (&cwd)) error_exit (); if ( CVS_CHDIR (dir) < 0) error (1, errno, "could not chdir to %s", dir); save_update_dir = update_dir; update_dir = (char*)xmalloc (strlen (save_update_dir) + strlen (dir) + 5); strcpy (update_dir, save_update_dir); if (*update_dir != '\0') (void) strcat (update_dir, "/"); (void) strcat (update_dir, dir); } if(frame->which&W_LOCAL) { int directory_opened_status=-1; const char *version; const char *tag; const char *date; int nonbranch; char *repository = Name_Repository(NULL,update_dir); ParseTag(&tag, &date, &nonbranch, &version); directory_opened_status=open_directory(repository,".",tag,date,nonbranch,version,current_parsed_root->isremote); if (directory_opened_status!=-1) directory_opened = 1; xfree(repository); xfree(version); xfree(tag); xfree(date); } else { int directory_opened_status=-1; /* we can't simply call Name_Repository() here, if we do we get: -> Name_Repository((null),cvsnt/src) cvs server: in directory cvsnt/src: cvs [server aborted]: CVS directory without administration files present. Cannot continue until this directory is deleted or renamed. */ directory_opened_status=open_directory(NULL,".",NULL,NULL,0,NULL,1); /* We want to open the directory anyway, so treat it as 'remote'. This only affects rlog, etc. */ if (directory_opened_status!=-1) directory_opened = 1; } err += do_recursion (frame, 1); if(directory_opened) close_directory(); if (save_update_dir != NULL) { xfree (update_dir); update_dir = save_update_dir; if (restore_cwd (&cwd, NULL)) error_exit (); free_cwd (&cwd); } dirlist = save_dirlist; if (filelist) dellist (&filelist); return(err); }