From: dodd@mycenae.cchem.berkeley.edu (Lawrence R. Dodd)
Newsgroups: gnu.emacs.sources
Subject: crypt++.el -- enhanced version of crypt
Date: 24 Jan 93 07:44:09
Organization: Dept of Chemical Engineering, Polytechnic Univ, NY, USA
NNTP-Posting-Host: mycenae.cchem.berkeley.edu
Summary: code for handling all sorts of compressed and encrypted files
Keywords: crypt++.el, table-driven


Hello all,

  Here is an updated and enhanced version of `crypt.el' called `crypt++.el'.
  It main advantage over previous versions of crypt is that it is table-driven
  making the addition of alternate compression programs trivial. This version
  also allows the user to write out buffers in compressed or plain format
  independent of the origin of the buffer. It does this by monitoring the
  file-name extension passed to `write-file' (C-x C-w) to see if a change in
  format is possibly being requested.  It then (optionally) confirms with the
  user before writing the buffer.

  Since the elisp-archive seems to be in a state of dormancy, this file is
  also available via anonymous ftp to roebling.poly.edu in /pub/.  Please try
  to exercise customary ftp etiquette and use only on off-peak hours.

  Furthermore, since the package is named `crypt++.el' you should replace any
  occurrence of (require 'crypt) in your .emacs or (for instance) in
  `lispdir.el' with (require 'crypt++) otherwise this is a drop-in replacement
  for the previous versions of crypt (see INSTALLATION in header).

  Finally, if you have any questions, comments, bug-reports, or smart remarks
  concerning crypt++.el type M-x crypt-submit-report and a template for a bug
  report will be generated (this idea was taken from Barry A. Warsaw's
  c++-mode.el).

share and enjoy,

  Larry Dodd <dodd@roebling.poly.edu>
                 and
  Rod Whitby <rwhitby@research.canon.oz.au>


<file: ~/lisp/crypt++.el>
........................... cut along dotted line ...........................
;;; crypt++.el -- code for handling all sorts of compressed and encrypted files

(defconst crypt-version (substring "$Revision: 2.7 $" 11 -2)
  "The revision number of crypt++.el -- code for handling all sorts of
compression and encryption files. To send a bug report type
M-x crypt-submit-report. Complete RCS identity is 

   $Id: crypt++.el,v 2.7 1993/01/24 15:22:48 dodd Exp $")

;;; Copyright (C) 1993 Rod Whitby and Lawrence R. Dodd
;;; Copyright (C) 1988, 1989, 1990 Kyle E. Jones
;;;
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2 of the License, or
;;; (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

;;; AVAILABLE: 
;;; 
;;; via anonymous ftp to roebling.poly.edu [128.238.5.31] in /pub/crypt++.el
;;; (ange-ftp TAG: /roebling.poly.edu:/pub/crypt++.el)

;;; Send bug reports to:
;;;
;;;   Lawrence R. Dodd <dodd@roebling.poly.edu>
;;;   Department of Chemical Engineering
;;;   Polymer Research Institute
;;;   Polytechnic University 
;;;   Brooklyn, New York, 11201 USA
;;;
;;;   or
;;;                                                 _--_|\  
;;;   Rod Whitby <rwhitby@research.canon.oz.au>    /      \ 
;;;   Canon Information Systems Research Australia \_.--._/ 
;;;   1 Thomas Holt Drive, North Ryde, N.S.W., 2113      v
;;; 
;;;   or type M-x crypt-submit-report to generate a bug report template

;;; $Id: crypt++.el,v 2.7 1993/01/24 15:22:48 dodd Exp $
;;; $Date: 1993/01/24 15:22:48 $
;;; $Revision: 2.7 $
;;;
;;; version log
;;; 1.1 - original version of crypt.el
;;; 1.2 -
;;;   jwz: works with tar-mode.el
;;;   jwz: applied patch from piet, merged with Lawrence Dodd's gzip version
;;; 1.3 -
;;;   lrd: fixed compress-magic-regexp 
;;; 1.4, 1.5, 1.6 -
;;;   lrd: write-file compresses or gzips based on file extension
;;; 2.1 -
;;;   lrd: merged with Rod Whitby's table-driven version (major upgrade)
;;; 2.2 -
;;;   rjw: Changed file name to crypt++.el, so archie and lispdir can find it.
;;; 2.3 -
;;;   rjw: Separated the hook additions and minor mode alist additions.
;;; 2.4 -
;;;   rjw: Fixed the interactive form for crypt-buffer.
;;; 2.5 - 
;;;   lrd: doc mods, changed GNU free software notice (was out of date), added 
;;;   anonymous ftp information
;;; 2.6 - 
;;;   lrd: added back in definition of `buffer' in defun crypt-buffer caused 
;;;   an error when trying to read encrypted file; modified check for minor 
;;;   mode alist addition; added gzip magic number warning
;;; 2.7 - 
;;;   lrd: added `TO DO' and `KNOW BUGS' section to header

;; INSTALLATION:
;;
;; To use this package, simply put it in a file called "crypt++.el" in a Lisp
;; directory known to Emacs, byte-compile it, and put the line:
;;
;;                      (require 'crypt++)
;;
;; in your ~/.emacs file or in the file default.el in the ../lisp directory of
;; the Emacs distribution.  Do not bother trying to autoload this file; this
;; package uses find-file and write-file hooks and thus should be loaded the
;; first time you visit any sort of file.
;;
;; Users also may wish to toggle the values of `crypt-auto-decode-buffer,' 
;; `crypt-freeze-vs-fortran,' and `crypt-auto-write-buffer' from their 
;; defaults and possibly change the method of encryption and decryption using 
;; the values of `crypt-encryption-program' and `crypt-decryption-program.'
;;
;; Note that the magic number used by gzip (GNU zip) was changed by Jean-loup
;; Gailly in his Beta version 0.6. The magic regular expression used below in
;; `crypt-encoding-alist' reflects this change. If you are using a version of
;; gzip earlier than 0.6, then please upgrade.

;; DESCRIPTION
;;
;; The basic purpose of this package of Lisp functions is to recognize
;; automatically encrypted and encoded (i.e., compressed) files when they are
;; first visited or written.  The BUFFER corresponding to the file is decoded
;; and/or decrypted before it is presented to the user.  The file itself is
;; unchanged on the disk.  When the buffer is subsequently saved to disk, a
;; hook function re-encodes the buffer before the actual disk write takes
;; place.
;;
;; This package recognizes all sorts of compressed files by a magic number at
;; the beginning of these files but uses a heuristic to detect encrypted
;; files.  If you are asked for an encryption key for a file that is in fact
;; not encrypted, just hit RET and the file will be accepted as is, and the
;; crypt minor mode will not be entered.
;;
;; Other types of encoding programs may be added to crypt++ using the variable
;; `crypt-encoding-alist' which contains a table of encoding programs such as
;; compress, gzip (GNU zip), and compact.
;;
;; This new extended version of crypt now monitors the filename extensions of
;; buffers that are written out using write-file (C-x C-w).  If the filename
;; extension matches one of the extensions listed in `crypt-encoding-alist,'
;; then crypt++ will write the file out using the corresponding encoding
;; (compression) method. This is done whether or not the buffer originated
;; from a previously encoded (compressed) file.
;;
;; Thus, if the user is editing a file that may or may not have been encoded
;; originally (e.g., foobar.Z or foobar) and decides to write it to a
;; different file (e.g., barfoo or barfoo.z or barfoo.C).  Crypt++ will examine
;; the filename extension and write the buffer in plain format or an alternate
;; encoding (compression) format by searching through the entries in the table
;; of encoding methods `crypt-encoding-alist.'  This change in encoding state
;; is done automatically if the variable `crypt-auto-write-buffer' is t
;; otherwise the user is asked.

;; TO DO/KNOWN BUGS: 
;; 
;; * currently crypt++ assumes that if a file is both encrypted and encoded
;;   (i.e., compressed) that the order in which it was done was encryption
;;   first _then_ compression.  As has been pointed by many people compression
;;   following encryption is useless since the encrypted file is basically
;;   random.  On the other hand, many agree that doing encryption _following_
;;   compression is better since it makes it harder to crack the encryption.
;;   We would like to make the ordering of these two user-configurable or if
;;   nothing else change the order. 
;; 
;; * some have complained that the encryption key that crypt++.el requests is
;;   visible via `ps' We would like to pass the key to the encryption and
;;   decryption program(s) in a more secure manner.


;; user definable variables

(defvar crypt-encryption-program "crypt"
  "*Command to be used for encryption.")

(defvar crypt-decryption-program "crypt"
  "*Command to be used for decryption.")

(defvar crypt-auto-decode-buffer t
  "*t value means that the buffers associated with encoded files will
be decoded automatically, without requesting confirmation from the user.
`nil' means to ask before doing the decoding.")

(defvar crypt-auto-write-buffer nil
  "*t value means buffers to be written to files ending in extensions
matching those in `crypt-encoding-alist' will be encoded automatically,
without requesting confirmation from the user. `nil' means to ask before doing
this encoding. Likewise, buffers originating from encoded files to be written
to different files ending in extensions that do not match any of those in
`crypt-encoding-alist' will be written in plain format automatically, without
requesting confirmation from the user. `nil' means to ask before doing this
decoding.")

(defvar crypt-freeze-vs-fortran t
  "*t values means that the .F file extension denotes a frozen file
rather than a Fortran file.")

(defvar crypt-encoding-alist
  (list
   '(compact "\377\037" "\\(\\.C\\)$" "compact" "uncompact" "Compact")
   '(compress "\037\235" "\\(\\.Z\\)$" "compress" "uncompress" "Compress")
   (and
    crypt-freeze-vs-fortran
    '(freeze "\037\236\\|\037\237" "\\(\\.F\\)$" "freeze" "unfreeze" "Freeze"))
   '(gzip "\037\213" "\\(\\.z\\)$" "gzip" "gzip -d" "Zip")
   ;; Add new elements here ...
   )
  "*A list of elements describing the encoding methods available, each of
which looks like

        \(ENCODING-TYPE MAGIC-REGEXP FILE-EXTENSION
                        ENCODE-PROGRAM DECODE-PROGRAM
                        MINOR-MODE
                        \)

ENCODING-TYPE is a symbol denoting the encoding type.  Currently known
encodings are (compress compact freeze gzip).

MAGIC-REGEXP is a regexp that matches the magic number at the
beginning of files encoded with ENCODING-TYPE.

FILE-EXTENSION is a string denoting the file extension usually
appended the filename of files encoded with ENCODING-TYPE.

ENCODE-PROGRAM is a string denoting the name of the program used to
encode files.

DECODE-PROGRAM is a string denoting the name of the program used to
decode files.

MINOR-MODE is a string denoting the name for the encoded minor mode as 
it will appear in the mode line.
")


;; buffer locals

(defvar buffer-save-encrypted nil
  "*Non-nil means that when this buffer is saved it will be written out
encrypted, using the commands in variables `crypt-encryption-program' and
`crypt-decryption-program.'  Automatically local to all buffers.")
(make-variable-buffer-local 'buffer-save-encrypted)

(defvar buffer-encryption-key nil
  "*Key to use when encrypting the current buffer, prior to saving it.
Automatically local to all buffers.")
(make-variable-buffer-local 'buffer-encryption-key)

(defvar buffer-save-encoded nil
  "*Non-nil means that when this buffer is saved it will be written out
encoded with ENCODING-TYPE, as with the ENCODING-PROGRAM command.
Automatically local to all buffers.")
(make-variable-buffer-local 'buffer-save-encoded)

(defvar buffer-encoding-type nil
  "*Non-nil means that this buffer is encoded with ENCODING-TYPE.
Automatically local to all buffers.")
(make-variable-buffer-local 'buffer-encoding-type)


;; defuns that work on the encoding-alist

(defun encoding-magic-regexp (encoding-type)
  "Returns a regexp that matches the magic number at the beginning of files
encoded with ENCODING-TYPE."
  (let ((elt (assoc encoding-type crypt-encoding-alist)))
    (and elt
         (nth 1 elt))))

(defun encoding-file-extension (encoding-type)
  "Returns a regexp that matches the file-extension typically associated with
files encoded with ENCODING-TYPE."
  (let ((elt (assoc encoding-type crypt-encoding-alist)))
    (and elt
         (nth 2 elt))))

(defun encoding-encode-program (encoding-type)
  "Returns a string denoting the name of the program used to encode files
encoded with ENCODING-TYPE."
  (let ((elt (assoc encoding-type crypt-encoding-alist)))
    (and elt
         (nth 3 elt))))

(defun encoding-decode-program (encoding-type)
  "Returns a string denoting the name of the program used to decode files
encoded with ENCODING-TYPE."
  (let ((elt (assoc encoding-type crypt-encoding-alist)))
    (and elt
         (nth 4 elt))))

(defun crypt-buffer-save-name (encoding-type)
  "Returns a variable name. t means that when the buffer is saved it will be
written out using its decoding program.  Automatically local to all buffers."
  (intern (concat "buffer-save-" (symbol-name encoding-type))))


;; Create a buffer-local variable for each type of encoding.
;; These variables are used to trigger the minor mode names.

(defvar crypt-minor-mode-encrypted
  '(buffer-save-encrypted " Crypt")
  "Minor mode alist entry for encrypted buffers.")

(defvar crypt-minor-mode-alist
  (append
   (list crypt-minor-mode-encrypted)
   (mapcar
    (function
     (lambda (element)
       (let ((variable (crypt-buffer-save-name (car element))))
	 (make-variable-buffer-local variable)
	 (list variable (concat " " (nth 5 element))))))
    crypt-encoding-alist))
"Alist derived from `crypt-encoding-alist' containing encoded minor modes.")


;; Encrypted files have no magic number, so we have to hack a way of
;; determining which new buffers start in crypt mode.  The current setup is
;; that we use only buffers that have a non-ASCII character very close to
;; beginning of buffer and that do NOT match crypt-magic-regexp-inverse.
;; Currently crypt-magic-regexp-inverse will match Sun OS, 4.x BSD, and
;; Ultrix executable magic numbers, so binaries can still be edited (heh)
;; without headaches.

(defconst crypt-magic-regexp-inverse
  "\\(..\\)?\\([\007\010\013]\001\\|\001[\007\010\013]\\)"
  "Regexp that must NOT match the beginning of an encrypted buffer.")

(defmacro save-point (&rest body)
  "Save value of point, evaluate FORMS, and restore value of point.
If the saved value of point is no longer valid go to (point-max).
This macro exists because, save-excursion loses track of point during
some types of deletions."
  (let ((var (make-symbol "saved-point")))
    (list 'let (list (list var '(point)))
          (list 'unwind-protect
                (cons 'progn body)
                (list 'goto-char var)))))


(defun crypt-find-file-hook ()
  "Hook run for decoding and/or decrypting the contents of a buffer. Part of
find-file-hooks"
  (let ((buffer-file-name buffer-file-name)
        (old-buffer-file-name buffer-file-name)
        (old-buffer-modified-p (buffer-modified-p))
        found
        elt regexp
        encrypted encoded
        case-fold-search buffer-read-only)

    (save-point
     (save-restriction
       (widen)
       (goto-char (point-min))

       ;; We can reasonably assume that either compaction or compression will
       ;; be used, or neither, but not both.

       ;; find the file type

       (let ((alist crypt-encoding-alist))
         (while (and alist (setq elt (car alist)) (not found))
           (if (looking-at (nth 1 elt))
               (progn (setq buffer-encoding-type (nth 0 elt))
                      (setq found t))
             ;; decrement 
             (setq alist (cdr alist)))))

       ;; do we have to decode? if not move on
       (if (and found 
                (or crypt-auto-decode-buffer
                    (y-or-n-p (format "Decode buffer %s? "
                                      (buffer-name)))))
           (progn
             (message "Decoding %s..." (buffer-name))
             (encode-buffer (current-buffer) t)

             ;; We can not actually go into encoding mode yet because the
             ;; major mode may change later on and blow away all local
             ;; variables (and thus the minor modes).  So we make a note to go
             ;; into encoding mode later.

             (setq encoded buffer-encoding-type)

             ;; here we strip the encoded file's extension so that later we
             ;; can set the buffer's major mode based on this modified name
             ;; instead of the name with the extension.

             (if (string-match (encoding-file-extension buffer-encoding-type)
                               buffer-file-name)
                 (setq buffer-file-name
                       (substring buffer-file-name 0 (match-beginning 1))))

             (if (not (input-pending-p))
                 (message "Decoding %s... done" (buffer-name)))))

       ;; Now peek at the file and see if it still looks like a binary file.
       ;; If so, try the crypt-magic-regexp-inverse against it and if it FAILS
       ;; we assume that this is an encrypted buffer.

       (cond (

              (and (not (eobp))
                   (re-search-forward "[\200-\377]" (min (point-max) 15) t)
                   (goto-char (point-min))
                   (not (looking-at crypt-magic-regexp-inverse)))

              (if (not buffer-encryption-key)
                  (call-interactively 'set-encryption-key))

              ;; if user did not enter a key, turn off crypt mode.  good for
              ;; binaries that crypt-magic-regexp-inverse doesn't recognize.
              ;; -- thanx to Paul Dworkin (paul@media-lab.media.mit.edu)

              (if (equal buffer-encryption-key "")

                  (message "No key given, buffer %s assumed normal."
                           (buffer-name))

                (message "Decrypting %s..." (buffer-name))

                (crypt-buffer buffer-encryption-key nil)

                ;; Tuck the key away for safe keeping since setting the major
                ;; mode may well blow it away.

                (setq encrypted buffer-encryption-key)

                (if (not (input-pending-p))
                    (message "Decrypting %s... done" (buffer-name))))))
       ))

    ;; OK, if any changes have been made to the buffer we need to rerun the
    ;; code the does automatic selection of major mode.

    (cond (

           (or encoded encrypted)
           (set-auto-mode)
           (hack-local-variables)

           ;; Now set our own minor modes.
           (if encoded
               (progn
                 (setq buffer-encoding-type encoded)
                 (encoded-mode 1)))

           (if encrypted
               (progn (crypt-mode 1)
                      (setq buffer-encryption-key encrypted)))

           ;; Restore buffer file name now, so that lock file entry is removed
           ;; properly.

           (setq buffer-file-name old-buffer-file-name)

           ;; Restore buffer modified flag to its previous value.  This will
           ;; also remove the lock file entry for the buffer if the previous
           ;; value was nil; this is why buffer-file-name had to be manually
           ;; restored above.

           (set-buffer-modified-p old-buffer-modified-p)))))


;; This function should be called ONLY as a write-file hook.
;; Odd things will happen if it is called elsewhere.

(defun crypt-write-file-hook ()
  
  "Writes out file, if need be, in a non-plain format. Note this terminates
the calls in write-file-hooks so should probably be at the end of that list."
  
  ;; We flag a buffer to be written out in encoded form if the file ends in
  ;; one of the file-extensions in crypt-encoding-alist. Conversely, we write
  ;; out a buffer as a plain file if it does _not_ end in one of these
  ;; file-extensions even if buffer-save-encoded is non-`nil'.
  
  (let ((alist crypt-encoding-alist)
	case-fold-search found elt)
    
    ;; search through the file name extensions for a match
    (while (and alist (setq elt (car alist)) (not found))
      (if (string-match (nth 2 elt) buffer-file-name)
	  (setq found t)
	;; decrement
	(setq alist (cdr alist))))
    
    ;; did we find a match? 
    (if found 
	
	;; file name ends in a very provocative extension

	;; check to see if we should write as an encoded file
	(if buffer-save-encoded

	    ;; already encoded - do the methods of encoding match? - if not 
	    ;; then change the method of encoding
	    (if (and 
		 (not (eq (nth 0 elt) buffer-encoding-type))
		 (or crypt-auto-write-buffer
		     (y-or-n-p (concat "write file using " (nth 3 elt) "? "))))

		;; case one 
		;; turn off original encoding and turn on new encoding
		(progn (encoded-mode -1)
		       (setq buffer-encoding-type (nth 0 elt))
		       (encoded-mode 1)))
	  
	  ;; was a plain file
	  (if (or crypt-auto-write-buffer
		  (y-or-n-p (concat "write file using " (nth 3 elt) "? ")))

	      ;; case two
	      ;; turn on encoding flags and _then_ the minor mode
	      (progn (setq buffer-save-encoded t)
		     (setq buffer-encoding-type (nth 0 elt))
		     (encoded-mode 1))))
      
      ;; no match - a plain-jane file extension - but if the encoded flag is
      ;; non-`nil' then the user may really want it written out in plain
      ;; format so we must override this flag
      (if (and buffer-save-encoded
	       (or crypt-auto-write-buffer
		   (y-or-n-p "write as a plain file? ")))

	  ;; case three
	  ;; turn off the minor mode and _then_ the flags
	  (progn (encoded-mode -1)
		 (setq buffer-save-encoded nil)
		 (setq buffer-encoding-type nil)))))

  ;; Now decide whether or not we need to continue with this defun. Does the
  ;; buffer need to be saved in a non-plain form?  If not then writing is not
  ;; done here but later in the write-file-hooks (probably at the end).

  (if (or buffer-save-encoded buffer-save-encrypted)
      
      (save-excursion
        (save-restriction
          (let 
              
              ;; BINDINGS
              ((copy-buffer (get-buffer-create " *crypt copy buffer*"))
               (selective-display selective-display)
               (buffer-read-only))
            
            ;; FORMS
            (copy-to-buffer copy-buffer 1 (1+ (buffer-size)))
            (narrow-to-region (point) (point))
	    
            (unwind-protect
		
                (progn
                  (insert-buffer-substring copy-buffer)
                  (kill-buffer copy-buffer)
		  
                  ;; selective-display non-`nil' means we must convert
                  ;; carriage returns to newlines now, and set
                  ;; selective-display temporarily to nil.
		  
                  (cond (selective-display
                         (goto-char (point-min))
                         (subst-char-in-region (point-min) (point-max) ?\r ?\n)
                         (setq selective-display nil)))
                  
                  (cond
                   (buffer-save-encrypted
                    (if (null buffer-encryption-key)
                        (error "No encryption key set for buffer %s"
                               (buffer-name)))
                    (if (not (stringp buffer-encryption-key))
                        (error "Encryption key is not a string"))
                    (message "Encrypting %s..." (buffer-name))
                    (crypt-buffer buffer-encryption-key t)))
                  
                  (cond
                   (buffer-save-encoded
                    (message "Encoding %s..." (buffer-name))
                    (encode-buffer)))
                  
                  ;; write buffer/region to disk
                  (write-region (point-min) (point-max) buffer-file-name nil t)
                  (delete-region (point-min) (point-max))
                  (set-buffer-modified-p nil)
                  
                  ;; return t so that basic-save-buffer will
                  ;; know that the save has already been done.
		  
                  ;; NOTE: this TERMINATES write-file-hooks so any hooks
                  ;; following crypt-write-file-hook will not be executed
		  
                  t )
              ;; unwind...sit back...take a load off...have a beer
              ;; If the crypted stuff has already been removed
              ;; then this is a no-op.
              (delete-region (point-min) (point-max))))))))

              
;;;; Defuns that do the actual decoding-encoding and decryption-encryption

;;; ENCRYPTING

(defun crypt-region (start end encrypt key)

  "Encrypt/decrypt the text in the region. From a program, this function takes
four args: START, END, ENCRYPT and KEY. When called interactively START and
END default to point and mark \(START being the lesser of the two\), KEY is
prompted for. If ENCRYPT is t encryption is done otherwise decrypt is done
using contents of variables `crypt-encryption-program' and
`crypt-decryption-program.'"

  (interactive
   (progn
     (barf-if-buffer-read-only)
     (list (region-beginning) (region-end)
      (y-or-n-p "Encrypt? ") 
      (read-string-no-echo "Crypt region using key: "))))

  (save-point
   (let ((opoint-max (point-max)))
     (call-process-region 
      start end shell-file-name t t nil "-c"
      (concat 
       (if encrypt crypt-encryption-program crypt-decryption-program)
       " " key)))))

(defun crypt-buffer (key encrypt &optional buffer)

  "Using KEY, if prefix arg (or ENCRYPT non-nil from a program), then encrypt
BUFFER \(defaults to the current buffer\), otherwise decrypt."

  (interactive
   (progn
     (barf-if-buffer-read-only)
     (list (read-string-no-echo "Crypt buffer using key: ")
	   current-prefix-arg
	   (read-buffer "Crypt buffer: " (current-buffer)))))

  (or buffer (setq buffer (current-buffer)))
  (save-excursion (set-buffer buffer)
		  (crypt-region (point-min) (point-max) encrypt key)))


;;; ENCODING

(defun encode-region (start end &optional undo)

  "Encode the text in the region. From a program, this function takes three
args: START, END and UNDO. When called interactively START and END default to
point and mark \(START being the lesser of the two\).  Prefix arg \(or
optional second arg non-nil\) UNDO means decode."

  (interactive "*r\nP")

  (save-point

   (call-process-region
    start end shell-file-name t t nil "-c"
    (if undo (encoding-decode-program buffer-encoding-type)
      (encoding-encode-program buffer-encoding-type)))

   (cond ((not undo)
          (goto-char start)
          (let (case-fold-search)
            (if (not (looking-at (encoding-magic-regexp buffer-encoding-type)))
                (error "%s failed!" (if undo "Decoding" "Encoding"))))))))

(defun encode-buffer (&optional buffer undo)

  "Encode BUFFER \(defaults to the current buffer\). Prefix arg \(or second
arg non-nil from a program) UNDO means decode." 

  (interactive (list (current-buffer) current-prefix-arg))
  (or buffer (setq buffer (current-buffer)))
  (save-excursion (set-buffer buffer)
		  (encode-region (point-min) (point-max) undo)))



;;;; MODES

(defun crypt-mode (&optional arg)

  "Toggle crypt mode. With arg, turn crypt mode on iff arg is positive,
otherwise turn it off. In crypt mode, buffers are automatically encrypted
before being written.  If crypt mode is toggled and a key has been set for the
current buffer, then the current buffer is marked modified, since it needs to
be rewritten with \(or without\) encryption.

Use \\[set-encryption-key] to set the encryption key for the current buffer.

Entering crypt mode causes auto-saving to be turned off in the current buffer,
as there is no way \(in Emacs Lisp\) to force auto save files to be
encrypted."

  (interactive "P")
  (let ((oldval buffer-save-encrypted))
    (setq buffer-save-encrypted
          (if arg (> arg 0) (not buffer-save-encrypted)))
    (if buffer-save-encrypted
        (auto-save-mode 0)
      (auto-save-mode (if (and auto-save-default buffer-file-name) 1 0)))
    (if buffer-encryption-key
        (set-buffer-modified-p
         (not (eq oldval buffer-save-encrypted))))))



(defun encoded-mode (&optional arg)

  "Toggle encoded mode. With arg, turn encoded mode on iff arg is positive,
otherwise turn it off. In encoded mode, buffers are automatically encoded
before being written. If encoded mode is toggled, the current buffer is
marked modified, since it needs to be written with (or without) encoding.

Entering encoded mode causes auto-saving to be turned off in the current
buffer, as there is no way (in Emacs Lisp) to force auto save files to be
encoded."

  (interactive "P")
  (let ((oldval buffer-save-encoded))

    (setq buffer-save-encoded
          (if arg (> arg 0) (not buffer-save-encoded)))
    (set-variable (crypt-buffer-save-name buffer-encoding-type)
                  buffer-save-encoded)

    (if buffer-save-encoded
        (auto-save-mode 0)
      (auto-save-mode (if (and auto-save-default buffer-file-name) 1 0)))

    (set-buffer-modified-p (not (eq oldval buffer-save-encoded)))))


;;;; Additional crypt defuns 

(defun read-string-no-echo (prompt &optional confirm)

  "Read a string from the minibuffer, prompting with PROMPT. Optional second
argument CONFIRM non-nil means that the user will be asked to type the string
a second time for confirmation and if there is a mismatch, the process is
repeated.

           Line editing keys are --
             C-h, DEL      rubout
             C-u, C-x      line kill
             C-q, C-v      literal next"

  (catch 'return-value
    (save-excursion
      (let ((input-buffer (get-buffer-create " *password*"))
            (cursor-in-echo-area t)
            (echo-keystrokes 0)
            char string help-form done kill-ring)
        (set-buffer input-buffer)
        (unwind-protect
            (while t
              (erase-buffer)
              (message prompt)
              (while (not (memq (setq char (read-char)) '(?\C-m ?\C-j)))
                (if (setq form
                          (cdr
                           (assq char
                                 '((?\C-h . (delete-char -1))
                                   (?\C-? . (delete-char -1))
                                   (?\C-u . (delete-region 1 (point)))
                                   (?\C-x . (delete-region 1 (point)))
                                   (?\C-q . (quoted-insert 1))
                                   (?\C-v . (quoted-insert 1))))))
                    (condition-case error-data
                        (eval form)
                      (error t))
                  (insert char))
                (message prompt))
              (cond ((and confirm string)
                     (cond ((not (string= string (buffer-string)))
                            (message
                             (concat prompt "[Mismatch... try again.]"))
                            (ding)
                            (sit-for 2)
                            (setq string nil))
                           (t (throw 'return-value string))))
                    (confirm
                     (setq string (buffer-string))
                     (message (concat prompt "[Retype to confirm...]"))
                     (sit-for 2))
                    (t (throw 'return-value (buffer-string)))))
          (set-buffer-modified-p nil)
          (kill-buffer input-buffer))))))

(defun set-encryption-key (key &optional buffer)

  "Set the encryption KEY for BUFFER. KEY should be a string. BUFFER should be
a buffer or the name of one; it defaults to the current buffer.  If BUFFER is
in crypt mode, then it is also marked as modified, since it needs to be saved
with the new key."

  (interactive
   (progn
     (barf-if-buffer-read-only)
     (list
      (read-string-no-echo
       (format "Set encryption key for buffer %s: " (buffer-name))))))
  (or buffer (setq buffer (current-buffer)))
  (save-excursion
    (set-buffer buffer)
    (if (equal key buffer-encryption-key)
        (message "Key is identical to original, no change.")
      (setq buffer-encryption-key key)
      ;; don't touch the modify flag unless we're in crypt-mode.
      (if buffer-save-encrypted
          (set-buffer-modified-p t)))))


;; Install the hooks, then add the mode indicators to the minor mode alist.

;; Check that the hooks are not already installed.

(cond
 ((not (memq 'crypt-write-file-hook write-file-hooks))
  ;; make this hook last on purpose
  (setq write-file-hooks (append write-file-hooks
                                 (list 'crypt-write-file-hook))
        find-file-hooks (cons 'crypt-find-file-hook find-file-hooks)
        find-file-not-found-hooks (cons 'crypt-find-file-hook
                                        find-file-not-found-hooks))))

;; Check that the mode indicators are not already installed.

(cond
 ((not (memq crypt-minor-mode-encrypted minor-mode-alist))
  ;; add the mode indicators
  (setq minor-mode-alist (append crypt-minor-mode-alist
				 minor-mode-alist))))


;;;; BUG REPORTS

;;; this section is provided for reports.
;;; adopted from Barry A. Warsaw's c++-mode.el

(defvar crypt-mailer 'mail
  "*Mail package to use to generate report mail buffer.")

(defconst crypt-help-address
  "dodd@roebling.poly.edu, rwhitby@research.canon.oz.au"
  "Address(es) accepting submission of reports on crypt++.el.")

(defconst crypt-maintainer "Larry and Rod"
  "First name(s) of people accepting submission of reports on crypt++.el.")

(defconst crypt-file "crypt++.el"
  "Name of file containing emacs lisp code.")

(defun crypt-submit-report ()
  "Submit via mail a report using the mailer in crypt-mailer, filename in
crypt-file, to address in crypt-help-address."
  (interactive)
  (funcall crypt-mailer)
  (insert crypt-help-address)
  (if (re-search-forward "^subject:[ \t]+" (point-max) 'move)
      (insert "Report on " crypt-file " version " crypt-version))
  (if (not (re-search-forward mail-header-separator (point-max) 'move))
      (progn (goto-char (point-max))
             (insert "\n" mail-header-separator "\n")
             (goto-char (point-max)))
    (forward-line 1))
  (set-mark (point)) ;user should see mark change
  (insert "\n\n---------\n")
  (insert (emacs-version) "\n")
  (insert "code: " crypt-file ",v " crypt-version)
  (insert "\n\n")
  (insert "current value of crypt-encoding-alist:\n\n")
  (insert (prin1-to-string crypt-encoding-alist))
  (exchange-point-and-mark)
  (insert "\n" crypt-maintainer ",\n\n  ")
  (message "%s, please write the message, use C-c C-c to send" (user-login-name)))

;; provide this package
(provide 'crypt++)
........................... cut along dotted line ...........................
<end file: ~/lisp/crypt++.el>