Remove extern declarations from .c files, and them to .h files.
[bpt/emacs.git] / src / dired.c
index ef6c3e3..aec64f9 100644 (file)
@@ -1,13 +1,13 @@
 /* Lisp functions for making directory listings.
    Copyright (C) 1985, 1986, 1993, 1994, 1999, 2000, 2001, 2002, 2003,
-                 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+                 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,9 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
 #include <config.h>
@@ -25,22 +23,15 @@ Boston, MA 02110-1301, USA.  */
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <setjmp.h>
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
-#ifndef VMS
 #include <grp.h>
-#endif
 
 #include <errno.h>
 
-#ifdef VMS
-#include <string.h>
-#include <rms.h>
-#include <rmsdef.h>
-#endif
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -64,23 +55,19 @@ Boston, MA 02110-1301, USA.  */
 
 #else /* not SYSV_SYSTEM_DIR */
 
-#ifdef NONSYSTEM_DIR_LIBRARY
-#include "ndir.h"
-#else /* not NONSYSTEM_DIR_LIBRARY */
 #ifdef MSDOS
 #include <dirent.h>
 #else
 #include <sys/dir.h>
 #endif
-#endif /* not NONSYSTEM_DIR_LIBRARY */
 
 #include <sys/stat.h>
 
 #ifndef MSDOS
 #define DIRENTRY struct direct
 
-extern DIR *opendir ();
-extern struct direct *readdir ();
+extern DIR *opendir (char *);
+extern struct direct *readdir (DIR *);
 
 #endif /* not MSDOS */
 #endif /* not SYSV_SYSTEM_DIR */
@@ -96,16 +83,17 @@ extern struct direct *readdir ();
 #include "systime.h"
 #include "buffer.h"
 #include "commands.h"
+#include "character.h"
 #include "charset.h"
 #include "coding.h"
 #include "regex.h"
 #include "blockinput.h"
 
 /* Returns a search buffer, with a fastmap allocated and ready to go.  */
-extern struct re_pattern_buffer *compile_pattern ();
+extern struct re_pattern_buffer *compile_pattern (Lisp_Object, struct re_registers *, Lisp_Object, int, int);
 
 /* From filemode.c.  Can't go in Lisp.h because of `stat'.  */
-extern void filemodestring P_ ((struct stat *, char *));
+extern void filemodestring (struct stat *, char *);
 
 /* if system does not have symbolic links, it does not have lstat.
    In that case, use ordinary stat instead.  */
@@ -114,9 +102,7 @@ extern void filemodestring P_ ((struct stat *, char *));
 #define lstat stat
 #endif
 
-extern int completion_ignore_case;
-extern Lisp_Object Qcompletion_ignore_case;
-extern Lisp_Object Vcompletion_regexp_list;
+extern Lisp_Object Vw32_get_true_file_attributes;
 
 Lisp_Object Vcompletion_ignored_extensions;
 Lisp_Object Qdirectory_files;
@@ -126,12 +112,19 @@ Lisp_Object Qfile_name_all_completions;
 Lisp_Object Qfile_attributes;
 Lisp_Object Qfile_attributes_lessp;
 
-static int scmp P_ ((unsigned char *, unsigned char *, int));
+static int scmp (const unsigned char *, const unsigned char *, int);
 \f
+#ifdef WINDOWSNT
+Lisp_Object
+directory_files_internal_w32_unwind (Lisp_Object arg)
+{
+  Vw32_get_true_file_attributes = arg;
+  return Qnil;
+}
+#endif
 
 Lisp_Object
-directory_files_internal_unwind (dh)
-     Lisp_Object dh;
+directory_files_internal_unwind (Lisp_Object dh)
 {
   DIR *d = (DIR *) XSAVE_VALUE (dh)->pointer;
   BLOCK_INPUT;
@@ -146,10 +139,7 @@ directory_files_internal_unwind (dh)
    In the latter case, ID_FORMAT is passed to Ffile_attributes.  */
 
 Lisp_Object
-directory_files_internal (directory, full, match, nosort, attrs, id_format)
-     Lisp_Object directory, full, match, nosort;
-     int attrs;
-     Lisp_Object id_format;
+directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, int attrs, Lisp_Object id_format)
 {
   DIR *d;
   int directory_nbytes;
@@ -159,6 +149,9 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
   int count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
   DIRENTRY *dp;
+#ifdef WINDOWSNT
+  Lisp_Object w32_save = Qnil;
+#endif
 
   /* Because of file name handlers, these functions might call
      Ffuncall, and cause a GC.  */
@@ -175,10 +168,6 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
         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 VMS
-      bufp = compile_pattern (match, 0,
-                             buffer_defaults.downcase_table, 0, 1);
-#else  /* !VMS */
 # ifdef WINDOWSNT
       /* Windows users want case-insensitive wildcards.  */
       bufp = compile_pattern (match, 0,
@@ -186,14 +175,15 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
 # else /* !WINDOWSNT */
       bufp = compile_pattern (match, 0, Qnil, 0, 1);
 # endif         /* !WINDOWSNT */
-#endif  /* !VMS */
     }
 
   /* 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);
+  if (STRING_MULTIBYTE (dirfilename))
+    dirfilename = ENCODE_FILE (dirfilename);
+  encoded_directory = (STRING_MULTIBYTE (directory)
+                      ? ENCODE_FILE (directory) : 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!  */
@@ -210,15 +200,35 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
   record_unwind_protect (directory_files_internal_unwind,
                         make_save_value (d, 0));
 
+#ifdef WINDOWSNT
+  if (attrs)
+    {
+      extern int is_slow_fs (const char *);
+
+      /* Do this only once to avoid doing it (in w32.c:stat) for each
+        file in the directory, when we call Ffile_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 (SDATA (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.  */
-#ifndef VMS
   if (directory_nbytes == 0
       || !IS_ANY_SEP (SREF (directory, directory_nbytes - 1)))
     needsep = 1;
-#endif /* not VMS */
 
   /* Loop reading blocks until EOF or error.  */
   for (;;)
@@ -250,7 +260,7 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
          name = finalname = make_unibyte_string (dp->d_name, len);
          GCPRO2 (finalname, name);
 
-         /* Note: ENCODE_FILE can GC; it should protect its argument,
+         /* Note: DECODE_FILE can GC; it should protect its argument,
             though.  */
          name = DECODE_FILE (name);
          len = SBYTES (name);
@@ -275,15 +285,14 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
                  int nchars;
 
                  fullname = make_uninit_multibyte_string (nbytes, nbytes);
-                 bcopy (SDATA (directory), SDATA (fullname),
-                        directory_nbytes);
+                 memcpy (SDATA (fullname), SDATA (directory),
+                         directory_nbytes);
 
                  if (needsep)
                    SSET (fullname, directory_nbytes, DIRECTORY_SEP);
 
-                 bcopy (SDATA (name),
-                        SDATA (fullname) + directory_nbytes + needsep,
-                        len);
+                 memcpy (SDATA (fullname) + directory_nbytes + needsep,
+                         SDATA (name), len);
 
                  nchars = chars_in_text (SDATA (fullname), nbytes);
 
@@ -328,6 +337,10 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
   BLOCK_INPUT;
   closedir (d);
   UNBLOCK_INPUT;
+#ifdef WINDOWSNT
+  if (attrs)
+    Vw32_get_true_file_attributes = w32_save;
+#endif
 
   /* Discard the unwind protect.  */
   specpdl_ptr = specpdl + count;
@@ -347,9 +360,9 @@ 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.
+ Otherwise, the list returned is sorted with `string-lessp'.
  NOSORT is useful if you plan to sort the result yourself.  */)
-     (directory, full, match, nosort)
-     Lisp_Object directory, full, match, nosort;
+  (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort)
 {
   Lisp_Object handler;
   directory = Fexpand_file_name (directory, Qnil);
@@ -377,8 +390,7 @@ ID-FORMAT specifies the preferred format of attributes uid and gid, see
 `file-attributes' for further documentation.
 On MS-Windows, performance depends on `w32-get-true-file-attributes',
 which see.  */)
-     (directory, full, match, nosort, id_format)
-     Lisp_Object directory, full, match, nosort, id_format;
+  (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, Lisp_Object id_format)
 {
   Lisp_Object handler;
   directory = Fexpand_file_name (directory, Qnil);
@@ -394,7 +406,7 @@ which see.  */)
 }
 
 \f
-Lisp_Object file_name_completion ();
+Lisp_Object file_name_completion (Lisp_Object file, Lisp_Object dirname, int all_flag, int ver_flag, Lisp_Object predicate);
 
 DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
        2, 3, 0,
@@ -409,8 +421,7 @@ completion (in absolute form) and ignore it if PREDICATE returns nil.
 
 This function ignores some of the possible completions as
 determined by the variable `completion-ignored-extensions', which see.  */)
-     (file, directory, predicate)
-     Lisp_Object file, directory, predicate;
+  (Lisp_Object file, Lisp_Object directory, Lisp_Object predicate)
 {
   Lisp_Object handler;
 
@@ -433,8 +444,7 @@ DEFUN ("file-name-all-completions", Ffile_name_all_completions,
        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 file, Lisp_Object directory)
 {
   Lisp_Object handler;
 
@@ -453,18 +463,14 @@ These are all file names in directory DIRECTORY which begin with FILE.  */)
   return file_name_completion (file, directory, 1, 0, Qnil);
 }
 
-static int file_name_completion_stat ();
+static int file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_addr);
+Lisp_Object Qdefault_directory;
 
 Lisp_Object
-file_name_completion (file, dirname, all_flag, ver_flag, predicate)
-     Lisp_Object file, dirname;
-     int all_flag, ver_flag;
-     Lisp_Object predicate;
+file_name_completion (Lisp_Object file, Lisp_Object dirname, int all_flag, int ver_flag, Lisp_Object predicate)
 {
   DIR *d;
-  int bestmatchsize = 0, skip;
-  register int compare, matchsize;
-  unsigned char *p1, *p2;
+  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
@@ -474,27 +480,16 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
   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;
 
   elt = Qnil;
 
-#ifdef VMS
-  extern DIRENTRY * readdirver ();
-
-  DIRENTRY *((* readfunc) ());
-
-  /* Filename completion on VMS ignores case, since VMS filesys does.  */
-  specbind (Qcompletion_ignore_case, Qt);
-
-  readfunc = readdir;
-  if (ver_flag)
-    readfunc = readdirver;
-  file = Fupcase (file);
-#else  /* not VMS */
   CHECK_STRING (file);
-#endif /* not VMS */
 
 #ifdef FILE_SYSTEM_CASE
   file = FILE_SYSTEM_CASE (file);
@@ -503,90 +498,100 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
   encoded_file = encoded_dir = Qnil;
   GCPRO5 (file, dirname, bestmatch, encoded_file, encoded_dir);
   dirname = Fexpand_file_name (dirname, Qnil);
+  specbind (Qdefault_directory, dirname);
 
   /* Do completion on the encoded file name
      because the other names in the directory are (we presume)
      encoded likewise.  We decode the completed string at the end.  */
-  encoded_file = ENCODE_FILE (file);
+  /* Actually, this is not quite true any more: we do most of the completion
+     work with decoded file names, but we still do some filtering based
+     on the encoded file name.  */
+  encoded_file = STRING_MULTIBYTE (file) ? ENCODE_FILE (file) : file;
 
   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.
+  BLOCK_INPUT;
+  d = opendir (SDATA (Fdirectory_file_name (encoded_dir)));
+  UNBLOCK_INPUT;
+  if (!d)
+    report_file_error ("Opening directory", Fcons (dirname, Qnil));
 
-     ** It would not actually be helpful to the user to ignore any possible
-     completions when making a list of them.**  */
+  record_unwind_protect (directory_files_internal_unwind,
+                        make_save_value (d, 0));
 
-  for (passcount = !!all_flag; NILP (bestmatch) && passcount < 2; passcount++)
+  /* Loop reading blocks */
+  /* (att3b compiler bug requires do a null comparison this way) */
+  while (1)
     {
-      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));
+      DIRENTRY *dp;
+      int len;
+      int canexclude = 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;
-
-#ifdef VMS
-         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; }
-#endif
+                        ))
+       { QUIT; continue; }
 
-         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 0 /* FIXME: The `scmp' call compares an encoded and a decoded string. */
+         /* If this entry matches the current bestmatch, the only
+            thing it can do is increase matchcount, so don't bother
+            investigating it any further.  */
+         if (!completion_ignore_case
+             /* The return result depends on whether it's the sole match.  */
+             && matchcount > 1
+             && !includeall /* This match may allow includeall to 0.  */
+             && len >= bestmatchsize
+             && 0 > scmp (dp->d_name, SDATA (bestmatch), bestmatchsize))
+           continue;
+#endif
+
+         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;
                     CONSP (tem); tem = XCDR (tem))
                  {
                    int elt_len;
+                   unsigned char *p1;
 
                    elt = XCAR (tem);
                    if (!STRINGP (elt))
@@ -610,10 +615,10 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
                  }
            }
          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))
@@ -636,135 +641,165 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
 
          /* If an ignored-extensions match was found,
             don't process this name as a completion.  */
-         if (!passcount && CONSP (tem))
-           continue;
+         if (CONSP (tem))
+           canexclude = 1;
 
-         if (!passcount)
-           {
-             Lisp_Object regexps;
-             Lisp_Object zero;
-             XSETFASTINT (zero, 0);
+         if (!includeall && canexclude)
+           /* We're not including all files and this file can be excluded.  */
+           continue;
 
-             /* Ignore this element if it fails to match all the regexps.  */
-             for (regexps = Vcompletion_regexp_list; CONSP (regexps);
-                  regexps = XCDR (regexps))
-               {
-                 tem = Fstring_match (XCAR (regexps),
-                                      make_string (dp->d_name, len), zero);
-                 if (NILP (tem))
-                   break;
-               }
-             if (CONSP (regexps))
-               continue;
+         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;
            }
+       }
+      /* 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.  */
+       if (completion_ignore_case)
+         {
+           for (regexps = Vcompletion_regexp_list; CONSP (regexps);
+                regexps = XCDR (regexps))
+             if (fast_string_match_ignore_case (XCAR (regexps), name) < 0)
+               break;
+         }
+       else
+         {
+           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;
 
-         /* This is a possible completion */
-         if (directoryp)
-           {
-             /* This completion is a directory; make it end with '/' */
-             name = Ffile_name_as_directory (make_string (dp->d_name, len));
-           }
-         else
-           name = make_string (dp->d_name, len);
-
-         /* Test the predicate, if any.  */
-
-         if (!NILP (predicate))
-           {
-             Lisp_Object decoded;
-             Lisp_Object val;
-             struct gcpro gcpro1;
-
-             GCPRO1 (name);
-             decoded = Fexpand_file_name (DECODE_FILE (name), dirname);
-             val = call1 (predicate, decoded);
-             UNGCPRO;
+         GCPRO1 (name);
+         val = call1 (predicate, name);
+         UNGCPRO;
 
-             if (NILP (val))
-               continue;
-           }
+         if (NILP (val))
+           continue;
+       }
 
-         /* Suitably record this match.  */
+      /* Suitably record this match.  */
 
-         matchcount++;
+      matchcount++;
 
-         if (all_flag)
-           {
-             name = DECODE_FILE (name);
-             bestmatch = Fcons (name, bestmatch);
-           }
-         else if (NILP (bestmatch))
+      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)
            {
-             bestmatch = name;
-             bestmatchsize = SCHARS (name);
+             /* 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;
            }
-         else
-           {
-             compare = min (bestmatchsize, len);
-             p1 = SDATA (bestmatch);
-             p2 = (unsigned char *) dp->d_name;
-             matchsize = scmp (p1, p2, compare);
-             if (matchsize < 0)
-               matchsize = compare;
-             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 == len
-                      && 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 == len)
-                       ==
-                       (matchsize + !!directoryp
-                        == SCHARS (bestmatch)))
-                      && !bcmp (p2, SDATA (encoded_file), SCHARS (encoded_file))
-                      && bcmp (p1, SDATA (encoded_file), SCHARS (encoded_file))))
-                   bestmatch = name;
-               }
+         bestmatchsize = matchsize;
+
+         /* If the best completion so far is reduced to the string
+            we're trying to complete, then we already know there's no
+            other completion, so there's no point looking any further.  */
+         if (matchsize <= SCHARS (file)
+             && !includeall /* A future match may allow includeall to 0.  */
+             /* If completion-ignore-case is non-nil, don't
+                short-circuit because we want to find the best
+                possible match *including* case differences.  */
+             && (!completion_ignore_case || matchsize == 0)
+             /* The return value depends on whether it's the sole match.  */
+             && matchcount > 1)
+           break;
 
-             /* If this dirname all matches, see if implicit following
-                slash does too.  */
-             if (directoryp
-                 && compare == matchsize
-                 && bestmatchsize > matchsize
-                 && IS_ANY_SEP (p1[matchsize]))
-               matchsize++;
-             bestmatchsize = matchsize;
-           }
        }
-      /* This closes the directory.  */
-      bestmatch = unbind_to (inner_count, bestmatch);
     }
 
   UNGCPRO;
+  /* This closes the directory.  */
   bestmatch = unbind_to (count, bestmatch);
 
   if (all_flag || NILP (bestmatch))
-    {
-      if (STRINGP (bestmatch))
-       bestmatch = DECODE_FILE (bestmatch);
-      return bestmatch;
-    }
-  if (matchcount == 1 && bestmatchsize == SCHARS (file))
+    return bestmatch;
+  /* Return t if the supplied string is an exact match (counting case);
+     it does not require any change to be made.  */
+  if (matchcount == 1 && !NILP (Fequal (bestmatch, file)))
     return Qt;
   bestmatch = Fsubstring (bestmatch, make_number (0),
                          make_number (bestmatchsize));
-  /* Now that we got the right initial segment of BESTMATCH,
-     decode it from the coding system in use.  */
-  bestmatch = DECODE_FILE (bestmatch);
   return bestmatch;
 }
 
@@ -774,9 +809,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
    else number of chars that match at the beginning.  */
 
 static int
-scmp (s1, s2, len)
-     register unsigned char *s1, *s2;
-     int len;
+scmp (const unsigned char *s1, const unsigned char *s2, int len)
 {
   register int l = len;
 
@@ -797,10 +830,7 @@ scmp (s1, s2, len)
 }
 
 static int
-file_name_completion_stat (dirname, dp, st_addr)
-     Lisp_Object dirname;
-     DIRENTRY *dp;
-     struct stat *st_addr;
+file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_addr)
 {
   int len = NAMLEN (dp);
   int pos = SCHARS (dirname);
@@ -808,7 +838,6 @@ file_name_completion_stat (dirname, dp, st_addr)
   char *fullname = (char *) alloca (len + pos + 2);
 
 #ifdef MSDOS
-#if __DJGPP__ > 1
   /* Some fields of struct stat are *very* expensive to compute on MS-DOS,
      but aren't required here.  Avoid computing the following fields:
      st_inode, st_size and st_nlink for directories, and the execute bits
@@ -817,16 +846,13 @@ file_name_completion_stat (dirname, dp, st_addr)
   unsigned short save_djstat_flags = _djstat_flags;
 
   _djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
-#endif /* __DJGPP__ > 1 */
 #endif /* MSDOS */
 
-  bcopy (SDATA (dirname), fullname, pos);
-#ifndef VMS
+  memcpy (fullname, SDATA (dirname), pos);
   if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
     fullname[pos++] = DIRECTORY_SEP;
-#endif
 
-  bcopy (dp->d_name, fullname + pos, len);
+  memcpy (fullname + pos, dp->d_name, len);
   fullname[pos + len] = 0;
 
 #ifdef S_IFLNK
@@ -839,61 +865,47 @@ file_name_completion_stat (dirname, dp, st_addr)
 #else
   value = stat (fullname, st_addr);
 #ifdef MSDOS
-#if __DJGPP__ > 1
   _djstat_flags = save_djstat_flags;
-#endif /* __DJGPP__ > 1 */
 #endif /* MSDOS */
   return value;
 #endif /* S_IFLNK */
 }
 \f
-#ifdef VMS
-
-DEFUN ("file-name-all-versions", Ffile_name_all_versions,
-       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;
+Lisp_Object
+make_time (time_t time)
 {
-  return file_name_completion (file, directory, 1, 1, Qnil);
+  return Fcons (make_number (time >> 16),
+               Fcons (make_number (time & 0177777), Qnil));
 }
 
-DEFUN ("file-version-limit", Ffile_version_limit, Sfile_version_limit, 1, 1, 0,
-       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;
+static char *
+stat_uname (struct stat *st)
 {
-  Lisp_Object retval;
-  struct FAB    fab;
-  struct RAB    rab;
-  struct XABFHC xabfhc;
-  int status;
+#ifdef WINDOWSNT
+  return st->st_uname;
+#else
+  struct passwd *pw = (struct passwd *) getpwuid (st->st_uid);
 
-  filename = Fexpand_file_name (filename, Qnil);
-  fab      = cc$rms_fab;
-  xabfhc   = cc$rms_xabfhc;
-  fab.fab$l_fna = SDATA (filename);
-  fab.fab$b_fns = strlen (fab.fab$l_fna);
-  fab.fab$l_xab = (char *) &xabfhc;
-  status = sys$open (&fab, 0, 0);
-  if (status != RMS$_NORMAL)   /* Probably non-existent file */
-    return Qnil;
-  sys$close (&fab, 0, 0);
-  if (xabfhc.xab$w_verlimit == 32767)
-    return Qnil;               /* No version limit */
+  if (pw)
+    return pw->pw_name;
   else
-    return make_number (xabfhc.xab$w_verlimit);
+    return NULL;
+#endif
 }
 
-#endif /* VMS */
-\f
-Lisp_Object
-make_time (time)
-     time_t time;
+static char *
+stat_gname (struct stat *st)
 {
-  return Fcons (make_number (time >> 16),
-               Fcons (make_number (time & 0177777), Qnil));
+#ifdef WINDOWSNT
+  return st->st_gname;
+#else
+  struct group *gr = (struct group *) getgrgid (st->st_gid);
+
+  if (gr)
+    return gr->gr_name;
+  else
+    return NULL;
+#endif
 }
 
 DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0,
@@ -901,48 +913,57 @@ DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0,
 Value is nil if specified file cannot be opened.
 
 ID-FORMAT specifies the preferred format of attributes uid and gid (see
-below) - valid values are 'string and 'integer. The latter is the default,
-but we plan to change that, so you should specify a non-nil value for
-ID-FORMAT if you use the returned uid or gid.
+below) - valid values are 'string and 'integer.  The latter is the
+default, but we plan to change that, so you should specify a non-nil value
+for ID-FORMAT if you use the returned uid or gid.
 
 Elements of the attribute list are:
  0. t for directory, string (name linked to) for symbolic link, or nil.
  1. Number of links to file.
- 2. File uid as a string or an integer.  If a string value cannot be
-  looked up, the integer value is returned.
+ 2. File uid as a string or a number.  If a string value cannot be
+  looked up, a numeric value, either an integer or a float, is returned.
  3. File gid, likewise.
  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.
+  (See a note below about access time on FAT-based filesystems.)
+ 5. Last modification time, likewise.  This is the time of the last
+  change to the file's contents.
+ 6. Last status change time, likewise.  This is the time of last change
+  to the file's attributes: owner and group, access mode bits, etc.
  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 if 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.
+10. inode number.  If inode number is larger than what Emacs integer
+  can hold, but still fits into a 32-bit number, this is a cons cell
+  containing two integers: first the high part, then the low 16 bits.
+  If the inode number is wider than 32 bits, this is of the form
+  (HIGH MIDDLE . LOW): first the high 24 bits, then middle 24 bits,
+  and finally the low 16 bits.
+11. Filesystem device number.  If it is larger than what the Emacs
+  integer can hold, this is a cons cell, similar to the inode number.
+
+On most filesystems, the combination of the inode and the device
+number uniquely identifies the file.
 
 On MS-Windows, performance depends on `w32-get-true-file-attributes',
-which see.  */)
-     (filename, id_format)
-     Lisp_Object filename, id_format;
+which see.
+
+On some FAT-based filesystems, only the date of last access is recorded,
+so last access time will always be midnight of that day.  */)
+  (Lisp_Object filename, Lisp_Object id_format)
 {
   Lisp_Object values[12];
   Lisp_Object encoded;
   struct stat s;
-  struct passwd *pw;
-  struct group *gr;
-#if defined (BSD4_2) || defined (BSD4_3)
+#ifdef BSD4_2
   Lisp_Object dirname;
   struct stat sdir;
-#endif
+#endif /* BSD4_2 */
   char modes[10];
   Lisp_Object handler;
   struct gcpro gcpro1;
-  EMACS_INT uid, gid, ino;
+  char *uname = NULL, *gname = NULL;
 
   filename = Fexpand_file_name (filename, Qnil);
 
@@ -977,43 +998,27 @@ which see.  */)
 #endif
     }
   values[1] = make_number (s.st_nlink);
-  /* When make_fixnum_or_float is called below with types that are
-     shorter than an int (e.g., `short'), GCC whines about comparison
-     being always false due to limited range of data type.  Fix by
-     copying s.st_uid and s.st_gid into int variables.  */
-#ifdef WINDOWSNT
-  /* Windows uses signed short for the uid and gid in the stat structure,
-     but we use an int for getuid (limited to the range 0-60000).
-     So users with uid > 32767 need their uid patched back here.  */ 
-  uid = (unsigned short) s.st_uid;
-  gid = (unsigned short) s.st_gid;
-#else
-  uid = s.st_uid;
-  gid = s.st_gid;
-#endif
-  if (NILP (id_format) || EQ (id_format, Qinteger))
-    {
-      values[2] = make_fixnum_or_float (uid);
-      values[3] = make_fixnum_or_float (gid);
-    }
-  else
+
+  if (!(NILP (id_format) || EQ (id_format, Qinteger)))
     {
       BLOCK_INPUT;
-      pw = (struct passwd *) getpwuid (uid);
-      values[2] = (pw ? build_string (pw->pw_name)
-                  : make_fixnum_or_float (uid));
-      gr = (struct group *) getgrgid (gid);
-      values[3] = (gr ? build_string (gr->gr_name)
-                  : make_fixnum_or_float (gid));
+      uname = stat_uname (&s);
+      gname = stat_gname (&s);
       UNBLOCK_INPUT;
     }
+  if (uname)
+    values[2] = DECODE_SYSTEM (build_string (uname));
+  else
+    values[2] = make_fixnum_or_float (s.st_uid);
+  if (gname)
+    values[3] = DECODE_SYSTEM (build_string (gname));
+  else
+    values[3] = make_fixnum_or_float (s.st_gid);
+
   values[4] = make_time (s.st_atime);
   values[5] = make_time (s.st_mtime);
   values[6] = make_time (s.st_ctime);
-  values[7] = make_number (s.st_size);
-  /* If the size is out of range for an integer, return a float.  */
-  if (XINT (values[7]) != s.st_size)
-    values[7] = make_float ((double)s.st_size);
+  values[7] = make_fixnum_or_float (s.st_size);
   /* If the size is negative, and its type is long, convert it back to
      positive.  */
   if (s.st_size < 0 && sizeof (s.st_size) == sizeof (long))
@@ -1021,43 +1026,42 @@ which see.  */)
 
   filemodestring (&s, modes);
   values[8] = make_string (modes, 10);
-#if defined (BSD4_2) || defined (BSD4_3) /* file gid will be dir gid */
+#ifdef BSD4_2 /* file gid will be dir gid */
   dirname = Ffile_name_directory (filename);
   if (! NILP (dirname))
     encoded = ENCODE_FILE (dirname);
   if (! NILP (dirname) && stat (SDATA (encoded), &sdir) == 0)
-    values[9] = (sdir.st_gid != gid) ? Qt : Qnil;
+    values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil;
   else                                 /* if we can't tell, assume worst */
     values[9] = Qt;
 #else                                  /* file gid will be egid */
-  values[9] = (gid != getegid ()) ? Qt : Qnil;
-#endif /* BSD4_2 (or BSD4_3) */
-  /* Shut up GCC warnings in FIXNUM_OVERFLOW_P below.  */
-#ifdef WINDOWSNT
-  {
-    /* The bit-shuffling we do in w32.c:stat can turn on the MSB, which
-       will produce negative inode numbers.  People don't like that, so
-       force a positive inode instead.  */
-    unsigned short tem = s.st_ino;
-    ino = tem;
-  }
-#else
-  ino = s.st_ino;
-#endif
-  if (FIXNUM_OVERFLOW_P (ino))
+  values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
+#endif /* not BSD4_2 */
+  if (!FIXNUM_OVERFLOW_P (s.st_ino))
+    /* Keep the most common cases as integers.  */
+    values[10] = make_number (s.st_ino);
+  else if (!FIXNUM_OVERFLOW_P (s.st_ino >> 16))
     /* To allow inode numbers larger than VALBITS, separate the bottom
        16 bits.  */
-    values[10] = Fcons (make_number (ino >> 16),
-                       make_number (ino & 0xffff));
+    values[10] = Fcons (make_number ((EMACS_INT)(s.st_ino >> 16)),
+                       make_number ((EMACS_INT)(s.st_ino & 0xffff)));
   else
-    /* But keep the most common cases as integers.  */
-    values[10] = make_number (ino);
-
-  /* Likewise for device, but don't let it become negative.  We used
-     to use FIXNUM_OVERFLOW_P here, but that won't catch large
-     positive numbers such as 0xFFEEDDCC.  */
-  if ((EMACS_INT)s.st_dev < 0
-      || (EMACS_INT)s.st_dev > MOST_POSITIVE_FIXNUM)
+    {
+      /* To allow inode numbers beyond 32 bits, separate into 2 24-bit
+        high parts and a 16-bit bottom part.
+        The code on the next line avoids a compiler warning on
+        systems where st_ino is 32 bit wide. (bug#766).  */
+      EMACS_INT high_ino = s.st_ino >> 31 >> 1;
+      EMACS_INT low_ino  = s.st_ino & 0xffffffff;
+
+      values[10] = Fcons (make_number (high_ino >> 8),
+                         Fcons (make_number (((high_ino & 0xff) << 16)
+                                             + (low_ino >> 16)),
+                                make_number (low_ino & 0xffff)));
+    }
+
+  /* Likewise for device.  */
+  if (FIXNUM_OVERFLOW_P (s.st_dev))
     values[11] = Fcons (make_number (s.st_dev >> 16),
                        make_number (s.st_dev & 0xffff));
   else
@@ -1069,21 +1073,21 @@ which see.  */)
 DEFUN ("file-attributes-lessp", Ffile_attributes_lessp, Sfile_attributes_lessp, 2, 2, 0,
        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;
+  (Lisp_Object f1, Lisp_Object f2)
 {
   return Fstring_lessp (Fcar (f1), Fcar (f2));
 }
 \f
 void
-syms_of_dired ()
+syms_of_dired (void)
 {
-  Qdirectory_files = intern ("directory-files");
-  Qdirectory_files_and_attributes = intern ("directory-files-and-attributes");
-  Qfile_name_completion = intern ("file-name-completion");
-  Qfile_name_all_completions = intern ("file-name-all-completions");
-  Qfile_attributes = intern ("file-attributes");
-  Qfile_attributes_lessp = intern ("file-attributes-lessp");
+  Qdirectory_files = intern_c_string ("directory-files");
+  Qdirectory_files_and_attributes = intern_c_string ("directory-files-and-attributes");
+  Qfile_name_completion = intern_c_string ("file-name-completion");
+  Qfile_name_all_completions = intern_c_string ("file-name-all-completions");
+  Qfile_attributes = intern_c_string ("file-attributes");
+  Qfile_attributes_lessp = intern_c_string ("file-attributes-lessp");
+  Qdefault_directory = intern_c_string ("default-directory");
 
   staticpro (&Qdirectory_files);
   staticpro (&Qdirectory_files_and_attributes);
@@ -1091,14 +1095,11 @@ syms_of_dired ()
   staticpro (&Qfile_name_all_completions);
   staticpro (&Qfile_attributes);
   staticpro (&Qfile_attributes_lessp);
+  staticpro (&Qdefault_directory);
 
   defsubr (&Sdirectory_files);
   defsubr (&Sdirectory_files_and_attributes);
   defsubr (&Sfile_name_completion);
-#ifdef VMS
-  defsubr (&Sfile_name_all_versions);
-  defsubr (&Sfile_version_limit);
-#endif /* VMS */
   defsubr (&Sfile_name_all_completions);
   defsubr (&Sfile_attributes);
   defsubr (&Sfile_attributes_lessp);