X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/6c5665e9c472bcca38284bef6142a60e19d96447..d7306fe6b15ccdc49a066c05e5e86df8e005e859:/src/dired.c diff --git a/src/dired.c b/src/dired.c index f54418a081..67002d32eb 100644 --- a/src/dired.c +++ b/src/dired.c @@ -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 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 . */ #include @@ -25,22 +23,15 @@ Boston, MA 02110-1301, USA. */ #include #include #include +#include #ifdef HAVE_PWD_H #include #endif -#ifndef VMS #include -#endif #include -#ifdef VMS -#include -#include -#include -#endif - #ifdef HAVE_UNISTD_H #include #endif @@ -64,15 +55,11 @@ 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 #else #include #endif -#endif /* not NONSYSTEM_DIR_LIBRARY */ #include @@ -118,6 +105,7 @@ extern void filemodestring P_ ((struct stat *, char *)); 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; @@ -129,6 +117,14 @@ Lisp_Object Qfile_attributes_lessp; static int scmp P_ ((unsigned char *, unsigned char *, int)); +#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) @@ -160,6 +156,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. */ @@ -176,10 +175,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, @@ -187,7 +182,6 @@ 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 @@ -213,15 +207,36 @@ 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 Lisp_Object Qlocal; + 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 (;;) @@ -331,6 +346,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; @@ -350,6 +369,7 @@ 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; @@ -457,6 +477,7 @@ These are all file names in directory DIRECTORY which begin with FILE. */) } static int file_name_completion_stat (); +Lisp_Object Qdefault_directory; Lisp_Object file_name_completion (file, dirname, all_flag, ver_flag, predicate) @@ -465,8 +486,7 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) Lisp_Object predicate; { DIR *d; - int bestmatchsize = 0, skip; - register int compare, matchsize; + 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 @@ -476,27 +496,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); @@ -505,6 +514,7 @@ 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) @@ -516,76 +526,81 @@ file_name_completion (file, dirname, all_flag, ver_flag, predicate) 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 (); + DIRENTRY *dp; + int len; + int canexclude = 0; - BLOCK_INPUT; - d = opendir (SDATA (Fdirectory_file_name (encoded_dir))); - UNBLOCK_INPUT; - if (!d) - report_file_error ("Opening directory", Fcons (dirname, Qnil)); - - 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 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 - directoryp = ((st.st_mode & S_IFMT) == S_IFDIR); - tem = Qnil; - if (directoryp) + 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; @@ -616,10 +631,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)) @@ -642,131 +657,162 @@ 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; - - /* 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); + if (CONSP (tem)) + canexclude = 1; - if (!passcount) - { - Lisp_Object regexps; - Lisp_Object zero; - XSETFASTINT (zero, 0); - - /* 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 (XCAR (regexps), name) < 0) - break; - if (CONSP (regexps)) - continue; - } + if (!includeall && canexclude) + /* We're not including all files and this file can be excluded. */ + continue; - /* This is a possible completion */ - if (directoryp) - { - /* This completion is a directory; make it end with '/' */ - name = Ffile_name_as_directory (name); + 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; - /* Test the predicate, if any. */ - - if (!NILP (predicate)) - { - Lisp_Object decoded; - Lisp_Object val; - struct gcpro gcpro1; - - GCPRO1 (name); - decoded = Fexpand_file_name (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) - 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 - { - Lisp_Object zero = make_number (0); - /* FIXME: This is a copy of the code in Ftry_completion. */ - compare = min (bestmatchsize, SCHARS (name)); - tem = Fcompare_strings (bestmatch, zero, - make_number (compare), - name, zero, - make_number (compare), - completion_ignore_case ? Qt : Qnil); - if (EQ (tem, Qt)) - matchsize = compare; - else if (XINT (tem) < 0) - matchsize = - XINT (tem) - 1; - else - matchsize = XINT (tem) - 1; + 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 (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 == 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; - } - 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)) return bestmatch; - if (matchcount == 1 && bestmatchsize == SCHARS (file)) + /* 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)); @@ -826,10 +872,8 @@ file_name_completion_stat (dirname, dp, st_addr) #endif /* MSDOS */ bcopy (SDATA (dirname), fullname, pos); -#ifndef VMS if (!IS_DIRECTORY_SEP (fullname[pos - 1])) fullname[pos++] = DIRECTORY_SEP; -#endif bcopy (dp->d_name, fullname + pos, len); fullname[pos + len] = 0; @@ -852,47 +896,6 @@ file_name_completion_stat (dirname, dp, st_addr) #endif /* S_IFLNK */ } -#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; -{ - return file_name_completion (file, directory, 1, 1, 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; -{ - Lisp_Object retval; - struct FAB fab; - struct RAB rab; - struct XABFHC xabfhc; - int status; - - 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 */ - else - return make_number (xabfhc.xab$w_verlimit); -} - -#endif /* VMS */ - Lisp_Object make_time (time) time_t time; @@ -931,6 +934,29 @@ stat_gname (struct stat *st) #endif } +/* Make an integer or float number for UID and GID, while being + careful not to produce negative numbers due to signed integer + overflow. */ +static Lisp_Object +make_uid (struct stat *st) +{ + EMACS_INT uid = st->st_uid; + + if (sizeof (st->st_uid) > sizeof (uid) || uid < 0 || FIXNUM_OVERFLOW_P (uid)) + return make_float ((double)st->st_uid); + return make_number (uid); +} + +static Lisp_Object +make_gid (struct stat *st) +{ + EMACS_INT gid = st->st_gid; + + if (sizeof (st->st_gid) > sizeof (gid) || gid < 0 || FIXNUM_OVERFLOW_P (gid)) + return make_float ((double)st->st_gid); + return make_number (gid); +} + 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. @@ -943,27 +969,37 @@ 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, - 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 a cons cell containing three integers: - first the high 24 bits, then middle 24 bits, and finally 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. */) +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; { @@ -977,8 +1013,8 @@ which see. */) char modes[10]; Lisp_Object handler; struct gcpro gcpro1; - EMACS_INT ino; - char *uname, *gname; + EMACS_INT ino, uid, gid; + char *uname = NULL, *gname = NULL; filename = Fexpand_file_name (filename, Qnil); @@ -1013,22 +1049,23 @@ which see. */) #endif } values[1] = make_number (s.st_nlink); - if (NILP (id_format) || EQ (id_format, Qinteger)) - { - values[2] = make_fixnum_or_float (s.st_uid); - values[3] = make_fixnum_or_float (s.st_gid); - } - else + + if (!(NILP (id_format) || EQ (id_format, Qinteger))) { BLOCK_INPUT; uname = stat_uname (&s); - values[2] = (uname ? build_string (uname) - : make_fixnum_or_float (s.st_uid)); gname = stat_gname (&s); - values[3] = (gname ? build_string (gname) - : make_fixnum_or_float (s.st_gid)); UNBLOCK_INPUT; } + if (uname) + values[2] = DECODE_SYSTEM (build_string (uname)); + else + values[2] = make_uid (&s); + if (gname) + values[3] = DECODE_SYSTEM (build_string (gname)); + else + values[3] = make_gid (&s); + values[4] = make_time (s.st_atime); values[5] = make_time (s.st_mtime); values[6] = make_time (s.st_ctime); @@ -1072,8 +1109,10 @@ which see. */) else { /* To allow inode numbers beyond 32 bits, separate into 2 24-bit - high parts and a 16-bit bottom part. */ - EMACS_INT high_ino = s.st_ino >> 32; + 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), @@ -1113,6 +1152,7 @@ syms_of_dired () Qfile_name_all_completions = intern ("file-name-all-completions"); Qfile_attributes = intern ("file-attributes"); Qfile_attributes_lessp = intern ("file-attributes-lessp"); + Qdefault_directory = intern ("default-directory"); staticpro (&Qdirectory_files); staticpro (&Qdirectory_files_and_attributes); @@ -1120,14 +1160,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);