changeset 94890:907ac6ecef89

(file_name_completion): Tweak the code so as to always do it in a single pass. Tighten the scope of some variables.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Tue, 13 May 2008 05:16:43 +0000
parents e80a3956601b
children 3a28e9f5eda0
files src/ChangeLog src/dired.c
diffstat 2 files changed, 173 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Tue May 13 05:15:57 2008 +0000
+++ b/src/ChangeLog	Tue May 13 05:16:43 2008 +0000
@@ -1,5 +1,8 @@
 2008-05-13  Stefan Monnier  <monnier@iro.umontreal.ca>
 
+	* dired.c (file_name_completion): Tweak the code so as to always do it
+	in a single pass.  Tighten the scope of some variables.
+
 	* dired.c (Qdefault_directory): New var.
 	(file_name_completion): Use it instead of Fexpand_file_name.
 	(syms_of_dired): Initialize it.
--- a/src/dired.c	Tue May 13 05:15:57 2008 +0000
+++ b/src/dired.c	Tue May 13 05:16:43 2008 +0000
@@ -466,8 +466,7 @@
      Lisp_Object predicate;
 {
   DIR *d;
-  int bestmatchsize = 0, skip;
-  register int compare, matchsize;
+  int bestmatchsize = 0;
   int matchcount = 0;
   /* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded.
      If ALL_FLAG is 0, BESTMATCH is either nil
@@ -477,7 +476,10 @@
   Lisp_Object encoded_dir;
   struct stat st;
   int directoryp;
-  int passcount;
+  /* If includeall is zero, exclude files in completion-ignored-extensions as
+     well as "." and "..".  Until shown otherwise, assume we can't exclude
+     anything.  */
+  int includeall = 1;
   int count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
 
@@ -518,76 +520,71 @@
 
   encoded_dir = ENCODE_FILE (dirname);
 
-  /* With passcount = 0, ignore files that end in an ignored extension.
-     If nothing found then try again with passcount = 1, don't ignore them.
-     If looking for all completions, start with passcount = 1,
-     so always take even the ignored ones.
-
-     ** It would not actually be helpful to the user to ignore any possible
-     completions when making a list of them.**  */
-
-  for (passcount = !!all_flag; NILP (bestmatch) && passcount < 2; passcount++)
-    {
-      int inner_count = SPECPDL_INDEX ();
+  BLOCK_INPUT;
+  d = opendir (SDATA (Fdirectory_file_name (encoded_dir)));
+  UNBLOCK_INPUT;
+  if (!d)
+    report_file_error ("Opening directory", Fcons (dirname, Qnil));
 
-      BLOCK_INPUT;
-      d = opendir (SDATA (Fdirectory_file_name (encoded_dir)));
-      UNBLOCK_INPUT;
-      if (!d)
-	report_file_error ("Opening directory", Fcons (dirname, Qnil));
+  record_unwind_protect (directory_files_internal_unwind,
+			 make_save_value (d, 0));
 
-      record_unwind_protect (directory_files_internal_unwind,
-                             make_save_value (d, 0));
-
-      /* Loop reading blocks */
-      /* (att3b compiler bug requires do a null comparison this way) */
-      while (1)
-	{
-	  DIRENTRY *dp;
-	  int len;
+  /* Loop reading blocks */
+  /* (att3b compiler bug requires do a null comparison this way) */
+  while (1)
+    {
+      DIRENTRY *dp;
+      int len;
+      int canexclude = 0;
 
 #ifdef VMS
-	  dp = (*readfunc) (d);
+      dp = (*readfunc) (d);
 #else
-	  errno = 0;
-	  dp = readdir (d);
-	  if (dp == NULL && (0
+      errno = 0;
+      dp = readdir (d);
+      if (dp == NULL && (0
 # ifdef EAGAIN
-			     || errno == EAGAIN
+			 || errno == EAGAIN
 # endif
 # ifdef EINTR
-			     || errno == EINTR
+			 || errno == EINTR
 # endif
-			     ))
-	    { QUIT; continue; }
+			 ))
+	{ QUIT; continue; }
 #endif
 
-	  if (!dp) break;
+      if (!dp) break;
+
+      len = NAMLEN (dp);
 
-	  len = NAMLEN (dp);
+      QUIT;
+      if (! DIRENTRY_NONEMPTY (dp)
+	  || len < SCHARS (encoded_file)
+	  || 0 <= scmp (dp->d_name, SDATA (encoded_file),
+			SCHARS (encoded_file)))
+	continue;
 
-	  QUIT;
-	  if (! DIRENTRY_NONEMPTY (dp)
-	      || len < SCHARS (encoded_file)
-	      || 0 <= scmp (dp->d_name, SDATA (encoded_file),
-			    SCHARS (encoded_file)))
-	    continue;
+      if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
+	continue;
 
-          if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
-            continue;
-
-          directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
-	  tem = Qnil;
-          if (directoryp)
+      directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
+      tem = Qnil;
+      /* If all_flag is set, always include all.
+	 It would not actually be helpful to the user to ignore any possible
+	 completions when making a list of them.  */
+      if (!all_flag)
+	{
+	  int skip;
+	  if (directoryp)
 	    {
 #ifndef TRIVIAL_DIRECTORY_ENTRY
 #define TRIVIAL_DIRECTORY_ENTRY(n) (!strcmp (n, ".") || !strcmp (n, ".."))
 #endif
 	      /* "." and ".." are never interesting as completions, and are
 		 actually in the way in a directory with only one file.  */
-	      if (!passcount && TRIVIAL_DIRECTORY_ENTRY (dp->d_name))
-		continue;
-	      if (!passcount && len > SCHARS (encoded_file))
+	      if (TRIVIAL_DIRECTORY_ENTRY (dp->d_name))
+		canexclude = 1;
+	      else if (len > SCHARS (encoded_file))
 		/* Ignore directories if they match an element of
 		   completion-ignored-extensions which ends in a slash.  */
 		for (tem = Vcompletion_ignored_extensions;
@@ -618,10 +615,10 @@
 		  }
 	    }
 	  else
-            {
+	    {
 	      /* Compare extensions-to-be-ignored against end of this file name */
 	      /* if name is not an exact match against specified string */
-	      if (!passcount && len > SCHARS (encoded_file))
+	      if (len > SCHARS (encoded_file))
 		/* and exit this for loop if a match is found */
 		for (tem = Vcompletion_ignored_extensions;
 		     CONSP (tem); tem = XCDR (tem))
@@ -644,121 +641,131 @@
 
 	  /* If an ignored-extensions match was found,
 	     don't process this name as a completion.  */
-	  if (!passcount && CONSP (tem))
+	  if (CONSP (tem))
+	    canexclude = 1;
+
+	  if (!includeall && canexclude)
+	    /* We're not including all files and this file can be excluded.  */
 	    continue;
 
-	  /* FIXME: If we move this `decode' earlier we can eliminate
-	     the repeated ENCODE_FILE on Vcompletion_ignored_extensions.  */
-	  name = make_unibyte_string (dp->d_name, len);
-	  name = DECODE_FILE (name);
-
-	  if (!passcount)
-	    {
-	      Lisp_Object regexps;
-	      Lisp_Object zero;
-	      XSETFASTINT (zero, 0);
-
-	      /* Ignore this element if it fails to match all the regexps.  */
-	      for (regexps = Vcompletion_regexp_list; CONSP (regexps);
-		   regexps = XCDR (regexps))
-		if (fast_string_match (XCAR (regexps), name) < 0)
-		  break;
-	      if (CONSP (regexps))
-		continue;
-	    }
-
-	  /* This is a possible completion */
-	  if (directoryp)
-	    /* This completion is a directory; make it end with '/'.  */
-	    name = Ffile_name_as_directory (name);
-
-	  /* Test the predicate, if any.  */
-	  if (!NILP (predicate))
-	    {
-	      Lisp_Object val;
-	      struct gcpro gcpro1;
-
-	      GCPRO1 (name);
-	      val = call1 (predicate, name);
-	      UNGCPRO;
-
-	      if (NILP (val))
-		continue;
-	    }
-
-	  /* Suitably record this match.  */
-
-	  matchcount++;
-
-	  if (all_flag)
-	    bestmatch = Fcons (name, bestmatch);
-	  else if (NILP (bestmatch))
-	    {
-	      bestmatch = name;
-	      bestmatchsize = SCHARS (name);
-	    }
-	  else
-	    {
-	      Lisp_Object zero = make_number (0);
-	      /* FIXME: This is a copy of the code in Ftry_completion.  */
-	      compare = min (bestmatchsize, SCHARS (name));
-	      tem = Fcompare_strings (bestmatch, zero,
-				      make_number (compare),
-				      name, zero,
-				      make_number (compare),
-				      completion_ignore_case ? Qt : Qnil);
-	      if (EQ (tem, Qt))
-		matchsize = compare;
-	      else if (XINT (tem) < 0)
-		matchsize = - XINT (tem) - 1;
-	      else
-		matchsize = XINT (tem) - 1;
-
-	      if (completion_ignore_case)
-		{
-		  /* If this is an exact match except for case,
-		     use it as the best match rather than one that is not
-		     an exact match.  This way, we get the case pattern
-		     of the actual match.  */
-		  /* This tests that the current file is an exact match
-		     but BESTMATCH is not (it is too long).  */
-		  if ((matchsize == SCHARS (name)
-		       && matchsize + !!directoryp
-		          < SCHARS (bestmatch))
-		      ||
-		      /* If there is no exact match ignoring case,
-			 prefer a match that does not change the case
-			 of the input.  */
-		      /* If there is more than one exact match aside from
-			 case, and one of them is exact including case,
-			 prefer that one.  */
-		      /* This == checks that, of current file and BESTMATCH,
-			 either both or neither are exact.  */
-		      (((matchsize == SCHARS (name))
-			==
-			(matchsize + !!directoryp == SCHARS (bestmatch)))
-		       && (tem = Fcompare_strings (name, zero,
-						   make_number (SCHARS (file)),
-						   file, zero,
-						   Qnil,
-						   Qnil),
-			   EQ (Qt, tem))
-		       && (tem = Fcompare_strings (bestmatch, zero,
-						   make_number (SCHARS (file)),
-						   file, zero,
-						   Qnil,
-						   Qnil),
-			   ! EQ (Qt, tem))))
-		    bestmatch = name;
-		}
-	      bestmatchsize = matchsize;
+	  if (includeall && !canexclude)
+	    { /* If we have one non-excludable file, we want to exclude the
+		 excudable files.  */
+	      includeall = 0;
+	      /* Throw away any previous excludable match found.  */
+	      bestmatch = Qnil;
+	      bestmatchsize = 0;
+	      matchcount = 0;
 	    }
 	}
-      /* This closes the directory.  */
-      bestmatch = unbind_to (inner_count, bestmatch);
+      /* FIXME: If we move this `decode' earlier we can eliminate
+	 the repeated ENCODE_FILE on Vcompletion_ignored_extensions.  */
+      name = make_unibyte_string (dp->d_name, len);
+      name = DECODE_FILE (name);
+
+      {
+	Lisp_Object regexps;
+	Lisp_Object zero;
+	XSETFASTINT (zero, 0);
+
+	/* Ignore this element if it fails to match all the regexps.  */
+	for (regexps = Vcompletion_regexp_list; CONSP (regexps);
+	     regexps = XCDR (regexps))
+	  if (fast_string_match (XCAR (regexps), name) < 0)
+	    break;
+	if (CONSP (regexps))
+	  continue;
+      }
+
+      /* This is a possible completion */
+      if (directoryp)
+	/* This completion is a directory; make it end with '/'.  */
+	name = Ffile_name_as_directory (name);
+
+      /* Test the predicate, if any.  */
+      if (!NILP (predicate))
+	{
+	  Lisp_Object val;
+	  struct gcpro gcpro1;
+
+	  GCPRO1 (name);
+	  val = call1 (predicate, name);
+	  UNGCPRO;
+
+	  if (NILP (val))
+	    continue;
+	}
+
+      /* Suitably record this match.  */
+
+      matchcount++;
+
+      if (all_flag)
+	bestmatch = Fcons (name, bestmatch);
+      else if (NILP (bestmatch))
+	{
+	  bestmatch = name;
+	  bestmatchsize = SCHARS (name);
+	}
+      else
+	{
+	  Lisp_Object zero = make_number (0);
+	  /* FIXME: This is a copy of the code in Ftry_completion.  */
+	  int compare = min (bestmatchsize, SCHARS (name));
+	  Lisp_Object tem
+	    = Fcompare_strings (bestmatch, zero,
+				make_number (compare),
+				name, zero,
+				make_number (compare),
+				completion_ignore_case ? Qt : Qnil);
+	  int matchsize
+	    = (EQ (tem, Qt)     ? compare
+	       : XINT (tem) < 0 ? - XINT (tem) - 1
+	       :                  XINT (tem) - 1);
+
+	  if (completion_ignore_case)
+	    {
+	      /* If this is an exact match except for case,
+		 use it as the best match rather than one that is not
+		 an exact match.  This way, we get the case pattern
+		 of the actual match.  */
+	      /* This tests that the current file is an exact match
+		 but BESTMATCH is not (it is too long).  */
+	      if ((matchsize == SCHARS (name)
+		   && matchsize + !!directoryp
+		   < SCHARS (bestmatch))
+		  ||
+		  /* If there is no exact match ignoring case,
+		     prefer a match that does not change the case
+		     of the input.  */
+		  /* If there is more than one exact match aside from
+		     case, and one of them is exact including case,
+		     prefer that one.  */
+		  /* This == checks that, of current file and BESTMATCH,
+		     either both or neither are exact.  */
+		  (((matchsize == SCHARS (name))
+		    ==
+		    (matchsize + !!directoryp == SCHARS (bestmatch)))
+		   && (tem = Fcompare_strings (name, zero,
+					       make_number (SCHARS (file)),
+					       file, zero,
+					       Qnil,
+					       Qnil),
+		       EQ (Qt, tem))
+		   && (tem = Fcompare_strings (bestmatch, zero,
+					       make_number (SCHARS (file)),
+					       file, zero,
+					       Qnil,
+					       Qnil),
+		       ! EQ (Qt, tem))))
+		bestmatch = name;
+	    }
+	  bestmatchsize = matchsize;
+	}
     }
 
   UNGCPRO;
+  /* This closes the directory.  */
   bestmatch = unbind_to (count, bestmatch);
 
   if (all_flag || NILP (bestmatch))