;;; jka-compr.el --- reading/writing/loading compressed files
-;; Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
;; Author: jka@ece.cmu.edu (Jay K. Adams)
;; Maintainer: FSF
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
-;;; Commentary:
+;;; Commentary:
;; This package implements low-level support for reading, writing,
;; and loading compressed files. It hooks into the low-level file
;; APPLICATION NOTES:
;;
;; crypt++
-;; jka-compr can coexist with crpyt++ if you take all the decompression
+;; jka-compr can coexist with crypt++ if you take all the decompression
;; entries out of the crypt-encoding-list. Clearly problems will arise if
;; you have two programs trying to compress/decompress files. jka-compr
;; will not "work with" crypt++ in the following sense: you won't be able to
;; ACKNOWLEDGMENTS
-;;
+;;
;; jka-compr is a V19 adaptation of jka-compr for V18 of Emacs. Many people
-;; have made helpful suggestions, reported bugs, and even fixed bugs in
+;; have made helpful suggestions, reported bugs, and even fixed bugs in
;; jka-compr. I recall the following people as being particularly helpful.
;;
;; Jean-loup Gailly
:type 'string
:group 'jka-compr)
-(defvar jka-compr-use-shell
+(defvar jka-compr-use-shell
(not (memq system-type '(ms-dos windows-nt))))
;;; I have this defined so that .Z files are assumed to be in unix
"bzip2ing" "bzip2" nil
"bunzip2ing" "bzip2" ("-d")
nil t "BZh"]
+ ["\\.tbz\\'"
+ "bzip2ing" "bzip2" nil
+ "bunzip2ing" "bzip2" ("-d")
+ nil nil "BZh"]
["\\.tgz\\'"
"zipping" "gzip" ("-c" "-q")
"unzipping" "gzip" ("-c" "-q" "-d")
["\\.g?z\\(~\\|\\.~[0-9]+~\\)?\\'"
"zipping" "gzip" ("-c" "-q")
"unzipping" "gzip" ("-c" "-q" "-d")
- t t "\037\213"])
+ t t "\037\213"]
+ ;; dzip is gzip with random access. Its compression program can't
+ ;; read/write stdin/out, so .dz files can only be viewed without
+ ;; saving, having their contents decompressed with gzip.
+ ["\\.dz\\'"
+ nil nil nil
+ "unzipping" "gzip" ("-c" "-q" "-d")
+ nil t "\037\213"])
"List of vectors that describe available compression techniques.
Each element, which describes a compression technique, is a vector of
type of compression (nil means no message)
compress-program is a program that performs this compression
+ (nil means visit file in read-only mode)
compress-args is a list of args to pass to the compress program
(string :tag "Magic Bytes")))
:group 'jka-compr)
-(defvar jka-compr-mode-alist-additions
- (list (cons "\\.tgz\\'" 'tar-mode))
- "A list of pairs to add to `auto-mode-alist' when jka-compr is installed.")
+(defcustom jka-compr-mode-alist-additions
+ (list (cons "\\.tgz\\'" 'tar-mode) (cons "\\.tbz\\'" 'tar-mode))
+ "A list of pairs to add to `auto-mode-alist' when jka-compr is installed."
+ :type '(repeat (cons string symbol))
+ :group 'jka-compr)
+
+(defcustom jka-compr-load-suffixes '(".gz")
+ "List of suffixes to try when loading files."
+ :type '(repeat string)
+ :group 'jka-compr)
;; List of all the elements we actually added to file-coding-system-alist.
(defvar jka-compr-added-to-file-coding-system-alist nil)
(signal 'compression-error
(list "Opening input file" (format "error %s" message) infile)))
-
-
-(defvar jka-compr-dd-program
- "/bin/dd")
+
+
+(defcustom jka-compr-dd-program "/bin/dd"
+ "How to invoke `dd'."
+ :type 'string
+ :group 'jka-compr)
(defvar jka-compr-dd-blocksize 256)
"Call program PROG with ARGS args taking input from INFILE.
Fourth and fifth args, BEG and LEN, specify which part of the output
to keep: LEN chars starting BEG chars from the beginning."
- (let* ((skip (/ beg jka-compr-dd-blocksize))
- (prefix (- beg (* skip jka-compr-dd-blocksize)))
- (count (and len (1+ (/ (+ len prefix) jka-compr-dd-blocksize))))
- (start (point))
- (err-file (jka-compr-make-temp-name))
- (run-string (format "%s %s 2> %s | %s bs=%d skip=%d %s 2> /dev/null"
- prog
- (mapconcat 'identity args " ")
- err-file
- jka-compr-dd-program
- jka-compr-dd-blocksize
- skip
- ;; dd seems to be unreliable about
- ;; providing the last block. So, always
- ;; read one more than you think you need.
- (if count (concat "count=" (1+ count)) ""))))
-
- (unwind-protect
- (or (memq (call-process jka-compr-shell
- infile t nil "-c"
- run-string)
- jka-compr-acceptable-retval-list)
-
- (jka-compr-error prog args infile message err-file))
-
- (jka-compr-delete-temp-file err-file))
+ (let ((start (point))
+ (prefix beg))
+ (if (and jka-compr-use-shell jka-compr-dd-program)
+ ;; Put the uncompression output through dd
+ ;; to discard the part we don't want.
+ (let ((skip (/ beg jka-compr-dd-blocksize))
+ (err-file (jka-compr-make-temp-name))
+ count)
+ ;; Update PREFIX based on the text that we won't read in.
+ (setq prefix (- beg (* skip jka-compr-dd-blocksize))
+ count (and len (1+ (/ (+ len prefix) jka-compr-dd-blocksize))))
+ (unwind-protect
+ (or (memq (call-process
+ jka-compr-shell infile t nil "-c"
+ (format
+ "%s %s 2> %s | %s bs=%d skip=%d %s 2> %s"
+ prog
+ (mapconcat 'identity args " ")
+ err-file
+ jka-compr-dd-program
+ jka-compr-dd-blocksize
+ skip
+ ;; dd seems to be unreliable about
+ ;; providing the last block. So, always
+ ;; read one more than you think you need.
+ (if count (format "count=%d" (1+ count)) "")
+ null-device))
+ jka-compr-acceptable-retval-list)
+ (jka-compr-error prog args infile message err-file))
+ (jka-compr-delete-temp-file err-file)))
+ ;; Run the uncompression program directly.
+ ;; We get the whole file and must delete what we don't want.
+ (jka-compr-call-process prog message infile t nil args))
;; Delete the stuff after what we want, if there is any.
(and
(jka-compr-delete-temp-file err-file)))
- (or (zerop
+ (or (eq 0
(apply 'call-process
prog
infile
(info (jka-compr-get-compression-info visit-file))
(magic (and info (jka-compr-info-file-magic-bytes info))))
+ ;; If START is nil, use the whole buffer.
+ (if (null start)
+ (setq start 1 end (1+ (buffer-size))))
+
;; If we uncompressed this file when visiting it,
;; then recompress it when writing it
;; even if the contents look compressed already.
(let ((can-append (jka-compr-info-can-append info))
(compress-program (jka-compr-info-compress-program info))
(compress-message (jka-compr-info-compress-message info))
- (uncompress-program (jka-compr-info-uncompress-program info))
- (uncompress-message (jka-compr-info-uncompress-message info))
(compress-args (jka-compr-info-compress-args info))
- (uncompress-args (jka-compr-info-uncompress-args info))
(base-name (file-name-nondirectory visit-file))
temp-file temp-buffer
;; we need to leave `last-coding-system-used' set to its
;; that `basic-save-buffer' sees the right value.
(coding-system-used last-coding-system-used))
+ (or compress-program
+ (error "No compression program defined"))
+
(setq temp-buffer (get-buffer-create " *jka-compr-wr-temp*"))
(with-current-buffer temp-buffer
(widen) (erase-buffer))
(setq temp-file (jka-compr-make-temp-name)))
- (and
+ (and
compress-message
(message "%s %s..." compress-message base-name))
(setq last-coding-system-used coding-system-used)
nil)
-
+
(jka-compr-run-real-handler 'write-region
(list start end filename append visit)))))
(local-copy
(jka-compr-run-real-handler 'file-local-copy (list filename)))
local-file
- size start
- (coding-system-for-read
- (or coding-system-for-read
- ;; If multibyte characters are disabled,
- ;; don't do that conversion.
- (and (null enable-multibyte-characters)
- (or (auto-coding-alist-lookup
- (jka-compr-byte-compiler-base-file-name file))
- 'raw-text))
- (let ((coding (find-operation-coding-system
- 'insert-file-contents
- (jka-compr-byte-compiler-base-file-name file))))
- (and (consp coding) (car coding)))
- 'undecided)) )
+ size start)
(setq local-file (or local-copy filename))
(unwind-protect ; to make sure local-copy gets deleted
(progn
-
+
(and
uncompress-message
(message "%s %s..." uncompress-message base-name))
(condition-case error-code
- (progn
+ (let ((coding-system-for-read 'no-conversion))
(if replace
(goto-char (point-min)))
(setq start (point))
uncompress-args)))
(setq size (- (point) start))
(if replace
- (let* ((del-beg (point))
- (del-end (+ del-beg size)))
- (delete-region del-beg
- (min del-end (point-max)))))
+ (delete-region (point) (point-max)))
(goto-char start))
(error
(if (and (eq (car error-code) 'file-error)
(eq (nth 3 error-code) local-file))
(if visit
(setq notfound error-code)
- (signal 'file-error
+ (signal 'file-error
(cons "Opening input file"
(nthcdr 2 error-code))))
(signal (car error-code) (cdr error-code))))))
(file-exists-p local-copy)
(delete-file local-copy)))
+ (unless notfound
+ (decode-coding-inserted-region
+ (point) (+ (point) size)
+ (jka-compr-byte-compiler-base-file-name file)
+ visit beg end replace))
+
(and
visit
(progn
(setq buffer-file-name filename)
(setq jka-compr-really-do-compress t)
(set-visited-file-modtime)))
-
+
(and
uncompress-message
(message "%s %s...done" uncompress-message base-name))
;;; (setq size insval)))
;;; (setq p (cdr p))))
+ (or (jka-compr-info-compress-program info)
+ (message "You can't save this buffer because compression program is not defined"))
+
(list filename size))
(jka-compr-run-real-handler 'insert-file-contents
(unwind-protect
(with-current-buffer temp-buffer
-
+
(and
uncompress-message
(message "%s %s..." uncompress-message base-name))
-
+
;; Here we must read the output of uncompress program
;; and write it to TEMP-FILE without any code
;; conversion. An appropriate code conversion (if
(kill-buffer temp-buffer))
temp-file)
-
+
(jka-compr-run-real-handler 'file-local-copy (list filename)))))
Lisp programs can bind this to t to do that.
It is not recommended to set this variable permanently to anything but nil.")
+(put 'jka-compr-handler 'safe-magic t)
(defun jka-compr-handler (operation &rest args)
(save-match-data
(let ((jka-op (get operation 'jka-compr)))
inhibit-first-line-modes-suffixes)))))
jka-compr-compression-info-list)
(setq auto-mode-alist
- (append auto-mode-alist jka-compr-mode-alist-additions)))
+ (append auto-mode-alist jka-compr-mode-alist-additions))
+
+ ;; Make sure that (load "foo") will find /bla/foo.el.gz.
+ (setq load-suffixes
+ (apply 'append
+ (mapcar (lambda (suffix)
+ (cons suffix
+ (mapcar (lambda (ext) (concat suffix ext))
+ jka-compr-load-suffixes)))
+ load-suffixes))))
(defun jka-compr-uninstall ()
(eq (nth 2 entry) 'jka-compr)))
(setcdr last (cdr (cdr last)))
(setq last (cdr last))))
-
+
(setq auto-mode-alist (cdr ama)))
(let* ((ama (cons nil file-coding-system-alist))
(if (member entry jka-compr-added-to-file-coding-system-alist)
(setcdr last (cdr (cdr last)))
(setq last (cdr last))))
-
- (setq file-coding-system-alist (cdr ama))))
-
+ (setq file-coding-system-alist (cdr ama)))
+
+ ;; Remove the suffixes that were added by jka-compr.
+ (let ((suffixes nil)
+ (re (jka-compr-build-file-regexp)))
+ (dolist (suffix load-suffixes)
+ (unless (string-match re suffix)
+ (push suffix suffixes)))
+ (setq load-suffixes (nreverse suffixes))))
+
+
(defun jka-compr-installed-p ()
"Return non-nil if jka-compr is installed.
The return value is the entry in `file-name-handler-alist' for jka-compr."
(jka-compr-uninstall))
-;;; Note this definition must be at the end of the file, because
-;;; `define-minor-mode' actually calls the mode-function if the
-;;; associated variable is non-nil, which requires that all needed
-;;; functions be already defined. [This is arguably a bug in d-m-m]
;;;###autoload
(define-minor-mode auto-compression-mode
"Toggle automatic file compression and uncompression.
(provide 'jka-compr)
-;; jka-compr.el ends here.
+;;; arch-tag: 3f15b630-e9a7-46c4-a22a-94afdde86ebc
+;;; jka-compr.el ends here