diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
commit | 650c20f1ca4e07591a727e1cfcc74b3363d15985 (patch) | |
tree | 85d11f6437cde22f410c25e0e5f71a3131ebd07d /src/dired.c | |
parent | 8869332684c2302b5ba1ead4568bbc7ba1c0183e (diff) | |
parent | 4b85ae6a24380fb67a3315eaec9233f17a872473 (diff) | |
download | emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.gz emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.bz2 emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.zip |
Merge 'master' into noverlay
Diffstat (limited to 'src/dired.c')
-rw-r--r-- | src/dired.c | 452 |
1 files changed, 246 insertions, 206 deletions
diff --git a/src/dired.c b/src/dired.c index 28d1cffb44c..c2c099f0a5f 100644 --- a/src/dired.c +++ b/src/dired.c @@ -1,5 +1,5 @@ /* Lisp functions for making directory listings. - Copyright (C) 1985-1986, 1993-1994, 1999-2017 Free Software + Copyright (C) 1985-1986, 1993-1994, 1999-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -20,7 +20,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <config.h> -#include <stdio.h> #include <sys/stat.h> #ifdef HAVE_PWD_H @@ -40,7 +39,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "systime.h" #include "buffer.h" #include "coding.h" -#include "regex.h" #ifdef MSDOS #include "msdos.h" /* for fstatat */ @@ -81,9 +79,9 @@ dirent_type (struct dirent *dp) } static DIR * -open_directory (Lisp_Object dirname, int *fdp) +open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp) { - char *name = SSDATA (dirname); + char *name = SSDATA (encoded_dirname); DIR *d; int fd, opendir_errno; @@ -167,62 +165,41 @@ read_dirent (DIR *dir, Lisp_Object dirname) Lisp_Object directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, bool attrs, - Lisp_Object id_format) + Lisp_Object id_format, Lisp_Object return_count) { - ptrdiff_t directory_nbytes; - Lisp_Object list, dirfilename, encoded_directory; - struct re_pattern_buffer *bufp = NULL; - bool needsep = 0; - ptrdiff_t count = SPECPDL_INDEX (); -#ifdef WINDOWSNT - Lisp_Object w32_save = Qnil; -#endif + EMACS_INT ind = 0, last = MOST_POSITIVE_FIXNUM; + + if (!NILP (return_count)) + { + CHECK_FIXNAT (return_count); + last = XFIXNAT (return_count); + } + + if (!NILP (match)) + CHECK_STRING (match); /* Don't let the compiler optimize away all copies of DIRECTORY, which would break GC; see Bug#16986. */ Lisp_Object volatile directory_volatile = directory; - /* Because of file name handlers, these functions might call - Ffuncall, and cause a GC. */ - list = encoded_directory = dirfilename = Qnil; - dirfilename = Fdirectory_file_name (directory); - - if (!NILP (match)) - { - CHECK_STRING (match); - - /* MATCH might be a flawed regular expression. Rather than - catching and signaling our own errors, we just call - 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 WINDOWSNT - /* Windows users want case-insensitive wildcards. */ - bufp = compile_pattern (match, 0, - BVAR (&buffer_defaults, case_canon_table), 0, 1); -# else /* !WINDOWSNT */ - bufp = compile_pattern (match, 0, Qnil, 0, 1); -# endif /* !WINDOWSNT */ - } + Lisp_Object dirfilename = Fdirectory_file_name (directory); /* 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); - - /* 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! */ + Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename); int fd; - DIR *d = open_directory (dirfilename, &fd); + DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd); /* Unfortunately, we can now invoke expand-file-name and file-attributes on filenames, both of which can throw, so we must do a proper unwind-protect. */ + specpdl_ref count = SPECPDL_INDEX (); record_unwind_protect_ptr (directory_files_internal_unwind, d); #ifdef WINDOWSNT + Lisp_Object w32_save = Qnil; if (attrs) { /* Do this only once to avoid doing it (in w32.c:stat) for each @@ -234,7 +211,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, { /* w32.c:stat will notice these bindings and avoid calling GetDriveType for each file. */ - if (is_slow_fs (SSDATA (dirfilename))) + if (is_slow_fs (SSDATA (encoded_dirfilename))) Vw32_get_true_file_attributes = Qnil; else Vw32_get_true_file_attributes = Qt; @@ -242,75 +219,85 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, } #endif - directory_nbytes = SBYTES (directory); + if (!NILP (full) && !STRING_MULTIBYTE (directory)) + { /* We will be concatenating 'directory' with local file name. + We always decode local file names, so in order to safely concatenate + them we need 'directory' to be decoded as well (bug#56469). */ + directory = DECODE_FILE (directory); + } + + ptrdiff_t directory_nbytes = SBYTES (directory); re_match_object = Qt; /* Decide whether we need to add a directory separator. */ - if (directory_nbytes == 0 - || !IS_ANY_SEP (SREF (directory, directory_nbytes - 1))) - needsep = 1; + bool needsep = (directory_nbytes == 0 + || !IS_ANY_SEP (SREF (directory, directory_nbytes - 1))); - /* Loop reading directory entries. */ + /* Windows users want case-insensitive wildcards. */ + Lisp_Object case_table = Qnil; +#ifdef WINDOWSNT + case_table = BVAR (&buffer_defaults, case_canon_table); +#endif + + /* Read directory entries and accumulate them into LIST. */ + Lisp_Object list = Qnil; for (struct dirent *dp; (dp = read_dirent (d, directory)); ) { ptrdiff_t len = dirent_namelen (dp); Lisp_Object name = make_unibyte_string (dp->d_name, len); Lisp_Object finalname = name; - /* Note: DECODE_FILE can GC; it should protect its argument, - though. */ + /* This can GC. */ name = DECODE_FILE (name); - len = SBYTES (name); - /* Now that we have unwind_protect in place, we might as well - allow matching to be interrupted. */ maybe_quit (); - bool wanted = (NILP (match) - || re_search (bufp, SSDATA (name), len, 0, len, 0) >= 0); + if (!NILP (match) + && fast_string_match_internal (match, name, case_table) < 0) + continue; - if (wanted) + Lisp_Object fileattrs UNINIT; + if (attrs) { - if (!NILP (full)) - { - Lisp_Object fullname; - ptrdiff_t nbytes = len + directory_nbytes + needsep; - ptrdiff_t 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 = multibyte_chars_in_text (SDATA (fullname), nbytes); - - /* Some bug somewhere. */ - if (nchars > nbytes) - emacs_abort (); + fileattrs = file_attributes (fd, dp->d_name, directory, name, + id_format); + if (NILP (fileattrs)) + continue; + } - STRING_SET_CHARS (fullname, nchars); - if (nchars == nbytes) - STRING_SET_UNIBYTE (fullname); + if (!NILP (full)) + { + ptrdiff_t name_nbytes = SBYTES (name); + ptrdiff_t nbytes = directory_nbytes + needsep + name_nbytes; + ptrdiff_t nchars = SCHARS (directory) + needsep + SCHARS (name); + /* DECODE_FILE may return non-ASCII unibyte strings (e.g. when + file-name-coding-system is 'binary'), so we don't know for sure + that the bytes we have follow our internal utf-8 representation + for multibyte strings. If nchars == nbytes we don't need to + care and just return a unibyte string; and if not, that means + one of 'name' or 'directory' is multibyte, in which case we + presume that the other one would also be multibyte if it + contained non-ASCII. + FIXME: This last presumption is broken when 'directory' is + multibyte (with non-ASCII), and 'name' is unibyte with non-ASCII + (because file-name-coding-system is 'binary'). */ + finalname = (nchars == nbytes) + ? make_uninit_string (nbytes) + : make_uninit_multibyte_string (nchars, nbytes); + memcpy (SDATA (finalname), SDATA (directory), directory_nbytes); + if (needsep) + SSET (finalname, directory_nbytes, DIRECTORY_SEP); + memcpy (SDATA (finalname) + directory_nbytes + needsep, + SDATA (name), name_nbytes); + } + else + finalname = name; - finalname = fullname; - } - else - finalname = name; + if (ind == last) + break; + ind ++; - if (attrs) - { - Lisp_Object fileattrs - = file_attributes (fd, dp->d_name, directory, name, id_format); - list = Fcons (Fcons (finalname, fileattrs), list); - } - else - list = Fcons (finalname, list); - } + list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list); } closedir (d); @@ -320,7 +307,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, #endif /* Discard the unwind protect. */ - specpdl_ptr = specpdl + count; + specpdl_ptr = specpdl_ref_to_ptr (count); if (NILP (nosort)) list = Fsort (Fnreverse (list), @@ -331,57 +318,72 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, } -DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 4, 0, +DEFUN ("directory-files", Fdirectory_files, Sdirectory_files, 1, 5, 0, doc: /* Return a list of names of files in DIRECTORY. -There are three optional arguments: +There are four optional arguments: If FULL is non-nil, return absolute file names. Otherwise return names that are relative to the specified directory. -If MATCH is non-nil, mention only file names that match the regexp MATCH. +If MATCH is non-nil, mention only file names whose non-directory part + matches 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. */) - (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort) + NOSORT is useful if you plan to sort the result yourself. +If COUNT is non-nil and a natural number, the function will return + COUNT number of file names (if so many are present). */) + (Lisp_Object directory, Lisp_Object full, Lisp_Object match, + Lisp_Object nosort, Lisp_Object count) { - Lisp_Object handler; directory = Fexpand_file_name (directory, Qnil); /* If the file name has special constructs in it, - call the corresponding file handler. */ - handler = Ffind_file_name_handler (directory, Qdirectory_files); + call the corresponding file name handler. */ + Lisp_Object handler = Ffind_file_name_handler (directory, Qdirectory_files); if (!NILP (handler)) - return call5 (handler, Qdirectory_files, directory, - full, match, nosort); + return call6 (handler, Qdirectory_files, directory, + full, match, nosort, count); - return directory_files_internal (directory, full, match, nosort, false, Qnil); + return directory_files_internal (directory, full, match, nosort, + false, Qnil, count); } DEFUN ("directory-files-and-attributes", Fdirectory_files_and_attributes, - Sdirectory_files_and_attributes, 1, 5, 0, + Sdirectory_files_and_attributes, 1, 6, 0, doc: /* Return a list of names of files and their attributes in DIRECTORY. -There are four optional arguments: +Value is a list of the form: + + ((FILE1 . FILE1-ATTRS) (FILE2 . FILE2-ATTRS) ...) + +where each FILEn-ATTRS is the attributes of FILEn as returned +by `file-attributes'. + +This function accepts five optional arguments: If FULL is non-nil, return absolute file names. Otherwise return names that are relative to the specified directory. -If MATCH is non-nil, mention only file names that match the regexp MATCH. +If MATCH is non-nil, mention only file names whose non-directory part + matches the regexp MATCH. If NOSORT is non-nil, the list is not sorted--its order is unpredictable. NOSORT is useful if you plan to sort the result yourself. ID-FORMAT specifies the preferred format of attributes uid and gid, see -`file-attributes' for further documentation. + `file-attributes' for further documentation. +If COUNT is non-nil and a natural number, the function will return + COUNT number of file names (if so many are present). On MS-Windows, performance depends on `w32-get-true-file-attributes', which see. */) - (Lisp_Object directory, Lisp_Object full, Lisp_Object match, Lisp_Object nosort, Lisp_Object id_format) + (Lisp_Object directory, Lisp_Object full, Lisp_Object match, + Lisp_Object nosort, Lisp_Object id_format, Lisp_Object count) { - Lisp_Object handler; directory = Fexpand_file_name (directory, Qnil); /* If the file name has special constructs in it, - call the corresponding file handler. */ - handler = Ffind_file_name_handler (directory, Qdirectory_files_and_attributes); + call the corresponding file name handler. */ + Lisp_Object handler + = Ffind_file_name_handler (directory, Qdirectory_files_and_attributes); if (!NILP (handler)) - return call6 (handler, Qdirectory_files_and_attributes, - directory, full, match, nosort, id_format); + return call7 (handler, Qdirectory_files_and_attributes, + directory, full, match, nosort, id_format, count); return directory_files_internal (directory, full, match, nosort, - true, id_format); + true, id_format, count); } @@ -409,13 +411,13 @@ is matched against file and directory names relative to DIRECTORY. */) directory = Fexpand_file_name (directory, Qnil); /* If the directory name has special constructs in it, - call the corresponding file handler. */ + call the corresponding file name handler. */ handler = Ffind_file_name_handler (directory, Qfile_name_completion); if (!NILP (handler)) return call4 (handler, Qfile_name_completion, file, directory, predicate); /* If the file name has special constructs in it, - call the corresponding file handler. */ + call the corresponding file name handler. */ handler = Ffind_file_name_handler (file, Qfile_name_completion); if (!NILP (handler)) return call4 (handler, Qfile_name_completion, file, directory, predicate); @@ -437,13 +439,13 @@ is matched against file and directory names relative to DIRECTORY. */) directory = Fexpand_file_name (directory, Qnil); /* If the directory name has special constructs in it, - call the corresponding file handler. */ + call the corresponding file name handler. */ handler = Ffind_file_name_handler (directory, Qfile_name_all_completions); if (!NILP (handler)) return call3 (handler, Qfile_name_all_completions, file, directory); /* If the file name has special constructs in it, - call the corresponding file handler. */ + call the corresponding file name handler. */ handler = Ffind_file_name_handler (file, Qfile_name_all_completions); if (!NILP (handler)) return call3 (handler, Qfile_name_all_completions, file, directory); @@ -471,7 +473,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, anything. */ bool includeall = 1; bool check_decoded = false; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); elt = Qnil; @@ -498,8 +500,8 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, decoded names in order to filter false positives, such as "a" falsely matching "a-ring". */ if (!NILP (file_encoding) - && !NILP (Fplist_get (Fcoding_system_plist (file_encoding), - Qdecomposed_characters))) + && !NILP (plist_get (Fcoding_system_plist (file_encoding), + Qdecomposed_characters))) { check_decoded = true; if (STRING_MULTIBYTE (file)) @@ -512,22 +514,36 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, } } int fd; - DIR *d = open_directory (encoded_dir, &fd); + DIR *d = open_directory (dirname, encoded_dir, &fd); record_unwind_protect_ptr (directory_files_internal_unwind, d); /* Loop reading directory entries. */ + Lisp_Object zero = make_fixnum (0); + ptrdiff_t enc_file_len = SCHARS (encoded_file); + Lisp_Object file_len = make_fixnum (SCHARS (file)); for (struct dirent *dp; (dp = read_dirent (d, dirname)); ) { ptrdiff_t len = dirent_namelen (dp); bool canexclude = 0; maybe_quit (); - if (len < SCHARS (encoded_file) - || (scmp (dp->d_name, SSDATA (encoded_file), - SCHARS (encoded_file)) - >= 0)) + + if (len < enc_file_len + /* scmp cannot reliably compare non-ASCII strings while + ignoring letter-case. */ + || (!completion_ignore_case + && scmp (dp->d_name, SSDATA (encoded_file), enc_file_len) >= 0)) continue; + name = make_unibyte_string (dp->d_name, len); + name = DECODE_FILE (name); + ptrdiff_t name_blen = SBYTES (name), name_len = SCHARS (name); + if (completion_ignore_case + && !BASE_EQ (Fcompare_strings (name, zero, file_len, file, zero, + file_len, Qt), + Qt)) + continue; + switch (dirent_type (dp)) { case DT_DIR: @@ -550,6 +566,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, if (!all_flag) { ptrdiff_t skip; + Lisp_Object cmp_len = make_fixnum (name_len); #if 0 /* FIXME: The `scmp' call compares an encoded and a decoded string. */ /* If this entry matches the current bestmatch, the only @@ -573,7 +590,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, actually in the way in a directory with only one file. */ if (TRIVIAL_DIRECTORY_ENTRY (dp->d_name)) canexclude = 1; - else if (len > SCHARS (encoded_file)) + else if (len > enc_file_len) /* Ignore directories if they match an element of completion-ignored-extensions which ends in a slash. */ for (tem = Vcompletion_ignored_extensions; @@ -585,21 +602,33 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, elt = XCAR (tem); if (!STRINGP (elt)) continue; - /* Need to encode ELT, since scmp compares unibyte - strings only. */ - elt = ENCODE_FILE (elt); - elt_len = SCHARS (elt) - 1; /* -1 for trailing / */ + elt_len = SBYTES (elt) - 1; /* -1 for trailing / */ if (elt_len <= 0) continue; p1 = SSDATA (elt); if (p1[elt_len] != '/') continue; - skip = len - elt_len; + skip = name_blen - elt_len; if (skip < 0) continue; - if (scmp (dp->d_name + skip, p1, elt_len) >= 0) + if (!completion_ignore_case + && scmp (SSDATA (name) + skip, p1, elt_len) >= 0) continue; + if (completion_ignore_case) + { + elt_len = SCHARS (elt) - 1; + skip = name_len - elt_len; + cmp_len = make_fixnum (elt_len); + if (skip < 0 + || !BASE_EQ (Fcompare_strings (name, + make_fixnum (skip), + Qnil, + elt, zero, cmp_len, + Qt), + Qt)) + continue; + } break; } } @@ -607,22 +636,35 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, { /* Compare extensions-to-be-ignored against end of this file name */ /* if name is not an exact match against specified string */ - if (len > SCHARS (encoded_file)) + if (len > enc_file_len) /* and exit this for loop if a match is found */ for (tem = Vcompletion_ignored_extensions; CONSP (tem); tem = XCDR (tem)) { elt = XCAR (tem); if (!STRINGP (elt)) continue; - /* Need to encode ELT, since scmp compares unibyte - strings only. */ - elt = ENCODE_FILE (elt); - skip = len - SCHARS (elt); + ptrdiff_t elt_len = SBYTES (elt); + skip = len - elt_len; if (skip < 0) continue; - if (scmp (dp->d_name + skip, SSDATA (elt), SCHARS (elt)) - >= 0) + if (!completion_ignore_case + && (scmp (SSDATA (name) + skip, SSDATA (elt), elt_len) + >= 0)) continue; + if (completion_ignore_case) + { + elt_len = SCHARS (elt); + skip = name_len - elt_len; + cmp_len = make_fixnum (elt_len); + if (skip < 0 + || !BASE_EQ (Fcompare_strings (name, + make_fixnum (skip), + Qnil, + elt, zero, cmp_len, + Qt), + Qt)) + continue; + } break; } } @@ -646,24 +688,18 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, 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, table = (completion_ignore_case - ? Vascii_canon_table : Qnil); + Lisp_Object regexps, table = (completion_ignore_case + ? Vascii_canon_table : Qnil); - /* Ignore this element if it fails to match all the regexps. */ - for (regexps = Vcompletion_regexp_list; CONSP (regexps); - regexps = XCDR (regexps)) - if (fast_string_match_internal (XCAR (regexps), name, table) < 0) - break; + /* Ignore this element if it fails to match all the regexps. */ + for (regexps = Vcompletion_regexp_list; CONSP (regexps); + regexps = XCDR (regexps)) + if (fast_string_match_internal (XCAR (regexps), name, table) < 0) + break; - if (CONSP (regexps)) - continue; - } + if (CONSP (regexps)) + continue; /* This is a possible completion */ if (directoryp) @@ -677,17 +713,15 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, /* Reject entries where the encoded strings match, but the decoded don't. For example, "a" should not match "a-ring" on file systems that store decomposed characters. */ - Lisp_Object zero = make_number (0); - if (check_decoded && SCHARS (file) <= SCHARS (name)) { /* FIXME: This is a copy of the code below. */ ptrdiff_t compare = SCHARS (file); Lisp_Object cmp - = Fcompare_strings (name, zero, make_number (compare), - file, zero, make_number (compare), + = Fcompare_strings (name, zero, make_fixnum (compare), + file, zero, make_fixnum (compare), completion_ignore_case ? Qt : Qnil); - if (!EQ (cmp, Qt)) + if (!BASE_EQ (cmp, Qt)) continue; } @@ -707,10 +741,11 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, /* FIXME: This is a copy of the code in Ftry_completion. */ ptrdiff_t compare = min (bestmatchsize, SCHARS (name)); Lisp_Object cmp - = Fcompare_strings (bestmatch, zero, make_number (compare), - name, zero, make_number (compare), + = Fcompare_strings (bestmatch, zero, make_fixnum (compare), + name, zero, make_fixnum (compare), completion_ignore_case ? Qt : Qnil); - ptrdiff_t matchsize = EQ (cmp, Qt) ? compare : eabs (XINT (cmp)) - 1; + ptrdiff_t matchsize = BASE_EQ (cmp, Qt) + ? compare : eabs (XFIXNUM (cmp)) - 1; if (completion_ignore_case) { @@ -735,17 +770,17 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, == (matchsize + directoryp == SCHARS (bestmatch))) && (cmp = Fcompare_strings (name, zero, - make_number (SCHARS (file)), + make_fixnum (SCHARS (file)), file, zero, Qnil, Qnil), - EQ (Qt, cmp)) + BASE_EQ (Qt, cmp)) && (cmp = Fcompare_strings (bestmatch, zero, - make_number (SCHARS (file)), + make_fixnum (SCHARS (file)), file, zero, Qnil, Qnil), - ! EQ (Qt, cmp)))) + ! BASE_EQ (Qt, cmp)))) bestmatch = name; } bestmatchsize = matchsize; @@ -775,8 +810,8 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, 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)); + bestmatch = Fsubstring (bestmatch, make_fixnum (0), + make_fixnum (bestmatchsize)); return bestmatch; } @@ -792,6 +827,9 @@ scmp (const char *s1, const char *s2, ptrdiff_t len) if (completion_ignore_case) { + /* WARNING: This only works for pure ASCII strings, as we + compare bytes, not characters! Use Fcompare_strings for + comparing non-ASCII strings case-insensitively. */ while (l && (downcase ((unsigned char) *s1++) == downcase ((unsigned char) *s2++))) @@ -854,7 +892,7 @@ stat_gname (struct stat *st) DEFUN ("file-attributes", Ffile_attributes, Sfile_attributes, 1, 2, 0, doc: /* Return a list of attributes of file FILENAME. -Value is nil if specified file cannot be opened. +Value is nil if specified file does not exist. ID-FORMAT specifies the preferred format of attributes uid and gid (see below) - valid values are `string' and `integer'. The latter is the @@ -872,28 +910,22 @@ provided: `file-attribute-type', `file-attribute-link-number', 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 a number. If a string value cannot be - looked up, a numeric value, either an integer or a float, is returned. + 2. File uid as a string or (if ID-FORMAT is `integer' or a string value + cannot be looked up) as an integer. 3. File gid, likewise. - 4. Last access time, as a list of integers (HIGH LOW USEC PSEC) in the - same style as (current-time). + 4. Last access time, in the style of `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. 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. + 7. Size in bytes, as an integer. 8. File modes, as a string of ten letters or dashes as in ls -l. 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 - (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. +10. inode number, as a nonnegative integer. +11. Filesystem device number, as an integer. +Large integers are bignums, so `eq' might not work on them. On most filesystems, the combination of the inode and the device number uniquely identifies the file. @@ -913,11 +945,12 @@ so last access time will always be midnight of that day. */) return Qnil; /* If the file name has special constructs in it, - call the corresponding file handler. */ + call the corresponding file name handler. */ handler = Ffind_file_name_handler (filename, Qfile_attributes); if (!NILP (handler)) - { /* Only pass the extra arg if it is used to help backward compatibility - with old file handlers which do not implement the new arg. --Stef */ + { /* Only pass the extra arg if it is used to help backward + compatibility with old file name handlers which do not + implement the new arg. --Stef */ if (NILP (id_format)) return call2 (handler, Qfile_attributes, filename); else @@ -934,7 +967,7 @@ file_attributes (int fd, char const *name, Lisp_Object dirname, Lisp_Object filename, Lisp_Object id_format) { - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); struct stat s; /* An array to hold the mode string generated by filemodestring, @@ -945,15 +978,22 @@ file_attributes (int fd, char const *name, int err = EINVAL; -#ifdef O_PATH - int namefd = openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW); +#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG + int namefd = emacs_openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW, 0); if (namefd < 0) err = errno; else { record_unwind_protect_int (close_file_unwind, namefd); if (fstat (namefd, &s) != 0) - err = errno; + { + err = errno; + /* The Linux kernel before version 3.6 does not support + fstat on O_PATH file descriptors. Handle this error like + missing support for O_PATH. */ + if (err == EBADF) + err = EINVAL; + } else { err = 0; @@ -972,15 +1012,14 @@ file_attributes (int fd, char const *name, information to be accurate. */ w32_stat_get_owner_group = 1; #endif - if (fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) - err = 0; + err = emacs_fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW) == 0 ? 0 : errno; #ifdef WINDOWSNT w32_stat_get_owner_group = 0; #endif } if (err != 0) - return unbind_to (count, Qnil); + return unbind_to (count, file_attribute_errno (filename, err)); Lisp_Object file_type; if (S_ISLNK (s.st_mode)) @@ -989,7 +1028,7 @@ file_attributes (int fd, char const *name, symlink is replaced between the call to fstatat and the call to emacs_readlinkat. Detect this race unless the replacement is also a symlink. */ - file_type = emacs_readlinkat (fd, name); + file_type = check_emacs_readlinkat (fd, filename, name); if (NILP (file_type)) return unbind_to (count, Qnil); } @@ -1008,13 +1047,13 @@ file_attributes (int fd, char const *name, return CALLN (Flist, file_type, - make_number (s.st_nlink), + make_fixnum (s.st_nlink), (uname ? DECODE_SYSTEM (build_unibyte_string (uname)) - : make_fixnum_or_float (s.st_uid)), + : INT_TO_INTEGER (s.st_uid)), (gname ? DECODE_SYSTEM (build_unibyte_string (gname)) - : make_fixnum_or_float (s.st_gid)), + : INT_TO_INTEGER (s.st_gid)), make_lisp_time (get_stat_atime (&s)), make_lisp_time (get_stat_mtime (&s)), make_lisp_time (get_stat_ctime (&s)), @@ -1023,17 +1062,18 @@ file_attributes (int fd, char const *name, 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. */ - make_fixnum_or_float (sizeof (s.st_size) == 4 - ? s.st_size & 0xffffffffu - : s.st_size), + INT_TO_INTEGER (sizeof (s.st_size) == 4 + ? s.st_size & 0xffffffffu + : s.st_size), make_string (modes, 10), Qt, - INTEGER_TO_CONS (s.st_ino), - INTEGER_TO_CONS (s.st_dev)); + INT_TO_INTEGER (s.st_ino), + INT_TO_INTEGER (s.st_dev)); } -DEFUN ("file-attributes-lessp", Ffile_attributes_lessp, Sfile_attributes_lessp, 2, 2, 0, +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. */) (Lisp_Object f1, Lisp_Object f2) @@ -1057,7 +1097,7 @@ return a list with one element, taken from `user-real-login-name'. */) endpwent (); #endif - if (EQ (users, Qnil)) + if (NILP (users)) /* At least current user is always known. */ users = list1 (Vuser_real_login_name); return users; |