gnu-emacs-sources
[Top][All Lists]
Advanced

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

df-mode.el


From: Frederik Fouvry
Subject: df-mode.el
Date: 05 Nov 2004 19:12:31 +0100
User-agent: Emacs Gnus

A slightly updated version of df-mode.el (a minor mode that checks if
there is sufficient disk space before a buffer is saved; very useful
on disks that are almost full).

Frederik Fouvry


;;; df-mode.el --- Minor mode to show space left on devices in the mode line 

;; Copyright (C) 1999 by Association April
;; Copyright (C) 2004 by Frederik Fouvry

;; Author: Benjamin Drieu <address@hidden>
;;         Frederik Fouvry <address@hidden>
;; Keywords: unix, tools

;; This file is NOT part of GNU Emacs.
;; This is free software.

;; GNU Emacs as this program are free software; you can redistribute
;; them and/or modify them under the terms of the GNU General Public
;; License as published by the Free Software Foundation; either
;; version 2, or (at your option) any later version.

;; They are both distributed in the hope that they 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 GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.


;;; Commentary:

;;  This is an extension to display disk usage in the mode line.  Disk
;;  space remaining is updated every `df-interval' seconds.
;; 
;;  If you work with a lot of users sharing the same partition, it
;;  sometimes happens that you have no place left to save your work,
;;  which can be extremely annoying, and might lead to loss of work.
;;  This package allows you to have disk space available and buffer
;;  size displayed in the mode line, so you know when you can save
;;  your file or when it is time to do some tidying up.
;;
;;  Comments and suggestions are welcome.
;;
;;  df is simple to use:
;;  - Make sure this file is in your load-path
;;  - Put in ~/.emacs:
;;      (autoload 'df-mode "df-mode" nil t)
;;      (df-mode 1)
;;    The minor mode can be toggled with M-x df-mode.
;;
;;  Version: 2004-11-05
;;  Note: this is a rewrite of df.el by Benjamin Drieu:
;;  - Added customization
;;  - Completely rewrote internals (the actual "engine" has been simplified, but
;;    the customisation enhanced)
;;  - The partitions that are checked are now buffer-local
;;  - Added the function that checks disk space before writing a buffer

;;; Code:

(require 'timer)

(defgroup df nil
  "Disk space monitoring"
  :group 'environment
  :version "21.3")

;;;###autoload
(defcustom df-mode nil
  "Toggle df-mode.
Setting this variable directly does not take effect;
use either \\[customize] or the function `df-mode'."
  :set (lambda (symbol value) (df-mode (or value 0)))
  :initialize 'custom-initialize-default
  :type    'boolean
  :group   'df
  :require 'df-mode)

(defcustom df-program "df"
  "*The command to use to retrieve disk usage statistics."
  :type 'string
  :group 'df)

(defcustom df-interval 60 
  "*The time (in seconds) between updates of the disk usage
statistics in the mode line."
  :type 'integer
  :group 'df)

(defcustom df-block-size 1000
  "*The size of the blocks (in kilobytes) that `df-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 1024)
                 (const :tag "POSIX" 512)
                 (integer :tag "Another value"))
  :group 'df)

(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))
  :group 'df)

(defcustom df-output-regexp
  "^\\S-+\\s-+\\(?:[0-9]+\\s-+\\)\\{2\\}\\([0-9]+\\)\\s-+[0-9]\\{1,3\\}% .+$"
;; Linux, non POSIX:
;; "\\s-+\\(?:[0-9]+\\s-*\\)\\{2\\}\\([0-9]+\\)\\s-*[0-9]\\{1,3\\}% /"
  "*Regexp describing the output of `df-program'. The amount of free
space should be in group 1.

See also `df-switches'."
  :type 'regexp
  :group 'df)

(defcustom df-switches '("-P")
  "*This is a list of strings containing command line switches for
`df-program'.  If you add or remove switches that change the format of
the output from `df-program', you need to adapt `df-output-regexp' as
well.

In GNU df, \"-P\" makes `df-program' print each result on one line."
  :type '(repeat string)
  :group 'df)

(defcustom df-block-size-switch "-B"
  "*Command line switch to `df-program' for setting the block size.
The size (see `df-block-size') will be attached directly after the
switch (without spaces)."
  :type 'string
  :group 'df)


(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-timer nil
  "Variable containing the timer for `df-update'.")

(defvar df-buffer-weight 0)

;; The smallest unit should be the first
(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))))


;; Sets `df-mode' to t if `df-timer' is a timer and it is in
;; `timer-list', nil otherwise.  Side effect: if `df-timer' is a
;; timer, but not in `timer-list', `df-timer' is set to nil as well.
;; (ie postcondition: (null df-mode) <=> (not (timerp df-timer)).
(defun df-mode-set-df-mode ()
  (setq df-mode (not (null (and (timerp df-timer)
                                (or (member df-timer timer-list)
                                    (setq df-timer nil)))))))


(defun df-update ()
  "Updates the disk usage statistics.  It is run every `df-interval'
seconds."
  (interactive)
  (setq df-buffer-weight (and (buffer-file-name)
                              (/ (buffer-size) df-unit)))
  (set-process-filter
   (apply 'start-process df-program nil df-program
          (cons (format "%s%d" df-block-size-switch df-block-size)
                (append df-switches
                        (list (or (buffer-file-name)
                                  (expand-file-name default-directory))))))
   'df-filter))


;; For float values:
;; (format "%%.%df" (- (ceiling (log df-unit 10)) (ceiling (log (car res) 10))))
;; but trailing 0s should be removed
(defun df-filter (proc string)
  "Filter for the output from `df-program'.  It sets the mode-line
value."
  (setq df-string
        (format " %s/%s"
                (if df-buffer-weight
                    (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))
                    (progn
                      (setq df-free-space
                            (string-to-number (match-string 1 string)))
                      (let ((res (df-reduce-number df-free-space)))
                        (format "%d%s" (round (car res)) (cdr res))))
                  "-"))))


;; Is used in run-hooks-with-args-until-success, so it should always return
;; nil.
(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."
  (df-update)
  (when (and (numberp df-buffer-weight)
             (numberp df-free-space)
             (> df-buffer-weight df-free-space)
             (not (yes-or-no-p 
                   "It seems there is not enough disk space.  Save anyway? ")))
    (error "Buffer %s not saved" (buffer-name)))
  nil)


;;;###autoload
(defun df-mode (&optional arg)
  "Toggle display of space left on the filesystem on 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."
  (interactive "P")
  (if (or (and (null arg) (null df-timer))
          (and arg (> (prefix-numeric-value arg) 0)))
      (unless df-mode
        (make-variable-buffer-local 'df-buffer-weight)
        (make-variable-buffer-local 'df-string)
        (make-variable-buffer-local 'df-free-space)
        (setq df-timer (run-with-timer 0 df-interval 'df-update))
        (add-to-list 'minor-mode-alist '(df-mode df-string) t)
        (add-hook 'write-file-hooks 'df-check)
        (add-hook 'find-file-hooks 'df-update)
        (df-update))
    (progn
      (cancel-timer df-timer)
      (setq df-timer nil)))
  (df-mode-set-df-mode))

(provide 'df-mode)

;;; df-mode.el ends here


reply via email to

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