home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / gnu / emacs / sources / 961 < prev    next >
Encoding:
Internet Message Format  |  1993-01-24  |  34.8 KB

  1. Path: sparky!uunet!ferkel.ucsb.edu!taco!gatech!paladin.american.edu!howland.reston.ans.net!zaphod.mps.ohio-state.edu!magnus.acs.ohio-state.edu!usenet.ins.cwru.edu!agate!agate.berkeley.edu!dodd
  2. From: dodd@mycenae.cchem.berkeley.edu (Lawrence R. Dodd)
  3. Newsgroups: gnu.emacs.sources
  4. Subject: crypt++.el -- enhanced version of crypt
  5. Date: 24 Jan 93 07:44:09
  6. Organization: Dept of Chemical Engineering, Polytechnic Univ, NY, USA
  7. Lines: 900
  8. Message-ID: <DODD.93Jan24074409@mycenae.cchem.berkeley.edu>
  9. NNTP-Posting-Host: mycenae.cchem.berkeley.edu
  10. Summary: code for handling all sorts of compressed and encrypted files
  11. Keywords: crypt++.el, table-driven
  12.  
  13.  
  14. Hello all,
  15.  
  16.   Here is an updated and enhanced version of `crypt.el' called `crypt++.el'.
  17.   It main advantage over previous versions of crypt is that it is table-driven
  18.   making the addition of alternate compression programs trivial. This version
  19.   also allows the user to write out buffers in compressed or plain format
  20.   independent of the origin of the buffer. It does this by monitoring the
  21.   file-name extension passed to `write-file' (C-x C-w) to see if a change in
  22.   format is possibly being requested.  It then (optionally) confirms with the
  23.   user before writing the buffer.
  24.  
  25.   Since the elisp-archive seems to be in a state of dormancy, this file is
  26.   also available via anonymous ftp to roebling.poly.edu in /pub/.  Please try
  27.   to exercise customary ftp etiquette and use only on off-peak hours.
  28.  
  29.   Furthermore, since the package is named `crypt++.el' you should replace any
  30.   occurrence of (require 'crypt) in your .emacs or (for instance) in
  31.   `lispdir.el' with (require 'crypt++) otherwise this is a drop-in replacement
  32.   for the previous versions of crypt (see INSTALLATION in header).
  33.  
  34.   Finally, if you have any questions, comments, bug-reports, or smart remarks
  35.   concerning crypt++.el type M-x crypt-submit-report and a template for a bug
  36.   report will be generated (this idea was taken from Barry A. Warsaw's
  37.   c++-mode.el).
  38.  
  39. share and enjoy,
  40.  
  41.   Larry Dodd <dodd@roebling.poly.edu>
  42.                  and
  43.   Rod Whitby <rwhitby@research.canon.oz.au>
  44.  
  45.  
  46. <file: ~/lisp/crypt++.el>
  47. ........................... cut along dotted line ...........................
  48. ;;; crypt++.el -- code for handling all sorts of compressed and encrypted files
  49.  
  50. (defconst crypt-version (substring "$Revision: 2.7 $" 11 -2)
  51.   "The revision number of crypt++.el -- code for handling all sorts of
  52. compression and encryption files. To send a bug report type
  53. M-x crypt-submit-report. Complete RCS identity is 
  54.  
  55.    $Id: crypt++.el,v 2.7 1993/01/24 15:22:48 dodd Exp $")
  56.  
  57. ;;; Copyright (C) 1993 Rod Whitby and Lawrence R. Dodd
  58. ;;; Copyright (C) 1988, 1989, 1990 Kyle E. Jones
  59. ;;;
  60. ;;; This program is free software; you can redistribute it and/or modify
  61. ;;; it under the terms of the GNU General Public License as published by
  62. ;;; the Free Software Foundation; either version 2 of the License, or
  63. ;;; (at your option) any later version.
  64. ;;;
  65. ;;; This program is distributed in the hope that it will be useful,
  66. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  67. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  68. ;;; GNU General Public License for more details.
  69. ;;;
  70. ;;; You should have received a copy of the GNU General Public License
  71. ;;; along with this program; if not, write to the Free Software
  72. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  73.  
  74. ;;; AVAILABLE: 
  75. ;;; 
  76. ;;; via anonymous ftp to roebling.poly.edu [128.238.5.31] in /pub/crypt++.el
  77. ;;; (ange-ftp TAG: /roebling.poly.edu:/pub/crypt++.el)
  78.  
  79. ;;; Send bug reports to:
  80. ;;;
  81. ;;;   Lawrence R. Dodd <dodd@roebling.poly.edu>
  82. ;;;   Department of Chemical Engineering
  83. ;;;   Polymer Research Institute
  84. ;;;   Polytechnic University 
  85. ;;;   Brooklyn, New York, 11201 USA
  86. ;;;
  87. ;;;   or
  88. ;;;                                                 _--_|\  
  89. ;;;   Rod Whitby <rwhitby@research.canon.oz.au>    /      \ 
  90. ;;;   Canon Information Systems Research Australia \_.--._/ 
  91. ;;;   1 Thomas Holt Drive, North Ryde, N.S.W., 2113      v
  92. ;;; 
  93. ;;;   or type M-x crypt-submit-report to generate a bug report template
  94.  
  95. ;;; $Id: crypt++.el,v 2.7 1993/01/24 15:22:48 dodd Exp $
  96. ;;; $Date: 1993/01/24 15:22:48 $
  97. ;;; $Revision: 2.7 $
  98. ;;;
  99. ;;; version log
  100. ;;; 1.1 - original version of crypt.el
  101. ;;; 1.2 -
  102. ;;;   jwz: works with tar-mode.el
  103. ;;;   jwz: applied patch from piet, merged with Lawrence Dodd's gzip version
  104. ;;; 1.3 -
  105. ;;;   lrd: fixed compress-magic-regexp 
  106. ;;; 1.4, 1.5, 1.6 -
  107. ;;;   lrd: write-file compresses or gzips based on file extension
  108. ;;; 2.1 -
  109. ;;;   lrd: merged with Rod Whitby's table-driven version (major upgrade)
  110. ;;; 2.2 -
  111. ;;;   rjw: Changed file name to crypt++.el, so archie and lispdir can find it.
  112. ;;; 2.3 -
  113. ;;;   rjw: Separated the hook additions and minor mode alist additions.
  114. ;;; 2.4 -
  115. ;;;   rjw: Fixed the interactive form for crypt-buffer.
  116. ;;; 2.5 - 
  117. ;;;   lrd: doc mods, changed GNU free software notice (was out of date), added 
  118. ;;;   anonymous ftp information
  119. ;;; 2.6 - 
  120. ;;;   lrd: added back in definition of `buffer' in defun crypt-buffer caused 
  121. ;;;   an error when trying to read encrypted file; modified check for minor 
  122. ;;;   mode alist addition; added gzip magic number warning
  123. ;;; 2.7 - 
  124. ;;;   lrd: added `TO DO' and `KNOW BUGS' section to header
  125.  
  126. ;; INSTALLATION:
  127. ;;
  128. ;; To use this package, simply put it in a file called "crypt++.el" in a Lisp
  129. ;; directory known to Emacs, byte-compile it, and put the line:
  130. ;;
  131. ;;                      (require 'crypt++)
  132. ;;
  133. ;; in your ~/.emacs file or in the file default.el in the ../lisp directory of
  134. ;; the Emacs distribution.  Do not bother trying to autoload this file; this
  135. ;; package uses find-file and write-file hooks and thus should be loaded the
  136. ;; first time you visit any sort of file.
  137. ;;
  138. ;; Users also may wish to toggle the values of `crypt-auto-decode-buffer,' 
  139. ;; `crypt-freeze-vs-fortran,' and `crypt-auto-write-buffer' from their 
  140. ;; defaults and possibly change the method of encryption and decryption using 
  141. ;; the values of `crypt-encryption-program' and `crypt-decryption-program.'
  142. ;;
  143. ;; Note that the magic number used by gzip (GNU zip) was changed by Jean-loup
  144. ;; Gailly in his Beta version 0.6. The magic regular expression used below in
  145. ;; `crypt-encoding-alist' reflects this change. If you are using a version of
  146. ;; gzip earlier than 0.6, then please upgrade.
  147.  
  148. ;; DESCRIPTION
  149. ;;
  150. ;; The basic purpose of this package of Lisp functions is to recognize
  151. ;; automatically encrypted and encoded (i.e., compressed) files when they are
  152. ;; first visited or written.  The BUFFER corresponding to the file is decoded
  153. ;; and/or decrypted before it is presented to the user.  The file itself is
  154. ;; unchanged on the disk.  When the buffer is subsequently saved to disk, a
  155. ;; hook function re-encodes the buffer before the actual disk write takes
  156. ;; place.
  157. ;;
  158. ;; This package recognizes all sorts of compressed files by a magic number at
  159. ;; the beginning of these files but uses a heuristic to detect encrypted
  160. ;; files.  If you are asked for an encryption key for a file that is in fact
  161. ;; not encrypted, just hit RET and the file will be accepted as is, and the
  162. ;; crypt minor mode will not be entered.
  163. ;;
  164. ;; Other types of encoding programs may be added to crypt++ using the variable
  165. ;; `crypt-encoding-alist' which contains a table of encoding programs such as
  166. ;; compress, gzip (GNU zip), and compact.
  167. ;;
  168. ;; This new extended version of crypt now monitors the filename extensions of
  169. ;; buffers that are written out using write-file (C-x C-w).  If the filename
  170. ;; extension matches one of the extensions listed in `crypt-encoding-alist,'
  171. ;; then crypt++ will write the file out using the corresponding encoding
  172. ;; (compression) method. This is done whether or not the buffer originated
  173. ;; from a previously encoded (compressed) file.
  174. ;;
  175. ;; Thus, if the user is editing a file that may or may not have been encoded
  176. ;; originally (e.g., foobar.Z or foobar) and decides to write it to a
  177. ;; different file (e.g., barfoo or barfoo.z or barfoo.C).  Crypt++ will examine
  178. ;; the filename extension and write the buffer in plain format or an alternate
  179. ;; encoding (compression) format by searching through the entries in the table
  180. ;; of encoding methods `crypt-encoding-alist.'  This change in encoding state
  181. ;; is done automatically if the variable `crypt-auto-write-buffer' is t
  182. ;; otherwise the user is asked.
  183.  
  184. ;; TO DO/KNOWN BUGS: 
  185. ;; 
  186. ;; * currently crypt++ assumes that if a file is both encrypted and encoded
  187. ;;   (i.e., compressed) that the order in which it was done was encryption
  188. ;;   first _then_ compression.  As has been pointed by many people compression
  189. ;;   following encryption is useless since the encrypted file is basically
  190. ;;   random.  On the other hand, many agree that doing encryption _following_
  191. ;;   compression is better since it makes it harder to crack the encryption.
  192. ;;   We would like to make the ordering of these two user-configurable or if
  193. ;;   nothing else change the order. 
  194. ;; 
  195. ;; * some have complained that the encryption key that crypt++.el requests is
  196. ;;   visible via `ps' We would like to pass the key to the encryption and
  197. ;;   decryption program(s) in a more secure manner.
  198.  
  199.  
  200. ;; user definable variables
  201.  
  202. (defvar crypt-encryption-program "crypt"
  203.   "*Command to be used for encryption.")
  204.  
  205. (defvar crypt-decryption-program "crypt"
  206.   "*Command to be used for decryption.")
  207.  
  208. (defvar crypt-auto-decode-buffer t
  209.   "*t value means that the buffers associated with encoded files will
  210. be decoded automatically, without requesting confirmation from the user.
  211. `nil' means to ask before doing the decoding.")
  212.  
  213. (defvar crypt-auto-write-buffer nil
  214.   "*t value means buffers to be written to files ending in extensions
  215. matching those in `crypt-encoding-alist' will be encoded automatically,
  216. without requesting confirmation from the user. `nil' means to ask before doing
  217. this encoding. Likewise, buffers originating from encoded files to be written
  218. to different files ending in extensions that do not match any of those in
  219. `crypt-encoding-alist' will be written in plain format automatically, without
  220. requesting confirmation from the user. `nil' means to ask before doing this
  221. decoding.")
  222.  
  223. (defvar crypt-freeze-vs-fortran t
  224.   "*t values means that the .F file extension denotes a frozen file
  225. rather than a Fortran file.")
  226.  
  227. (defvar crypt-encoding-alist
  228.   (list
  229.    '(compact "\377\037" "\\(\\.C\\)$" "compact" "uncompact" "Compact")
  230.    '(compress "\037\235" "\\(\\.Z\\)$" "compress" "uncompress" "Compress")
  231.    (and
  232.     crypt-freeze-vs-fortran
  233.     '(freeze "\037\236\\|\037\237" "\\(\\.F\\)$" "freeze" "unfreeze" "Freeze"))
  234.    '(gzip "\037\213" "\\(\\.z\\)$" "gzip" "gzip -d" "Zip")
  235.    ;; Add new elements here ...
  236.    )
  237.   "*A list of elements describing the encoding methods available, each of
  238. which looks like
  239.  
  240.         \(ENCODING-TYPE MAGIC-REGEXP FILE-EXTENSION
  241.                         ENCODE-PROGRAM DECODE-PROGRAM
  242.                         MINOR-MODE
  243.                         \)
  244.  
  245. ENCODING-TYPE is a symbol denoting the encoding type.  Currently known
  246. encodings are (compress compact freeze gzip).
  247.  
  248. MAGIC-REGEXP is a regexp that matches the magic number at the
  249. beginning of files encoded with ENCODING-TYPE.
  250.  
  251. FILE-EXTENSION is a string denoting the file extension usually
  252. appended the filename of files encoded with ENCODING-TYPE.
  253.  
  254. ENCODE-PROGRAM is a string denoting the name of the program used to
  255. encode files.
  256.  
  257. DECODE-PROGRAM is a string denoting the name of the program used to
  258. decode files.
  259.  
  260. MINOR-MODE is a string denoting the name for the encoded minor mode as 
  261. it will appear in the mode line.
  262. ")
  263.  
  264.  
  265. ;; buffer locals
  266.  
  267. (defvar buffer-save-encrypted nil
  268.   "*Non-nil means that when this buffer is saved it will be written out
  269. encrypted, using the commands in variables `crypt-encryption-program' and
  270. `crypt-decryption-program.'  Automatically local to all buffers.")
  271. (make-variable-buffer-local 'buffer-save-encrypted)
  272.  
  273. (defvar buffer-encryption-key nil
  274.   "*Key to use when encrypting the current buffer, prior to saving it.
  275. Automatically local to all buffers.")
  276. (make-variable-buffer-local 'buffer-encryption-key)
  277.  
  278. (defvar buffer-save-encoded nil
  279.   "*Non-nil means that when this buffer is saved it will be written out
  280. encoded with ENCODING-TYPE, as with the ENCODING-PROGRAM command.
  281. Automatically local to all buffers.")
  282. (make-variable-buffer-local 'buffer-save-encoded)
  283.  
  284. (defvar buffer-encoding-type nil
  285.   "*Non-nil means that this buffer is encoded with ENCODING-TYPE.
  286. Automatically local to all buffers.")
  287. (make-variable-buffer-local 'buffer-encoding-type)
  288.  
  289.  
  290. ;; defuns that work on the encoding-alist
  291.  
  292. (defun encoding-magic-regexp (encoding-type)
  293.   "Returns a regexp that matches the magic number at the beginning of files
  294. encoded with ENCODING-TYPE."
  295.   (let ((elt (assoc encoding-type crypt-encoding-alist)))
  296.     (and elt
  297.          (nth 1 elt))))
  298.  
  299. (defun encoding-file-extension (encoding-type)
  300.   "Returns a regexp that matches the file-extension typically associated with
  301. files encoded with ENCODING-TYPE."
  302.   (let ((elt (assoc encoding-type crypt-encoding-alist)))
  303.     (and elt
  304.          (nth 2 elt))))
  305.  
  306. (defun encoding-encode-program (encoding-type)
  307.   "Returns a string denoting the name of the program used to encode files
  308. encoded with ENCODING-TYPE."
  309.   (let ((elt (assoc encoding-type crypt-encoding-alist)))
  310.     (and elt
  311.          (nth 3 elt))))
  312.  
  313. (defun encoding-decode-program (encoding-type)
  314.   "Returns a string denoting the name of the program used to decode files
  315. encoded with ENCODING-TYPE."
  316.   (let ((elt (assoc encoding-type crypt-encoding-alist)))
  317.     (and elt
  318.          (nth 4 elt))))
  319.  
  320. (defun crypt-buffer-save-name (encoding-type)
  321.   "Returns a variable name. t means that when the buffer is saved it will be
  322. written out using its decoding program.  Automatically local to all buffers."
  323.   (intern (concat "buffer-save-" (symbol-name encoding-type))))
  324.  
  325.  
  326. ;; Create a buffer-local variable for each type of encoding.
  327. ;; These variables are used to trigger the minor mode names.
  328.  
  329. (defvar crypt-minor-mode-encrypted
  330.   '(buffer-save-encrypted " Crypt")
  331.   "Minor mode alist entry for encrypted buffers.")
  332.  
  333. (defvar crypt-minor-mode-alist
  334.   (append
  335.    (list crypt-minor-mode-encrypted)
  336.    (mapcar
  337.     (function
  338.      (lambda (element)
  339.        (let ((variable (crypt-buffer-save-name (car element))))
  340.      (make-variable-buffer-local variable)
  341.      (list variable (concat " " (nth 5 element))))))
  342.     crypt-encoding-alist))
  343. "Alist derived from `crypt-encoding-alist' containing encoded minor modes.")
  344.  
  345.  
  346. ;; Encrypted files have no magic number, so we have to hack a way of
  347. ;; determining which new buffers start in crypt mode.  The current setup is
  348. ;; that we use only buffers that have a non-ASCII character very close to
  349. ;; beginning of buffer and that do NOT match crypt-magic-regexp-inverse.
  350. ;; Currently crypt-magic-regexp-inverse will match Sun OS, 4.x BSD, and
  351. ;; Ultrix executable magic numbers, so binaries can still be edited (heh)
  352. ;; without headaches.
  353.  
  354. (defconst crypt-magic-regexp-inverse
  355.   "\\(..\\)?\\([\007\010\013]\001\\|\001[\007\010\013]\\)"
  356.   "Regexp that must NOT match the beginning of an encrypted buffer.")
  357.  
  358. (defmacro save-point (&rest body)
  359.   "Save value of point, evaluate FORMS, and restore value of point.
  360. If the saved value of point is no longer valid go to (point-max).
  361. This macro exists because, save-excursion loses track of point during
  362. some types of deletions."
  363.   (let ((var (make-symbol "saved-point")))
  364.     (list 'let (list (list var '(point)))
  365.           (list 'unwind-protect
  366.                 (cons 'progn body)
  367.                 (list 'goto-char var)))))
  368.  
  369.  
  370. (defun crypt-find-file-hook ()
  371.   "Hook run for decoding and/or decrypting the contents of a buffer. Part of
  372. find-file-hooks"
  373.   (let ((buffer-file-name buffer-file-name)
  374.         (old-buffer-file-name buffer-file-name)
  375.         (old-buffer-modified-p (buffer-modified-p))
  376.         found
  377.         elt regexp
  378.         encrypted encoded
  379.         case-fold-search buffer-read-only)
  380.  
  381.     (save-point
  382.      (save-restriction
  383.        (widen)
  384.        (goto-char (point-min))
  385.  
  386.        ;; We can reasonably assume that either compaction or compression will
  387.        ;; be used, or neither, but not both.
  388.  
  389.        ;; find the file type
  390.  
  391.        (let ((alist crypt-encoding-alist))
  392.          (while (and alist (setq elt (car alist)) (not found))
  393.            (if (looking-at (nth 1 elt))
  394.                (progn (setq buffer-encoding-type (nth 0 elt))
  395.                       (setq found t))
  396.              ;; decrement 
  397.              (setq alist (cdr alist)))))
  398.  
  399.        ;; do we have to decode? if not move on
  400.        (if (and found 
  401.                 (or crypt-auto-decode-buffer
  402.                     (y-or-n-p (format "Decode buffer %s? "
  403.                                       (buffer-name)))))
  404.            (progn
  405.              (message "Decoding %s..." (buffer-name))
  406.              (encode-buffer (current-buffer) t)
  407.  
  408.              ;; We can not actually go into encoding mode yet because the
  409.              ;; major mode may change later on and blow away all local
  410.              ;; variables (and thus the minor modes).  So we make a note to go
  411.              ;; into encoding mode later.
  412.  
  413.              (setq encoded buffer-encoding-type)
  414.  
  415.              ;; here we strip the encoded file's extension so that later we
  416.              ;; can set the buffer's major mode based on this modified name
  417.              ;; instead of the name with the extension.
  418.  
  419.              (if (string-match (encoding-file-extension buffer-encoding-type)
  420.                                buffer-file-name)
  421.                  (setq buffer-file-name
  422.                        (substring buffer-file-name 0 (match-beginning 1))))
  423.  
  424.              (if (not (input-pending-p))
  425.                  (message "Decoding %s... done" (buffer-name)))))
  426.  
  427.        ;; Now peek at the file and see if it still looks like a binary file.
  428.        ;; If so, try the crypt-magic-regexp-inverse against it and if it FAILS
  429.        ;; we assume that this is an encrypted buffer.
  430.  
  431.        (cond (
  432.  
  433.               (and (not (eobp))
  434.                    (re-search-forward "[\200-\377]" (min (point-max) 15) t)
  435.                    (goto-char (point-min))
  436.                    (not (looking-at crypt-magic-regexp-inverse)))
  437.  
  438.               (if (not buffer-encryption-key)
  439.                   (call-interactively 'set-encryption-key))
  440.  
  441.               ;; if user did not enter a key, turn off crypt mode.  good for
  442.               ;; binaries that crypt-magic-regexp-inverse doesn't recognize.
  443.               ;; -- thanx to Paul Dworkin (paul@media-lab.media.mit.edu)
  444.  
  445.               (if (equal buffer-encryption-key "")
  446.  
  447.                   (message "No key given, buffer %s assumed normal."
  448.                            (buffer-name))
  449.  
  450.                 (message "Decrypting %s..." (buffer-name))
  451.  
  452.                 (crypt-buffer buffer-encryption-key nil)
  453.  
  454.                 ;; Tuck the key away for safe keeping since setting the major
  455.                 ;; mode may well blow it away.
  456.  
  457.                 (setq encrypted buffer-encryption-key)
  458.  
  459.                 (if (not (input-pending-p))
  460.                     (message "Decrypting %s... done" (buffer-name))))))
  461.        ))
  462.  
  463.     ;; OK, if any changes have been made to the buffer we need to rerun the
  464.     ;; code the does automatic selection of major mode.
  465.  
  466.     (cond (
  467.  
  468.            (or encoded encrypted)
  469.            (set-auto-mode)
  470.            (hack-local-variables)
  471.  
  472.            ;; Now set our own minor modes.
  473.            (if encoded
  474.                (progn
  475.                  (setq buffer-encoding-type encoded)
  476.                  (encoded-mode 1)))
  477.  
  478.            (if encrypted
  479.                (progn (crypt-mode 1)
  480.                       (setq buffer-encryption-key encrypted)))
  481.  
  482.            ;; Restore buffer file name now, so that lock file entry is removed
  483.            ;; properly.
  484.  
  485.            (setq buffer-file-name old-buffer-file-name)
  486.  
  487.            ;; Restore buffer modified flag to its previous value.  This will
  488.            ;; also remove the lock file entry for the buffer if the previous
  489.            ;; value was nil; this is why buffer-file-name had to be manually
  490.            ;; restored above.
  491.  
  492.            (set-buffer-modified-p old-buffer-modified-p)))))
  493.  
  494.  
  495. ;; This function should be called ONLY as a write-file hook.
  496. ;; Odd things will happen if it is called elsewhere.
  497.  
  498. (defun crypt-write-file-hook ()
  499.   
  500.   "Writes out file, if need be, in a non-plain format. Note this terminates
  501. the calls in write-file-hooks so should probably be at the end of that list."
  502.   
  503.   ;; We flag a buffer to be written out in encoded form if the file ends in
  504.   ;; one of the file-extensions in crypt-encoding-alist. Conversely, we write
  505.   ;; out a buffer as a plain file if it does _not_ end in one of these
  506.   ;; file-extensions even if buffer-save-encoded is non-`nil'.
  507.   
  508.   (let ((alist crypt-encoding-alist)
  509.     case-fold-search found elt)
  510.     
  511.     ;; search through the file name extensions for a match
  512.     (while (and alist (setq elt (car alist)) (not found))
  513.       (if (string-match (nth 2 elt) buffer-file-name)
  514.       (setq found t)
  515.     ;; decrement
  516.     (setq alist (cdr alist))))
  517.     
  518.     ;; did we find a match? 
  519.     (if found 
  520.     
  521.     ;; file name ends in a very provocative extension
  522.  
  523.     ;; check to see if we should write as an encoded file
  524.     (if buffer-save-encoded
  525.  
  526.         ;; already encoded - do the methods of encoding match? - if not 
  527.         ;; then change the method of encoding
  528.         (if (and 
  529.          (not (eq (nth 0 elt) buffer-encoding-type))
  530.          (or crypt-auto-write-buffer
  531.              (y-or-n-p (concat "write file using " (nth 3 elt) "? "))))
  532.  
  533.         ;; case one 
  534.         ;; turn off original encoding and turn on new encoding
  535.         (progn (encoded-mode -1)
  536.                (setq buffer-encoding-type (nth 0 elt))
  537.                (encoded-mode 1)))
  538.       
  539.       ;; was a plain file
  540.       (if (or crypt-auto-write-buffer
  541.           (y-or-n-p (concat "write file using " (nth 3 elt) "? ")))
  542.  
  543.           ;; case two
  544.           ;; turn on encoding flags and _then_ the minor mode
  545.           (progn (setq buffer-save-encoded t)
  546.              (setq buffer-encoding-type (nth 0 elt))
  547.              (encoded-mode 1))))
  548.       
  549.       ;; no match - a plain-jane file extension - but if the encoded flag is
  550.       ;; non-`nil' then the user may really want it written out in plain
  551.       ;; format so we must override this flag
  552.       (if (and buffer-save-encoded
  553.            (or crypt-auto-write-buffer
  554.            (y-or-n-p "write as a plain file? ")))
  555.  
  556.       ;; case three
  557.       ;; turn off the minor mode and _then_ the flags
  558.       (progn (encoded-mode -1)
  559.          (setq buffer-save-encoded nil)
  560.          (setq buffer-encoding-type nil)))))
  561.  
  562.   ;; Now decide whether or not we need to continue with this defun. Does the
  563.   ;; buffer need to be saved in a non-plain form?  If not then writing is not
  564.   ;; done here but later in the write-file-hooks (probably at the end).
  565.  
  566.   (if (or buffer-save-encoded buffer-save-encrypted)
  567.       
  568.       (save-excursion
  569.         (save-restriction
  570.           (let 
  571.               
  572.               ;; BINDINGS
  573.               ((copy-buffer (get-buffer-create " *crypt copy buffer*"))
  574.                (selective-display selective-display)
  575.                (buffer-read-only))
  576.             
  577.             ;; FORMS
  578.             (copy-to-buffer copy-buffer 1 (1+ (buffer-size)))
  579.             (narrow-to-region (point) (point))
  580.         
  581.             (unwind-protect
  582.         
  583.                 (progn
  584.                   (insert-buffer-substring copy-buffer)
  585.                   (kill-buffer copy-buffer)
  586.           
  587.                   ;; selective-display non-`nil' means we must convert
  588.                   ;; carriage returns to newlines now, and set
  589.                   ;; selective-display temporarily to nil.
  590.           
  591.                   (cond (selective-display
  592.                          (goto-char (point-min))
  593.                          (subst-char-in-region (point-min) (point-max) ?\r ?\n)
  594.                          (setq selective-display nil)))
  595.                   
  596.                   (cond
  597.                    (buffer-save-encrypted
  598.                     (if (null buffer-encryption-key)
  599.                         (error "No encryption key set for buffer %s"
  600.                                (buffer-name)))
  601.                     (if (not (stringp buffer-encryption-key))
  602.                         (error "Encryption key is not a string"))
  603.                     (message "Encrypting %s..." (buffer-name))
  604.                     (crypt-buffer buffer-encryption-key t)))
  605.                   
  606.                   (cond
  607.                    (buffer-save-encoded
  608.                     (message "Encoding %s..." (buffer-name))
  609.                     (encode-buffer)))
  610.                   
  611.                   ;; write buffer/region to disk
  612.                   (write-region (point-min) (point-max) buffer-file-name nil t)
  613.                   (delete-region (point-min) (point-max))
  614.                   (set-buffer-modified-p nil)
  615.                   
  616.                   ;; return t so that basic-save-buffer will
  617.                   ;; know that the save has already been done.
  618.           
  619.                   ;; NOTE: this TERMINATES write-file-hooks so any hooks
  620.                   ;; following crypt-write-file-hook will not be executed
  621.           
  622.                   t )
  623.               ;; unwind...sit back...take a load off...have a beer
  624.               ;; If the crypted stuff has already been removed
  625.               ;; then this is a no-op.
  626.               (delete-region (point-min) (point-max))))))))
  627.  
  628.               
  629. ;;;; Defuns that do the actual decoding-encoding and decryption-encryption
  630.  
  631. ;;; ENCRYPTING
  632.  
  633. (defun crypt-region (start end encrypt key)
  634.  
  635.   "Encrypt/decrypt the text in the region. From a program, this function takes
  636. four args: START, END, ENCRYPT and KEY. When called interactively START and
  637. END default to point and mark \(START being the lesser of the two\), KEY is
  638. prompted for. If ENCRYPT is t encryption is done otherwise decrypt is done
  639. using contents of variables `crypt-encryption-program' and
  640. `crypt-decryption-program.'"
  641.  
  642.   (interactive
  643.    (progn
  644.      (barf-if-buffer-read-only)
  645.      (list (region-beginning) (region-end)
  646.       (y-or-n-p "Encrypt? ") 
  647.       (read-string-no-echo "Crypt region using key: "))))
  648.  
  649.   (save-point
  650.    (let ((opoint-max (point-max)))
  651.      (call-process-region 
  652.       start end shell-file-name t t nil "-c"
  653.       (concat 
  654.        (if encrypt crypt-encryption-program crypt-decryption-program)
  655.        " " key)))))
  656.  
  657. (defun crypt-buffer (key encrypt &optional buffer)
  658.  
  659.   "Using KEY, if prefix arg (or ENCRYPT non-nil from a program), then encrypt
  660. BUFFER \(defaults to the current buffer\), otherwise decrypt."
  661.  
  662.   (interactive
  663.    (progn
  664.      (barf-if-buffer-read-only)
  665.      (list (read-string-no-echo "Crypt buffer using key: ")
  666.        current-prefix-arg
  667.        (read-buffer "Crypt buffer: " (current-buffer)))))
  668.  
  669.   (or buffer (setq buffer (current-buffer)))
  670.   (save-excursion (set-buffer buffer)
  671.           (crypt-region (point-min) (point-max) encrypt key)))
  672.  
  673.  
  674. ;;; ENCODING
  675.  
  676. (defun encode-region (start end &optional undo)
  677.  
  678.   "Encode the text in the region. From a program, this function takes three
  679. args: START, END and UNDO. When called interactively START and END default to
  680. point and mark \(START being the lesser of the two\).  Prefix arg \(or
  681. optional second arg non-nil\) UNDO means decode."
  682.  
  683.   (interactive "*r\nP")
  684.  
  685.   (save-point
  686.  
  687.    (call-process-region
  688.     start end shell-file-name t t nil "-c"
  689.     (if undo (encoding-decode-program buffer-encoding-type)
  690.       (encoding-encode-program buffer-encoding-type)))
  691.  
  692.    (cond ((not undo)
  693.           (goto-char start)
  694.           (let (case-fold-search)
  695.             (if (not (looking-at (encoding-magic-regexp buffer-encoding-type)))
  696.                 (error "%s failed!" (if undo "Decoding" "Encoding"))))))))
  697.  
  698. (defun encode-buffer (&optional buffer undo)
  699.  
  700.   "Encode BUFFER \(defaults to the current buffer\). Prefix arg \(or second
  701. arg non-nil from a program) UNDO means decode." 
  702.  
  703.   (interactive (list (current-buffer) current-prefix-arg))
  704.   (or buffer (setq buffer (current-buffer)))
  705.   (save-excursion (set-buffer buffer)
  706.           (encode-region (point-min) (point-max) undo)))
  707.  
  708.  
  709.  
  710. ;;;; MODES
  711.  
  712. (defun crypt-mode (&optional arg)
  713.  
  714.   "Toggle crypt mode. With arg, turn crypt mode on iff arg is positive,
  715. otherwise turn it off. In crypt mode, buffers are automatically encrypted
  716. before being written.  If crypt mode is toggled and a key has been set for the
  717. current buffer, then the current buffer is marked modified, since it needs to
  718. be rewritten with \(or without\) encryption.
  719.  
  720. Use \\[set-encryption-key] to set the encryption key for the current buffer.
  721.  
  722. Entering crypt mode causes auto-saving to be turned off in the current buffer,
  723. as there is no way \(in Emacs Lisp\) to force auto save files to be
  724. encrypted."
  725.  
  726.   (interactive "P")
  727.   (let ((oldval buffer-save-encrypted))
  728.     (setq buffer-save-encrypted
  729.           (if arg (> arg 0) (not buffer-save-encrypted)))
  730.     (if buffer-save-encrypted
  731.         (auto-save-mode 0)
  732.       (auto-save-mode (if (and auto-save-default buffer-file-name) 1 0)))
  733.     (if buffer-encryption-key
  734.         (set-buffer-modified-p
  735.          (not (eq oldval buffer-save-encrypted))))))
  736.  
  737.  
  738.  
  739. (defun encoded-mode (&optional arg)
  740.  
  741.   "Toggle encoded mode. With arg, turn encoded mode on iff arg is positive,
  742. otherwise turn it off. In encoded mode, buffers are automatically encoded
  743. before being written. If encoded mode is toggled, the current buffer is
  744. marked modified, since it needs to be written with (or without) encoding.
  745.  
  746. Entering encoded mode causes auto-saving to be turned off in the current
  747. buffer, as there is no way (in Emacs Lisp) to force auto save files to be
  748. encoded."
  749.  
  750.   (interactive "P")
  751.   (let ((oldval buffer-save-encoded))
  752.  
  753.     (setq buffer-save-encoded
  754.           (if arg (> arg 0) (not buffer-save-encoded)))
  755.     (set-variable (crypt-buffer-save-name buffer-encoding-type)
  756.                   buffer-save-encoded)
  757.  
  758.     (if buffer-save-encoded
  759.         (auto-save-mode 0)
  760.       (auto-save-mode (if (and auto-save-default buffer-file-name) 1 0)))
  761.  
  762.     (set-buffer-modified-p (not (eq oldval buffer-save-encoded)))))
  763.  
  764.  
  765. ;;;; Additional crypt defuns 
  766.  
  767. (defun read-string-no-echo (prompt &optional confirm)
  768.  
  769.   "Read a string from the minibuffer, prompting with PROMPT. Optional second
  770. argument CONFIRM non-nil means that the user will be asked to type the string
  771. a second time for confirmation and if there is a mismatch, the process is
  772. repeated.
  773.  
  774.            Line editing keys are --
  775.              C-h, DEL      rubout
  776.              C-u, C-x      line kill
  777.              C-q, C-v      literal next"
  778.  
  779.   (catch 'return-value
  780.     (save-excursion
  781.       (let ((input-buffer (get-buffer-create " *password*"))
  782.             (cursor-in-echo-area t)
  783.             (echo-keystrokes 0)
  784.             char string help-form done kill-ring)
  785.         (set-buffer input-buffer)
  786.         (unwind-protect
  787.             (while t
  788.               (erase-buffer)
  789.               (message prompt)
  790.               (while (not (memq (setq char (read-char)) '(?\C-m ?\C-j)))
  791.                 (if (setq form
  792.                           (cdr
  793.                            (assq char
  794.                                  '((?\C-h . (delete-char -1))
  795.                                    (?\C-? . (delete-char -1))
  796.                                    (?\C-u . (delete-region 1 (point)))
  797.                                    (?\C-x . (delete-region 1 (point)))
  798.                                    (?\C-q . (quoted-insert 1))
  799.                                    (?\C-v . (quoted-insert 1))))))
  800.                     (condition-case error-data
  801.                         (eval form)
  802.                       (error t))
  803.                   (insert char))
  804.                 (message prompt))
  805.               (cond ((and confirm string)
  806.                      (cond ((not (string= string (buffer-string)))
  807.                             (message
  808.                              (concat prompt "[Mismatch... try again.]"))
  809.                             (ding)
  810.                             (sit-for 2)
  811.                             (setq string nil))
  812.                            (t (throw 'return-value string))))
  813.                     (confirm
  814.                      (setq string (buffer-string))
  815.                      (message (concat prompt "[Retype to confirm...]"))
  816.                      (sit-for 2))
  817.                     (t (throw 'return-value (buffer-string)))))
  818.           (set-buffer-modified-p nil)
  819.           (kill-buffer input-buffer))))))
  820.  
  821. (defun set-encryption-key (key &optional buffer)
  822.  
  823.   "Set the encryption KEY for BUFFER. KEY should be a string. BUFFER should be
  824. a buffer or the name of one; it defaults to the current buffer.  If BUFFER is
  825. in crypt mode, then it is also marked as modified, since it needs to be saved
  826. with the new key."
  827.  
  828.   (interactive
  829.    (progn
  830.      (barf-if-buffer-read-only)
  831.      (list
  832.       (read-string-no-echo
  833.        (format "Set encryption key for buffer %s: " (buffer-name))))))
  834.   (or buffer (setq buffer (current-buffer)))
  835.   (save-excursion
  836.     (set-buffer buffer)
  837.     (if (equal key buffer-encryption-key)
  838.         (message "Key is identical to original, no change.")
  839.       (setq buffer-encryption-key key)
  840.       ;; don't touch the modify flag unless we're in crypt-mode.
  841.       (if buffer-save-encrypted
  842.           (set-buffer-modified-p t)))))
  843.  
  844.  
  845. ;; Install the hooks, then add the mode indicators to the minor mode alist.
  846.  
  847. ;; Check that the hooks are not already installed.
  848.  
  849. (cond
  850.  ((not (memq 'crypt-write-file-hook write-file-hooks))
  851.   ;; make this hook last on purpose
  852.   (setq write-file-hooks (append write-file-hooks
  853.                                  (list 'crypt-write-file-hook))
  854.         find-file-hooks (cons 'crypt-find-file-hook find-file-hooks)
  855.         find-file-not-found-hooks (cons 'crypt-find-file-hook
  856.                                         find-file-not-found-hooks))))
  857.  
  858. ;; Check that the mode indicators are not already installed.
  859.  
  860. (cond
  861.  ((not (memq crypt-minor-mode-encrypted minor-mode-alist))
  862.   ;; add the mode indicators
  863.   (setq minor-mode-alist (append crypt-minor-mode-alist
  864.                  minor-mode-alist))))
  865.  
  866.  
  867. ;;;; BUG REPORTS
  868.  
  869. ;;; this section is provided for reports.
  870. ;;; adopted from Barry A. Warsaw's c++-mode.el
  871.  
  872. (defvar crypt-mailer 'mail
  873.   "*Mail package to use to generate report mail buffer.")
  874.  
  875. (defconst crypt-help-address
  876.   "dodd@roebling.poly.edu, rwhitby@research.canon.oz.au"
  877.   "Address(es) accepting submission of reports on crypt++.el.")
  878.  
  879. (defconst crypt-maintainer "Larry and Rod"
  880.   "First name(s) of people accepting submission of reports on crypt++.el.")
  881.  
  882. (defconst crypt-file "crypt++.el"
  883.   "Name of file containing emacs lisp code.")
  884.  
  885. (defun crypt-submit-report ()
  886.   "Submit via mail a report using the mailer in crypt-mailer, filename in
  887. crypt-file, to address in crypt-help-address."
  888.   (interactive)
  889.   (funcall crypt-mailer)
  890.   (insert crypt-help-address)
  891.   (if (re-search-forward "^subject:[ \t]+" (point-max) 'move)
  892.       (insert "Report on " crypt-file " version " crypt-version))
  893.   (if (not (re-search-forward mail-header-separator (point-max) 'move))
  894.       (progn (goto-char (point-max))
  895.              (insert "\n" mail-header-separator "\n")
  896.              (goto-char (point-max)))
  897.     (forward-line 1))
  898.   (set-mark (point)) ;user should see mark change
  899.   (insert "\n\n---------\n")
  900.   (insert (emacs-version) "\n")
  901.   (insert "code: " crypt-file ",v " crypt-version)
  902.   (insert "\n\n")
  903.   (insert "current value of crypt-encoding-alist:\n\n")
  904.   (insert (prin1-to-string crypt-encoding-alist))
  905.   (exchange-point-and-mark)
  906.   (insert "\n" crypt-maintainer ",\n\n  ")
  907.   (message "%s, please write the message, use C-c C-c to send" (user-login-name)))
  908.  
  909. ;; provide this package
  910. (provide 'crypt++)
  911. ........................... cut along dotted line ...........................
  912. <end file: ~/lisp/crypt++.el>
  913.