Resurrect a comment lost in the previous commit.
[bpt/emacs.git] / lisp / jka-compr.el
CommitLineData
be010748
RS
1;;; jka-compr.el --- reading/writing/loading compressed files
2
523b128c 3;; Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
acd622cc
RS
4
5;; Author: jka@ece.cmu.edu (Jay K. Adams)
4228277d 6;; Maintainer: FSF
acd622cc
RS
7;; Keywords: data
8
f4454a14
RS
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
f4454a14 25
55535639 26;;; Commentary:
acd622cc 27
b578f267
EN
28;; This package implements low-level support for reading, writing,
29;; and loading compressed files. It hooks into the low-level file
30;; I/O functions (including write-region and insert-file-contents) so
31;; that they automatically compress or uncompress a file if the file
32;; appears to need it (based on the extension of the file name).
33;; Packages like Rmail, VM, GNUS, and Info should be able to work
34;; with compressed files without modification.
35
36
37;; INSTRUCTIONS:
38;;
7391213d
MB
39;; To use jka-compr, invoke the command `auto-compression-mode' (which
40;; see), or customize the variable of the same name. Its operation
41;; should be transparent to the user (except for messages appearing when
42;; a file is being compressed or uncompressed).
b578f267
EN
43;;
44;; The variable, jka-compr-compression-info-list can be used to
45;; customize jka-compr to work with other compression programs.
46;; The default value of this variable allows jka-compr to work with
47;; Unix compress and gzip.
48;;
49;; If you are concerned about the stderr output of gzip and other
50;; compression/decompression programs showing up in your buffers, you
51;; should set the discard-error flag in the compression-info-list.
52;; This will cause the stderr of all programs to be discarded.
53;; However, it also causes emacs to call compression/uncompression
54;; programs through a shell (which is specified by jka-compr-shell).
55;; This may be a drag if, on your system, starting up a shell is
56;; slow.
57;;
58;; If you don't want messages about compressing and decompressing
59;; to show up in the echo area, you can set the compress-name and
60;; decompress-name fields of the jka-compr-compression-info-list to
61;; nil.
62
63
64;; APPLICATION NOTES:
65;;
66;; crypt++
67;; jka-compr can coexist with crpyt++ if you take all the decompression
68;; entries out of the crypt-encoding-list. Clearly problems will arise if
69;; you have two programs trying to compress/decompress files. jka-compr
70;; will not "work with" crypt++ in the following sense: you won't be able to
71;; decode encrypted compressed files--that is, files that have been
72;; compressed then encrypted (in that order). Theoretically, crypt++ and
73;; jka-compr could properly handle a file that has been encrypted then
74;; compressed, but there is little point in trying to compress an encrypted
75;; file.
76;;
77
78
79;; ACKNOWLEDGMENTS
80;;
81;; jka-compr is a V19 adaptation of jka-compr for V18 of Emacs. Many people
82;; have made helpful suggestions, reported bugs, and even fixed bugs in
83;; jka-compr. I recall the following people as being particularly helpful.
84;;
85;; Jean-loup Gailly
86;; David Hughes
87;; Richard Pieri
88;; Daniel Quinlan
89;; Chris P. Ross
90;; Rick Sladkey
91;;
92;; Andy Norman's ange-ftp was the inspiration for the original jka-compr for
93;; Version 18 of Emacs.
94;;
95;; After I had made progress on the original jka-compr for V18, I learned of a
96;; package written by Kazushi Jam Marukawa, called jam-zcat, that did exactly
97;; what I was trying to do. I looked over the jam-zcat source code and
98;; probably got some ideas from it.
99;;
acd622cc
RS
100
101;;; Code:
102
bbf5eb28
RS
103(defgroup compression nil
104 "Data compression utilities"
105 :group 'data)
106
107(defgroup jka-compr nil
108 "jka-compr customization"
109 :group 'compression)
110
111
112(defcustom jka-compr-shell "sh"
acd622cc
RS
113 "*Shell to be used for calling compression programs.
114The value of this variable only matters if you want to discard the
115stderr of a compression/decompression program (see the documentation
bbf5eb28
RS
116for `jka-compr-compression-info-list')."
117 :type 'string
118 :group 'jka-compr)
acd622cc 119
eb915452
RS
120(defvar jka-compr-use-shell
121 (not (memq system-type '(ms-dos windows-nt))))
acd622cc
RS
122
123;;; I have this defined so that .Z files are assumed to be in unix
060c3cc9 124;;; compress format; and .gz files, in gzip format, and .bz2 files in bzip fmt.
bbf5eb28 125(defcustom jka-compr-compression-info-list
acd622cc 126 ;;[regexp
ede9c6a8
RS
127 ;; compr-message compr-prog compr-args
128 ;; uncomp-message uncomp-prog uncomp-args
e073a356 129 ;; can-append auto-mode-flag strip-extension-flag file-magic-bytes]
094cf604 130 '(["\\.Z\\(~\\|\\.~[0-9]+~\\)?\\'"
acd622cc
RS
131 "compressing" "compress" ("-c")
132 "uncompressing" "uncompress" ("-c")
e073a356 133 nil t "\037\235"]
df312987
DL
134 ;; Formerly, these had an additional arg "-c", but that fails with
135 ;; "Version 0.1pl2, 29-Aug-97." (RedHat 5.1 GNU/Linux) and
136 ;; "Version 0.9.0b, 9-Sept-98".
060c3cc9 137 ["\\.bz2\\'"
df312987
DL
138 "bzip2ing" "bzip2" nil
139 "bunzip2ing" "bzip2" ("-d")
e073a356 140 nil t "BZh"]
74b2c737
RS
141 ["\\.tgz\\'"
142 "zipping" "gzip" ("-c" "-q")
143 "unzipping" "gzip" ("-c" "-q" "-d")
e073a356 144 t nil "\037\213"]
0166aed1 145 ["\\.g?z\\(~\\|\\.~[0-9]+~\\)?\\'"
acd622cc
RS
146 "zipping" "gzip" ("-c" "-q")
147 "unzipping" "gzip" ("-c" "-q" "-d")
e073a356 148 t t "\037\213"])
acd622cc
RS
149
150 "List of vectors that describe available compression techniques.
151Each element, which describes a compression technique, is a vector of
ede9c6a8
RS
152the form [REGEXP COMPRESS-MSG COMPRESS-PROGRAM COMPRESS-ARGS
153UNCOMPRESS-MSG UNCOMPRESS-PROGRAM UNCOMPRESS-ARGS
e073a356 154APPEND-FLAG STRIP-EXTENSION-FLAG FILE-MAGIC-CHARS], where:
acd622cc
RS
155
156 regexp is a regexp that matches filenames that are
157 compressed with this format
158
ede9c6a8
RS
159 compress-msg is the message to issue to the user when doing this
160 type of compression (nil means no message)
161
acd622cc
RS
162 compress-program is a program that performs this compression
163
164 compress-args is a list of args to pass to the compress program
165
ede9c6a8
RS
166 uncompress-msg is the message to issue to the user when doing this
167 type of uncompression (nil means no message)
acd622cc
RS
168
169 uncompress-program is a program that performs this compression
170
171 uncompress-args is a list of args to pass to the uncompress program
172
173 append-flag is non-nil if this compression technique can be
174 appended
175
e073a356 176 strip-extension-flag non-nil means strip the regexp from file names
acd622cc
RS
177 before attempting to set the mode.
178
e073a356
RS
179 file-magic-chars is a string of characters that you would find
180 at the beginning of a file compressed in this way.
181
8fb1a583 182Because of the way `call-process' is defined, discarding the stderr output of
acd622cc 183a program adds the overhead of starting a shell each time the program is
bbf5eb28
RS
184invoked."
185 :type '(repeat (vector regexp
186 (choice :tag "Compress Message"
187 (string :format "%v")
188 (const :tag "No Message" nil))
189 (string :tag "Compress Program")
190 (repeat :tag "Compress Arguments" string)
191 (choice :tag "Uncompress Message"
192 (string :format "%v")
193 (const :tag "No Message" nil))
194 (string :tag "Uncompress Program")
195 (repeat :tag "Uncompress Arguments" string)
196 (boolean :tag "Append")
a3fdb58a
DL
197 (boolean :tag "Strip Extension")
198 (string :tag "Magic Bytes")))
bbf5eb28 199 :group 'jka-compr)
acd622cc 200
74b2c737
RS
201(defvar jka-compr-mode-alist-additions
202 (list (cons "\\.tgz\\'" 'tar-mode))
4eec33ae
RS
203 "A list of pairs to add to `auto-mode-alist' when jka-compr is installed.")
204
aab8a6e3
SM
205(defvar jka-compr-load-suffixes '(".gz")
206 "List of suffixes to try when loading files.")
207
4eec33ae
RS
208;; List of all the elements we actually added to file-coding-system-alist.
209(defvar jka-compr-added-to-file-coding-system-alist nil)
acd622cc 210
555235e6
RS
211(defvar jka-compr-file-name-handler-entry
212 nil
213 "The entry in `file-name-handler-alist' used by the jka-compr I/O functions.")
e073a356
RS
214
215(defvar jka-compr-really-do-compress nil
216 "Non-nil in a buffer whose visited file was uncompressed on visiting it.")
217(put 'jka-compr-really-do-compress 'permanent-local t)
555235e6 218\f
43a3bdcc 219;;; Functions for accessing the return value of jka-compr-get-compression-info
acd622cc
RS
220(defun jka-compr-info-regexp (info) (aref info 0))
221(defun jka-compr-info-compress-message (info) (aref info 1))
222(defun jka-compr-info-compress-program (info) (aref info 2))
223(defun jka-compr-info-compress-args (info) (aref info 3))
224(defun jka-compr-info-uncompress-message (info) (aref info 4))
225(defun jka-compr-info-uncompress-program (info) (aref info 5))
226(defun jka-compr-info-uncompress-args (info) (aref info 6))
227(defun jka-compr-info-can-append (info) (aref info 7))
228(defun jka-compr-info-strip-extension (info) (aref info 8))
e073a356 229(defun jka-compr-info-file-magic-bytes (info) (aref info 9))
acd622cc
RS
230
231
232(defun jka-compr-get-compression-info (filename)
233 "Return information about the compression scheme of FILENAME.
234The determination as to which compression scheme, if any, to use is
99bee6a4 235based on the filename itself and `jka-compr-compression-info-list'."
acd622cc
RS
236 (catch 'compression-info
237 (let ((case-fold-search nil))
238 (mapcar
239 (function (lambda (x)
240 (and (string-match (jka-compr-info-regexp x) filename)
241 (throw 'compression-info x))))
242 jka-compr-compression-info-list)
243 nil)))
244
245
246(put 'compression-error 'error-conditions '(compression-error file-error error))
247
248
30c78e11 249(defvar jka-compr-acceptable-retval-list '(0 2 141))
acd622cc
RS
250
251
252(defun jka-compr-error (prog args infile message &optional errfile)
253
254 (let ((errbuf (get-buffer-create " *jka-compr-error*"))
255 (curbuf (current-buffer)))
75e9c107
RS
256 (with-current-buffer errbuf
257 (widen) (erase-buffer)
258 (insert (format "Error while executing \"%s %s < %s\"\n\n"
259 prog
260 (mapconcat 'identity args " ")
261 infile))
262
263 (and errfile
264 (insert-file-contents errfile)))
acd622cc
RS
265 (display-buffer errbuf))
266
75e9c107
RS
267 (signal 'compression-error
268 (list "Opening input file" (format "error %s" message) infile)))
acd622cc
RS
269
270
271(defvar jka-compr-dd-program
272 "/bin/dd")
273
274
dfe05fac 275(defvar jka-compr-dd-blocksize 256)
acd622cc
RS
276
277
278(defun jka-compr-partial-uncompress (prog message args infile beg len)
279 "Call program PROG with ARGS args taking input from INFILE.
280Fourth and fifth args, BEG and LEN, specify which part of the output
ee139ed3 281to keep: LEN chars starting BEG chars from the beginning."
acd622cc
RS
282 (let* ((skip (/ beg jka-compr-dd-blocksize))
283 (prefix (- beg (* skip jka-compr-dd-blocksize)))
284 (count (and len (1+ (/ (+ len prefix) jka-compr-dd-blocksize))))
285 (start (point))
acd622cc
RS
286 (err-file (jka-compr-make-temp-name))
287 (run-string (format "%s %s 2> %s | %s bs=%d skip=%d %s 2> /dev/null"
288 prog
289 (mapconcat 'identity args " ")
290 err-file
291 jka-compr-dd-program
292 jka-compr-dd-blocksize
293 skip
dfe05fac
RS
294 ;; dd seems to be unreliable about
295 ;; providing the last block. So, always
296 ;; read one more than you think you need.
ee753ec8 297 (if count (format "count=%d" (1+ count)) ""))))
acd622cc
RS
298
299 (unwind-protect
300 (or (memq (call-process jka-compr-shell
301 infile t nil "-c"
302 run-string)
303 jka-compr-acceptable-retval-list)
304
305 (jka-compr-error prog args infile message err-file))
306
307 (jka-compr-delete-temp-file err-file))
308
ee139ed3 309 ;; Delete the stuff after what we want, if there is any.
acd622cc 310 (and
dfe05fac 311 len
ee139ed3 312 (< (+ start prefix len) (point))
dfe05fac 313 (delete-region (+ start prefix len) (point)))
acd622cc 314
ee139ed3 315 ;; Delete the stuff before what we want.
acd622cc
RS
316 (delete-region start (+ start prefix))))
317
318
319(defun jka-compr-call-process (prog message infile output temp args)
320 (if jka-compr-use-shell
321
a81635fc 322 (let ((err-file (jka-compr-make-temp-name))
baefb016 323 (coding-system-for-read (or coding-system-for-read 'undecided))
70c7850e 324 (coding-system-for-write 'no-conversion))
a81635fc 325
acd622cc
RS
326 (unwind-protect
327
328 (or (memq
329 (call-process jka-compr-shell infile
330 (if (stringp output) nil output)
331 nil
332 "-c"
333 (format "%s %s 2> %s %s"
334 prog
335 (mapconcat 'identity args " ")
336 err-file
337 (if (stringp output)
338 (concat "> " output)
339 "")))
340 jka-compr-acceptable-retval-list)
341
342 (jka-compr-error prog args infile message err-file))
343
344 (jka-compr-delete-temp-file err-file)))
345
346 (or (zerop
347 (apply 'call-process
348 prog
349 infile
350 (if (stringp output) temp output)
351 nil
352 args))
353 (jka-compr-error prog args infile message))
354
355 (and (stringp output)
75e9c107 356 (with-current-buffer temp
acd622cc 357 (write-region (point-min) (point-max) output)
75e9c107 358 (erase-buffer)))))
acd622cc
RS
359
360
361;;; Support for temp files. Much of this was inspired if not lifted
362;;; from ange-ftp.
363
bbf5eb28 364(defcustom jka-compr-temp-name-template
11757e2f 365 (expand-file-name "jka-com" temporary-file-directory)
362b539a 366 "Prefix added to all temp files created by jka-compr.
bbf5eb28
RS
367There should be no more than seven characters after the final `/'."
368 :type 'string
369 :group 'jka-compr)
acd622cc 370
acd622cc
RS
371(defun jka-compr-make-temp-name (&optional local-copy)
372 "This routine will return the name of a new file."
767d12f2
SM
373 (make-temp-file jka-compr-temp-name-template))
374
375(defalias 'jka-compr-delete-temp-file 'delete-file)
acd622cc
RS
376
377
378(defun jka-compr-write-region (start end file &optional append visit)
acd622cc
RS
379 (let* ((filename (expand-file-name file))
380 (visit-file (if (stringp visit) (expand-file-name visit) filename))
e073a356
RS
381 (info (jka-compr-get-compression-info visit-file))
382 (magic (and info (jka-compr-info-file-magic-bytes info))))
383
384 ;; If we uncompressed this file when visiting it,
385 ;; then recompress it when writing it
386 ;; even if the contents look compressed already.
387 (if (and jka-compr-really-do-compress
388 (eq start 1)
389 (eq end (1+ (buffer-size))))
390 (setq magic nil))
391
392 (if (and info
393 ;; If the contents to be written out
394 ;; are properly compressed already,
395 ;; don't try to compress them over again.
396 (not (and magic
397 (equal (if (stringp start)
398 (substring start 0 (min (length start)
399 (length magic)))
400 (buffer-substring start
401 (min end
402 (+ start (length magic)))))
403 magic))))
404 (let ((can-append (jka-compr-info-can-append info))
405 (compress-program (jka-compr-info-compress-program info))
406 (compress-message (jka-compr-info-compress-message info))
407 (uncompress-program (jka-compr-info-uncompress-program info))
408 (uncompress-message (jka-compr-info-uncompress-message info))
409 (compress-args (jka-compr-info-compress-args info))
410 (uncompress-args (jka-compr-info-uncompress-args info))
411 (base-name (file-name-nondirectory visit-file))
412 temp-file temp-buffer
413 ;; we need to leave `last-coding-system-used' set to its
414 ;; value after calling write-region the first time, so
415 ;; that `basic-save-buffer' sees the right value.
416 (coding-system-used last-coding-system-used))
417
418 (setq temp-buffer (get-buffer-create " *jka-compr-wr-temp*"))
419 (with-current-buffer temp-buffer
420 (widen) (erase-buffer))
421
422 (if (and append
423 (not can-append)
424 (file-exists-p filename))
425
426 (let* ((local-copy (file-local-copy filename))
427 (local-file (or local-copy filename)))
428
429 (setq temp-file local-file))
430
431 (setq temp-file (jka-compr-make-temp-name)))
432
433 (and
434 compress-message
435 (message "%s %s..." compress-message base-name))
436
437 (jka-compr-run-real-handler 'write-region
438 (list start end temp-file t 'dont))
439 ;; save value used by the real write-region
440 (setq coding-system-used last-coding-system-used)
441
442 ;; Here we must read the output of compress program as is
443 ;; without any code conversion.
444 (let ((coding-system-for-read 'no-conversion))
445 (jka-compr-call-process compress-program
446 (concat compress-message
447 " " base-name)
448 temp-file
449 temp-buffer
450 nil
451 compress-args))
452
453 (with-current-buffer temp-buffer
454 (let ((coding-system-for-write 'no-conversion))
455 (if (memq system-type '(ms-dos windows-nt))
456 (setq buffer-file-type t) )
457 (jka-compr-run-real-handler 'write-region
458 (list (point-min) (point-max)
459 filename
460 (and append can-append) 'dont))
461 (erase-buffer)) )
462
463 (jka-compr-delete-temp-file temp-file)
acd622cc 464
e073a356
RS
465 (and
466 compress-message
467 (message "%s %s...done" compress-message base-name))
468
469 (cond
470 ((eq visit t)
471 (setq buffer-file-name filename)
472 (setq jka-compr-really-do-compress t)
473 (set-visited-file-modtime))
474 ((stringp visit)
475 (setq buffer-file-name visit)
476 (let ((buffer-file-name filename))
477 (set-visited-file-modtime))))
478
479 (and (or (eq visit t)
480 (eq visit nil)
481 (stringp visit))
482 (message "Wrote %s" visit-file))
483
484 ;; ensure `last-coding-system-used' has an appropriate value
485 (setq last-coding-system-used coding-system-used)
486
487 nil)
acd622cc 488
e073a356
RS
489 (jka-compr-run-real-handler 'write-region
490 (list start end filename append visit)))))
acd622cc
RS
491
492
54b2aa5c 493(defun jka-compr-insert-file-contents (file &optional visit beg end replace)
acd622cc
RS
494 (barf-if-buffer-read-only)
495
496 (and (or beg end)
497 visit
498 (error "Attempt to visit less than an entire file"))
499
500 (let* ((filename (expand-file-name file))
501 (info (jka-compr-get-compression-info filename)))
502
503 (if info
504
505 (let ((uncompress-message (jka-compr-info-uncompress-message info))
506 (uncompress-program (jka-compr-info-uncompress-program info))
507 (uncompress-args (jka-compr-info-uncompress-args info))
508 (base-name (file-name-nondirectory filename))
509 (notfound nil)
8fb1a583
RS
510 (local-copy
511 (jka-compr-run-real-handler 'file-local-copy (list filename)))
acd622cc 512 local-file
a81635fc 513 size start
4eec33ae
RS
514 (coding-system-for-read
515 (or coding-system-for-read
bab00368
RS
516 ;; If multibyte characters are disabled,
517 ;; don't do that conversion.
518 (and (null enable-multibyte-characters)
c352d959 519 (or (auto-coding-alist-lookup
f8d8f627 520 (jka-compr-byte-compiler-base-file-name file))
c352d959 521 'raw-text))
7ec4806e
RS
522 (let ((coding (find-operation-coding-system
523 'insert-file-contents
524 (jka-compr-byte-compiler-base-file-name file))))
525 (and (consp coding) (car coding)))
4eec33ae 526 'undecided)) )
acd622cc
RS
527
528 (setq local-file (or local-copy filename))
529
530 (and
531 visit
532 (setq buffer-file-name filename))
533
534 (unwind-protect ; to make sure local-copy gets deleted
535
536 (progn
537
538 (and
539 uncompress-message
540 (message "%s %s..." uncompress-message base-name))
541
542 (condition-case error-code
543
544 (progn
094cf604
RS
545 (if replace
546 (goto-char (point-min)))
acd622cc
RS
547 (setq start (point))
548 (if (or beg end)
549 (jka-compr-partial-uncompress uncompress-program
550 (concat uncompress-message
551 " " base-name)
552 uncompress-args
553 local-file
554 (or beg 0)
555 (if (and beg end)
556 (- end beg)
557 end))
ae849784
RS
558 ;; If visiting, bind off buffer-file-name so that
559 ;; file-locking will not ask whether we should
560 ;; really edit the buffer.
561 (let ((buffer-file-name
562 (if visit nil buffer-file-name)))
563 (jka-compr-call-process uncompress-program
564 (concat uncompress-message
565 " " base-name)
566 local-file
567 t
568 nil
569 uncompress-args)))
acd622cc 570 (setq size (- (point) start))
094cf604
RS
571 (if replace
572 (let* ((del-beg (point))
573 (del-end (+ del-beg size)))
574 (delete-region del-beg
575 (min del-end (point-max)))))
576 (goto-char start))
acd622cc
RS
577 (error
578 (if (and (eq (car error-code) 'file-error)
579 (eq (nth 3 error-code) local-file))
580 (if visit
581 (setq notfound error-code)
582 (signal 'file-error
583 (cons "Opening input file"
584 (nthcdr 2 error-code))))
585 (signal (car error-code) (cdr error-code))))))
586
587 (and
588 local-copy
589 (file-exists-p local-copy)
590 (delete-file local-copy)))
591
592 (and
593 visit
594 (progn
8fb1a583 595 (unlock-buffer)
acd622cc 596 (setq buffer-file-name filename)
e073a356 597 (setq jka-compr-really-do-compress t)
acd622cc
RS
598 (set-visited-file-modtime)))
599
600 (and
601 uncompress-message
602 (message "%s %s...done" uncompress-message base-name))
603
604 (and
605 visit
606 notfound
607 (signal 'file-error
608 (cons "Opening input file" (nth 2 notfound))))
609
5c6f2f2a
RS
610 ;; This is done in insert-file-contents after we return.
611 ;; That is a little weird, but better to go along with it now
612 ;; than to change it now.
613
614;;; ;; Run the functions that insert-file-contents would.
615;;; (let ((p after-insert-file-functions)
616;;; (insval size))
617;;; (while p
618;;; (setq insval (funcall (car p) size))
619;;; (if insval
620;;; (progn
621;;; (or (integerp insval)
622;;; (signal 'wrong-type-argument
623;;; (list 'integerp insval)))
624;;; (setq size insval)))
625;;; (setq p (cdr p))))
094cf604 626
acd622cc
RS
627 (list filename size))
628
8fb1a583
RS
629 (jka-compr-run-real-handler 'insert-file-contents
630 (list file visit beg end replace)))))
acd622cc
RS
631
632
633(defun jka-compr-file-local-copy (file)
acd622cc
RS
634 (let* ((filename (expand-file-name file))
635 (info (jka-compr-get-compression-info filename)))
636
637 (if info
638
639 (let ((uncompress-message (jka-compr-info-uncompress-message info))
640 (uncompress-program (jka-compr-info-uncompress-program info))
641 (uncompress-args (jka-compr-info-uncompress-args info))
642 (base-name (file-name-nondirectory filename))
8fb1a583
RS
643 (local-copy
644 (jka-compr-run-real-handler 'file-local-copy (list filename)))
acd622cc 645 (temp-file (jka-compr-make-temp-name t))
30c78e11 646 (temp-buffer (get-buffer-create " *jka-compr-flc-temp*"))
acd622cc 647 (notfound nil)
acd622cc
RS
648 local-file)
649
650 (setq local-file (or local-copy filename))
651
652 (unwind-protect
653
75e9c107 654 (with-current-buffer temp-buffer
acd622cc
RS
655
656 (and
657 uncompress-message
658 (message "%s %s..." uncompress-message base-name))
acd622cc 659
baefb016
KH
660 ;; Here we must read the output of uncompress program
661 ;; and write it to TEMP-FILE without any code
662 ;; conversion. An appropriate code conversion (if
663 ;; necessary) is done by the later I/O operation
664 ;; (e.g. load).
665 (let ((coding-system-for-read 'no-conversion)
666 (coding-system-for-write 'no-conversion))
667
668 (jka-compr-call-process uncompress-program
669 (concat uncompress-message
670 " " base-name)
671 local-file
672 t
673 nil
674 uncompress-args)
675
676 (and
677 uncompress-message
678 (message "%s %s...done" uncompress-message base-name))
679
680 (write-region
681 (point-min) (point-max) temp-file nil 'dont)))
acd622cc
RS
682
683 (and
684 local-copy
685 (file-exists-p local-copy)
686 (delete-file local-copy))
687
acd622cc
RS
688 (kill-buffer temp-buffer))
689
690 temp-file)
691
8fb1a583 692 (jka-compr-run-real-handler 'file-local-copy (list filename)))))
acd622cc
RS
693
694
695;;; Support for loading compressed files.
696(defun jka-compr-load (file &optional noerror nomessage nosuffix)
697 "Documented as original."
698
699 (let* ((local-copy (jka-compr-file-local-copy file))
700 (load-file (or local-copy file)))
701
702 (unwind-protect
703
8fb1a583
RS
704 (let (inhibit-file-name-operation
705 inhibit-file-name-handlers)
acd622cc
RS
706 (or nomessage
707 (message "Loading %s..." file))
708
9b37d8a9
RS
709 (let ((load-force-doc-strings t))
710 (load load-file noerror t t))
acd622cc 711 (or nomessage
e645e77b
DL
712 (message "Loading %s...done." file))
713 ;; Fix up the load history to point at the right library.
714 (let ((l (assoc load-file load-history)))
715 ;; Remove .gz and .elc?.
716 (while (file-name-extension file)
717 (setq file (file-name-sans-extension file)))
718 (setcar l file)))
acd622cc 719
acd622cc
RS
720 (jka-compr-delete-temp-file local-copy))
721
722 t))
3068998d
RS
723
724(defun jka-compr-byte-compiler-base-file-name (file)
725 (let ((info (jka-compr-get-compression-info file)))
726 (if (and info (jka-compr-info-strip-extension info))
727 (save-match-data
728 (substring file 0 (string-match (jka-compr-info-regexp info) file)))
729 file)))
8fb1a583
RS
730\f
731(put 'write-region 'jka-compr 'jka-compr-write-region)
732(put 'insert-file-contents 'jka-compr 'jka-compr-insert-file-contents)
733(put 'file-local-copy 'jka-compr 'jka-compr-file-local-copy)
734(put 'load 'jka-compr 'jka-compr-load)
3068998d
RS
735(put 'byte-compiler-base-file-name 'jka-compr
736 'jka-compr-byte-compiler-base-file-name)
acd622cc 737
9fdf055b
KH
738(defvar jka-compr-inhibit nil
739 "Non-nil means inhibit automatic uncompression temporarily.
740Lisp programs can bind this to t to do that.
741It is not recommended to set this variable permanently to anything but nil.")
742
acd622cc 743(defun jka-compr-handler (operation &rest args)
8fb1a583
RS
744 (save-match-data
745 (let ((jka-op (get operation 'jka-compr)))
9fdf055b 746 (if (and jka-op (not jka-compr-inhibit))
8fb1a583
RS
747 (apply jka-op args)
748 (jka-compr-run-real-handler operation args)))))
acd622cc 749
99bee6a4
RS
750;; If we are given an operation that we don't handle,
751;; call the Emacs primitive for that operation,
752;; and manipulate the inhibit variables
753;; to prevent the primitive from calling our handler again.
754(defun jka-compr-run-real-handler (operation args)
755 (let ((inhibit-file-name-handlers
756 (cons 'jka-compr-handler
757 (and (eq inhibit-file-name-operation operation)
758 inhibit-file-name-handlers)))
759 (inhibit-file-name-operation operation))
760 (apply operation args)))
761
523b128c 762
acd622cc 763(defun jka-compr-build-file-regexp ()
defdd32e
SM
764 (mapconcat
765 'jka-compr-info-regexp
766 jka-compr-compression-info-list
767 "\\|"))
acd622cc
RS
768
769
770(defun jka-compr-install ()
771 "Install jka-compr.
919a07bb
RS
772This adds entries to `file-name-handler-alist' and `auto-mode-alist'
773and `inhibit-first-line-modes-suffixes'."
acd622cc
RS
774
775 (setq jka-compr-file-name-handler-entry
776 (cons (jka-compr-build-file-regexp) 'jka-compr-handler))
777
778 (setq file-name-handler-alist (cons jka-compr-file-name-handler-entry
779 file-name-handler-alist))
780
4eec33ae
RS
781 (setq jka-compr-added-to-file-coding-system-alist nil)
782
acd622cc
RS
783 (mapcar
784 (function (lambda (x)
4eec33ae
RS
785 ;; Don't do multibyte encoding on the compressed files.
786 (let ((elt (cons (jka-compr-info-regexp x)
787 '(no-conversion . no-conversion))))
788 (setq file-coding-system-alist
789 (cons elt file-coding-system-alist))
790 (setq jka-compr-added-to-file-coding-system-alist
791 (cons elt jka-compr-added-to-file-coding-system-alist)))
792
469f4e8c
RS
793 (and (jka-compr-info-strip-extension x)
794 ;; Make entries in auto-mode-alist so that modes
795 ;; are chosen right according to the file names
796 ;; sans `.gz'.
797 (setq auto-mode-alist
798 (cons (list (jka-compr-info-regexp x)
799 nil 'jka-compr)
800 auto-mode-alist))
801 ;; Also add these regexps to
802 ;; inhibit-first-line-modes-suffixes, so that a
803 ;; -*- line in the first file of a compressed tar
804 ;; file doesn't override tar-mode.
805 (setq inhibit-first-line-modes-suffixes
806 (cons (jka-compr-info-regexp x)
807 inhibit-first-line-modes-suffixes)))))
74b2c737
RS
808 jka-compr-compression-info-list)
809 (setq auto-mode-alist
aab8a6e3
SM
810 (append auto-mode-alist jka-compr-mode-alist-additions))
811
812 ;; Make sure that (load "foo") will find /bla/foo.el.gz.
813 (setq load-suffixes
814 (apply 'append
815 (mapcar (lambda (suffix)
816 (cons suffix
817 (mapcar (lambda (ext) (concat suffix ext))
818 jka-compr-load-suffixes)))
819 load-suffixes))))
acd622cc
RS
820
821
822(defun jka-compr-uninstall ()
823 "Uninstall jka-compr.
99bee6a4 824This removes the entries in `file-name-handler-alist' and `auto-mode-alist'
919a07bb
RS
825and `inhibit-first-line-modes-suffixes' that were added
826by `jka-compr-installed'."
827 ;; Delete from inhibit-first-line-modes-suffixes
828 ;; what jka-compr-install added.
829 (mapcar
830 (function (lambda (x)
831 (and (jka-compr-info-strip-extension x)
832 (setq inhibit-first-line-modes-suffixes
833 (delete (jka-compr-info-regexp x)
834 inhibit-first-line-modes-suffixes)))))
835 jka-compr-compression-info-list)
acd622cc
RS
836
837 (let* ((fnha (cons nil file-name-handler-alist))
838 (last fnha))
839
840 (while (cdr last)
841 (if (eq (cdr (car (cdr last))) 'jka-compr-handler)
842 (setcdr last (cdr (cdr last)))
843 (setq last (cdr last))))
844
845 (setq file-name-handler-alist (cdr fnha)))
846
847 (let* ((ama (cons nil auto-mode-alist))
848 (last ama)
849 entry)
850
851 (while (cdr last)
852 (setq entry (car (cdr last)))
74b2c737
RS
853 (if (or (member entry jka-compr-mode-alist-additions)
854 (and (consp (cdr entry))
855 (eq (nth 2 entry) 'jka-compr)))
acd622cc
RS
856 (setcdr last (cdr (cdr last)))
857 (setq last (cdr last))))
858
4eec33ae
RS
859 (setq auto-mode-alist (cdr ama)))
860
861 (let* ((ama (cons nil file-coding-system-alist))
862 (last ama)
863 entry)
864
865 (while (cdr last)
866 (setq entry (car (cdr last)))
867 (if (member entry jka-compr-added-to-file-coding-system-alist)
868 (setcdr last (cdr (cdr last)))
869 (setq last (cdr last))))
870
aab8a6e3
SM
871 (setq file-coding-system-alist (cdr ama)))
872
873 ;; Remove the suffixes that were added by jka-compr.
874 (let ((suffixes nil)
875 (re (jka-compr-build-file-regexp)))
876 (dolist (suffix load-suffixes)
877 (unless (string-match re suffix)
878 (push suffix suffixes)))
879 (setq load-suffixes (nreverse suffixes))))
acd622cc
RS
880
881
882(defun jka-compr-installed-p ()
883 "Return non-nil if jka-compr is installed.
99bee6a4 884The return value is the entry in `file-name-handler-alist' for jka-compr."
acd622cc
RS
885
886 (let ((fnha file-name-handler-alist)
887 (installed nil))
888
889 (while (and fnha (not installed))
890 (and (eq (cdr (car fnha)) 'jka-compr-handler)
891 (setq installed (car fnha)))
892 (setq fnha (cdr fnha)))
893
894 installed))
895
896
7dbc9c8a
MB
897;;; Add the file I/O hook if it does not already exist.
898;;; Make sure that jka-compr-file-name-handler-entry is eq to the
899;;; entry for jka-compr in file-name-handler-alist.
900(and (jka-compr-installed-p)
901 (jka-compr-uninstall))
902
903
6fee86a3
MB
904;;;###autoload
905(define-minor-mode auto-compression-mode
906 "Toggle automatic file compression and uncompression.
907With prefix argument ARG, turn auto compression on if positive, else off.
908Returns the new status of auto compression (non-nil means on)."
754005f7 909 :global t :group 'jka-compr
6fee86a3
MB
910 (let* ((installed (jka-compr-installed-p))
911 (flag auto-compression-mode))
912 (cond
913 ((and flag installed) t) ; already installed
914 ((and (not flag) (not installed)) nil) ; already not installed
915 (flag (jka-compr-install))
916 (t (jka-compr-uninstall)))))
917
918
919;;;###autoload
920(defmacro with-auto-compression-mode (&rest body)
7dbc9c8a 921 "Evalute BODY with automatic file compression and uncompression enabled."
6fee86a3
MB
922 (let ((already-installed (make-symbol "already-installed")))
923 `(let ((,already-installed (jka-compr-installed-p)))
924 (unwind-protect
925 (progn
926 (unless ,already-installed
927 (jka-compr-install))
928 ,@body)
929 (unless ,already-installed
930 (jka-compr-uninstall))))))
931(put 'with-auto-compression-mode 'lisp-indent-function 0)
932
933
acd622cc
RS
934(provide 'jka-compr)
935
55535639 936;;; jka-compr.el ends here