(defgroup df nil
"Disk space monitoring"
(defvar df-timer nil
"Variable containing the timer for `df-update'.")
(defsubst df-cancel-timer ()
(setq df-timer (when df-timer (cancel-timer df-timer))))
(defsubst df-initialise-timer ()
(when (> df-interval 0)
(setq df-timer (run-at-time 0 df-interval 'df-update))))
(defcustom df-interval 60
"*The time \(in seconds) between updates of the disk usage
statistics in the mode line. A value of 0 switches off the check
but leaves the mode on (i.e. it does not update the information
in the mode line)."
:set (lambda (sym val)
(set-default sym val)
(defcustom df-block-size 1000
"*The size of the blocks \(in kilobytes) that
`directory-free-space-program' is asked to count. It must be a
non-zero positive integer. \"1k\" in the mode-line denotes one
such block. Common values are 1000 and 1024 \(bytes) . The
POSIX value of 512 can also be used, as can other values, but
they might be confusing (in the case of the POSIX value \"1k\"
denotes 512 bytes).
`df-block-size' is not to be confused with `df-unit', which is
the factor of difference between the unit prefixes."
:type '(choice (const 1000)
(const :tag "POSIX" 512)
(integer :tag "Another value"))
(defcustom df-unit 1000
"*This is the difference in order of magnitude between the
subsequent elements in `df-unit-array': `df-unit'k is 1M, and so
on. In SI, this is 1000, but elsewhere 1M \(for instance) is
commonly taken to be 1024k."
:type '(choice (const :tag "SI" 1000)
(const :tag "Human-readable" 1024))
"*Regexp describing the output of
`directory-free-space-program'. The amount of free space should
be in group 1.
See also `df-switches'."
(defcustom df-switches '("-P")
"*This is a list of strings containing command line switches
for `directory-free-space-program'. If you add or remove
switches that change the format of the output from
`directory-free-space-program', you need to adapt
`df-output-regexp' as well.
In GNU df, \"-P\" makes `directory-free-space-program' print each
result on one line."
:type '(repeat string)
(defcustom df-block-size-switch "-B"
"*Command line switch to `directory-free-space-program' for
setting the block size. The size \(see `df-block-size') will be
attached directly after the switch \(without spaces)."
(defvar df-string ""
"Variable containing the string to be inserted into the mode line.")
(defvar df-free-space nil
"Variable containing the amount of free space on the current partition.")
(defvar df-buffer-weight 0)
(defconst df-unit-array ["k" "M" "G" "T" "P" "E" "Z" "Y"]
"Array with unit prefixes. Every next element should be
`df-unit' times bigger than the current one.")
(defun df-reduce-number (number)
"Takes NUMBER \(assumed to be in the unit \"k\", the first
element of `df-unit-array'), and converts it to the largest unit
suitable \(i.e. where the value lies between 0 and `df-unit').
It returns a cons with the new value and its unit."
(if (> number 0)
(let* ((number (float number))
(exponent (floor (log number df-unit))))
(cons (/ number (expt df-unit exponent))
(aref df-unit-array exponent)))
(cons 0 (aref df-unit-array 0))))
(defun df-find-existing-path (path)
(if (file-directory-p path)
(file-name-directory (directory-file-name path)))))
(defun df-update ()
"Updates the disk usage statistics \(if `df-interval' > 0). It
is run every `df-interval' seconds."
(if (> df-interval 0)
(setq df-buffer-weight (and (buffer-file-name)
(/ (buffer-size) df-unit)))
(let* ((default-directory (df-find-existing-path default-directory)))
(apply 'start-process directory-free-space-program nil
(cons (format "%s%d" df-block-size-switch df-block-size)
(append df-switches (list default-directory))))
(defun df-filter (proc string)
"Filter for the output from `directory-free-space-program'.
It sets the mode-line value."
(format " %s/%s"
(let ((res (df-reduce-number df-buffer-weight)))
(format "%d%s" (round (car res)) (cdr res)))
(if (and (string-match df-output-regexp string)
(match-string 1 string))
(string-to-number (match-string 1 string)))
(let ((res (df-reduce-number df-free-space)))
(format "%d%s" (round (car res)) (cdr res))))
(defun df-check (&rest args)
"Check whether there is still enough space to save the file.
If not, ask whether the file should be saved or not."
(when (and (numberp df-buffer-weight)
(> df-buffer-weight df-free-space)
"It seems there is not enough disk space. Save anyway? ")))
(error "Buffer %s not saved" (buffer-name)))
"Toggle display of space that is left on the filesystem on
which the contents of the current buffer is stored. This display
is updated every `df-interval' seconds.
With a numeric argument, enable this display if ARG is positive."
(add-hook 'write-file-functions 'df-check)
(add-hook 'find-file-hook 'df-update)
(remove-hook 'write-file-functions 'df-check)
(remove-hook 'find-file-hook 'df-update)