(jka-compr-compression-info-list): Doc fix.
[bpt/emacs.git] / lisp / jka-compr.el
CommitLineData
acd622cc
RS
1;;; jka-compr.el - reading/writing/loading compressed files.
2;;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3
4;; Author: jka@ece.cmu.edu (Jay K. Adams)
dfe05fac 5;; Version: 0.11
acd622cc
RS
6;; Keywords: data
7
8;;; Commentary:
9
10;;; This package implements low-level support for reading, writing,
11;;; and loading compressed files. It hooks into the low-level file
12;;; I/O functions (including write-region and insert-file-contents) so
13;;; that they automatically compress or uncompress a file if the file
14;;; appears to need it (based on the extension of the file name).
dfe05fac 15;;; Packages like Rmail, VM, GNUS, and Info should be able to work
acd622cc
RS
16;;; with compressed files without modification.
17
18
19;;; INSTRUCTIONS:
20;;;
21;;; To use jka-compr, simply load this package, and edit as usual.
22;;; Its operation should be transparent to the user (except for
23;;; messages appearing when a file is being compressed or
24;;; uncompressed).
25;;;
26;;; The variable, jka-compr-compression-info-list can be used to
27;;; customize jka-compr to work with other compression programs.
28;;; The default value of this variable allows jka-compr to work with
29;;; Unix compress and gzip.
30;;;
31;;; If you are concerned about the stderr output of gzip and other
32;;; compression/decompression programs showing up in your buffers, you
33;;; should set the discard-error flag in the compression-info-list.
34;;; This will cause the stderr of all programs to be discarded.
35;;; However, it also causes emacs to call compression/uncompression
36;;; programs through a shell (which is specified by jka-compr-shell).
37;;; This may be a drag if, on your system, starting up a shell is
38;;; slow.
39;;;
40;;; If you don't want messages about compressing and decompressing
41;;; to show up in the echo area, you can set the compress-name and
42;;; decompress-name fields of the jka-compr-compression-info-list to
43;;; nil.
44
45
46;;; APPLICATION NOTES:
47;;;
48;;; rmail, vm, gnus, etc.
49;;; To use compressed mail folders, .newsrc files, etc., you need
50;;; only compress the file. Since jka-compr searches for .gz
51;;; versions of the files it's finding, you need not change
52;;; variables within rmail, gnus, etc.
53;;;
54;;;
55;;; crypt++
56;;; jka-compr can coexist with crpyt++ if you take all the decompression
57;;; entries out of the crypt-encoding-list. Clearly problems will arise if
58;;; you have two programs trying to compress/decompress files. jka-compr
59;;; will not "work with" crypt++ in the following sense: you won't be able to
60;;; decode encrypted compressed files--that is, files that have been
61;;; compressed then encrypted (in that order). Theoretically, crypt++ and
62;;; jka-compr could properly handle a file that has been encrypted then
63;;; compressed, but there is little point in trying to compress an encrypted
64;;; file.
65;;;
66;;;
67;;; tar-mode
68;;; Some people like to use extensions like .trz for compressed tar files.
69;;; To handle these sorts of files, you have to add an entry to
70;;; jka-compr-compression-info-list that looks something like this:
71;;;
72;;; ["\\.trz\\'" "\037\213"
73;;; "zip" "gzip" nil ("-q")
74;;; "unzip" "gzip" nil ("-q" "-d")
75;;; t
76;;; nil]
77;;;
78;;; The last nil in the vector (the "extension" field) prevents jka-compr
79;;; from attempting to add .trz to an ordinary file name when it is looking
80;;; for a compressed version of that file (i.e. don't look for things like
81;;; foobar.c.trz).
82;;;
83;;; Finally, to make tar-mode start up automatically, you have to add an
84;;; entry to auto-mode-alist that looks like this
85;;;
86;;; ("\\.trz\\'" . tar-mode)
87;;;
88
89
90;;; ACKNOWLEDGMENTS
91;;;
92;;; jka-compr is a V19 adaptation of jka-compr for V18 of Emacs. Many people
93;;; have made helpful suggestions, reported bugs, and even fixed bugs in
94;;; jka-compr. I recall the following people as being particularly helpful.
95;;;
96;;; Jean-loup Gailly
97;;; David Hughes
98;;; Richard Pieri
99;;; Daniel Quinlan
100;;; Chris P. Ross
101;;; Rick Sladkey
102;;;
103;;; Andy Norman's ange-ftp was the inspiration for the original jka-compr for
104;;; Version 18 of Emacs.
105;;;
106;;; After I had made progress on the original jka-compr for V18, I learned of a
107;;; package written by Kazushi Jam Marukawa, called jam-zcat, that did exactly
108;;; what I was trying to do. I looked over the jam-zcat source code and
109;;; probably got some ideas from it.
110;;;
111
112;;; Code:
113
114(defvar jka-compr-shell "sh"
115 "*Shell to be used for calling compression programs.
116The value of this variable only matters if you want to discard the
117stderr of a compression/decompression program (see the documentation
99bee6a4 118for `jka-compr-compression-info-list').")
acd622cc
RS
119
120
121(defvar jka-compr-use-shell t)
122
123
124;;; I have this defined so that .Z files are assumed to be in unix
125;;; compress format; and .gz files, in gzip format.
126(defvar jka-compr-compression-info-list
127 ;;[regexp
ede9c6a8
RS
128 ;; compr-message compr-prog compr-args
129 ;; uncomp-message uncomp-prog uncomp-args
acd622cc
RS
130 ;; can-append auto-mode-flag]
131 '(["\\.Z~?\\'"
132 "compressing" "compress" ("-c")
133 "uncompressing" "uncompress" ("-c")
134 nil t]
135 ["\\.gz~?\\'"
136 "zipping" "gzip" ("-c" "-q")
137 "unzipping" "gzip" ("-c" "-q" "-d")
138 t t])
139
140 "List of vectors that describe available compression techniques.
141Each element, which describes a compression technique, is a vector of
ede9c6a8
RS
142the form [REGEXP COMPRESS-MSG COMPRESS-PROGRAM COMPRESS-ARGS
143UNCOMPRESS-MSG UNCOMPRESS-PROGRAM UNCOMPRESS-ARGS
144APPEND-FLAG EXTENSION], where:
acd622cc
RS
145
146 regexp is a regexp that matches filenames that are
147 compressed with this format
148
ede9c6a8
RS
149 compress-msg is the message to issue to the user when doing this
150 type of compression (nil means no message)
151
acd622cc
RS
152 compress-program is a program that performs this compression
153
154 compress-args is a list of args to pass to the compress program
155
ede9c6a8
RS
156 uncompress-msg is the message to issue to the user when doing this
157 type of uncompression (nil means no message)
acd622cc
RS
158
159 uncompress-program is a program that performs this compression
160
161 uncompress-args is a list of args to pass to the uncompress program
162
163 append-flag is non-nil if this compression technique can be
164 appended
165
166 auto-mode flag non-nil means strip the regexp from file names
167 before attempting to set the mode.
168
169Because of the way call-process is defined, discarding the stderr output of
170a program adds the overhead of starting a shell each time the program is
171invoked.")
172
173
174;;; Functions for accessing the return value of jka-get-compression-info
175(defun jka-compr-info-regexp (info) (aref info 0))
176(defun jka-compr-info-compress-message (info) (aref info 1))
177(defun jka-compr-info-compress-program (info) (aref info 2))
178(defun jka-compr-info-compress-args (info) (aref info 3))
179(defun jka-compr-info-uncompress-message (info) (aref info 4))
180(defun jka-compr-info-uncompress-program (info) (aref info 5))
181(defun jka-compr-info-uncompress-args (info) (aref info 6))
182(defun jka-compr-info-can-append (info) (aref info 7))
183(defun jka-compr-info-strip-extension (info) (aref info 8))
184
185
186(defun jka-compr-get-compression-info (filename)
187 "Return information about the compression scheme of FILENAME.
188The determination as to which compression scheme, if any, to use is
99bee6a4 189based on the filename itself and `jka-compr-compression-info-list'."
acd622cc
RS
190 (catch 'compression-info
191 (let ((case-fold-search nil))
192 (mapcar
193 (function (lambda (x)
194 (and (string-match (jka-compr-info-regexp x) filename)
195 (throw 'compression-info x))))
196 jka-compr-compression-info-list)
197 nil)))
198
199
200(put 'compression-error 'error-conditions '(compression-error file-error error))
201
202
203(defvar jka-compr-acceptable-retval-list '(0 141))
204
205
206(defun jka-compr-error (prog args infile message &optional errfile)
207
208 (let ((errbuf (get-buffer-create " *jka-compr-error*"))
209 (curbuf (current-buffer)))
210 (set-buffer errbuf)
211 (widen) (erase-buffer)
212 (insert (format "Error while executing \"%s %s < %s\"\n\n"
213 prog
214 (mapconcat 'identity args " ")
215 infile))
216
217 (and errfile
218 (insert-file-contents errfile))
219
220 (set-buffer curbuf)
221 (display-buffer errbuf))
222
223 (signal 'compression-error (list "Opening input file" (format "error %s" message) infile)))
224
225
226(defvar jka-compr-dd-program
227 "/bin/dd")
228
229
dfe05fac 230(defvar jka-compr-dd-blocksize 256)
acd622cc
RS
231
232
233(defun jka-compr-partial-uncompress (prog message args infile beg len)
234 "Call program PROG with ARGS args taking input from INFILE.
235Fourth and fifth args, BEG and LEN, specify which part of the output
236to discard. All output is discarded unless it comes within LEN chars after
237the BEGth char."
238
239 (let* ((skip (/ beg jka-compr-dd-blocksize))
240 (prefix (- beg (* skip jka-compr-dd-blocksize)))
241 (count (and len (1+ (/ (+ len prefix) jka-compr-dd-blocksize))))
242 (start (point))
acd622cc
RS
243 (err-file (jka-compr-make-temp-name))
244 (run-string (format "%s %s 2> %s | %s bs=%d skip=%d %s 2> /dev/null"
245 prog
246 (mapconcat 'identity args " ")
247 err-file
248 jka-compr-dd-program
249 jka-compr-dd-blocksize
250 skip
dfe05fac
RS
251 ;; dd seems to be unreliable about
252 ;; providing the last block. So, always
253 ;; read one more than you think you need.
254 (if count (concat "count=" (1+ count)) ""))))
acd622cc
RS
255
256 (unwind-protect
257 (or (memq (call-process jka-compr-shell
258 infile t nil "-c"
259 run-string)
260 jka-compr-acceptable-retval-list)
261
262 (jka-compr-error prog args infile message err-file))
263
264 (jka-compr-delete-temp-file err-file))
265
266 (and
dfe05fac
RS
267 len
268 (delete-region (+ start prefix len) (point)))
acd622cc
RS
269
270 (delete-region start (+ start prefix))))
271
272
273(defun jka-compr-call-process (prog message infile output temp args)
274 (if jka-compr-use-shell
275
276 (let ((err-file (jka-compr-make-temp-name)))
277
278 (unwind-protect
279
280 (or (memq
281 (call-process jka-compr-shell infile
282 (if (stringp output) nil output)
283 nil
284 "-c"
285 (format "%s %s 2> %s %s"
286 prog
287 (mapconcat 'identity args " ")
288 err-file
289 (if (stringp output)
290 (concat "> " output)
291 "")))
292 jka-compr-acceptable-retval-list)
293
294 (jka-compr-error prog args infile message err-file))
295
296 (jka-compr-delete-temp-file err-file)))
297
298 (or (zerop
299 (apply 'call-process
300 prog
301 infile
302 (if (stringp output) temp output)
303 nil
304 args))
305 (jka-compr-error prog args infile message))
306
307 (and (stringp output)
308 (let ((cbuf (current-buffer)))
309 (set-buffer temp)
310 (write-region (point-min) (point-max) output)
311 (erase-buffer)
312 (set-buffer cbuf)))))
313
314
315;;; Support for temp files. Much of this was inspired if not lifted
316;;; from ange-ftp.
317
318(defvar jka-compr-temp-name-template
319 "/tmp/jka-com"
320 "Prefix added to all temp files created by jka-compr.
99bee6a4 321There should be no more than seven characters after the final `/'")
acd622cc
RS
322
323(defvar jka-compr-temp-name-table (make-vector 31 nil))
324
325(defun jka-compr-make-temp-name (&optional local-copy)
326 "This routine will return the name of a new file."
327 (let* ((lastchar ?a)
328 (prevchar ?a)
329 (template (concat jka-compr-temp-name-template "aa"))
330 (lastpos (1- (length template)))
331 (not-done t)
332 file
333 entry)
334
335 (while not-done
336 (aset template lastpos lastchar)
337 (setq file (concat (make-temp-name template) "#"))
338 (setq entry (intern file jka-compr-temp-name-table))
339 (if (or (get entry 'active)
340 (file-exists-p file))
341
342 (progn
343 (setq lastchar (1+ lastchar))
344 (if (> lastchar ?z)
345 (progn
346 (setq prevchar (1+ prevchar))
347 (setq lastchar ?a)
348 (if (> prevchar ?z)
349 (error "Can't allocate temp file.")
350 (aset template (1- lastpos) prevchar)))))
351
352 (put entry 'active (not local-copy))
353 (setq not-done nil)))
354
355 file))
356
357
358(defun jka-compr-delete-temp-file (temp)
359
360 (put (intern temp jka-compr-temp-name-table)
361 'active nil)
362
363 (condition-case ()
364 (delete-file temp)
365 (error nil)))
366
367
368(defun jka-compr-write-region (start end file &optional append visit)
369 "Documented as original."
370 (interactive "r\nFWrite region to file: ")
371
372 (let* ((filename (expand-file-name file))
373 (visit-file (if (stringp visit) (expand-file-name visit) filename))
374 (info (jka-compr-get-compression-info visit-file)))
375
376 (if info
377
378 (let ((can-append (jka-compr-info-can-append info))
379 (compress-program (jka-compr-info-compress-program info))
380 (compress-message (jka-compr-info-compress-message info))
381 (uncompress-program (jka-compr-info-uncompress-program info))
382 (uncompress-message (jka-compr-info-uncompress-message info))
383 (compress-args (jka-compr-info-compress-args info))
384 (uncompress-args (jka-compr-info-uncompress-args info))
385 (temp-file (jka-compr-make-temp-name))
386 (base-name (file-name-nondirectory visit-file))
387 cbuf temp-buffer)
388
389 (setq cbuf (current-buffer)
390 temp-buffer (get-buffer-create " *jka-compr-temp*"))
391 (set-buffer temp-buffer)
392 (widen) (erase-buffer)
393 (set-buffer cbuf)
394
395 (and append
396 (not can-append)
397 (file-exists-p filename)
398 (let* ((local-copy (file-local-copy filename))
399 (local-file (or local-copy filename)))
400
401 (unwind-protect
402
403 (progn
404
405 (and
406 uncompress-message
407 (message "%s %s..." uncompress-message base-name))
408
409 (jka-compr-call-process uncompress-program
410 (concat uncompress-message
411 " " base-name)
412 local-file
413 temp-file
414 temp-buffer
415 uncompress-args)
416 (and
417 uncompress-message
418 (message "%s %s...done" uncompress-message base-name)))
419
420 (and
421 local-copy
422 (file-exists-p local-copy)
423 (delete-file local-copy)))))
424
425 (and
426 compress-message
427 (message "%s %s..." compress-message base-name))
428
429 (write-region start end temp-file t 'dont)
430
431 (jka-compr-call-process compress-program
432 (concat compress-message
433 " " base-name)
434 temp-file
435 temp-buffer
436 nil
437 compress-args)
438
439 (set-buffer temp-buffer)
440 (write-region (point-min) (point-max)
441 filename (and append can-append) 'dont)
442 (erase-buffer)
443 (set-buffer cbuf)
444
445 (jka-compr-delete-temp-file temp-file)
446
447 (and
448 compress-message
449 (message "%s %s...done" compress-message base-name))
450
451 (cond
452 ((eq visit t)
453 (setq buffer-file-name filename)
454 (set-visited-file-modtime))
455 ((stringp visit)
456 (setq buffer-file-name visit)
457 (let ((buffer-file-name filename))
458 (set-visited-file-modtime))))
459
460 (and (or (eq visit t)
461 (eq visit nil)
462 (stringp visit))
463 (message "Wrote %s" visit-file))
464
465 nil)
466
467 (write-region start end filename append visit))))
468
469
54b2aa5c 470(defun jka-compr-insert-file-contents (file &optional visit beg end replace)
acd622cc
RS
471 "Documented as original."
472
473 (barf-if-buffer-read-only)
474
475 (and (or beg end)
476 visit
477 (error "Attempt to visit less than an entire file"))
478
479 (let* ((filename (expand-file-name file))
480 (info (jka-compr-get-compression-info filename)))
481
482 (if info
483
484 (let ((uncompress-message (jka-compr-info-uncompress-message info))
485 (uncompress-program (jka-compr-info-uncompress-program info))
486 (uncompress-args (jka-compr-info-uncompress-args info))
487 (base-name (file-name-nondirectory filename))
488 (notfound nil)
489 (local-copy (file-local-copy filename))
490 local-file
491 size start)
492
493 (setq local-file (or local-copy filename))
494
495 (and
496 visit
497 (setq buffer-file-name filename))
498
499 (unwind-protect ; to make sure local-copy gets deleted
500
501 (progn
502
503 (and
504 uncompress-message
505 (message "%s %s..." uncompress-message base-name))
506
507 (condition-case error-code
508
509 (progn
510 (setq start (point))
511 (if (or beg end)
512 (jka-compr-partial-uncompress uncompress-program
513 (concat uncompress-message
514 " " base-name)
515 uncompress-args
516 local-file
517 (or beg 0)
518 (if (and beg end)
519 (- end beg)
520 end))
521 (jka-compr-call-process uncompress-program
522 (concat uncompress-message
523 " " base-name)
524 local-file
525 t
526 nil
527 uncompress-args))
528 (setq size (- (point) start))
529 (goto-char start))
530
531
532 (error
533 (if (and (eq (car error-code) 'file-error)
534 (eq (nth 3 error-code) local-file))
535 (if visit
536 (setq notfound error-code)
537 (signal 'file-error
538 (cons "Opening input file"
539 (nthcdr 2 error-code))))
540 (signal (car error-code) (cdr error-code))))))
541
542 (and
543 local-copy
544 (file-exists-p local-copy)
545 (delete-file local-copy)))
546
547 (and
548 visit
549 (progn
550 (setq buffer-file-name filename)
551 (set-visited-file-modtime)))
552
553 (and
554 uncompress-message
555 (message "%s %s...done" uncompress-message base-name))
556
557 (and
558 visit
559 notfound
560 (signal 'file-error
561 (cons "Opening input file" (nth 2 notfound))))
562
563 (list filename size))
564
54b2aa5c 565 (insert-file-contents file visit beg end replace))))
acd622cc
RS
566
567
568(defun jka-compr-file-local-copy (file)
569 "Documented as original."
570
571 (let* ((filename (expand-file-name file))
572 (info (jka-compr-get-compression-info filename)))
573
574 (if info
575
576 (let ((uncompress-message (jka-compr-info-uncompress-message info))
577 (uncompress-program (jka-compr-info-uncompress-program info))
578 (uncompress-args (jka-compr-info-uncompress-args info))
579 (base-name (file-name-nondirectory filename))
580 (local-copy (file-local-copy filename))
581 (temp-file (jka-compr-make-temp-name t))
582 (temp-buffer (get-buffer-create " *jka-compr-temp*"))
583 (notfound nil)
584 (cbuf (current-buffer))
585 local-file)
586
587 (setq local-file (or local-copy filename))
588
589 (unwind-protect
590
591 (progn
592
593 (and
594 uncompress-message
595 (message "%s %s..." uncompress-message base-name))
596
597 (set-buffer temp-buffer)
598
599 (jka-compr-call-process uncompress-program
600 (concat uncompress-message
601 " " base-name)
602 local-file
603 t
604 nil
605 uncompress-args)
606
607 (and
608 uncompress-message
609 (message "%s %s...done" uncompress-message base-name))
610
611 (write-region
612 (point-min) (point-max) temp-file nil 'dont))
613
614 (and
615 local-copy
616 (file-exists-p local-copy)
617 (delete-file local-copy))
618
619 (set-buffer cbuf)
620 (kill-buffer temp-buffer))
621
622 temp-file)
623
624 (file-local-copy filename))))
625
626
627;;; Support for loading compressed files.
628(defun jka-compr-load (file &optional noerror nomessage nosuffix)
629 "Documented as original."
630
631 (let* ((local-copy (jka-compr-file-local-copy file))
632 (load-file (or local-copy file)))
633
634 (unwind-protect
635
636 (progn
637
638 (setq file-name-handler-alist
639 (cons jka-compr-file-name-handler-entry
640 file-name-handler-alist))
641
642 (or nomessage
643 (message "Loading %s..." file))
644
645 (load load-file noerror t t)
646
647 (or nomessage
648 (message "Loading %s...done." file)))
649
650 (setq file-name-handler-alist
651 (delq jka-compr-file-name-handler-entry
652 file-name-handler-alist))
653
654 (jka-compr-delete-temp-file local-copy))
655
656 t))
657
658
659(defvar jka-compr-file-name-handler-entry
660 nil
99bee6a4 661 "The entry in `file-name-handler-alist' used by the jka-compr I/O functions.")
acd622cc
RS
662
663
664(defun jka-compr-handler (operation &rest args)
665
1cb46df6
KH
666 (let ((jka-op (intern-soft (symbol-name operation) jka-compr-op-table))
667 (match-data (match-data)))
acd622cc
RS
668
669 (unwind-protect
670 (progn
671 (setq file-name-handler-alist
672 (delq jka-compr-file-name-handler-entry
673 file-name-handler-alist))
674 (if jka-op
675 (apply jka-op args)
99bee6a4 676 (jka-compr-run-real-handler operation args)))
acd622cc
RS
677
678 (setq file-name-handler-alist
679 (cons jka-compr-file-name-handler-entry
1cb46df6
KH
680 file-name-handler-alist))
681 (store-match-data match-data))))
acd622cc 682
99bee6a4
RS
683;; If we are given an operation that we don't handle,
684;; call the Emacs primitive for that operation,
685;; and manipulate the inhibit variables
686;; to prevent the primitive from calling our handler again.
687(defun jka-compr-run-real-handler (operation args)
688 (let ((inhibit-file-name-handlers
689 (cons 'jka-compr-handler
690 (and (eq inhibit-file-name-operation operation)
691 inhibit-file-name-handlers)))
692 (inhibit-file-name-operation operation))
693 (apply operation args)))
694
acd622cc
RS
695
696(defvar jka-compr-op-table
697 (make-vector 127 0)
dfe05fac 698 "Hash table of operations supported by jka-compr.")
acd622cc
RS
699
700
701(defun jka-compr-intern-operation (op)
702 (let ((opsym (intern (symbol-name op) jka-compr-op-table))
703 (jka-fn (intern (concat "jka-compr-" (symbol-name op)))))
704 (fset opsym jka-fn)))
705
706
707(defvar jka-compr-operation-list
708 '(
709 write-region
710 insert-file-contents
711 file-local-copy
712 load
713 )
714 "List of file operations implemented by jka-compr.")
715
716
717(mapcar
718 (function
719 (lambda (fn)
720 (jka-compr-intern-operation fn)))
721 jka-compr-operation-list)
722
723
724(defun toggle-auto-compression (arg)
725 "Toggle automatic file compression and decompression.
726With prefix argument ARG, turn auto compression on if positive, else off.
727Returns the new status of auto compression (non-nil means on)."
728 (interactive "P")
729 (let* ((installed (jka-compr-installed-p))
730 (flag (if (null arg)
731 (not installed)
732 (or (eq arg t) (listp arg) (and (integerp arg) (> arg 0))))))
733
734 (cond
735 ((and flag installed) t) ; already installed
736
737 ((and (not flag) (not installed)) nil) ; already not installed
738
739 (flag
740 (jka-compr-install))
741
742 (t
743 (jka-compr-uninstall)))
744
745
746 (and (interactive-p)
747 (if flag
748 (message "Automatic file (de)compression is now ON.")
749 (message "Automatic file (de)compression is now OFF.")))
750
751 flag))
752
753
754(defun jka-compr-build-file-regexp ()
755 (concat
756 "\\("
757 (mapconcat
758 'jka-compr-info-regexp
759 jka-compr-compression-info-list
760 "\\)\\|\\(")
761 "\\)"))
762
763
764(defun jka-compr-install ()
765 "Install jka-compr.
99bee6a4 766This adds entries to `file-name-handler-alist' and `auto-mode-alist'."
acd622cc
RS
767
768 (setq jka-compr-file-name-handler-entry
769 (cons (jka-compr-build-file-regexp) 'jka-compr-handler))
770
771 (setq file-name-handler-alist (cons jka-compr-file-name-handler-entry
772 file-name-handler-alist))
773
774 (mapcar
775 (function (lambda (x)
776 (and
777 (jka-compr-info-strip-extension x)
778 (setq auto-mode-alist (cons (list (jka-compr-info-regexp x)
779 nil 'jka-compr)
780 auto-mode-alist)))))
781
782 jka-compr-compression-info-list))
783
784
785(defun jka-compr-uninstall ()
786 "Uninstall jka-compr.
99bee6a4
RS
787This removes the entries in `file-name-handler-alist' and `auto-mode-alist'
788that were created by `jka-compr-installed'."
acd622cc
RS
789
790 (let* ((fnha (cons nil file-name-handler-alist))
791 (last fnha))
792
793 (while (cdr last)
794 (if (eq (cdr (car (cdr last))) 'jka-compr-handler)
795 (setcdr last (cdr (cdr last)))
796 (setq last (cdr last))))
797
798 (setq file-name-handler-alist (cdr fnha)))
799
800 (let* ((ama (cons nil auto-mode-alist))
801 (last ama)
802 entry)
803
804 (while (cdr last)
805 (setq entry (car (cdr last)))
806 (if (and (consp (cdr entry))
807 (eq (nth 2 entry) 'jka-compr))
808 (setcdr last (cdr (cdr last)))
809 (setq last (cdr last))))
810
811 (setq auto-mode-alist (cdr ama))))
812
813
814(defun jka-compr-installed-p ()
815 "Return non-nil if jka-compr is installed.
99bee6a4 816The return value is the entry in `file-name-handler-alist' for jka-compr."
acd622cc
RS
817
818 (let ((fnha file-name-handler-alist)
819 (installed nil))
820
821 (while (and fnha (not installed))
822 (and (eq (cdr (car fnha)) 'jka-compr-handler)
823 (setq installed (car fnha)))
824 (setq fnha (cdr fnha)))
825
826 installed))
827
828
829;;; Add the file I/O hook if it does not already exist.
830;;; Make sure that jka-compr-file-name-handler-entry is eq to the
831;;; entry for jka-compr in file-name-handler-alist.
832(and (jka-compr-installed-p)
833 (jka-compr-uninstall))
834
835(jka-compr-install)
836
837
838(provide 'jka-compr)
839
840;; jka-compr.el ends here.