emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 135bca5: Port file-system-info to non-Microsoft


From: Paul Eggert
Subject: [Emacs-diffs] master 135bca5: Port file-system-info to non-Microsoft
Date: Mon, 2 Oct 2017 01:33:24 -0400 (EDT)

branch: master
commit 135bca574c31b7bf6df6c63d28f180956928dde7
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    Port file-system-info to non-Microsoft
    
    * admin/merge-gnulib (GNULIB_MODULES): Add fsusage.
    * doc/emacs/files.texi (Directories): Remove documentation of
    now-obsolete directory-free-space-program and
    directory-free-space-args.
    * etc/NEWS: Mention change.
    * etc/PROBLEMS: Slow df is no longer a problem.
    * lib/fsusage.c, lib/fsusage.h, m4/fsusage.m4:
    New files, copied from Gnulib.
    * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate.
    * lisp/dired.el (dired-free-space-program)
    (dired-free-space-args): These aliases are now obsolete.
    * lisp/files.el (directory-free-space-program)
    (directory-free-space-args): Now obsolete.
    (get-free-disk-space): Just call file-system-info instead
    of the now-obsolete directory-free-space-program.
    * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_fsusage): New macro.
    * src/fileio.c: Include fsusage.h.
    (blocks_to_bytes, Ffile_system_info) [!DOS_NT]: New functions.
    (syms_of_fileio) [!DOS_NT]: Defsubr file-system-info.
---
 admin/merge-gnulib   |   2 +-
 doc/emacs/files.texi |   7 +-
 etc/NEWS             |   4 +
 etc/PROBLEMS         |  10 +-
 lib/fsusage.c        | 288 +++++++++++++++++++++++++++++++++++++++++++
 lib/fsusage.h        |  40 ++++++
 lib/gnulib.mk.in     |  13 +-
 lisp/dired.el        |   6 +-
 lisp/files.el        |  49 ++------
 m4/fsusage.m4        | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++
 m4/gnulib-comp.m4    |   9 ++
 nt/gnulib-cfg.mk     |   1 +
 src/fileio.c         |  39 ++++++
 13 files changed, 750 insertions(+), 54 deletions(-)

diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 60104e8..4b1dc59 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -33,7 +33,7 @@ GNULIB_MODULES='
   d-type diffseq dtoastr dtotimespec dup2
   environ execinfo explicit_bzero faccessat
   fcntl fcntl-h fdatasync fdopendir
-  filemode filevercmp flexmember fstatat fsync
+  filemode filevercmp flexmember fstatat fsusage fsync
   getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog
   ignore-value intprops largefile lstat
   manywarnings memrchr minmax mkostemp mktime nstrftime
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 18f1c28..2c4a0ca 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -1279,13 +1279,8 @@ default), and @code{list-directory-verbose-switches} is 
a string
 giving the switches to use in a verbose listing (@code{"-l"} by
 default).
 
address@hidden directory-free-space-program
address@hidden directory-free-space-args
   In verbose directory listings, Emacs adds information about the
-amount of free space on the disk that contains the directory.  To do
-this, it runs the program specified by
address@hidden with arguments
address@hidden
+amount of free space on the disk that contains the directory.
 
   The command @kbd{M-x delete-directory} prompts for a directory's name
 using the minibuffer, and deletes the directory if it is empty.  If
diff --git a/etc/NEWS b/etc/NEWS
index 42c1b04..28789a9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -73,6 +73,10 @@ calling 'eldoc-message' directly.
 
 * Lisp Changes in Emacs 27.1
 
+** The 'file-system-info' function is now available on all platforms.
+instead of just Microsoft platforms.  This fixes a get-free-disk-space
+bug on OS X 10.8 and later (Bug#28639).
+
 
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index 94c78b6..2da9932 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -557,7 +557,7 @@ and then choose /usr/bin/netkit-ftp.
 
 *** Dired is very slow.
 
-This could happen if invocation of the 'df' program takes a long
+This could happen if getting a file system's status takes a long
 time.  Possible reasons for this include:
 
   - ClearCase mounted filesystems (VOBs) that sometimes make 'df'
@@ -565,12 +565,8 @@ time.  Possible reasons for this include:
 
   - slow automounters on some old versions of Unix;
 
-  - slow operation of some versions of 'df'.
-
-To work around the problem, you could either (a) set the variable
-'directory-free-space-program' to nil, and thus prevent Emacs from
-invoking 'df'; (b) use 'df' from the GNU Coreutils package; or
-(c) use CVS, which is Free Software, instead of ClearCase.
+To work around the problem, you could use Git or some other
+free-software program, instead of ClearCase.
 
 *** ps-print commands fail to find prologue files ps-prin*.ps.
 
diff --git a/lib/fsusage.c b/lib/fsusage.c
new file mode 100644
index 0000000..a0f763b
--- /dev/null
+++ b/lib/fsusage.c
@@ -0,0 +1,288 @@
+/* fsusage.c -- return space usage of mounted file systems
+
+   Copyright (C) 1991-1992, 1996, 1998-1999, 2002-2006, 2009-2017 Free Software
+   Foundation, Inc.
+
+   This program 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 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   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 this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "fsusage.h"
+
+#include <limits.h>
+#include <sys/types.h>
+
+#if STAT_STATVFS || STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
+# include <sys/statvfs.h>
+#else
+/* Don't include backward-compatibility files unless they're needed.
+   Eventually we'd like to remove all this cruft.  */
+# include <fcntl.h>
+# include <unistd.h>
+# include <sys/stat.h>
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+#endif
+#if HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+#endif
+# if HAVE_SYS_FS_S5PARAM_H      /* Fujitsu UXP/V */
+#  include <sys/fs/s5param.h>
+# endif
+# if HAVE_SYS_STATFS_H
+#  include <sys/statfs.h>
+# endif
+# if HAVE_DUSTAT_H              /* AIX PS/2 */
+#  include <sys/dustat.h>
+# endif
+# include "full-read.h"
+#endif
+
+/* Many space usage primitives use all 1 bits to denote a value that is
+   not applicable or unknown.  Propagate this information by returning
+   a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
+   is unsigned and narrower than uintmax_t.  */
+#define PROPAGATE_ALL_ONES(x) \
+  ((sizeof (x) < sizeof (uintmax_t) \
+    && (~ (x) == (sizeof (x) < sizeof (int) \
+                  ? - (1 << (sizeof (x) * CHAR_BIT)) \
+                  : 0))) \
+   ? UINTMAX_MAX : (uintmax_t) (x))
+
+/* Extract the top bit of X as an uintmax_t value.  */
+#define EXTRACT_TOP_BIT(x) ((x) \
+                            & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
+
+/* If a value is negative, many space usage primitives store it into an
+   integer variable by assignment, even if the variable's type is unsigned.
+   So, if a space usage variable X's top bit is set, convert X to the
+   uintmax_t value V such that (- (uintmax_t) V) is the negative of
+   the original value.  If X's top bit is clear, just yield X.
+   Use PROPAGATE_TOP_BIT if the original value might be negative;
+   otherwise, use PROPAGATE_ALL_ONES.  */
+#define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
+
+#ifdef STAT_STATVFS
+/* Return true if statvfs works.  This is false for statvfs on systems
+   with GNU libc on Linux kernels before 2.6.36, which stats all
+   preceding entries in /proc/mounts; that makes df hang if even one
+   of the corresponding file systems is hard-mounted but not available.  */
+# if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
+/* The FRSIZE fallback is not required in this case.  */
+#  undef STAT_STATFS2_FRSIZE
+static int statvfs_works (void) { return 1; }
+# else
+#  include <string.h> /* for strverscmp */
+#  include <sys/utsname.h>
+#  include <sys/statfs.h>
+#  define STAT_STATFS2_BSIZE 1
+
+static int
+statvfs_works (void)
+{
+  static int statvfs_works_cache = -1;
+  struct utsname name;
+  if (statvfs_works_cache < 0)
+    statvfs_works_cache = (uname (&name) == 0
+                           && 0 <= strverscmp (name.release, "2.6.36"));
+  return statvfs_works_cache;
+}
+# endif
+#endif
+
+
+/* Fill in the fields of FSP with information about space usage for
+   the file system on which FILE resides.
+   DISK is the device on which FILE is mounted, for space-getting
+   methods that need to know it.
+   Return 0 if successful, -1 if not.  When returning -1, ensure that
+   ERRNO is either a system error value, or zero if DISK is NULL
+   on a system that requires a non-NULL value.  */
+int
+get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
+{
+#ifdef STAT_STATVFS     /* POSIX, except pre-2.6.36 glibc/Linux */
+
+  if (statvfs_works ())
+    {
+      struct statvfs vfsd;
+
+      if (statvfs (file, &vfsd) < 0)
+        return -1;
+
+      /* f_frsize isn't guaranteed to be supported.  */
+      fsp->fsu_blocksize = (vfsd.f_frsize
+                            ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
+                            : PROPAGATE_ALL_ONES (vfsd.f_bsize));
+
+      fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
+      fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
+      fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
+      fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
+      fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
+      fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
+      return 0;
+    }
+
+#endif
+
+#if defined STAT_STATVFS64            /* AIX */
+
+  struct statvfs64 fsd;
+
+  if (statvfs64 (file, &fsd) < 0)
+    return -1;
+
+  /* f_frsize isn't guaranteed to be supported.  */
+  fsp->fsu_blocksize = (fsd.f_frsize
+                        ? PROPAGATE_ALL_ONES (fsd.f_frsize)
+                        : PROPAGATE_ALL_ONES (fsd.f_bsize));
+
+#elif defined STAT_STATFS2_FS_DATA      /* Ultrix */
+
+  struct fs_data fsd;
+
+  if (statfs (file, &fsd) != 1)
+    return -1;
+
+  fsp->fsu_blocksize = 1024;
+  fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
+  fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
+  fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
+  fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
+  fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
+  fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
+
+#elif defined STAT_STATFS3_OSF1         /* OSF/1 */
+
+  struct statfs fsd;
+
+  if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
+    return -1;
+
+  fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
+
+#elif defined STAT_STATFS2_FRSIZE        /* 2.6 < glibc/Linux < 2.6.36 */
+
+  struct statfs fsd;
+
+  if (statfs (file, &fsd) < 0)
+    return -1;
+
+  fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
+
+#elif defined STAT_STATFS2_BSIZE        /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, 
\
+                                           Mac OS X < 10.4, FreeBSD < 5.0, \
+                                           NetBSD < 3.0, OpenBSD < 4.4 */
+
+  struct statfs fsd;
+
+  if (statfs (file, &fsd) < 0)
+    return -1;
+
+  fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
+
+# ifdef STATFS_TRUNCATES_BLOCK_COUNTS
+
+  /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
+     struct statfs are truncated to 2GB.  These conditions detect that
+     truncation, presumably without botching the 4.1.1 case, in which
+     the values are not truncated.  The correct counts are stored in
+     undocumented spare fields.  */
+  if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
+    {
+      fsd.f_blocks = fsd.f_spare[0];
+      fsd.f_bfree = fsd.f_spare[1];
+      fsd.f_bavail = fsd.f_spare[2];
+    }
+# endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
+
+#elif defined STAT_STATFS2_FSIZE        /* 4.4BSD and older NetBSD */
+
+  struct statfs fsd;
+
+  if (statfs (file, &fsd) < 0)
+    return -1;
+
+  fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
+
+#elif defined STAT_STATFS4              /* SVR3, Dynix, old Irix, old AIX, \
+                                           Dolphin */
+
+# if !_AIX && !defined _SEQUENT_ && !defined DOLPHIN
+#  define f_bavail f_bfree
+# endif
+
+  struct statfs fsd;
+
+  if (statfs (file, &fsd, sizeof fsd, 0) < 0)
+    return -1;
+
+  /* Empirically, the block counts on most SVR3 and SVR3-derived
+     systems seem to always be in terms of 512-byte blocks,
+     no matter what value f_bsize has.  */
+# if _AIX || defined _CRAY
+   fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
+# else
+   fsp->fsu_blocksize = 512;
+# endif
+
+#endif
+
+#if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1                \
+     || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE       \
+     || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
+
+  fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
+  fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
+  fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
+  fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
+  fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
+  fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
+
+#endif
+
+  (void) disk;  /* avoid argument-unused warning */
+  return 0;
+}
+
+#if defined _AIX && defined _I386
+/* AIX PS/2 does not supply statfs.  */
+
+int
+statfs (char *file, struct statfs *fsb)
+{
+  struct stat stats;
+  struct dustat fsd;
+
+  if (stat (file, &stats) != 0)
+    return -1;
+  if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
+    return -1;
+  fsb->f_type   = 0;
+  fsb->f_bsize  = fsd.du_bsize;
+  fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
+  fsb->f_bfree  = fsd.du_tfree;
+  fsb->f_bavail = fsd.du_tfree;
+  fsb->f_files  = (fsd.du_isize - 2) * fsd.du_inopb;
+  fsb->f_ffree  = fsd.du_tinode;
+  fsb->f_fsid.val[0] = fsd.du_site;
+  fsb->f_fsid.val[1] = fsd.du_pckno;
+  return 0;
+}
+
+#endif /* _AIX && _I386 */
diff --git a/lib/fsusage.h b/lib/fsusage.h
new file mode 100644
index 0000000..f78edc6
--- /dev/null
+++ b/lib/fsusage.h
@@ -0,0 +1,40 @@
+/* fsusage.h -- declarations for file system space usage info
+
+   Copyright (C) 1991-1992, 1997, 2003-2006, 2009-2017 Free Software
+   Foundation, Inc.
+
+   This program 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 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   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 this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Space usage statistics for a file system.  Blocks are 512-byte. */
+
+#if !defined FSUSAGE_H_
+# define FSUSAGE_H_
+
+# include <stdint.h>
+# include <stdbool.h>
+
+struct fs_usage
+{
+  uintmax_t fsu_blocksize;      /* Size of a block.  */
+  uintmax_t fsu_blocks;         /* Total blocks. */
+  uintmax_t fsu_bfree;          /* Free blocks available to superuser. */
+  uintmax_t fsu_bavail;         /* Free blocks available to non-superuser. */
+  bool fsu_bavail_top_bit_set;  /* 1 if fsu_bavail represents a value < 0.  */
+  uintmax_t fsu_files;          /* Total file nodes. */
+  uintmax_t fsu_ffree;          /* Free file nodes. */
+};
+
+int get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp);
+
+#endif
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 0f795b3..e9358a6 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib 
--m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux 
--avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix 
--avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=opendir 
--avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv 
--avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool 
--avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h 
-- [...]
+# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib 
--m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux 
--avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix 
--avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=opendir 
--avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv 
--avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool 
--avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h 
-- [...]
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -1516,6 +1516,17 @@ EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
 endif
 ## end   gnulib module fstatat
 
+## begin gnulib module fsusage
+ifeq (,$(OMIT_GNULIB_MODULE_fsusage))
+
+
+EXTRA_DIST += fsusage.c fsusage.h
+
+EXTRA_libgnu_a_SOURCES += fsusage.c
+
+endif
+## end   gnulib module fsusage
+
 ## begin gnulib module fsync
 ifeq (,$(OMIT_GNULIB_MODULE_fsync))
 
diff --git a/lisp/dired.el b/lisp/dired.el
index 9e09d34..1ec3ac4 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -198,8 +198,10 @@ The target is used in the prompt for file copy, rename 
etc."
 
 ; These variables were deleted and the replacements are on files.el.
 ; We leave aliases behind for back-compatibility.
-(defvaralias 'dired-free-space-program 'directory-free-space-program)
-(defvaralias 'dired-free-space-args 'directory-free-space-args)
+(define-obsolete-variable-alias 'dired-free-space-program
+  'directory-free-space-program "27.1")
+(define-obsolete-variable-alias 'dired-free-space-args
+  'directory-free-space-args "27.1")
 
 ;;; Hook variables
 
diff --git a/lisp/files.el b/lisp/files.el
index 336bbc8..194c87a 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -6386,58 +6386,33 @@ if you want to specify options, use 
`directory-free-space-args'.
 
 A value of nil disables this feature.
 
-If the function `file-system-info' is defined, it is always used in
-preference to the program given by this variable."
+This variable is obsolete; Emacs no longer uses it."
   :type '(choice (string :tag "Program") (const :tag "None" nil))
   :group 'dired)
+(make-obsolete-variable 'directory-free-space-program
+                       "ignored, as Emacs uses `file-system-info' instead"
+                       "27.1")
 
 (defcustom directory-free-space-args
   (purecopy (if (eq system-type 'darwin) "-k" "-Pk"))
   "Options to use when running `directory-free-space-program'."
   :type 'string
   :group 'dired)
+(make-obsolete-variable 'directory-free-space-args
+                       "ignored, as Emacs uses `file-system-info' instead"
+                       "27.1")
 
 (defun get-free-disk-space (dir)
   "Return the amount of free space on directory DIR's file system.
 The return value is a string describing the amount of free
 space (normally, the number of free 1KB blocks).
 
-This function calls `file-system-info' if it is available, or
-invokes the program specified by `directory-free-space-program'
-and `directory-free-space-args'.  If the system call or program
-is unsuccessful, or if DIR is a remote directory, this function
-returns nil."
+If DIR's free space cannot be obtained, or if DIR is a remote
+directory, this function returns nil."
   (unless (file-remote-p (expand-file-name dir))
-    ;; Try to find the number of free blocks.  Non-Posix systems don't
-    ;; always have df, but might have an equivalent system call.
-    (if (fboundp 'file-system-info)
-       (let ((fsinfo (file-system-info dir)))
-         (if fsinfo
-             (format "%.0f" (/ (nth 2 fsinfo) 1024))))
-      (setq dir (expand-file-name dir))
-      (save-match-data
-       (with-temp-buffer
-         (when (and directory-free-space-program
-                    ;; Avoid failure if the default directory does
-                    ;; not exist (Bug#2631, Bug#3911).
-                     (let ((default-directory
-                             (locate-dominating-file dir 'file-directory-p)))
-                       (eq (process-file directory-free-space-program
-                                        nil t nil
-                                        directory-free-space-args
-                                         (file-relative-name dir))
-                          0)))
-           ;; Assume that the "available" column is before the
-           ;; "capacity" column.  Find the "%" and scan backward.
-           (goto-char (point-min))
-           (forward-line 1)
-           (when (re-search-forward
-                  "[[:space:]]+[^[:space:]]+%[^%]*$"
-                  (line-end-position) t)
-             (goto-char (match-beginning 0))
-             (let ((endpt (point)))
-               (skip-chars-backward "^[:space:]")
-               (buffer-substring-no-properties (point) endpt)))))))))
+    (let ((avail (nth 2 (file-system-info dir))))
+      (if avail
+         (format "%.0f" (/ avail 1024))))))
 
 ;; The following expression replaces `dired-move-to-filename-regexp'.
 (defvar directory-listing-before-filename-regexp
diff --git a/m4/fsusage.m4 b/m4/fsusage.m4
new file mode 100644
index 0000000..1d6ad41
--- /dev/null
+++ b/m4/fsusage.m4
@@ -0,0 +1,336 @@
+# serial 32
+# Obtaining file system usage information.
+
+# Copyright (C) 1997-1998, 2000-2001, 2003-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Written by Jim Meyering.
+
+AC_DEFUN([gl_FSUSAGE],
+[
+  AC_CHECK_HEADERS_ONCE([sys/param.h])
+  AC_CHECK_HEADERS_ONCE([sys/vfs.h sys/fs_types.h])
+  AC_CHECK_HEADERS([sys/mount.h], [], [],
+    [AC_INCLUDES_DEFAULT
+     [#if HAVE_SYS_PARAM_H
+       #include <sys/param.h>
+      #endif]])
+  gl_FILE_SYSTEM_USAGE([gl_cv_fs_space=yes], [gl_cv_fs_space=no])
+])
+
+# Try to determine how a program can obtain file system usage information.
+# If successful, define the appropriate symbol (see fsusage.c) and
+# execute ACTION-IF-FOUND.  Otherwise, execute ACTION-IF-NOT-FOUND.
+#
+# gl_FILE_SYSTEM_USAGE([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+
+AC_DEFUN([gl_FILE_SYSTEM_USAGE],
+[
+dnl Enable large-file support. This has the effect of changing the size
+dnl of field f_blocks in 'struct statvfs' from 32 bit to 64 bit on
+dnl glibc/Hurd, HP-UX 11, Solaris (32-bit mode). It also changes the size
+dnl of field f_blocks in 'struct statfs' from 32 bit to 64 bit on
+dnl Mac OS X >= 10.5 (32-bit mode).
+AC_REQUIRE([AC_SYS_LARGEFILE])
+
+AC_MSG_CHECKING([how to get file system space usage])
+ac_fsusage_space=no
+
+# Perform only the link test since it seems there are no variants of the
+# statvfs function.  This check is more than just AC_CHECK_FUNCS([statvfs])
+# because that got a false positive on SCO OSR5.  Adding the declaration
+# of a 'struct statvfs' causes this test to fail (as it should) on such
+# systems.  That system is reported to work fine with STAT_STATFS4 which
+# is what it gets when this test fails.
+if test $ac_fsusage_space = no; then
+  # glibc/{Hurd,kFreeBSD}, FreeBSD >= 5.0, NetBSD >= 3.0,
+  # OpenBSD >= 4.4, AIX, HP-UX, IRIX, Solaris, Cygwin, Interix, BeOS.
+  AC_CACHE_CHECK([for statvfs function (SVR4)], [fu_cv_sys_stat_statvfs],
+                 [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+#ifdef __osf__
+"Do not use Tru64's statvfs implementation"
+#endif
+
+#include <sys/statvfs.h>
+
+struct statvfs fsd;
+
+#if defined __APPLE__ && defined __MACH__
+#include <limits.h>
+/* On Mac OS X >= 10.5, f_blocks in 'struct statvfs' is a 32-bit quantity;
+   that commonly limits file systems to 4 TiB.  Whereas f_blocks in
+   'struct statfs' is a 64-bit type, thanks to the large-file support
+   that was enabled above.  In this case, don't use statvfs(); use statfs()
+   instead.  */
+int check_f_blocks_size[sizeof fsd.f_blocks * CHAR_BIT <= 32 ? -1 : 1];
+#endif
+]],
+                                    [[statvfs (0, &fsd);]])],
+                                 [fu_cv_sys_stat_statvfs=yes],
+                                 [fu_cv_sys_stat_statvfs=no])])
+  if test $fu_cv_sys_stat_statvfs = yes; then
+    ac_fsusage_space=yes
+    # AIX >= 5.2 has statvfs64 that has a wider f_blocks field than statvfs.
+    # glibc, HP-UX, IRIX, Solaris have statvfs64 as well, but on these systems
+    # statvfs with large-file support is already equivalent to statvfs64.
+    AC_CACHE_CHECK([whether to use statvfs64],
+      [fu_cv_sys_stat_statvfs64],
+      [AC_LINK_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <sys/types.h>
+              #include <sys/statvfs.h>
+              struct statvfs64 fsd;
+              int check_f_blocks_larger_in_statvfs64
+                [sizeof (((struct statvfs64 *) 0)->f_blocks)
+                 > sizeof (((struct statvfs *) 0)->f_blocks)
+                 ? 1 : -1];
+            ]],
+            [[statvfs64 (0, &fsd);]])],
+         [fu_cv_sys_stat_statvfs64=yes],
+         [fu_cv_sys_stat_statvfs64=no])
+      ])
+    if test $fu_cv_sys_stat_statvfs64 = yes; then
+      AC_DEFINE([STAT_STATVFS64], [1],
+                [  Define if statvfs64 should be preferred over statvfs.])
+    else
+      AC_DEFINE([STAT_STATVFS], [1],
+                [  Define if there is a function named statvfs.  (SVR4)])
+    fi
+  fi
+fi
+
+# Check for this unconditionally so we have a
+# good fallback on glibc/Linux > 2.6 < 2.6.36
+AC_MSG_CHECKING([for two-argument statfs with statfs.f_frsize member])
+AC_CACHE_VAL([fu_cv_sys_stat_statfs2_frsize],
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+  int
+  main ()
+  {
+  struct statfs fsd;
+  fsd.f_frsize = 0;
+  return statfs (".", &fsd) != 0;
+  }]])],
+  [fu_cv_sys_stat_statfs2_frsize=yes],
+  [fu_cv_sys_stat_statfs2_frsize=no],
+  [fu_cv_sys_stat_statfs2_frsize=no])])
+AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_frsize])
+if test $fu_cv_sys_stat_statfs2_frsize = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS2_FRSIZE], [1],
+[  Define if statfs takes 2 args and struct statfs has a field named f_frsize.
+   (glibc/Linux > 2.6)])
+fi
+
+if test $ac_fsusage_space = no; then
+  # DEC Alpha running OSF/1
+  AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)])
+  AC_CACHE_VAL([fu_cv_sys_stat_statfs3_osf1],
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+  int
+  main ()
+  {
+    struct statfs fsd;
+    fsd.f_fsize = 0;
+    return statfs (".", &fsd, sizeof (struct statfs)) != 0;
+  }]])],
+    [fu_cv_sys_stat_statfs3_osf1=yes],
+    [fu_cv_sys_stat_statfs3_osf1=no],
+    [fu_cv_sys_stat_statfs3_osf1=no])])
+  AC_MSG_RESULT([$fu_cv_sys_stat_statfs3_osf1])
+  if test $fu_cv_sys_stat_statfs3_osf1 = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS3_OSF1], [1],
+              [   Define if  statfs takes 3 args.  (DEC Alpha running OSF/1)])
+  fi
+fi
+
+if test $ac_fsusage_space = no; then
+  # glibc/Linux, Mac OS X, FreeBSD < 5.0, NetBSD < 3.0, OpenBSD < 4.4.
+  # (glibc/{Hurd,kFreeBSD}, FreeBSD >= 5.0, NetBSD >= 3.0,
+  # OpenBSD >= 4.4, AIX, HP-UX, OSF/1, Cygwin already handled above.)
+  # (On IRIX you need to include <sys/statfs.h>, not only <sys/mount.h> and
+  # <sys/vfs.h>.)
+  # (On Solaris, statfs has 4 arguments.)
+  AC_MSG_CHECKING([for two-argument statfs with statfs.f_bsize dnl
+member (AIX, 4.3BSD)])
+  AC_CACHE_VAL([fu_cv_sys_stat_statfs2_bsize],
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+  int
+  main ()
+  {
+  struct statfs fsd;
+  fsd.f_bsize = 0;
+  return statfs (".", &fsd) != 0;
+  }]])],
+    [fu_cv_sys_stat_statfs2_bsize=yes],
+    [fu_cv_sys_stat_statfs2_bsize=no],
+    [fu_cv_sys_stat_statfs2_bsize=no])])
+  AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_bsize])
+  if test $fu_cv_sys_stat_statfs2_bsize = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS2_BSIZE], [1],
+[  Define if statfs takes 2 args and struct statfs has a field named f_bsize.
+   (4.3BSD, SunOS 4, HP-UX, AIX PS/2)])
+  fi
+fi
+
+if test $ac_fsusage_space = no; then
+  # SVR3
+  # (Solaris already handled above.)
+  AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)])
+  AC_CACHE_VAL([fu_cv_sys_stat_statfs4],
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+#include <sys/statfs.h>
+  int
+  main ()
+  {
+  struct statfs fsd;
+  return statfs (".", &fsd, sizeof fsd, 0) != 0;
+  }]])],
+    [fu_cv_sys_stat_statfs4=yes],
+    [fu_cv_sys_stat_statfs4=no],
+    [fu_cv_sys_stat_statfs4=no])])
+  AC_MSG_RESULT([$fu_cv_sys_stat_statfs4])
+  if test $fu_cv_sys_stat_statfs4 = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS4], [1],
+      [  Define if statfs takes 4 args.  (SVR3, Dynix, old Irix, old AIX, 
Dolphin)])
+  fi
+fi
+
+if test $ac_fsusage_space = no; then
+  # 4.4BSD and older NetBSD
+  # (OSF/1 already handled above.)
+  # (On AIX, you need to include <sys/statfs.h>, not only <sys/mount.h>.)
+  # (On Solaris, statfs has 4 arguments and 'struct statfs' is not declared in
+  # <sys/mount.h>.)
+  AC_MSG_CHECKING([for two-argument statfs with statfs.f_fsize dnl
+member (4.4BSD and NetBSD)])
+  AC_CACHE_VAL([fu_cv_sys_stat_statfs2_fsize],
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+  int
+  main ()
+  {
+  struct statfs fsd;
+  fsd.f_fsize = 0;
+  return statfs (".", &fsd) != 0;
+  }]])],
+    [fu_cv_sys_stat_statfs2_fsize=yes],
+    [fu_cv_sys_stat_statfs2_fsize=no],
+    [fu_cv_sys_stat_statfs2_fsize=no])])
+  AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_fsize])
+  if test $fu_cv_sys_stat_statfs2_fsize = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS2_FSIZE], [1],
+[  Define if statfs takes 2 args and struct statfs has a field named f_fsize.
+   (4.4BSD, NetBSD)])
+  fi
+fi
+
+if test $ac_fsusage_space = no; then
+  # Ultrix
+  AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)])
+  AC_CACHE_VAL([fu_cv_sys_stat_fs_data],
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+  int
+  main ()
+  {
+  struct fs_data fsd;
+  /* Ultrix's statfs returns 1 for success,
+     0 for not mounted, -1 for failure.  */
+  return statfs (".", &fsd) != 1;
+  }]])],
+    [fu_cv_sys_stat_fs_data=yes],
+    [fu_cv_sys_stat_fs_data=no],
+    [fu_cv_sys_stat_fs_data=no])])
+  AC_MSG_RESULT([$fu_cv_sys_stat_fs_data])
+  if test $fu_cv_sys_stat_fs_data = yes; then
+    ac_fsusage_space=yes
+    AC_DEFINE([STAT_STATFS2_FS_DATA], [1],
+[  Define if statfs takes 2 args and the second argument has
+   type struct fs_data.  (Ultrix)])
+  fi
+fi
+
+AS_IF([test $ac_fsusage_space = yes], [$1], [$2])
+
+])
+
+
+# Check for SunOS statfs brokenness wrt partitions 2GB and larger.
+# If <sys/vfs.h> exists and struct statfs has a member named f_spare,
+# enable the work-around code in fsusage.c.
+AC_DEFUN([gl_STATFS_TRUNCATES],
+[
+  AC_MSG_CHECKING([for statfs that truncates block counts])
+  AC_CACHE_VAL([fu_cv_sys_truncating_statfs],
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#if !defined(sun) && !defined(__sun)
+choke -- this is a workaround for a Sun-specific problem
+#endif
+#include <sys/types.h>
+#include <sys/vfs.h>]],
+      [[struct statfs t; long c = *(t.f_spare);
+        if (c) return 0;]])],
+    [fu_cv_sys_truncating_statfs=yes],
+    [fu_cv_sys_truncating_statfs=no])])
+  if test $fu_cv_sys_truncating_statfs = yes; then
+    AC_DEFINE([STATFS_TRUNCATES_BLOCK_COUNTS], [1],
+      [Define if the block counts reported by statfs may be truncated to 2GB
+       and the correct values may be stored in the f_spare array.
+       (SunOS 4.1.2, 4.1.3, and 4.1.3_U1 are reported to have this problem.
+       SunOS 4.1.1 seems not to be affected.)])
+  fi
+  AC_MSG_RESULT([$fu_cv_sys_truncating_statfs])
+])
+
+
+# Prerequisites of lib/fsusage.c not done by gl_FILE_SYSTEM_USAGE.
+AC_DEFUN([gl_PREREQ_FSUSAGE_EXTRA],
+[
+  AC_CHECK_HEADERS([dustat.h sys/fs/s5param.h sys/statfs.h])
+  gl_STATFS_TRUNCATES
+])
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index c551752..cb255fc 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -87,6 +87,7 @@ AC_DEFUN([gl_EARLY],
   # Code from module flexmember:
   # Code from module fpending:
   # Code from module fstatat:
+  # Code from module fsusage:
   # Code from module fsync:
   # Code from module getdtablesize:
   # Code from module getgroups:
@@ -256,6 +257,11 @@ AC_DEFUN([gl_INIT],
     AC_LIBOBJ([fstatat])
   fi
   gl_SYS_STAT_MODULE_INDICATOR([fstatat])
+  gl_FSUSAGE
+  if test $gl_cv_fs_space = yes; then
+    AC_LIBOBJ([fsusage])
+    gl_PREREQ_FSUSAGE_EXTRA
+  fi
   gl_FUNC_FSYNC
   if test $HAVE_FSYNC = 0; then
     AC_LIBOBJ([fsync])
@@ -864,6 +870,8 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/fpending.c
   lib/fpending.h
   lib/fstatat.c
+  lib/fsusage.c
+  lib/fsusage.h
   lib/fsync.c
   lib/ftoastr.c
   lib/ftoastr.h
@@ -995,6 +1003,7 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/flexmember.m4
   m4/fpending.m4
   m4/fstatat.m4
+  m4/fsusage.m4
   m4/fsync.m4
   m4/getdtablesize.m4
   m4/getgroups.m4
diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk
index 419099e..f621667 100644
--- a/nt/gnulib-cfg.mk
+++ b/nt/gnulib-cfg.mk
@@ -49,6 +49,7 @@ OMIT_GNULIB_MODULE_dirent = true
 OMIT_GNULIB_MODULE_dirfd = true
 OMIT_GNULIB_MODULE_fcntl = true
 OMIT_GNULIB_MODULE_fcntl-h = true
+OMIT_GNULIB_MODULE_fsusage = true
 OMIT_GNULIB_MODULE_inttypes-incomplete = true
 OMIT_GNULIB_MODULE_open = true
 OMIT_GNULIB_MODULE_pipe2 = true
diff --git a/src/fileio.c b/src/fileio.c
index adb3534..1137027 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -96,6 +96,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <acl.h>
 #include <allocator.h>
 #include <careadlinkat.h>
+#include <fsusage.h>
 #include <stat-time.h>
 #include <tempname.h>
 
@@ -5765,6 +5766,40 @@ effect except for flushing STREAM's data.  */)
   return (set_binary_mode (fileno (fp), binmode) == O_BINARY) ? Qt : Qnil;
 }
 
+#ifndef DOS_NT
+
+/* Yield a Lisp float as close as possible to BLOCKSIZE * BLOCKS, with
+   the result negated if NEGATE.  */
+static Lisp_Object
+blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, bool negate)
+{
+  /* On typical platforms the following code is accurate to 53 bits,
+     which is close enough.  BLOCKSIZE is invariably a power of 2, so
+     converting it to double does not lose information.  */
+  double bs = blocksize;
+  return make_float (negate ? -bs * -blocks : bs * blocks);
+}
+
+DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
+       doc: /* Return storage information about the file system FILENAME is on.
+Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total
+storage of the file system, FREE is the free storage, and AVAIL is the
+storage available to a non-superuser.  All 3 numbers are in bytes.
+If the underlying system call fails, value is nil.  */)
+  (Lisp_Object filename)
+{
+  Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (filename, Qnil));
+  struct fs_usage u;
+  if (get_fs_usage (SSDATA (encoded), NULL, &u) != 0)
+    return Qnil;
+  return list3 (blocks_to_bytes (u.fsu_blocksize, u.fsu_blocks, false),
+               blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false),
+               blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail,
+                                u.fsu_bavail_top_bit_set));
+}
+
+#endif /* !DOS_NT */
+
 void
 init_fileio (void)
 {
@@ -6115,6 +6150,10 @@ This includes interactive calls to `delete-file' and
 
   defsubr (&Sset_binary_mode);
 
+#ifndef DOS_NT
+  defsubr (&Sfile_system_info);
+#endif
+
 #ifdef HAVE_SYNC
   defsubr (&Sunix_sync);
 #endif



reply via email to

[Prev in Thread] Current Thread [Next in Thread]