/* Lisp functions for making directory listings.
- Copyright (C) 1985-1986, 1993-1994, 1999-2012 Free Software Foundation, Inc.
+ Copyright (C) 1985-1986, 1993-1994, 1999-2013 Free Software
+ Foundation, Inc.
This file is part of GNU Emacs.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <setjmp.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#include <grp.h>
#include <errno.h>
+#include <fcntl.h>
#include <unistd.h>
-/* 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
- at compile-time which one it will be, since it really depends on
- the sort of system providing the filesystem you're reading from,
- not the system you are running on. Paul Eggert
- <eggert@bi.twinsun.com> says this occurs when Emacs is running on a
- SunOS 4.1.2 host, reading a directory that is remote-mounted from a
- Solaris 2.1 host and is in a native Solaris 2.1 filesystem.
-
- Since applying strlen to the name always works, we'll just do that. */
-#define NAMLEN(p) strlen (p->d_name)
-
-#ifdef HAVE_DIRENT_H
-
#include <dirent.h>
-#define DIRENTRY struct dirent
-
-#else /* not HAVE_DIRENT_H */
-
-#include <sys/dir.h>
-#include <sys/stat.h>
-
-#define DIRENTRY struct direct
-
-extern DIR *opendir (char *);
-extern struct direct *readdir (DIR *);
-
-#endif /* HAVE_DIRENT_H */
-
#include <filemode.h>
-
-#ifdef MSDOS
-#define DIRENTRY_NONEMPTY(p) ((p)->d_name[0] != 0)
-#else
-#define DIRENTRY_NONEMPTY(p) ((p)->d_ino)
-#endif
+#include <stat-time.h>
#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"
static Lisp_Object Qfile_attributes;
static Lisp_Object Qfile_attributes_lessp;
-static int scmp (const char *, const char *, int);
-static Lisp_Object Ffile_attributes (Lisp_Object, Lisp_Object);
+static ptrdiff_t scmp (const char *, const char *, ptrdiff_t);
+static Lisp_Object file_attributes (int, char const *, Lisp_Object);
\f
+/* Return the number of bytes in DP's name. */
+static ptrdiff_t
+dirent_namelen (struct dirent *dp)
+{
+#ifdef _D_EXACT_NAMLEN
+ return _D_EXACT_NAMLEN (dp);
+#else
+ return strlen (dp->d_name);
+#endif
+}
+
+static DIR *
+open_directory (char const *name, int *fdp)
+{
+ DIR *d;
+ int fd, opendir_errno;
+
+ block_input ();
+
+#ifdef DOS_NT
+ /* Directories cannot be opened. The emulation assumes that any
+ file descriptor other than AT_FDCWD corresponds to the most
+ recently opened directory. This hack is good enough for Emacs. */
+ fd = 0;
+ d = opendir (name);
+ opendir_errno = errno;
+#else
+ fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
+ if (fd < 0)
+ {
+ opendir_errno = errno;
+ d = 0;
+ }
+ else
+ {
+ d = fdopendir (fd);
+ opendir_errno = errno;
+ if (! d)
+ close (fd);
+ }
+#endif
+
+ unblock_input ();
+
+ *fdp = fd;
+ errno = opendir_errno;
+ return d;
+}
+
#ifdef WINDOWSNT
Lisp_Object
directory_files_internal_w32_unwind (Lisp_Object arg)
static Lisp_Object
directory_files_internal_unwind (Lisp_Object dh)
{
- DIR *d = (DIR *) XSAVE_VALUE (dh)->pointer;
- BLOCK_INPUT;
+ DIR *d = XSAVE_POINTER (dh, 0);
+ block_input ();
closedir (d);
- UNBLOCK_INPUT;
+ unblock_input ();
return Qnil;
}
/* 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.
+ If not ATTRS, return a list of directory filenames;
+ if ATTRS, return a list of directory filenames and their attributes.
In the latter case, ID_FORMAT is passed to Ffile_attributes. */
Lisp_Object
-directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, int attrs, Lisp_Object id_format)
+directory_files_internal (Lisp_Object directory, Lisp_Object full,
+ Lisp_Object match, Lisp_Object nosort, bool attrs,
+ Lisp_Object id_format)
{
DIR *d;
- int directory_nbytes;
+ int fd;
+ ptrdiff_t directory_nbytes;
Lisp_Object list, dirfilename, encoded_directory;
struct re_pattern_buffer *bufp = NULL;
- int needsep = 0;
- int count = SPECPDL_INDEX ();
+ bool needsep = 0;
+ ptrdiff_t count = SPECPDL_INDEX ();
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
- DIRENTRY *dp;
+ struct dirent *dp;
#ifdef WINDOWSNT
Lisp_Object w32_save = Qnil;
#endif
/* 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! */
- BLOCK_INPUT;
- d = opendir (SSDATA (dirfilename));
- UNBLOCK_INPUT;
+ d = open_directory (SSDATA (dirfilename), &fd);
if (d == NULL)
report_file_error ("Opening directory", Fcons (directory, Qnil));
file-attributes on filenames, both of which can throw, so we must
do a proper unwind-protect. */
record_unwind_protect (directory_files_internal_unwind,
- make_save_value (d, 0));
+ make_save_pointer (d));
#ifdef WINDOWSNT
if (attrs)
/* Loop reading blocks until EOF or error. */
for (;;)
{
+ ptrdiff_t len;
+ bool wanted = 0;
+ Lisp_Object name, finalname;
+ struct gcpro gcpro1, gcpro2;
+
errno = 0;
dp = readdir (d);
+ if (!dp)
+ {
+ if (errno == EAGAIN || errno == EINTR)
+ {
+ QUIT;
+ continue;
+ }
+ break;
+ }
- if (dp == NULL && (0
-#ifdef EAGAIN
- || errno == EAGAIN
-#endif
-#ifdef EINTR
- || errno == EINTR
-#endif
- ))
- { QUIT; continue; }
+ len = dirent_namelen (dp);
+ name = finalname = make_unibyte_string (dp->d_name, len);
+ GCPRO2 (finalname, name);
+
+ /* Note: DECODE_FILE can GC; it should protect its argument,
+ though. */
+ name = DECODE_FILE (name);
+ len = SBYTES (name);
+
+ /* Now that we have unwind_protect in place, we might as well
+ allow matching to be interrupted. */
+ immediate_quit = 1;
+ QUIT;
- if (dp == NULL)
- break;
+ if (NILP (match)
+ || (0 <= re_search (bufp, SSDATA (name), len, 0, len, 0)))
+ wanted = 1;
- if (DIRENTRY_NONEMPTY (dp))
+ immediate_quit = 0;
+
+ if (wanted)
{
- int len;
- int wanted = 0;
- Lisp_Object name, finalname;
- struct gcpro gcpro1, gcpro2;
+ if (!NILP (full))
+ {
+ Lisp_Object fullname;
+ ptrdiff_t nbytes = len + directory_nbytes + needsep;
+ ptrdiff_t nchars;
- len = NAMLEN (dp);
- name = finalname = make_unibyte_string (dp->d_name, len);
- GCPRO2 (finalname, name);
+ fullname = make_uninit_multibyte_string (nbytes, nbytes);
+ memcpy (SDATA (fullname), SDATA (directory),
+ directory_nbytes);
- /* Note: DECODE_FILE can GC; it should protect its argument,
- though. */
- name = DECODE_FILE (name);
- len = SBYTES (name);
+ if (needsep)
+ SSET (fullname, directory_nbytes, DIRECTORY_SEP);
- /* Now that we have unwind_protect in place, we might as well
- allow matching to be interrupted. */
- immediate_quit = 1;
- QUIT;
+ memcpy (SDATA (fullname) + directory_nbytes + needsep,
+ SDATA (name), len);
- if (NILP (match)
- || (0 <= re_search (bufp, SSDATA (name), len, 0, len, 0)))
- wanted = 1;
+ nchars = chars_in_text (SDATA (fullname), nbytes);
- immediate_quit = 0;
+ /* Some bug somewhere. */
+ if (nchars > nbytes)
+ emacs_abort ();
- if (wanted)
- {
- if (!NILP (full))
- {
- Lisp_Object fullname;
- int nbytes = len + directory_nbytes + needsep;
- int nchars;
-
- fullname = make_uninit_multibyte_string (nbytes, nbytes);
- memcpy (SDATA (fullname), SDATA (directory),
- directory_nbytes);
-
- if (needsep)
- SSET (fullname, directory_nbytes, DIRECTORY_SEP);
-
- memcpy (SDATA (fullname) + directory_nbytes + needsep,
- SDATA (name), len);
-
- nchars = chars_in_text (SDATA (fullname), nbytes);
-
- /* Some bug somewhere. */
- if (nchars > nbytes)
- abort ();
-
- STRING_SET_CHARS (fullname, nchars);
- if (nchars == nbytes)
- STRING_SET_UNIBYTE (fullname);
-
- finalname = fullname;
- }
- else
- finalname = name;
-
- if (attrs)
- {
- /* Construct an expanded filename for the directory entry.
- Use the decoded names for input to Ffile_attributes. */
- Lisp_Object decoded_fullname, fileattrs;
- struct gcpro gcpro1, gcpro2;
-
- decoded_fullname = fileattrs = Qnil;
- GCPRO2 (decoded_fullname, fileattrs);
-
- /* Both Fexpand_file_name and Ffile_attributes can GC. */
- decoded_fullname = Fexpand_file_name (name, directory);
- fileattrs = Ffile_attributes (decoded_fullname, id_format);
-
- list = Fcons (Fcons (finalname, fileattrs), list);
- UNGCPRO;
- }
- else
- list = Fcons (finalname, list);
+ STRING_SET_CHARS (fullname, nchars);
+ if (nchars == nbytes)
+ STRING_SET_UNIBYTE (fullname);
+
+ finalname = fullname;
}
+ else
+ finalname = name;
- UNGCPRO;
+ if (attrs)
+ {
+ Lisp_Object fileattrs
+ = file_attributes (fd, dp->d_name, id_format);
+ list = Fcons (Fcons (finalname, fileattrs), list);
+ }
+ else
+ list = Fcons (finalname, list);
}
+
+ UNGCPRO;
}
- BLOCK_INPUT;
+ block_input ();
closedir (d);
- UNBLOCK_INPUT;
+ unblock_input ();
#ifdef WINDOWSNT
if (attrs)
Vw32_get_true_file_attributes = w32_save;
}
\f
-static Lisp_Object file_name_completion
- (Lisp_Object file, Lisp_Object dirname, int all_flag, int ver_flag,
- Lisp_Object predicate);
+static Lisp_Object file_name_completion (Lisp_Object, Lisp_Object, bool,
+ Lisp_Object);
DEFUN ("file-name-completion", Ffile_name_completion, Sfile_name_completion,
2, 3, 0,
if (!NILP (handler))
return call4 (handler, Qfile_name_completion, file, directory, predicate);
- return file_name_completion (file, directory, 0, 0, predicate);
+ return file_name_completion (file, directory, 0, predicate);
}
DEFUN ("file-name-all-completions", Ffile_name_all_completions,
if (!NILP (handler))
return call3 (handler, Qfile_name_all_completions, file, directory);
- return file_name_completion (file, directory, 1, 0, Qnil);
+ return file_name_completion (file, directory, 1, Qnil);
}
-static int file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_addr);
+static int file_name_completion_stat (int, struct dirent *, struct stat *);
static Lisp_Object Qdefault_directory;
static Lisp_Object
-file_name_completion (Lisp_Object file, Lisp_Object dirname, int all_flag, int ver_flag, Lisp_Object predicate)
+file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
+ Lisp_Object predicate)
{
DIR *d;
- int bestmatchsize = 0;
+ int fd;
+ 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
Lisp_Object encoded_file;
Lisp_Object encoded_dir;
struct stat st;
- int directoryp;
- /* If includeall is zero, exclude files in completion-ignored-extensions as
+ bool directoryp;
+ /* If not INCLUDEALL, 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 ();
+ bool includeall = 1;
+ ptrdiff_t count = SPECPDL_INDEX ();
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
elt = Qnil;
CHECK_STRING (file);
-#ifdef FILE_SYSTEM_CASE
- file = FILE_SYSTEM_CASE (file);
-#endif
bestmatch = Qnil;
encoded_file = encoded_dir = Qnil;
GCPRO5 (file, dirname, bestmatch, encoded_file, encoded_dir);
on the encoded file name. */
encoded_file = STRING_MULTIBYTE (file) ? ENCODE_FILE (file) : file;
- encoded_dir = ENCODE_FILE (dirname);
+ encoded_dir = ENCODE_FILE (Fdirectory_file_name (dirname));
- BLOCK_INPUT;
- d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
- UNBLOCK_INPUT;
+ d = open_directory (SSDATA (encoded_dir), &fd);
if (!d)
report_file_error ("Opening directory", Fcons (dirname, Qnil));
record_unwind_protect (directory_files_internal_unwind,
- make_save_value (d, 0));
+ make_save_pointer (d));
/* Loop reading blocks */
/* (att3b compiler bug requires do a null comparison this way) */
while (1)
{
- DIRENTRY *dp;
- int len;
- int canexclude = 0;
+ struct dirent *dp;
+ ptrdiff_t len;
+ bool canexclude = 0;
errno = 0;
dp = readdir (d);
- if (dp == NULL && (0
-# ifdef EAGAIN
- || errno == EAGAIN
-# endif
-# ifdef EINTR
- || errno == EINTR
-# endif
- ))
- { QUIT; continue; }
-
- if (!dp) break;
+ if (!dp)
+ {
+ if (errno == EAGAIN || errno == EINTR)
+ {
+ QUIT;
+ continue;
+ }
+ break;
+ }
- len = NAMLEN (dp);
+ len = dirent_namelen (dp);
QUIT;
- if (! DIRENTRY_NONEMPTY (dp)
- || len < SCHARS (encoded_file)
+ if (len < SCHARS (encoded_file)
|| 0 <= scmp (dp->d_name, SSDATA (encoded_file),
SCHARS (encoded_file)))
continue;
- if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
+ if (file_name_completion_stat (fd, dp, &st) < 0)
continue;
- directoryp = S_ISDIR (st.st_mode);
+ directoryp = S_ISDIR (st.st_mode) != 0;
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
for (tem = Vcompletion_ignored_extensions;
CONSP (tem); tem = XCDR (tem))
{
- int elt_len;
+ ptrdiff_t elt_len;
char *p1;
elt = XCAR (tem);
/* Suitably record this match. */
- matchcount++;
+ matchcount += matchcount <= 1;
if (all_flag)
bestmatch = Fcons (name, bestmatch);
{
Lisp_Object zero = make_number (0);
/* FIXME: This is a copy of the code in Ftry_completion. */
- int compare = min (bestmatchsize, SCHARS (name));
+ 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 (cmp, Qt) ? compare
- : XINT (cmp) < 0 ? - XINT (cmp) - 1
- : XINT (cmp) - 1);
+ ptrdiff_t matchsize = EQ (cmp, Qt) ? compare : eabs (XINT (cmp)) - 1;
if (completion_ignore_case)
{
/* 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))
+ && matchsize + directoryp < SCHARS (bestmatch))
||
/* If there is no exact match ignoring case,
prefer a match that does not change the case
either both or neither are exact. */
(((matchsize == SCHARS (name))
==
- (matchsize + !!directoryp == SCHARS (bestmatch)))
+ (matchsize + directoryp == SCHARS (bestmatch)))
&& (cmp = Fcompare_strings (name, zero,
make_number (SCHARS (file)),
file, zero,
Return -1 if strings match,
else number of chars that match at the beginning. */
-static int
-scmp (const char *s1, const char *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)
{
}
static int
-file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_addr)
+file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr)
{
- int len = NAMLEN (dp);
- int pos = SCHARS (dirname);
int value;
- char *fullname = (char *) alloca (len + pos + 2);
#ifdef MSDOS
/* Some fields of struct stat are *very* expensive to compute on MS-DOS,
_djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
#endif /* MSDOS */
- memcpy (fullname, SDATA (dirname), pos);
- if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
- fullname[pos++] = DIRECTORY_SEP;
-
- memcpy (fullname + pos, dp->d_name, len);
- fullname[pos + len] = 0;
-
/* 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);
+ value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW);
if (value == 0 && S_ISLNK (st_addr->st_mode))
- stat (fullname, st_addr);
+ fstatat (fd, dp->d_name, st_addr, 0);
#ifdef MSDOS
_djstat_flags = save_djstat_flags;
#endif /* MSDOS */
#ifdef WINDOWSNT
return st->st_uname;
#else
- struct passwd *pw = (struct passwd *) getpwuid (st->st_uid);
+ struct passwd *pw = getpwuid (st->st_uid);
if (pw)
return pw->pw_name;
#ifdef WINDOWSNT
return st->st_gname;
#else
- struct group *gr = (struct group *) getgrgid (st->st_gid);
+ struct group *gr = getgrgid (st->st_gid);
if (gr)
return gr->gr_name;
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.
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.
+ 9. An unspecified value, present only for backward compatibility.
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
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;
-#ifdef BSD4_2
- Lisp_Object dirname;
- struct stat sdir;
-#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;
filename = Fexpand_file_name (filename, Qnil);
return call3 (handler, Qfile_attributes, filename, id_format);
}
- GCPRO1 (filename);
encoded = ENCODE_FILE (filename);
- UNGCPRO;
+ return file_attributes (AT_FDCWD, SSDATA (encoded), id_format);
+}
+
+static Lisp_Object
+file_attributes (int fd, char const *name, Lisp_Object id_format)
+{
+ Lisp_Object values[12];
+ struct stat s;
+ int lstat_result;
+
+ /* An array to hold the mode string generated by filemodestring,
+ including its terminating space and null byte. */
+ char modes[sizeof "-rwxr-xr-x "];
+
+ char *uname = NULL, *gname = NULL;
+
+#ifdef WINDOWSNT
+ /* We usually don't request accurate owner and group info, because
+ it can be very expensive on Windows to get that, and most callers
+ of 'lstat' don't need that. But here we do want that information
+ to be accurate. */
+ w32_stat_get_owner_group = 1;
+#endif
+
+ lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW);
+
+#ifdef WINDOWSNT
+ w32_stat_get_owner_group = 0;
+#endif
- if (lstat (SSDATA (encoded), &s) < 0)
+ if (lstat_result < 0)
return Qnil;
- values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename)
+ values[0] = (S_ISLNK (s.st_mode) ? emacs_readlinkat (fd, name)
: S_ISDIR (s.st_mode) ? Qt : Qnil);
values[1] = make_number (s.st_nlink);
if (!(NILP (id_format) || EQ (id_format, Qinteger)))
{
- BLOCK_INPUT;
+ block_input ();
uname = stat_uname (&s);
gname = stat_gname (&s);
- UNBLOCK_INPUT;
+ unblock_input ();
}
if (uname)
values[2] = DECODE_SYSTEM (build_string (uname));
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[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
filemodestring (&s, modes);
values[8] = make_string (modes, 10);
-#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 != s.st_gid) ? Qt : Qnil;
- else /* if we can't tell, assume worst */
- values[9] = Qt;
-#else /* file gid will be egid */
- values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
-#endif /* not BSD4_2 */
+ values[9] = Qt;
values[10] = INTEGER_TO_CONS (s.st_ino);
values[11] = INTEGER_TO_CONS (s.st_dev);