Use macro SPECPDL_INDEX.
[bpt/emacs.git] / src / dired.c
index ce2e5cb..0a119af 100644 (file)
@@ -98,8 +98,6 @@ extern struct re_pattern_buffer *compile_pattern ();
 /* From filemode.c.  Can't go in Lisp.h because of `stat'.  */
 extern void filemodestring P_ ((struct stat *, char *));
 
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
 /* if system does not have symbolic links, it does not have lstat.
    In that case, use ordinary stat instead.  */
 
@@ -119,6 +117,8 @@ Lisp_Object Qfile_name_completion;
 Lisp_Object Qfile_name_all_completions;
 Lisp_Object Qfile_attributes;
 Lisp_Object Qfile_attributes_lessp;
+
+static int scmp P_ ((unsigned char *, unsigned char *, int));
 \f
 
 Lisp_Object
@@ -133,6 +133,7 @@ directory_files_internal_unwind (dh)
 /* Function shared by Fdirectory_files and Fdirectory_files_and_attributes.  
    When ATTRS is zero, return a list of directory filenames; when
    non-zero, return a list of directory filenames and their attributes.  */
+
 Lisp_Object
 directory_files_internal (directory, full, match, nosort, attrs)
      Lisp_Object directory, full, match, nosort;
@@ -143,7 +144,7 @@ directory_files_internal (directory, full, match, nosort, attrs)
   Lisp_Object list, dirfilename, encoded_directory;
   struct re_pattern_buffer *bufp = NULL;
   int needsep = 0;
-  int count = specpdl_ptr - specpdl;
+  int count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
   DIRENTRY *dp;
   int retry_p;
@@ -157,7 +158,7 @@ directory_files_internal (directory, full, match, nosort, attrs)
 
   if (!NILP (match))
     {
-      CHECK_STRING (match, 3);
+      CHECK_STRING (match);
 
       /* MATCH might be a flawed regular expression.  Rather than
         catching and signaling our own errors, we just call
@@ -209,9 +210,19 @@ directory_files_internal (directory, full, match, nosort, attrs)
 #endif /* not VMS */
 
   /* Loop reading blocks until EOF or error.  */
-  errno = 0;
-  while ((dp = readdir (d)) != NULL)
+  for (;;)
     {
+      errno = 0;
+      dp = readdir (d);
+
+#ifdef EAGAIN
+      if (dp == NULL && errno == EAGAIN)
+       continue;
+#endif
+      
+      if (dp == NULL)
+       break;
+
       if (DIRENTRY_NONEMPTY (dp))
        {
          int len;
@@ -299,9 +310,6 @@ directory_files_internal (directory, full, match, nosort, attrs)
     }
 
   retry_p = 0;
-#ifdef EAGAIN
-  retry_p |= errno == EAGAIN;
-#endif
 #ifdef EINTR
   retry_p |= errno == EINTR;
 #endif
@@ -326,14 +334,14 @@ directory_files_internal (directory, full, match, nosort, attrs)
 
 
 DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 4, 0,
-  "Return a list of names of files in DIRECTORY.\n\
-There are three optional arguments:\n\
-If FULL is non-nil, return absolute file names.  Otherwise return names\n\
- that are relative to the specified directory.\n\
-If MATCH is non-nil, mention only file names that match the regexp MATCH.\n\
-If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
- NOSORT is useful if you plan to sort the result yourself.")
-  (directory, full, match, nosort)
+       doc: /* Return a list of names of files in DIRECTORY.
+There are three optional arguments:
+If FULL is non-nil, return absolute file names.  Otherwise return names
+ that are relative to the specified directory.
+If MATCH is non-nil, mention only file names that match the regexp MATCH.
+If NOSORT is non-nil, the list is not sorted--its order is unpredictable.
+ NOSORT is useful if you plan to sort the result yourself.  */)
+     (directory, full, match, nosort)
      Lisp_Object directory, full, match, nosort;
 {
   Lisp_Object handler;
@@ -357,15 +365,16 @@ If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
   return directory_files_internal (directory, full, match, nosort, 0);
 }
 
-DEFUN ("directory-files-and-attributes", Fdirectory_files_and_attributes, Sdirectory_files_and_attributes, 1, 4, 0,
-  "Return a list of names of files and their attributes in DIRECTORY.\n\
-There are three optional arguments:\n\
-If FULL is non-nil, return absolute file names.  Otherwise return names\n\
- that are relative to the specified directory.\n\
-If MATCH is non-nil, mention only file names that match the regexp MATCH.\n\
-If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
- NOSORT is useful if you plan to sort the result yourself.")
-  (directory, full, match, nosort)
+DEFUN ("directory-files-and-attributes", Fdirectory_files_and_attributes,
+       Sdirectory_files_and_attributes, 1, 4, 0,
+       doc: /* Return a list of names of files and their attributes in DIRECTORY.
+There are three optional arguments:
+If FULL is non-nil, return absolute file names.  Otherwise return names
+ that are relative to the specified directory.
+If MATCH is non-nil, mention only file names that match the regexp MATCH.
+If NOSORT is non-nil, the list is not sorted--its order is unpredictable.
+ NOSORT is useful if you plan to sort the result yourself.  */)
+     (directory, full, match, nosort)
      Lisp_Object directory, full, match, nosort;
 {
   Lisp_Object handler;
@@ -393,13 +402,16 @@ If NOSORT is non-nil, the list is not sorted--its order is unpredictable.\n\
 Lisp_Object file_name_completion ();
 
 DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
-  2, 2, 0,
-  "Complete file name FILE in directory DIRECTORY.\n\
-Returns the longest string\n\
-common to all file names in DIRECTORY that start with FILE.\n\
-If there is only one and FILE matches it exactly, returns t.\n\
-Returns nil if DIR contains no name starting with FILE.")
-  (file, directory)
+       2, 2, 0,
+       doc: /* Complete file name FILE in directory DIRECTORY.
+Returns the longest string
+common to all file names in DIRECTORY that start with FILE.
+If there is only one and FILE matches it exactly, returns t.
+Returns nil if DIR contains no name starting with FILE.
+
+This function ignores some of the possible completions as
+determined by the variable `completion-ignored-extensions', which see.  */)
+     (file, directory)
      Lisp_Object file, directory;
 {
   Lisp_Object handler;
@@ -420,10 +432,10 @@ Returns nil if DIR contains no name starting with FILE.")
 }
 
 DEFUN ("file-name-all-completions", Ffile_name_all_completions,
-  Sfile_name_all_completions, 2, 2, 0,
-  "Return a list of all completions of file name FILE in directory DIRECTORY.\n\
-These are all file names in directory DIRECTORY which begin with FILE.")
-  (file, directory)
+       Sfile_name_all_completions, 2, 2, 0,
+       doc: /* Return a list of all completions of file name FILE in directory DIRECTORY.
+These are all file names in directory DIRECTORY which begin with FILE.  */)
+     (file, directory)
      Lisp_Object file, directory;
 {
   Lisp_Object handler;
@@ -461,7 +473,7 @@ file_name_completion (file, dirname, all_flag, ver_flag)
   struct stat st;
   int directoryp;
   int passcount;
-  int count = specpdl_ptr - specpdl;
+  int count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
 
   elt = Qnil;
@@ -479,7 +491,7 @@ file_name_completion (file, dirname, all_flag, ver_flag)
     readfunc = readdirver;
   file = Fupcase (file);
 #else  /* not VMS */
-  CHECK_STRING (file, 0);
+  CHECK_STRING (file);
 #endif /* not VMS */
 
 #ifdef FILE_SYSTEM_CASE
@@ -549,6 +561,34 @@ file_name_completion (file, dirname, all_flag, ver_flag)
                 actually in the way in a directory contains only one file.  */
              if (!passcount && TRIVIAL_DIRECTORY_ENTRY (dp->d_name))
                continue;
+             if (!passcount && len > XSTRING (encoded_file)->size)
+               /* Ignore directories if they match an element of
+                  completion-ignored-extensions which ends in a slash.  */
+               for (tem = Vcompletion_ignored_extensions;
+                    CONSP (tem); tem = XCDR (tem))
+                 {
+                   int elt_len;
+
+                   elt = XCAR (tem);
+                   if (!STRINGP (elt))
+                     continue;
+                   /* Need to encode ELT, since scmp compares unibyte
+                      strings only.  */
+                   elt = ENCODE_FILE (elt);
+                   elt_len = XSTRING (elt)->size - 1; /* -1 for trailing / */
+                   if (elt_len <= 0)
+                     continue;
+                   p1 = XSTRING (elt)->data;
+                   if (p1[elt_len] != '/')
+                     continue;
+                   skip = len - elt_len;
+                   if (skip < 0)
+                     continue;
+
+                   if (0 <= scmp (dp->d_name + skip, p1, elt_len))
+                     continue;
+                   break;
+                 }
            }
          else
             {
@@ -561,6 +601,9 @@ file_name_completion (file, dirname, all_flag, ver_flag)
                  {
                    elt = XCAR (tem);
                    if (!STRINGP (elt)) continue;
+                   /* Need to encode ELT, since scmp compares unibyte
+                      strings only.  */
+                   elt = ENCODE_FILE (elt);
                    skip = len - XSTRING (elt)->size;
                    if (skip < 0) continue;
 
@@ -587,7 +630,8 @@ file_name_completion (file, dirname, all_flag, ver_flag)
              for (regexps = Vcompletion_regexp_list; CONSP (regexps);
                   regexps = XCDR (regexps))
                {
-                 tem = Fstring_match (XCAR (regexps), elt, zero);
+                 tem = Fstring_match (XCAR (regexps),
+                                      make_string (dp->d_name, len), zero);
                  if (NILP (tem))
                    break;
                }
@@ -698,6 +742,34 @@ file_name_completion (file, dirname, all_flag, ver_flag)
   return Fsignal (Qquit, Qnil);
 }
 
+/* Compare exactly LEN chars of strings at S1 and S2,
+   ignoring case if appropriate.
+   Return -1 if strings match,
+   else number of chars that match at the beginning.  */
+
+static int
+scmp (s1, s2, len)
+     register unsigned char *s1, *s2;
+     int len;
+{
+  register int l = len;
+
+  if (completion_ignore_case)
+    {
+      while (l && DOWNCASE (*s1++) == DOWNCASE (*s2++))
+       l--;
+    }
+  else
+    {
+      while (l && *s1++ == *s2++)
+       l--;
+    }
+  if (l == 0)
+    return -1;
+  else
+    return len - l;
+}
+
 static int
 file_name_completion_stat (dirname, dp, st_addr)
      Lisp_Object dirname;
@@ -752,18 +824,18 @@ file_name_completion_stat (dirname, dp, st_addr)
 #ifdef VMS
 
 DEFUN ("file-name-all-versions", Ffile_name_all_versions,
-  Sfile_name_all_versions, 2, 2, 0,
-  "Return a list of all versions of file name FILE in directory DIRECTORY.")
-  (file, directory)
+       Sfile_name_all_versions, 2, 2, 0,
+       doc: /* Return a list of all versions of file name FILE in directory DIRECTORY.  */)
+     (file, directory)
      Lisp_Object file, directory;
 {
   return file_name_completion (file, directory, 1, 1);
 }
 
 DEFUN ("file-version-limit", Ffile_version_limit, Sfile_version_limit, 1, 1, 0,
-  "Return the maximum number of versions allowed for FILE.\n\
-Returns nil if the file cannot be opened or if there is no version limit.")
-  (filename)
+       doc: /* Return the maximum number of versions allowed for FILE.
+Returns nil if the file cannot be opened or if there is no version limit.  */)
+     (filename)
      Lisp_Object filename;
 {
   Lisp_Object retval;
@@ -799,28 +871,29 @@ make_time (time)
 }
 
 DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 1, 0,
-  "Return a list of attributes of file FILENAME.\n\
-Value is nil if specified file cannot be opened.\n\
-Otherwise, list elements are:\n\
- 0. t for directory, string (name linked to) for symbolic link, or nil.\n\
- 1. Number of links to file.\n\
- 2. File uid.\n\
- 3. File gid.\n\
- 4. Last access time, as a list of two integers.\n\
-  First integer has high-order 16 bits of time, second has low 16 bits.\n\
- 5. Last modification time, likewise.\n\
- 6. Last status change time, likewise.\n\
- 7. Size in bytes.\n\
-  This is a floating point number if the size is too large for an integer.\n\
- 8. File modes, as a string of ten letters or dashes as in ls -l.\n\
- 9. t iff file's gid would change if file were deleted and recreated.\n\
-10. inode number.  If inode number is larger than the Emacs integer,\n\
-  this is a cons cell containing two integers: first the high part,\n\
-  then the low 16 bits.\n\
-11. Device number.\n\
-\n\
-If file does not exist, returns nil.")
-  (filename)
+       doc: /* Return a list of attributes of file FILENAME.
+Value is nil if specified file cannot be opened.
+Otherwise, list elements are:
+ 0. t for directory, string (name linked to) for symbolic link, or nil.
+ 1. Number of links to file.
+ 2. File uid.
+ 3. File gid.
+ 4. Last access time, as a list of two integers.
+  First integer has high-order 16 bits of time, second has low 16 bits.
+ 5. Last modification time, likewise.
+ 6. Last status change time, likewise.
+ 7. Size in bytes.
+  This is a floating point number if the size is too large for an integer.
+ 8. File modes, as a string of ten letters or dashes as in ls -l.
+ 9. t iff file's gid would change if file were deleted and recreated.
+10. inode number.  If inode number is larger than the Emacs integer,
+  this is a cons cell containing two integers: first the high part,
+  then the low 16 bits.
+11. Device number.  If it is larger than the Emacs integer, this is
+  a cons cell, similar to the inode number.
+
+If file does not exist, returns nil.  */)
+     (filename)
      Lisp_Object filename;
 {
   Lisp_Object values[12];
@@ -881,7 +954,7 @@ If file does not exist, returns nil.")
   values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
 #endif /* BSD4_2 (or BSD4_3) */
   /* Cast -1 to avoid warning if int is not as wide as VALBITS.  */
-  if (s.st_ino & (((EMACS_INT) (-1)) << VALBITS))
+  if (FIXNUM_OVERFLOW_P (s.st_ino))
     /* To allow inode numbers larger than VALBITS, separate the bottom
        16 bits.  */
     values[10] = Fcons (make_number (s.st_ino >> 16),
@@ -891,7 +964,7 @@ If file does not exist, returns nil.")
     values[10] = make_number (s.st_ino);
 
   /* Likewise for device.  */
-  if (s.st_dev & (((EMACS_INT) (-1)) << VALBITS))
+  if (FIXNUM_OVERFLOW_P (s.st_dev))
     values[11] = Fcons (make_number (s.st_dev >> 16),
                        make_number (s.st_dev & 0xffff));
   else
@@ -901,9 +974,9 @@ If file does not exist, returns nil.")
 }
 
 DEFUN ("file-attributes-lessp", Ffile_attributes_lessp, Sfile_attributes_lessp, 2, 2, 0,
-  "Return t if first arg file attributes list is less than second.\n\
-Comparison is in lexicographic order and case is significant.")
-  (f1, f2)
+       doc: /* Return t if first arg file attributes list is less than second.
+Comparison is in lexicographic order and case is significant.  */)
+     (f1, f2)
      Lisp_Object f1, f2;
 {
   return Fstring_lessp (Fcar (f1), Fcar (f2));
@@ -943,8 +1016,10 @@ syms_of_dired ()
 #endif /* VMS */
 
   DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
-    "*Completion ignores filenames ending in any string in this list.\n\
-This variable does not affect lists of possible completions,\n\
-but does affect the commands that actually do completions.");
+              doc: /* *Completion ignores filenames ending in any string in this list.
+Directories are ignored if they match any string in this list which
+ends in a slash.
+This variable does not affect lists of possible completions,
+but does affect the commands that actually do completions.  */);
   Vcompletion_ignored_extensions = Qnil;
 }