(mouse-scroll-subr): Don't do anything special at eob.
[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
8fb1a583 169Because of the way `call-process' is defined, discarding the stderr output of
acd622cc
RS
170a program adds the overhead of starting a shell each time the program is
171invoked.")
172
173
555235e6
RS
174(defvar jka-compr-file-name-handler-entry
175 nil
176 "The entry in `file-name-handler-alist' used by the jka-compr I/O functions.")
555235e6 177\f
acd622cc
RS
178;;; Functions for accessing the return value of jka-get-compression-info
179(defun jka-compr-info-regexp (info) (aref info 0))
180(defun jka-compr-info-compress-message (info) (aref info 1))
181(defun jka-compr-info-compress-program (info) (aref info 2))
182(defun jka-compr-info-compress-args (info) (aref info 3))
183(defun jka-compr-info-uncompress-message (info) (aref info 4))
184(defun jka-compr-info-uncompress-program (info) (aref info 5))
185(defun jka-compr-info-uncompress-args (info) (aref info 6))
186(defun jka-compr-info-can-append (info) (aref info 7))
187(defun jka-compr-info-strip-extension (info) (aref info 8))
188
189
190(defun jka-compr-get-compression-info (filename)
191 "Return information about the compression scheme of FILENAME.
192The determination as to which compression scheme, if any, to use is
99bee6a4 193based on the filename itself and `jka-compr-compression-info-list'."
acd622cc
RS
194 (catch 'compression-info
195 (let ((case-fold-search nil))
196 (mapcar
197 (function (lambda (x)
198 (and (string-match (jka-compr-info-regexp x) filename)
199 (throw 'compression-info x))))
200 jka-compr-compression-info-list)
201 nil)))
202
203
204(put 'compression-error 'error-conditions '(compression-error file-error error))
205
206
207(defvar jka-compr-acceptable-retval-list '(0 141))
208
209
210(defun jka-compr-error (prog args infile message &optional errfile)
211
212 (let ((errbuf (get-buffer-create " *jka-compr-error*"))
213 (curbuf (current-buffer)))
214 (set-buffer errbuf)
215 (widen) (erase-buffer)
216 (insert (format "Error while executing \"%s %s < %s\"\n\n"
217 prog
218 (mapconcat 'identity args " ")
219 infile))
220
221 (and errfile
222 (insert-file-contents errfile))
223
224 (set-buffer curbuf)
225 (display-buffer errbuf))
226
227 (signal 'compression-error (list "Opening input file" (format "error %s" message) infile)))
228
229
230(defvar jka-compr-dd-program
231 "/bin/dd")
232
233
dfe05fac 234(defvar jka-compr-dd-blocksize 256)
acd622cc
RS
235
236
237(defun jka-compr-partial-uncompress (prog message args infile beg len)
238 "Call program PROG with ARGS args taking input from INFILE.
239Fourth and fifth args, BEG and LEN, specify which part of the output
240to discard. All output is discarded unless it comes within LEN chars after
241the BEGth char."
242
243 (let* ((skip (/ beg jka-compr-dd-blocksize))
244 (prefix (- beg (* skip jka-compr-dd-blocksize)))
245 (count (and len (1+ (/ (+ len prefix) jka-compr-dd-blocksize))))
246 (start (point))
acd622cc
RS
247 (err-file (jka-compr-make-temp-name))
248 (run-string (format "%s %s 2> %s | %s bs=%d skip=%d %s 2> /dev/null"
249 prog
250 (mapconcat 'identity args " ")
251 err-file
252 jka-compr-dd-program
253 jka-compr-dd-blocksize
254 skip
dfe05fac
RS
255 ;; dd seems to be unreliable about
256 ;; providing the last block. So, always
257 ;; read one more than you think you need.
258 (if count (concat "count=" (1+ count)) ""))))
acd622cc
RS
259
260 (unwind-protect
261 (or (memq (call-process jka-compr-shell
262 infile t nil "-c"
263 run-string)
264 jka-compr-acceptable-retval-list)
265
266 (jka-compr-error prog args infile message err-file))
267
268 (jka-compr-delete-temp-file err-file))
269
270 (and
dfe05fac
RS
271 len
272 (delete-region (+ start prefix len) (point)))
acd622cc
RS
273
274 (delete-region start (+ start prefix))))
275
276
277(defun jka-compr-call-process (prog message infile output temp args)
278 (if jka-compr-use-shell
279
280 (let ((err-file (jka-compr-make-temp-name)))
281
282 (unwind-protect
283
284 (or (memq
285 (call-process jka-compr-shell infile
286 (if (stringp output) nil output)
287 nil
288 "-c"
289 (format "%s %s 2> %s %s"
290 prog
291 (mapconcat 'identity args " ")
292 err-file
293 (if (stringp output)
294 (concat "> " output)
295 "")))
296 jka-compr-acceptable-retval-list)
297
298 (jka-compr-error prog args infile message err-file))
299
300 (jka-compr-delete-temp-file err-file)))
301
302 (or (zerop
303 (apply 'call-process
304 prog
305 infile
306 (if (stringp output) temp output)
307 nil
308 args))
309 (jka-compr-error prog args infile message))
310
311 (and (stringp output)
312 (let ((cbuf (current-buffer)))
313 (set-buffer temp)
314 (write-region (point-min) (point-max) output)
315 (erase-buffer)
316 (set-buffer cbuf)))))
317
318
319;;; Support for temp files. Much of this was inspired if not lifted
320;;; from ange-ftp.
321
322(defvar jka-compr-temp-name-template
323 "/tmp/jka-com"
324 "Prefix added to all temp files created by jka-compr.
99bee6a4 325There should be no more than seven characters after the final `/'")
acd622cc
RS
326
327(defvar jka-compr-temp-name-table (make-vector 31 nil))
328
329(defun jka-compr-make-temp-name (&optional local-copy)
330 "This routine will return the name of a new file."
331 (let* ((lastchar ?a)
332 (prevchar ?a)
333 (template (concat jka-compr-temp-name-template "aa"))
334 (lastpos (1- (length template)))
335 (not-done t)
336 file
337 entry)
338
339 (while not-done
340 (aset template lastpos lastchar)
341 (setq file (concat (make-temp-name template) "#"))
342 (setq entry (intern file jka-compr-temp-name-table))
343 (if (or (get entry 'active)
344 (file-exists-p file))
345
346 (progn
347 (setq lastchar (1+ lastchar))
348 (if (> lastchar ?z)
349 (progn
350 (setq prevchar (1+ prevchar))
351 (setq lastchar ?a)
352 (if (> prevchar ?z)
353 (error "Can't allocate temp file.")
354 (aset template (1- lastpos) prevchar)))))
355
356 (put entry 'active (not local-copy))
357 (setq not-done nil)))
358
359 file))
360
361
362(defun jka-compr-delete-temp-file (temp)
363
364 (put (intern temp jka-compr-temp-name-table)
365 'active nil)
366
367 (condition-case ()
368 (delete-file temp)
369 (error nil)))
370
371
372(defun jka-compr-write-region (start end file &optional append visit)
acd622cc
RS
373 (let* ((filename (expand-file-name file))
374 (visit-file (if (stringp visit) (expand-file-name visit) filename))
375 (info (jka-compr-get-compression-info visit-file)))
376
377 (if info
378
379 (let ((can-append (jka-compr-info-can-append info))
380 (compress-program (jka-compr-info-compress-program info))
381 (compress-message (jka-compr-info-compress-message info))
382 (uncompress-program (jka-compr-info-uncompress-program info))
383 (uncompress-message (jka-compr-info-uncompress-message info))
384 (compress-args (jka-compr-info-compress-args info))
385 (uncompress-args (jka-compr-info-uncompress-args info))
386 (temp-file (jka-compr-make-temp-name))
387 (base-name (file-name-nondirectory visit-file))
388 cbuf temp-buffer)
389
390 (setq cbuf (current-buffer)
391 temp-buffer (get-buffer-create " *jka-compr-temp*"))
392 (set-buffer temp-buffer)
393 (widen) (erase-buffer)
394 (set-buffer cbuf)
395
396 (and append
397 (not can-append)
398 (file-exists-p filename)
399 (let* ((local-copy (file-local-copy filename))
400 (local-file (or local-copy filename)))
401
402 (unwind-protect
403
404 (progn
405
406 (and
407 uncompress-message
408 (message "%s %s..." uncompress-message base-name))
409
410 (jka-compr-call-process uncompress-program
411 (concat uncompress-message
412 " " base-name)
413 local-file
414 temp-file
415 temp-buffer
416 uncompress-args)
417 (and
418 uncompress-message
419 (message "%s %s...done" uncompress-message base-name)))
420
421 (and
422 local-copy
423 (file-exists-p local-copy)
424 (delete-file local-copy)))))
425
426 (and
427 compress-message
428 (message "%s %s..." compress-message base-name))
429
8fb1a583
RS
430 (jka-compr-run-real-handler 'write-region
431 (list start end temp-file t 'dont))
acd622cc
RS
432
433 (jka-compr-call-process compress-program
434 (concat compress-message
435 " " base-name)
436 temp-file
437 temp-buffer
438 nil
439 compress-args)
440
441 (set-buffer temp-buffer)
8fb1a583
RS
442 (jka-compr-run-real-handler 'write-region
443 (list (point-min) (point-max)
444 filename
445 (and append can-append) 'dont))
acd622cc
RS
446 (erase-buffer)
447 (set-buffer cbuf)
448
449 (jka-compr-delete-temp-file temp-file)
450
451 (and
452 compress-message
453 (message "%s %s...done" compress-message base-name))
454
455 (cond
456 ((eq visit t)
457 (setq buffer-file-name filename)
458 (set-visited-file-modtime))
459 ((stringp visit)
460 (setq buffer-file-name visit)
461 (let ((buffer-file-name filename))
462 (set-visited-file-modtime))))
463
464 (and (or (eq visit t)
465 (eq visit nil)
466 (stringp visit))
467 (message "Wrote %s" visit-file))
468
469 nil)
470
8fb1a583
RS
471 (jka-compr-run-real-handler 'write-region
472 (list start end filename append visit)))))
acd622cc
RS
473
474
54b2aa5c 475(defun jka-compr-insert-file-contents (file &optional visit beg end replace)
acd622cc
RS
476 (barf-if-buffer-read-only)
477
478 (and (or beg end)
479 visit
480 (error "Attempt to visit less than an entire file"))
481
482 (let* ((filename (expand-file-name file))
483 (info (jka-compr-get-compression-info filename)))
484
485 (if info
486
487 (let ((uncompress-message (jka-compr-info-uncompress-message info))
488 (uncompress-program (jka-compr-info-uncompress-program info))
489 (uncompress-args (jka-compr-info-uncompress-args info))
490 (base-name (file-name-nondirectory filename))
491 (notfound nil)
8fb1a583
RS
492 (local-copy
493 (jka-compr-run-real-handler 'file-local-copy (list filename)))
acd622cc
RS
494 local-file
495 size start)
496
497 (setq local-file (or local-copy filename))
498
499 (and
500 visit
501 (setq buffer-file-name filename))
502
503 (unwind-protect ; to make sure local-copy gets deleted
504
505 (progn
506
507 (and
508 uncompress-message
509 (message "%s %s..." uncompress-message base-name))
510
511 (condition-case error-code
512
513 (progn
514 (setq start (point))
515 (if (or beg end)
516 (jka-compr-partial-uncompress uncompress-program
517 (concat uncompress-message
518 " " base-name)
519 uncompress-args
520 local-file
521 (or beg 0)
522 (if (and beg end)
523 (- end beg)
524 end))
525 (jka-compr-call-process uncompress-program
526 (concat uncompress-message
527 " " base-name)
528 local-file
529 t
530 nil
531 uncompress-args))
532 (setq size (- (point) start))
533 (goto-char start))
534
535
536 (error
537 (if (and (eq (car error-code) 'file-error)
538 (eq (nth 3 error-code) local-file))
539 (if visit
540 (setq notfound error-code)
541 (signal 'file-error
542 (cons "Opening input file"
543 (nthcdr 2 error-code))))
544 (signal (car error-code) (cdr error-code))))))
545
546 (and
547 local-copy
548 (file-exists-p local-copy)
549 (delete-file local-copy)))
550
551 (and
552 visit
553 (progn
8fb1a583 554 (unlock-buffer)
acd622cc
RS
555 (setq buffer-file-name filename)
556 (set-visited-file-modtime)))
557
558 (and
559 uncompress-message
560 (message "%s %s...done" uncompress-message base-name))
561
562 (and
563 visit
564 notfound
565 (signal 'file-error
566 (cons "Opening input file" (nth 2 notfound))))
567
568 (list filename size))
569
8fb1a583
RS
570 (jka-compr-run-real-handler 'insert-file-contents
571 (list file visit beg end replace)))))
acd622cc
RS
572
573
574(defun jka-compr-file-local-copy (file)
acd622cc
RS
575 (let* ((filename (expand-file-name file))
576 (info (jka-compr-get-compression-info filename)))
577
578 (if info
579
580 (let ((uncompress-message (jka-compr-info-uncompress-message info))
581 (uncompress-program (jka-compr-info-uncompress-program info))
582 (uncompress-args (jka-compr-info-uncompress-args info))
583 (base-name (file-name-nondirectory filename))
8fb1a583
RS
584 (local-copy
585 (jka-compr-run-real-handler 'file-local-copy (list filename)))
acd622cc
RS
586 (temp-file (jka-compr-make-temp-name t))
587 (temp-buffer (get-buffer-create " *jka-compr-temp*"))
588 (notfound nil)
589 (cbuf (current-buffer))
590 local-file)
591
592 (setq local-file (or local-copy filename))
593
594 (unwind-protect
595
596 (progn
597
598 (and
599 uncompress-message
600 (message "%s %s..." uncompress-message base-name))
601
602 (set-buffer temp-buffer)
603
604 (jka-compr-call-process uncompress-program
605 (concat uncompress-message
606 " " base-name)
607 local-file
608 t
609 nil
610 uncompress-args)
611
612 (and
613 uncompress-message
614 (message "%s %s...done" uncompress-message base-name))
615
616 (write-region
617 (point-min) (point-max) temp-file nil 'dont))
618
619 (and
620 local-copy
621 (file-exists-p local-copy)
622 (delete-file local-copy))
623
624 (set-buffer cbuf)
625 (kill-buffer temp-buffer))
626
627 temp-file)
628
8fb1a583 629 (jka-compr-run-real-handler 'file-local-copy (list filename)))))
acd622cc
RS
630
631
632;;; Support for loading compressed files.
633(defun jka-compr-load (file &optional noerror nomessage nosuffix)
634 "Documented as original."
635
636 (let* ((local-copy (jka-compr-file-local-copy file))
637 (load-file (or local-copy file)))
638
639 (unwind-protect
640
8fb1a583
RS
641 (let (inhibit-file-name-operation
642 inhibit-file-name-handlers)
acd622cc
RS
643 (or nomessage
644 (message "Loading %s..." file))
645
646 (load load-file noerror t t)
647
648 (or nomessage
649 (message "Loading %s...done." file)))
650
acd622cc
RS
651 (jka-compr-delete-temp-file local-copy))
652
653 t))
8fb1a583
RS
654\f
655(put 'write-region 'jka-compr 'jka-compr-write-region)
656(put 'insert-file-contents 'jka-compr 'jka-compr-insert-file-contents)
657(put 'file-local-copy 'jka-compr 'jka-compr-file-local-copy)
658(put 'load 'jka-compr 'jka-compr-load)
acd622cc 659
acd622cc 660(defun jka-compr-handler (operation &rest args)
8fb1a583
RS
661 (save-match-data
662 (let ((jka-op (get operation 'jka-compr)))
663 (if jka-op
664 (apply jka-op args)
665 (jka-compr-run-real-handler operation args)))))
acd622cc 666
99bee6a4
RS
667;; If we are given an operation that we don't handle,
668;; call the Emacs primitive for that operation,
669;; and manipulate the inhibit variables
670;; to prevent the primitive from calling our handler again.
671(defun jka-compr-run-real-handler (operation args)
672 (let ((inhibit-file-name-handlers
673 (cons 'jka-compr-handler
674 (and (eq inhibit-file-name-operation operation)
675 inhibit-file-name-handlers)))
676 (inhibit-file-name-operation operation))
677 (apply operation args)))
678
acd622cc
RS
679(defun toggle-auto-compression (arg)
680 "Toggle automatic file compression and decompression.
681With prefix argument ARG, turn auto compression on if positive, else off.
682Returns the new status of auto compression (non-nil means on)."
683 (interactive "P")
684 (let* ((installed (jka-compr-installed-p))
685 (flag (if (null arg)
686 (not installed)
687 (or (eq arg t) (listp arg) (and (integerp arg) (> arg 0))))))
688
689 (cond
690 ((and flag installed) t) ; already installed
691
692 ((and (not flag) (not installed)) nil) ; already not installed
693
694 (flag
695 (jka-compr-install))
696
697 (t
698 (jka-compr-uninstall)))
699
700
701 (and (interactive-p)
702 (if flag
703 (message "Automatic file (de)compression is now ON.")
704 (message "Automatic file (de)compression is now OFF.")))
705
706 flag))
707
708
709(defun jka-compr-build-file-regexp ()
710 (concat
711 "\\("
712 (mapconcat
713 'jka-compr-info-regexp
714 jka-compr-compression-info-list
715 "\\)\\|\\(")
716 "\\)"))
717
718
719(defun jka-compr-install ()
720 "Install jka-compr.
99bee6a4 721This adds entries to `file-name-handler-alist' and `auto-mode-alist'."
acd622cc
RS
722
723 (setq jka-compr-file-name-handler-entry
724 (cons (jka-compr-build-file-regexp) 'jka-compr-handler))
725
726 (setq file-name-handler-alist (cons jka-compr-file-name-handler-entry
727 file-name-handler-alist))
728
8fb1a583
RS
729 ;; Make entries in auto-mode-alist so that modes are chosen right
730 ;; according to the file names sans `.gz'.
acd622cc
RS
731 (mapcar
732 (function (lambda (x)
733 (and
734 (jka-compr-info-strip-extension x)
735 (setq auto-mode-alist (cons (list (jka-compr-info-regexp x)
736 nil 'jka-compr)
737 auto-mode-alist)))))
738
739 jka-compr-compression-info-list))
740
741
742(defun jka-compr-uninstall ()
743 "Uninstall jka-compr.
99bee6a4
RS
744This removes the entries in `file-name-handler-alist' and `auto-mode-alist'
745that were created by `jka-compr-installed'."
acd622cc
RS
746
747 (let* ((fnha (cons nil file-name-handler-alist))
748 (last fnha))
749
750 (while (cdr last)
751 (if (eq (cdr (car (cdr last))) 'jka-compr-handler)
752 (setcdr last (cdr (cdr last)))
753 (setq last (cdr last))))
754
755 (setq file-name-handler-alist (cdr fnha)))
756
757 (let* ((ama (cons nil auto-mode-alist))
758 (last ama)
759 entry)
760
761 (while (cdr last)
762 (setq entry (car (cdr last)))
763 (if (and (consp (cdr entry))
764 (eq (nth 2 entry) 'jka-compr))
765 (setcdr last (cdr (cdr last)))
766 (setq last (cdr last))))
767
768 (setq auto-mode-alist (cdr ama))))
769
770
771(defun jka-compr-installed-p ()
772 "Return non-nil if jka-compr is installed.
99bee6a4 773The return value is the entry in `file-name-handler-alist' for jka-compr."
acd622cc
RS
774
775 (let ((fnha file-name-handler-alist)
776 (installed nil))
777
778 (while (and fnha (not installed))
779 (and (eq (cdr (car fnha)) 'jka-compr-handler)
780 (setq installed (car fnha)))
781 (setq fnha (cdr fnha)))
782
783 installed))
784
785
786;;; Add the file I/O hook if it does not already exist.
787;;; Make sure that jka-compr-file-name-handler-entry is eq to the
788;;; entry for jka-compr in file-name-handler-alist.
789(and (jka-compr-installed-p)
790 (jka-compr-uninstall))
791
792(jka-compr-install)
793
794
795(provide 'jka-compr)
796
797;; jka-compr.el ends here.