Auto-generate EXFUN using make-docfile
[bpt/emacs.git] / src / dired.c
index 22fb932..48d3da1 100644 (file)
@@ -1,6 +1,5 @@
 /* Lisp functions for making directory listings.
-   Copyright (C) 1985, 1986, 1993, 1994, 1999, 2000, 2001, 2002, 2003,
-                 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 1985-1986, 1993-1994, 1999-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -31,10 +30,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <grp.h>
 
 #include <errno.h>
-
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif
 
 /* The d_nameln member of a struct dirent includes the '\0' character
    on some systems, but not on others.  What's worse, you can't tell
@@ -48,29 +44,25 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    Since applying strlen to the name always works, we'll just do that.  */
 #define NAMLEN(p) strlen (p->d_name)
 
-#ifdef SYSV_SYSTEM_DIR
+#ifdef HAVE_DIRENT_H
 
 #include <dirent.h>
 #define DIRENTRY struct dirent
 
-#else /* not SYSV_SYSTEM_DIR */
+#else /* not HAVE_DIRENT_H */
 
-#ifdef MSDOS
-#include <dirent.h>
-#else
 #include <sys/dir.h>
-#endif
-
 #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 */
+#endif /* HAVE_DIRENT_H */
+
+#include <filemode.h>
+#include <stat-time.h>
 
 #ifdef MSDOS
 #define DIRENTRY_NONEMPTY(p) ((p)->d_name[0] != 0)
@@ -80,41 +72,22 @@ extern struct direct *readdir ();
 
 #include "lisp.h"
 #include "systime.h"
+#include "character.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 ();
-
-/* From filemode.c.  Can't go in Lisp.h because of `stat'.  */
-extern void filemodestring P_ ((struct stat *, char *));
-
-/* if system does not have symbolic links, it does not have lstat.
-   In that case, use ordinary stat instead.  */
-
-#ifndef S_IFLNK
-#define lstat stat
-#endif
+static Lisp_Object Qdirectory_files;
+static Lisp_Object Qdirectory_files_and_attributes;
+static Lisp_Object Qfile_name_completion;
+static Lisp_Object Qfile_name_all_completions;
+static Lisp_Object Qfile_attributes;
+static Lisp_Object Qfile_attributes_lessp;
 
-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;
-Lisp_Object Qdirectory_files_and_attributes;
-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));
+static ptrdiff_t scmp (const char *, const char *, ptrdiff_t);
 \f
 #ifdef WINDOWSNT
 Lisp_Object
@@ -125,9 +98,8 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
 }
 #endif
 
-Lisp_Object
-directory_files_internal_unwind (dh)
-     Lisp_Object dh;
+static Lisp_Object
+directory_files_internal_unwind (Lisp_Object dh)
 {
   DIR *d = (DIR *) XSAVE_VALUE (dh)->pointer;
   BLOCK_INPUT;
@@ -142,17 +114,14 @@ 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;
+  ptrdiff_t directory_nbytes;
   Lisp_Object list, dirfilename, encoded_directory;
   struct re_pattern_buffer *bufp = NULL;
   int needsep = 0;
-  int count = SPECPDL_INDEX ();
+  ptrdiff_t count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
   DIRENTRY *dp;
 #ifdef WINDOWSNT
@@ -177,7 +146,7 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
 # ifdef WINDOWSNT
       /* Windows users want case-insensitive wildcards.  */
       bufp = compile_pattern (match, 0,
-                             buffer_defaults.case_canon_table, 0, 1);
+                             BVAR (&buffer_defaults, case_canon_table), 0, 1);
 # else /* !WINDOWSNT */
       bufp = compile_pattern (match, 0, Qnil, 0, 1);
 # endif         /* !WINDOWSNT */
@@ -195,7 +164,7 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
      which might compile a new regexp until we're done with the loop!  */
 
   BLOCK_INPUT;
-  d = opendir (SDATA (dirfilename));
+  d = opendir (SSDATA (dirfilename));
   UNBLOCK_INPUT;
   if (d == NULL)
     report_file_error ("Opening directory", Fcons (directory, Qnil));
@@ -209,7 +178,6 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
 #ifdef WINDOWSNT
   if (attrs)
     {
-      extern Lisp_Object Qlocal;
       extern int is_slow_fs (const char *);
 
       /* Do this only once to avoid doing it (in w32.c:stat) for each
@@ -258,7 +226,7 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
 
       if (DIRENTRY_NONEMPTY (dp))
        {
-         int len;
+         ptrdiff_t len;
          int wanted = 0;
          Lisp_Object name, finalname;
          struct gcpro gcpro1, gcpro2;
@@ -278,7 +246,7 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
          QUIT;
 
          if (NILP (match)
-             || (0 <= re_search (bufp, SDATA (name), len, 0, len, 0)))
+             || (0 <= re_search (bufp, SSDATA (name), len, 0, len, 0)))
            wanted = 1;
 
          immediate_quit = 0;
@@ -288,19 +256,18 @@ directory_files_internal (directory, full, match, nosort, attrs, id_format)
              if (!NILP (full))
                {
                  Lisp_Object fullname;
-                 int nbytes = len + directory_nbytes + needsep;
-                 int nchars;
+                 ptrdiff_t nbytes = len + directory_nbytes + needsep;
+                 ptrdiff_t 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);
 
@@ -370,8 +337,7 @@ 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);
@@ -399,8 +365,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);
@@ -416,7 +381,9 @@ which see.  */)
 }
 
 \f
-Lisp_Object file_name_completion ();
+static 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,
@@ -431,10 +398,10 @@ 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;
+  directory = Fexpand_file_name (directory, Qnil);
 
   /* If the directory name has special constructs in it,
      call the corresponding file handler.  */
@@ -455,10 +422,10 @@ 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;
+  directory = Fexpand_file_name (directory, Qnil);
 
   /* If the directory name has special constructs in it,
      call the corresponding file handler.  */
@@ -475,17 +442,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 ();
-Lisp_Object Qdefault_directory;
+static int file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_addr);
+static 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;
+static Lisp_Object
+file_name_completion (Lisp_Object file, Lisp_Object dirname, int all_flag, int ver_flag, Lisp_Object predicate)
 {
   DIR *d;
-  int bestmatchsize = 0;
+  ptrdiff_t 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
@@ -499,7 +463,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
      well as "." and "..".  Until shown otherwise, assume we can't exclude
      anything.  */
   int includeall = 1;
-  int count = SPECPDL_INDEX ();
+  ptrdiff_t count = SPECPDL_INDEX ();
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
 
   elt = Qnil;
@@ -512,7 +476,6 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
   bestmatch = Qnil;
   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
@@ -526,7 +489,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
   encoded_dir = ENCODE_FILE (dirname);
 
   BLOCK_INPUT;
-  d = opendir (SDATA (Fdirectory_file_name (encoded_dir)));
+  d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
   UNBLOCK_INPUT;
   if (!d)
     report_file_error ("Opening directory", Fcons (dirname, Qnil));
@@ -539,7 +502,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
   while (1)
     {
       DIRENTRY *dp;
-      int len;
+      ptrdiff_t len;
       int canexclude = 0;
 
       errno = 0;
@@ -561,21 +524,21 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
       QUIT;
       if (! DIRENTRY_NONEMPTY (dp)
          || len < SCHARS (encoded_file)
-         || 0 <= scmp (dp->d_name, SDATA (encoded_file),
+         || 0 <= scmp (dp->d_name, SSDATA (encoded_file),
                        SCHARS (encoded_file)))
        continue;
 
       if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
        continue;
 
-      directoryp = ((st.st_mode & S_IFMT) == S_IFDIR);
+      directoryp = S_ISDIR (st.st_mode);
       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;
+         ptrdiff_t skip;
 
 #if 0 /* FIXME: The `scmp' call compares an encoded and a decoded string. */
          /* If this entry matches the current bestmatch, the only
@@ -586,7 +549,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
              && matchcount > 1
              && !includeall /* This match may allow includeall to 0.  */
              && len >= bestmatchsize
-             && 0 > scmp (dp->d_name, SDATA (bestmatch), bestmatchsize))
+             && 0 > scmp (dp->d_name, SSDATA (bestmatch), bestmatchsize))
            continue;
 #endif
 
@@ -605,8 +568,8 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
                for (tem = Vcompletion_ignored_extensions;
                     CONSP (tem); tem = XCDR (tem))
                  {
-                   int elt_len;
-                   unsigned char *p1;
+                   ptrdiff_t elt_len;
+                   char *p1;
 
                    elt = XCAR (tem);
                    if (!STRINGP (elt))
@@ -617,7 +580,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
                    elt_len = SCHARS (elt) - 1; /* -1 for trailing / */
                    if (elt_len <= 0)
                      continue;
-                   p1 = SDATA (elt);
+                   p1 = SSDATA (elt);
                    if (p1[elt_len] != '/')
                      continue;
                    skip = len - elt_len;
@@ -647,7 +610,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
                    if (skip < 0) continue;
 
                    if (0 <= scmp (dp->d_name + skip,
-                                  SDATA (elt),
+                                  SSDATA (elt),
                                   SCHARS (elt)))
                      continue;
                    break;
@@ -665,7 +628,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
 
          if (includeall && !canexclude)
            { /* If we have one non-excludable file, we want to exclude the
-                excudable files.  */
+                excludable files.  */
              includeall = 0;
              /* Throw away any previous excludable match found.  */
              bestmatch = Qnil;
@@ -680,8 +643,6 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
 
       {
        Lisp_Object regexps;
-       Lisp_Object zero;
-       XSETFASTINT (zero, 0);
 
        /* Ignore this element if it fails to match all the regexps.  */
        if (completion_ignore_case)
@@ -724,7 +685,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
 
       /* Suitably record this match.  */
 
-      matchcount++;
+      matchcount += matchcount <= 1;
 
       if (all_flag)
        bestmatch = Fcons (name, bestmatch);
@@ -737,17 +698,17 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
        {
          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
+         ptrdiff_t compare = min (bestmatchsize, SCHARS (name));
+         Lisp_Object cmp
            = 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);
+         ptrdiff_t matchsize
+           = (EQ (cmp, Qt)     ? compare
+              : XINT (cmp) < 0 ? - XINT (cmp) - 1
+              :                  XINT (cmp) - 1);
 
          if (completion_ignore_case)
            {
@@ -771,18 +732,18 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
                  (((matchsize == SCHARS (name))
                    ==
                    (matchsize + !!directoryp == SCHARS (bestmatch)))
-                  && (tem = Fcompare_strings (name, zero,
+                  && (cmp = Fcompare_strings (name, zero,
                                               make_number (SCHARS (file)),
                                               file, zero,
                                               Qnil,
                                               Qnil),
-                      EQ (Qt, tem))
-                  && (tem = Fcompare_strings (bestmatch, zero,
+                      EQ (Qt, cmp))
+                  && (cmp = Fcompare_strings (bestmatch, zero,
                                               make_number (SCHARS (file)),
                                               file, zero,
                                               Qnil,
                                               Qnil),
-                      ! EQ (Qt, tem))))
+                      ! EQ (Qt, cmp))))
                bestmatch = name;
            }
          bestmatchsize = matchsize;
@@ -823,16 +784,16 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate)
    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;
+static ptrdiff_t
+scmp (const char *s1, const char *s2, ptrdiff_t len)
 {
-  register int l = len;
+  register ptrdiff_t l = len;
 
   if (completion_ignore_case)
     {
-      while (l && DOWNCASE (*s1++) == DOWNCASE (*s2++))
+      while (l
+            && (downcase ((unsigned char) *s1++)
+                == downcase ((unsigned char) *s2++)))
        l--;
     }
   else
@@ -847,18 +808,16 @@ 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);
+  ptrdiff_t len = NAMLEN (dp);
+  ptrdiff_t pos = SCHARS (dirname);
   int value;
-  char *fullname = (char *) alloca (len + pos + 2);
+  char *fullname;
+  USE_SAFE_ALLOCA;
+  SAFE_ALLOCA (fullname, char *, 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
@@ -867,42 +826,28 @@ 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);
+  memcpy (fullname, SDATA (dirname), pos);
   if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
     fullname[pos++] = DIRECTORY_SEP;
 
-  bcopy (dp->d_name, fullname + pos, len);
+  memcpy (fullname + pos, dp->d_name, len);
   fullname[pos + len] = 0;
 
-#ifdef S_IFLNK
   /* We want to return success if a link points to a nonexistent file,
      but we want to return the status for what the link points to,
      in case it is a directory.  */
   value = lstat (fullname, st_addr);
-  stat (fullname, st_addr);
-  return value;
-#else
-  value = stat (fullname, st_addr);
+  if (value == 0 && S_ISLNK (st_addr->st_mode))
+    stat (fullname, st_addr);
 #ifdef MSDOS
-#if __DJGPP__ > 1
   _djstat_flags = save_djstat_flags;
-#endif /* __DJGPP__ > 1 */
 #endif /* MSDOS */
+  SAFE_FREE ();
   return value;
-#endif /* S_IFLNK */
 }
 \f
-Lisp_Object
-make_time (time)
-     time_t time;
-{
-  return Fcons (make_number (time >> 16),
-               Fcons (make_number (time & 0177777), Qnil));
-}
-
 static char *
 stat_uname (struct stat *st)
 {
@@ -948,8 +893,8 @@ Elements of the attribute list are:
  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.
+ 4. Last access time, as a list of integers (HIGH LOW USEC PSEC) in the
+  same style as (current-time).
   (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.
@@ -959,11 +904,10 @@ Elements of the attribute list are:
   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 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,
+10. inode number.  If it is larger than what an Emacs integer can hold,
+  this is of the form (HIGH . LOW): first the high bits, then the low 16 bits.
+  If even HIGH is too large for an Emacs integer, this is instead of the form
+  (HIGH MIDDLE . LOW): first the high bits, then the 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.
@@ -976,17 +920,20 @@ 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.  */)
-     (filename, id_format)
-     Lisp_Object filename, id_format;
+  (Lisp_Object filename, Lisp_Object id_format)
 {
   Lisp_Object values[12];
   Lisp_Object encoded;
   struct stat s;
-#if defined (BSD4_2) || defined (BSD4_3)
+#ifdef BSD4_2
   Lisp_Object dirname;
   struct stat sdir;
-#endif
-  char modes[10];
+#endif /* BSD4_2 */
+
+  /* An array to hold the mode string generated by filemodestring,
+     including its terminating space and null byte.  */
+  char modes[sizeof "-rwxr-xr-x "];
+
   Lisp_Object handler;
   struct gcpro gcpro1;
   char *uname = NULL, *gname = NULL;
@@ -1009,20 +956,11 @@ so last access time will always be midnight of that day.  */)
   encoded = ENCODE_FILE (filename);
   UNGCPRO;
 
-  if (lstat (SDATA (encoded), &s) < 0)
+  if (lstat (SSDATA (encoded), &s) < 0)
     return Qnil;
 
-  switch (s.st_mode & S_IFMT)
-    {
-    default:
-      values[0] = Qnil; break;
-    case S_IFDIR:
-      values[0] = Qt; break;
-#ifdef S_IFLNK
-    case S_IFLNK:
-      values[0] = Ffile_symlink_p (filename); break;
-#endif
-    }
+  values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename)
+              : S_ISDIR (s.st_mode) ? Qt : Qnil);
   values[1] = make_number (s.st_nlink);
 
   if (!(NILP (id_format) || EQ (id_format, Qinteger)))
@@ -1041,18 +979,21 @@ so last access time will always be midnight of that day.  */)
   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_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))
-    values[7] = make_float ((double) ((unsigned long) s.st_size));
+  values[4] = make_lisp_time (get_stat_atime (&s));
+  values[5] = make_lisp_time (get_stat_mtime (&s));
+  values[6] = make_lisp_time (get_stat_ctime (&s));
+
+  /* If the file size is a 4-byte type, assume that files of sizes in
+     the 2-4 GiB range wrap around to negative values, as this is a
+     common bug on older 32-bit platforms.  */
+  if (sizeof (s.st_size) == 4)
+    values[7] = make_fixnum_or_float (s.st_size & 0xffffffffu);
+  else
+    values[7] = make_fixnum_or_float (s.st_size);
 
   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);
@@ -1062,67 +1003,70 @@ so last access time will always be midnight of that day.  */)
     values[9] = Qt;
 #else                                  /* file gid will be egid */
   values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
-#endif /* BSD4_2 (or BSD4_3) */
-  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 ((EMACS_INT)(s.st_ino >> 16)),
-                       make_number ((EMACS_INT)(s.st_ino & 0xffff)));
-  else
-    {
-      /* 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)));
-    }
+#endif /* not BSD4_2 */
+  values[10] = INTEGER_TO_CONS (s.st_ino);
+  values[11] = INTEGER_TO_CONS (s.st_dev);
 
-  /* 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
-    values[11] = make_number (s.st_dev);
-
-  return Flist (sizeof(values) / sizeof(values[0]), values);
+  return Flist (sizeof (values) / sizeof (values[0]), values);
 }
 
 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
+
+DEFUN ("system-users", Fsystem_users, Ssystem_users, 0, 0, 0,
+       doc: /* Return a list of user names currently registered in the system.
+If we don't know how to determine that on this platform, just
+return a list with one element, taken from `user-real-login-name'.  */)
+     (void)
+{
+  Lisp_Object users = Qnil;
+#if defined HAVE_GETPWENT && defined HAVE_ENDPWENT
+  struct passwd *pw;
+
+  while ((pw = getpwent ()))
+    users = Fcons (DECODE_SYSTEM (build_string (pw->pw_name)), users);
+
+  endpwent ();
+#endif
+  if (EQ (users, Qnil))
+    /* At least current user is always known. */
+    users = Fcons (Vuser_real_login_name, Qnil);
+  return users;
+}
+
+DEFUN ("system-groups", Fsystem_groups, Ssystem_groups, 0, 0, 0,
+       doc: /* Return a list of user group names currently registered in the system.
+The value may be nil if not supported on this platform.  */)
+     (void)
+{
+  Lisp_Object groups = Qnil;
+#if defined HAVE_GETGRENT && defined HAVE_ENDGRENT
+  struct group *gr;
+
+  while ((gr = getgrent ()))
+    groups = Fcons (DECODE_SYSTEM (build_string (gr->gr_name)), groups);
+
+  endgrent ();
+#endif
+  return groups;
+}
+
 void
-syms_of_dired ()
+syms_of_dired (void)
 {
-  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);
-  staticpro (&Qfile_name_completion);
-  staticpro (&Qfile_name_all_completions);
-  staticpro (&Qfile_attributes);
-  staticpro (&Qfile_attributes_lessp);
-  staticpro (&Qdefault_directory);
+  DEFSYM (Qdirectory_files, "directory-files");
+  DEFSYM (Qdirectory_files_and_attributes, "directory-files-and-attributes");
+  DEFSYM (Qfile_name_completion, "file-name-completion");
+  DEFSYM (Qfile_name_all_completions, "file-name-all-completions");
+  DEFSYM (Qfile_attributes, "file-attributes");
+  DEFSYM (Qfile_attributes_lessp, "file-attributes-lessp");
+  DEFSYM (Qdefault_directory, "default-directory");
 
   defsubr (&Sdirectory_files);
   defsubr (&Sdirectory_files_and_attributes);
@@ -1130,8 +1074,10 @@ syms_of_dired ()
   defsubr (&Sfile_name_all_completions);
   defsubr (&Sfile_attributes);
   defsubr (&Sfile_attributes_lessp);
+  defsubr (&Ssystem_users);
+  defsubr (&Ssystem_groups);
 
-  DEFVAR_LISP ("completion-ignored-extensions", &Vcompletion_ignored_extensions,
+  DEFVAR_LISP ("completion-ignored-extensions", Vcompletion_ignored_extensions,
               doc: /* Completion ignores file names ending in any string in this list.
 It does not ignore them if all possible completions end in one of
 these strings or when displaying a list of completions.
@@ -1139,6 +1085,3 @@ It ignores directory names if they match any string in this list which
 ends in a slash.  */);
   Vcompletion_ignored_extensions = Qnil;
 }
-
-/* arch-tag: 1ac8deca-4d8f-4d41-ade9-089154d98c03
-   (do not change this comment) */