home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / jka-load.el < prev    next >
Encoding:
Text File  |  1993-04-24  |  11.6 KB  |  358 lines

  1. ;;; jka-load.el - Support for loading remote or compressed emacs lisp files.
  2. ;;; bugs/comments to jka@ece.cmu.edu
  3. ;;; Version 0.1
  4. ;;; Last modified 4/23/93
  5.  
  6.  
  7. ;;; Copyright (C) 1993  Jay K. Adams
  8. ;;;
  9. ;;; This program is free software; you can redistribute it and/or modify
  10. ;;; it under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 2 of the License, or
  12. ;;; (at your option) any later version.
  13. ;;;
  14. ;;; This program is distributed in the hope that it will be useful,
  15. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with this program; if not, write to the Free Software
  21. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23.  
  24. ;;; LCD Archive Entry:
  25. ;;; jka-load|Jay Adams|jka@ece.cmu.edu|
  26. ;;; Load remote or compressed GNU Emacs lisp files.|
  27. ;;; 23-April-1993|0.1|~/misc/jka-load.el.Z|
  28.  
  29.  
  30. ;;; This package modifies the low-level elisp load function so that
  31. ;;; files are read in through insert-file-contents.  What this means
  32. ;;; is that if you have a package installed that makes
  33. ;;; insert-file-contents do special things like access remote files or
  34. ;;; automatically uncompress files (ange-ftp, jka-compr, or jam-zcat,
  35. ;;; for instance), then their action will also apply to loading lisp
  36. ;;; files.  I have found that using jka-load with a package like
  37. ;;; jka-compr (thus allowing you to compress all your emacs lisp
  38. ;;; files) is really useful since emacs lisp files are rarely accessed
  39. ;;; (usually only once per invocation of emacs) and they compress
  40. ;;; really well.
  41.  
  42.  
  43. ;;; Instructions:
  44. ;;; 
  45. ;;; Load the jka-load package.  Edit as usual.  The operation of this
  46. ;;; package should be completely invisible to the user.  
  47. ;;;
  48. ;;; After the package is loaded, all lisp file loading (whether done
  49. ;;; explicitly or through autoload or require) will be done using
  50. ;;; insert-file-contents.  Any package that modifies makes 
  51. ;;; insert-file-contents do special things to input files will now
  52. ;;; work with emacs lisp files.
  53.  
  54. ;;; History
  55. ;;;
  56. ;;; 4/20/93    Added code to insert a set-buffer command at the
  57. ;;;            beginning of the lisp code buffer before evaluating the
  58. ;;;            buffer.  This is so that the current-buffer will be
  59. ;;;            correct when reading the lisp code.
  60. ;;;
  61. ;;; 4/23/93    Version 0.1
  62.  
  63.  
  64. (defvar jka-load-regular-file-regexp "^/usr/gnu/lisp"
  65.   "*Regular expression that matches ordinary emacs lisp files.
  66. If the path name of a load file matches this regexp, it is loaded
  67. using jka-compr-real-load file.  Otherwise, it is loaded using
  68. jka-load-load.")
  69.  
  70.  
  71. (defvar jka-load-lisp-file-extensions '(".elc" ".el" "")
  72.   "List of extensions to try adding to emacs lisp load files.")
  73.  
  74.  
  75. ;;; This is sort of like the openp routine in lread.c except there is
  76. ;;; no exec_only arg and the suffix arg is a list instead of a string.
  77. ;;; In fact, if the lisp code looks a little strange here its because
  78. ;;; I pretty much transliterated the C version.
  79. (defun jka-load-openp (path str suffix)
  80.   "Duplicate the function of the openp routing in lread.c."
  81.   (catch 'result
  82.     (let ((absolute (file-name-absolute-p str))
  83.       filename suf try)
  84.       (while path
  85.     (catch 'continue
  86.       (setq filename (expand-file-name str (car path)))
  87.       (if (not (file-name-absolute-p filename))
  88.           (progn
  89.         (setq filename (expand-file-name str default-directory))
  90.         (if (not (file-name-absolute-p filename))
  91.             (throw 'continue nil))))
  92.  
  93.       (setq suf suffix)
  94.       (while suf
  95.         (setq try (concat filename (car suf)))
  96.         (and (file-readable-p try)
  97.          (not (file-directory-p try))
  98.          (throw 'result try))
  99.         (setq suf (cdr suf))))
  100.  
  101.     (if absolute
  102.         (throw 'result nil)
  103.       (setq path (cdr path)))))
  104.  
  105.     nil))
  106.       
  107.    
  108. (defun jka-load-load (file &optional noerror nomessage nosuffix)
  109.   "Documented as original."
  110.   (let* ((filename (jka-load-openp load-path file 
  111.                    (if nosuffix
  112.                        (cons "" nil)
  113.                      jka-load-lisp-file-extensions))))
  114.     (if (not filename)
  115.  
  116.     (or noerror
  117.         (error "Cannot open load file %s" file))
  118.  
  119.       (if (string-match jka-load-regular-file-regexp filename)
  120.  
  121.       (jka-load-real-load filename noerror nomessage nosuffix)
  122.  
  123.     (let ((cbuf (current-buffer))
  124.           (lbufname (concat " *jka-load-temp:" filename))
  125.           lbuf)
  126.  
  127.       (or nomessage
  128.           (message "Loading %s..." filename))
  129.  
  130.       (unwind-protect
  131.           (progn
  132.         (setq lbuf (get-buffer lbufname))
  133.         (if lbuf
  134.             (set-buffer lbuf)
  135.           (setq lbuf (get-buffer-create lbufname))
  136.           (set-buffer lbuf)
  137.           (insert-file-contents filename))
  138.         (set-buffer cbuf)
  139.         (jka-load-eval-buffer lbuf))
  140.         (and
  141.          lbuf
  142.          (kill-buffer lbuf)))
  143.  
  144.       (or nomessage
  145.           (message "Loading %s...done." filename)))))))
  146.  
  147.  
  148. ;;; Eval the contents of buffer BUF being careful not to change the
  149. ;;; current-buffer.  One way to do this is to write the buffer out to
  150. ;;; a temp file and load it back in.  That's sort of a drag because
  151. ;;; you would have to uniquely name each temp file so that loads
  152. ;;; within load files would work.  
  153. ;;;
  154. ;;; This function is sort of a hack.  It just puts a line at the
  155. ;;; beginning of the lisp code buffer that sets the current-buffer.
  156. ;;; Then it goes to the lisp code buffer and does eval-current-buffer.
  157. ;;; I think this is OK since buffers are gauranteed to have unique
  158. ;;; names.
  159. (defun jka-load-eval-buffer (buf)
  160.   (let* ((cbuf (current-buffer))
  161.      (set-buf-expr (list 'set-buffer
  162.                  (list 'get-buffer (buffer-name cbuf)))))
  163.     (print set-buf-expr buf)
  164.     (set-buffer buf)    
  165.     (eval-current-buffer)
  166.     (and
  167.      (buffer-name cbuf)
  168.      (set-buffer cbuf))))
  169.  
  170.  
  171. ;;; This is just a lisp rewrite of the require as it appears in fns.c.
  172. ;;; It has to be rewritten so that the new version of load will be called.
  173. (defun jka-load-require (feature &optional file)
  174.   "Documented as original."
  175.   (or (featurep feature)
  176.       (progn
  177.     (load (or file (symbol-name feature)) nil t nil)
  178.     (or (featurep feature)
  179.         (error "Required feature %s was not provided." feature))))
  180.   feature)
  181.  
  182.  
  183. ;;; See if sym's function binding is an autoload.
  184. (defun jka-load-is-autoload (sym)
  185.   "Return t if SYM's function definition is an autoload.
  186. SYM must have a function binding.  It is assumed to be an autoload if
  187. either the car of the definition is an autoload, or the definition
  188. seems to have been created by jka-load-autoload."
  189.  
  190.   (let ((fun (symbol-function sym))
  191.     (argdecl-pos 1)
  192.     (da-pos 4))
  193.  
  194.     (while (symbolp fun)
  195.       (setq fun (symbol-function fun)))
  196.  
  197.     ;; If it's a macro, move all the arg positions down one.
  198.     (and (listp fun)
  199.      (eq (car fun) 'macro)
  200.      (setq argdecl-pos 2)
  201.      (setq da-pos 5))
  202.  
  203.     ;; See if it looks like its still and autoload.  Detecting
  204.     ;; jka-load-autoload's is a little messy.
  205.     (and (listp fun)
  206.      (or (eq (car fun) 'autoload)
  207.          (and (equal (nth argdecl-pos fun)
  208.              '(&rest jka-load-autoload-args))
  209.           (eq (car (nth da-pos fun)) 'jka-load-do-autoload))))))
  210.  
  211.       
  212.   
  213. ;;; This seems to work in all useful cases.
  214. ;;; It differs from regular autoload in the following way: regular autoload has
  215. ;;; the ability to make a function (before it is loaded) appear to be both a
  216. ;;; macro and interactive.  Since jka-load-autoload binds the function to a
  217. ;;; lambda expression, it cannot make them interactive and macros at the same
  218. ;;; time.  The upshot is that if you autoload a funciton and declare it to be
  219. ;;; both a macro and interactive, it will not (before it is loaded) be
  220. ;;; interactive.
  221. (defun jka-load-autoload (fun file &optional doc int macro)
  222.   "Documented as original."
  223.   (and 
  224.    (or (not (fboundp fun))
  225.        (jka-load-is-autoload fun))
  226.    (fset fun
  227.      (append (and macro '(macro))
  228.          '(lambda (&rest jka-load-autoload-args))
  229.          ;; if doc and/or int are nil, put nils in the
  230.          ;; function definition.  This makes it easier to find
  231.          ;; out if a function definition was created by
  232.          ;; jka-load-autoload.
  233.          (list doc)
  234.          (list (and int '(interactive)))
  235.          (list (list 'jka-load-do-autoload
  236.                  (list 'quote fun)
  237.                  file 
  238.                  (and int '(interactive-p))
  239.                  macro
  240.                  'jka-load-autoload-args))))))
  241.  
  242.  
  243. ;;; Use verbose formal parameters for this procedure 'cause any symbol binding
  244. ;;; it does will be seen by the function that is being autoloaded (when it is
  245. ;;; executed for the first time).
  246. (defun jka-load-do-autoload (jka-load-autoload-fun
  247.                  jka-load-autoload-file
  248.                  jka-load-autoload-interactive
  249.                  jka-load-autoload-macro
  250.                  jka-load-autoload-args)
  251.   "Procedure called when autoloading a function defined by jka-load-autoload."
  252.  
  253.   ;; Load it
  254.   (load jka-load-autoload-file nil (not jka-load-autoload-interactive) nil)
  255.  
  256.   ;; Make sure it got defined
  257.   (and 
  258.    (or (not (fboundp jka-load-autoload-fun))
  259.        (jka-load-is-autoload jka-load-autoload-fun))
  260.    (error "Autoloading failed to define function %s." 
  261.       jka-load-autoload-fun))
  262.  
  263.   ;; Run it
  264.   (if jka-load-autoload-interactive
  265.       (call-interactively jka-load-autoload-fun)
  266.     (if jka-load-autoload-macro
  267.     (macroexpand (cons jka-load-autoload-fun jka-load-autoload-args))
  268.       (apply jka-load-autoload-fun jka-load-autoload-args))))
  269.  
  270.  
  271. ;;; This function was lifted from ange-ftp.  I added some args to make
  272. ;;; it a little more general. - jka
  273. (defun jka-load-overwrite-fn (fun saved-prefix new-prefix overwrite-msg)
  274.   "Replace FUN's function definition with NEW-PREFIX-FUN's, saving the
  275. original definition as SAVED-PREFIX-FUN.  The original documentation is
  276. placed on the new definition suitably augmented.  Third arg, OVERWRITE-MSG,
  277. is tacked on to the doc string of the new fun."
  278.  
  279.   (let* ((name (symbol-name fun))
  280.      (saved (intern (concat saved-prefix name)))
  281.      (new (intern (concat new-prefix name)))
  282.      (nfun (symbol-function new))
  283.      (exec-directory (if (or (equal (nth 3 command-line-args) "dump")
  284.                  (equal (nth 4 command-line-args) "dump"))
  285.                  "../etc/"
  286.                exec-directory)))             
  287.     
  288.     (while (symbolp nfun)
  289.       (setq nfun (symbol-function nfun)))
  290.     
  291.     (or (fboundp saved)
  292.     (progn
  293.       (fset saved (symbol-function fun))
  294.       (fset fun new)))
  295.     
  296.     (let* ((doc-str (jka-load-safe-documentation saved))
  297.        (ndoc-str (concat doc-str (and doc-str "\n")
  298.                  overwrite-msg)))
  299.       
  300.       (cond ((listp nfun)
  301.          ;; Probe to test whether function is in preloaded read-only
  302.          ;; memory, and if so make writable copy:
  303.          (condition-case nil
  304.          (setcar nfun (car nfun))
  305.            (error
  306.         (setq nfun (copy-sequence nfun)) ; shallow copy only
  307.         (fset new nfun)))
  308.          (let ((ndoc-cdr (nthcdr 2 nfun)))
  309.            (if (stringp (car ndoc-cdr))
  310.            ;; Replace the existing docstring.
  311.            (setcar ndoc-cdr ndoc-str)
  312.          ;; There is no docstring.  Insert the overwrite msg.
  313.          (setcdr ndoc-cdr (cons (car ndoc-cdr) (cdr ndoc-cdr)))
  314.          (setcar ndoc-cdr overwrite-msg))))
  315.         (t
  316.          ;; it's an emacs19 compiled-code object
  317.          (let ((new-code (append nfun nil))) ; turn it into a list
  318.            (if (nthcdr 4 new-code)
  319.            (setcar (nthcdr 4 new-code) ndoc-str)
  320.          (setcdr (nthcdr 3 new-code) (cons ndoc-str nil)))
  321.            (fset new (apply 'make-byte-code new-code))))))))
  322.  
  323.  
  324. ;;; Also lifted from ange-ftp.
  325. (defun jka-load-safe-documentation (fun)
  326.   "A documentation function that isn't quite as fragile."
  327.   (condition-case ()
  328.       (documentation fun)
  329.     (error nil)))
  330.  
  331.  
  332. ;;; do the overwirtes
  333.  
  334. (defvar jka-load-overwrite-list
  335.   '(
  336.     load
  337.     require
  338.     autoload
  339.     )
  340.   "List of functions overwritten by the jka-load package.")
  341.  
  342.  
  343. (mapcar
  344.  (function
  345.   (lambda (fn)
  346.     (jka-load-overwrite-fn
  347.      fn
  348.      "jka-load-real-"
  349.      "jka-load-"
  350.      "Note: This function has been modified to load lisp files through
  351. insert-file-contents.  It is defined in the jka-load package."
  352.      ))
  353.   )
  354.  jka-load-overwrite-list)
  355.  
  356.  
  357. (provide 'jka-load)
  358.