]> code.delx.au - gnu-emacs/blob - lisp/jka-cmpr-hook.el
automatically handle .xz suffix (XZ-compressed files), too
[gnu-emacs] / lisp / jka-cmpr-hook.el
1 ;;; jka-cmpr-hook.el --- preloaded code to enable jka-compr.el
2
3 ;; Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2002, 2003,
4 ;; 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
5
6 ;; Author: jka@ece.cmu.edu (Jay K. Adams)
7 ;; Maintainer: FSF
8 ;; Keywords: data
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; This file contains the code to enable and disable Auto-Compression mode.
28 ;; It is preloaded. The guts of this mode are in jka-compr.el, which
29 ;; is loaded only when you really try to uncompress something.
30
31 ;;; Code:
32
33 (defgroup compression nil
34 "Data compression utilities."
35 :group 'data)
36
37 (defgroup jka-compr nil
38 "jka-compr customization."
39 :group 'compression)
40
41 ;; List of all the elements we actually added to file-coding-system-alist.
42 (defvar jka-compr-added-to-file-coding-system-alist nil)
43
44 (defvar jka-compr-file-name-handler-entry
45 nil
46 "`file-name-handler-alist' entry used by jka-compr I/O functions.")
47
48 ;; Compiler defvars. These three variables will be defined later with
49 ;; `defcustom' when everything used in the :set functions is defined.
50 (defvar jka-compr-compression-info-list)
51 (defvar jka-compr-mode-alist-additions)
52 (defvar jka-compr-load-suffixes)
53
54 (defvar jka-compr-compression-info-list--internal nil
55 "Stored value of `jka-compr-compression-info-list'.
56 If Auto Compression mode is enabled, this is the value of
57 `jka-compr-compression-info-list' when `jka-compr-install' was last called.
58 Otherwise, it is nil.")
59
60 (defvar jka-compr-mode-alist-additions--internal nil
61 "Stored value of `jka-compr-mode-alist-additions'.
62 If Auto Compression mode is enabled, this is the value of
63 `jka-compr-mode-alist-additions' when `jka-compr-install' was last called.
64 Otherwise, it is nil.")
65
66 (defvar jka-compr-load-suffixes--internal nil
67 "Stored value of `jka-compr-load-suffixes'.
68 If Auto Compression mode is enabled, this is the value of
69 `jka-compr-load-suffixes' when `jka-compr-install' was last called.
70 Otherwise, it is nil.")
71
72 \f
73 (defun jka-compr-build-file-regexp ()
74 (mapconcat
75 'jka-compr-info-regexp
76 jka-compr-compression-info-list
77 "\\|"))
78
79 ;; Functions for accessing the return value of jka-compr-get-compression-info
80 (defun jka-compr-info-regexp (info) (aref info 0))
81 (defun jka-compr-info-compress-message (info) (aref info 1))
82 (defun jka-compr-info-compress-program (info) (aref info 2))
83 (defun jka-compr-info-compress-args (info) (aref info 3))
84 (defun jka-compr-info-uncompress-message (info) (aref info 4))
85 (defun jka-compr-info-uncompress-program (info) (aref info 5))
86 (defun jka-compr-info-uncompress-args (info) (aref info 6))
87 (defun jka-compr-info-can-append (info) (aref info 7))
88 (defun jka-compr-info-strip-extension (info) (aref info 8))
89 (defun jka-compr-info-file-magic-bytes (info) (aref info 9))
90
91
92 (defun jka-compr-get-compression-info (filename)
93 "Return information about the compression scheme of FILENAME.
94 The determination as to which compression scheme, if any, to use is
95 based on the filename itself and `jka-compr-compression-info-list'."
96 (catch 'compression-info
97 (let ((case-fold-search nil))
98 (mapc
99 (function (lambda (x)
100 (and (string-match (jka-compr-info-regexp x) filename)
101 (throw 'compression-info x))))
102 jka-compr-compression-info-list)
103 nil)))
104
105 (defun jka-compr-install ()
106 "Install jka-compr.
107 This adds entries to `file-name-handler-alist' and `auto-mode-alist'
108 and `inhibit-first-line-modes-suffixes'."
109
110 (setq jka-compr-file-name-handler-entry
111 (cons (jka-compr-build-file-regexp) 'jka-compr-handler))
112
113 (push jka-compr-file-name-handler-entry file-name-handler-alist)
114
115 (setq jka-compr-compression-info-list--internal
116 jka-compr-compression-info-list
117 jka-compr-mode-alist-additions--internal
118 jka-compr-mode-alist-additions
119 jka-compr-load-suffixes--internal
120 jka-compr-load-suffixes)
121
122 (dolist (x jka-compr-compression-info-list)
123 ;; Don't do multibyte encoding on the compressed files.
124 (let ((elt (cons (jka-compr-info-regexp x)
125 '(no-conversion . no-conversion))))
126 (push elt file-coding-system-alist)
127 (push elt jka-compr-added-to-file-coding-system-alist))
128
129 (and (jka-compr-info-strip-extension x)
130 ;; Make entries in auto-mode-alist so that modes
131 ;; are chosen right according to the file names
132 ;; sans `.gz'.
133 (push (list (jka-compr-info-regexp x) nil 'jka-compr) auto-mode-alist)
134 ;; Also add these regexps to
135 ;; inhibit-first-line-modes-suffixes, so that a
136 ;; -*- line in the first file of a compressed tar
137 ;; file doesn't override tar-mode.
138 (push (jka-compr-info-regexp x)
139 inhibit-first-line-modes-suffixes)))
140 (setq auto-mode-alist
141 (append auto-mode-alist jka-compr-mode-alist-additions))
142
143 ;; Make sure that (load "foo") will find /bla/foo.el.gz.
144 (setq load-file-rep-suffixes
145 (append load-file-rep-suffixes jka-compr-load-suffixes nil)))
146
147 (defun jka-compr-installed-p ()
148 "Return non-nil if jka-compr is installed.
149 The return value is the entry in `file-name-handler-alist' for jka-compr."
150
151 (let ((fnha file-name-handler-alist)
152 (installed nil))
153
154 (while (and fnha (not installed))
155 (and (eq (cdr (car fnha)) 'jka-compr-handler)
156 (setq installed (car fnha)))
157 (setq fnha (cdr fnha)))
158
159 installed))
160
161 (defun jka-compr-update ()
162 "Update Auto Compression mode for changes in option values.
163 If you change the options `jka-compr-compression-info-list',
164 `jka-compr-mode-alist-additions' or `jka-compr-load-suffixes'
165 outside Custom, while Auto Compression mode is already enabled
166 \(as it is by default), then you have to call this function
167 afterward to properly update other variables. Setting these
168 options through Custom does this automatically."
169 (when (jka-compr-installed-p)
170 (jka-compr-uninstall)
171 (jka-compr-install)))
172
173 (defun jka-compr-set (variable value)
174 "Internal Custom :set function."
175 (set-default variable value)
176 (jka-compr-update))
177
178 ;; I have this defined so that .Z files are assumed to be in unix
179 ;; compress format; and .gz files, in gzip format, and .bz2 files in bzip fmt.
180
181 ;; FIXME? It seems ugly that one has to add "\\(~\\|\\.~[0-9]+~\\)?" to
182 ;; all the regexps here, in order to match backup files etc.
183 ;; It's trivial to modify jka-compr-get-compression-info to match
184 ;; regexps against file-name-sans-versions, but this regexp is also
185 ;; used to build a file-name-handler-alist entry.
186 ;; find-file-name-handler does not use file-name-sans-versions.
187 ;; Perhaps it should,
188 ;; http://lists.gnu.org/archive/html/emacs-devel/2008-02/msg00812.html,
189 ;; but it's used all over the place and there are probably other ramifications.
190 ;; One could modify jka-compr-build-file-regexp to add the backup regexp,
191 ;; but jka-compr-compression-info-list is a defcustom to which
192 ;; anything could be added, so it's easiest to leave things as they are.
193 (defcustom jka-compr-compression-info-list
194 ;;[regexp
195 ;; compr-message compr-prog compr-args
196 ;; uncomp-message uncomp-prog uncomp-args
197 ;; can-append strip-extension-flag file-magic-bytes]
198 '(["\\.Z\\(~\\|\\.~[0-9]+~\\)?\\'"
199 "compressing" "compress" ("-c")
200 ;; gzip is more common than uncompress. It can only read, not write.
201 "uncompressing" "gzip" ("-c" "-q" "-d")
202 nil t "\037\235"]
203 ;; Formerly, these had an additional arg "-c", but that fails with
204 ;; "Version 0.1pl2, 29-Aug-97." (RedHat 5.1 GNU/Linux) and
205 ;; "Version 0.9.0b, 9-Sept-98".
206 ["\\.bz2\\(~\\|\\.~[0-9]+~\\)?\\'"
207 "bzip2ing" "bzip2" nil
208 "bunzip2ing" "bzip2" ("-d")
209 nil t "BZh"]
210 ["\\.tbz2?\\'"
211 "bzip2ing" "bzip2" nil
212 "bunzip2ing" "bzip2" ("-d")
213 nil nil "BZh"]
214 ["\\.\\(?:tgz\\|svgz\\|sifz\\)\\(~\\|\\.~[0-9]+~\\)?\\'"
215 "compressing" "gzip" ("-c" "-q")
216 "uncompressing" "gzip" ("-c" "-q" "-d")
217 t nil "\037\213"]
218 ["\\.g?z\\(~\\|\\.~[0-9]+~\\)?\\'"
219 "compressing" "gzip" ("-c" "-q")
220 "uncompressing" "gzip" ("-c" "-q" "-d")
221 t t "\037\213"]
222 ["\\.xz\\(~\\|\\.~[0-9]+~\\)?\\'"
223 "XZ compressing" "xz" ("-c" "-q")
224 "XZ uncompressing" "xz" ("-c" "-q" "-d")
225 t t "\3757zXZ\0"]
226 ;; dzip is gzip with random access. Its compression program can't
227 ;; read/write stdin/out, so .dz files can only be viewed without
228 ;; saving, having their contents decompressed with gzip.
229 ["\\.dz\\'"
230 nil nil nil
231 "uncompressing" "gzip" ("-c" "-q" "-d")
232 nil t "\037\213"])
233
234 "List of vectors that describe available compression techniques.
235 Each element, which describes a compression technique, is a vector of
236 the form [REGEXP COMPRESS-MSG COMPRESS-PROGRAM COMPRESS-ARGS
237 UNCOMPRESS-MSG UNCOMPRESS-PROGRAM UNCOMPRESS-ARGS
238 APPEND-FLAG STRIP-EXTENSION-FLAG FILE-MAGIC-CHARS], where:
239
240 regexp is a regexp that matches filenames that are
241 compressed with this format
242
243 compress-msg is the message to issue to the user when doing this
244 type of compression (nil means no message)
245
246 compress-program is a program that performs this compression
247 (nil means visit file in read-only mode)
248
249 compress-args is a list of args to pass to the compress program
250
251 uncompress-msg is the message to issue to the user when doing this
252 type of uncompression (nil means no message)
253
254 uncompress-program is a program that performs this compression
255
256 uncompress-args is a list of args to pass to the uncompress program
257
258 append-flag is non-nil if this compression technique can be
259 appended
260
261 strip-extension-flag non-nil means strip the regexp from file names
262 before attempting to set the mode.
263
264 file-magic-chars is a string of characters that you would find
265 at the beginning of a file compressed in this way.
266
267 If you set this outside Custom while Auto Compression mode is
268 already enabled \(as it is by default), you have to call
269 `jka-compr-update' after setting it to properly update other
270 variables. Setting this through Custom does that automatically."
271 :type '(repeat (vector regexp
272 (choice :tag "Compress Message"
273 (string :format "%v")
274 (const :tag "No Message" nil))
275 (choice :tag "Compress Program"
276 (string)
277 (const :tag "None" nil))
278 (repeat :tag "Compress Arguments" string)
279 (choice :tag "Uncompress Message"
280 (string :format "%v")
281 (const :tag "No Message" nil))
282 (choice :tag "Uncompress Program"
283 (string)
284 (const :tag "None" nil))
285 (repeat :tag "Uncompress Arguments" string)
286 (boolean :tag "Append")
287 (boolean :tag "Strip Extension")
288 (string :tag "Magic Bytes")))
289 :set 'jka-compr-set
290 :group 'jka-compr)
291
292 (defcustom jka-compr-mode-alist-additions
293 (list (cons "\\.tgz\\'" 'tar-mode) (cons "\\.tbz2?\\'" 'tar-mode))
294 "List of pairs added to `auto-mode-alist' when installing jka-compr.
295 Uninstalling jka-compr removes all pairs from `auto-mode-alist' that
296 installing added.
297
298 If you set this outside Custom while Auto Compression mode is
299 already enabled \(as it is by default), you have to call
300 `jka-compr-update' after setting it to properly update other
301 variables. Setting this through Custom does that automatically."
302 :type '(repeat (cons string symbol))
303 :set 'jka-compr-set
304 :group 'jka-compr)
305
306 (defcustom jka-compr-load-suffixes '(".gz")
307 "List of compression related suffixes to try when loading files.
308 Enabling Auto Compression mode appends this list to `load-file-rep-suffixes',
309 which see. Disabling Auto Compression mode removes all suffixes
310 from `load-file-rep-suffixes' that enabling added.
311
312 If you set this outside Custom while Auto Compression mode is
313 already enabled \(as it is by default), you have to call
314 `jka-compr-update' after setting it to properly update other
315 variables. Setting this through Custom does that automatically."
316 :type '(repeat string)
317 :set 'jka-compr-set
318 :group 'jka-compr)
319
320 (define-minor-mode auto-compression-mode
321 "Toggle automatic file compression and uncompression.
322 With prefix argument ARG, turn auto compression on if positive, else off.
323 Return the new status of auto compression (non-nil means on)."
324 :global t :init-value t :group 'jka-compr :version "22.1"
325 (let* ((installed (jka-compr-installed-p))
326 (flag auto-compression-mode))
327 (cond
328 ((and flag installed) t) ; already installed
329 ((and (not flag) (not installed)) nil) ; already not installed
330 (flag (jka-compr-install))
331 (t (jka-compr-uninstall)))))
332
333 (defmacro with-auto-compression-mode (&rest body)
334 "Evalute BODY with automatic file compression and uncompression enabled."
335 (let ((already-installed (make-symbol "already-installed")))
336 `(let ((,already-installed (jka-compr-installed-p)))
337 (unwind-protect
338 (progn
339 (unless ,already-installed
340 (jka-compr-install))
341 ,@body)
342 (unless ,already-installed
343 (jka-compr-uninstall))))))
344 (put 'with-auto-compression-mode 'lisp-indent-function 0)
345
346
347 ;; This is what we need to know about jka-compr-handler
348 ;; in order to decide when to call it.
349
350 (put 'jka-compr-handler 'safe-magic t)
351 (put 'jka-compr-handler 'operations '(byte-compiler-base-file-name
352 write-region insert-file-contents
353 file-local-copy load))
354
355 ;; Turn on the mode.
356 (when auto-compression-mode (auto-compression-mode 1))
357
358 (provide 'jka-cmpr-hook)
359
360 ;; arch-tag: 4bd73429-f400-45fe-a065-270a113e31a8
361 ;;; jka-cmpr-hook.el ends here