home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / zload / zload.el < prev   
Encoding:
Text File  |  1993-03-24  |  16.8 KB  |  500 lines

  1. ;; ;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Emacs-Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;; 
  3. ;; zload.el -- load and execute compressed elisp files
  4. ;; 
  5. ;; Copyright (C) 1992 by Kresten Krab Thorup
  6. ;;
  7. ;; Author          : Kresten Krab Thorup
  8. ;; Created On      : Tue Feb 16 17:24:41 1993
  9. ;; Last Modified By: Kresten Krab Thorup
  10. ;; Last Modified On: Fri Feb 26 01:23:27 1993
  11. ;; 
  12. ;; Update Count    : 341
  13. ;; Buffer Position : 12591
  14. ;; Minor Modes     : ( Fill)
  15. ;;
  16. ;; LCD Archive Entry:
  17. ;; zload.el|Kresten Krab Thorup|krab@iesd.auc.dk
  18. ;; |Load and execute compressed elisp files
  19. ;; |1993/03/03 23:52:31|1.15|iesd.auc.dk:zload.el
  20. ;; 
  21. ;; zload.el,v
  22. ;; Revision 1.15  1993/03/03  23:52:31  krab
  23. ;; Fixed bug in recursive loading.  Thanks to numme@itk.unit.no.
  24. ;;
  25. ;; Revision 1.14  1993/02/25  14:23:56  krab
  26. ;; Incorporated bugfixes and features suggested by Jay Adams.  These
  27. ;; are fixes to autoload stuff from his jka-load package.
  28. ;;
  29. ;; Revision 1.13  1993/02/23  21:24:34  krab
  30. ;; Incorporated cleanups from Alon Ziv
  31. ;;
  32. ;; Revision 1.11  1993/02/19  00:48:31  krab
  33. ;; Fixed up documentation, so it is presentable to the public.
  34. ;; released to gnu.emacs.sources.
  35. ;;
  36. ;; Revision 1.10  1993/02/18  22:53:44  krab
  37. ;; Incorporated new edbm facilities for the cache
  38. ;;
  39. ;; Revision 1.9  1993/02/18  19:26:11  krab
  40. ;; Implemented a caching mechanism, which allows users to avoid
  41. ;; waiting for the long seek times when looking for compressed files.
  42. ;;
  43. ;; Revision 1.8  1993/02/18  16:15:24  krab
  44. ;; Incorporated ideas of Alon Ziv (s2861785@techst02.technion.ac.il)
  45. ;; So that zload now handles autoload and require.  Autoload should
  46. ;; work in most cases including loading of macros and interactive
  47. ;; functions.
  48. ;;
  49. ;; Revision 1.7  1993/02/18  12:31:23  krab
  50. ;; Changed scheme to handle multiple decompression
  51. ;; programs much like crypt.
  52. ;;
  53. ;; Revision 1.6  1993/02/17  01:35:20  krab
  54. ;; Changed default values to ".Z" and "zcat" in place of gnuzip specifics
  55. ;;
  56. ;; Revision 1.5  1993/02/17  01:21:15  krab
  57. ;; Fixed zload::find-path to handle argument nosuffix correctly
  58. ;; Changed errors signaled, to look like those of the original load
  59. ;;
  60. ;; Revision 1.4  1993/02/16  17:00:21  krab
  61. ;; First public release to comp.sources.emacs
  62. ;;
  63. ;; Revision 1.3  1993/02/16  16:58:18  krab
  64. ;; Optimized for bytecompiled files
  65. ;;
  66. ;; Revision 1.2  1993/02/16  16:46:49  krab
  67. ;; Added documentation
  68. ;;
  69. ;; Revision 1.1  1993/02/16  16:36:17  krab
  70. ;; ** initial revision **
  71. ;;
  72. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  73. ;;
  74. ;; ** BRIEF INTRO **
  75. ;;
  76. ;; zload provides facilities to load and execute compressed elisp files.
  77. ;; When loaded it will hook itself onto the standard library functions
  78. ;; load (which is used by load-file and load-library), autoload and require.
  79. ;;
  80. ;; To use it, simply compress some of your elisp files, and zload will
  81. ;; recognize the files as compressed and decompress them when they are
  82. ;; loaded.  The decompression is done in memory, so you won't have any
  83. ;; files laying around.  If a non-compressed version of a file can be
  84. ;; found, and the .Z extension is not given explicitly, this version
  85. ;; will be loaded.  The code is optimized for finding byte compiled
  86. ;; compressed files.
  87. ;;
  88. ;; Since looking for all versions of a file takes a long time, zload
  89. ;; can utilize a cache to store the full pathnames of previously used
  90. ;; files.  The cache is on by default; to disable it, set
  91. ;; zload:use-cache to nil.
  92. ;;
  93. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  94. ;;
  95. ;; NAMING CONVENTIONS:  zload:<name> are public, zload::<name> are
  96. ;; private and zload:::<name> are local variables.
  97. ;;
  98. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  99.  
  100.  
  101. (provide 'zload)
  102.  
  103. (defconst zload:version "1.15"
  104.   "The revision number of zload.el -- code to load and execute
  105. compressed Elisp files.   Complete RCS identity is
  106.  
  107.     zload.el,v 1.15 1993/03/03 23:52:31 krab Exp")
  108.  
  109. (defvar zload:suffix-alist
  110.   '((".z" . ("gunzip" "-c"))
  111.     (".Z" . "zcat"))
  112.  
  113.   "*Alist of (SUFFIX . PROGRAM) pairs. PROGRAM may be either the name
  114. of a program that can decompress files with SUFFIX, or it may be a
  115. list with the program name as the first element and 
  116. of which the first is the name of the program, and the rest is
  117. arguments to supply.  PROGRAM will recieve compressed input on stdin,
  118. and should print the decompressed file on stdout")
  119.  
  120. (defvar zload:use-cache t
  121.   "*If non-nil zload will cache the location of compressed elisp
  122. files. This will speed up loading a lot if your load-path is long.")
  123.  
  124. (defvar zload:cache-file "~/.zload-cache"
  125.   "*Name of file holding cache information for zload.
  126. If this is not an absolute file name \(see file-name-absolute-p\),
  127. the caching mechanism will be disabled. ")
  128.  
  129. (defvar zload:search-compressed-first nil
  130.   "*If non-nil search for compressed files before non-compressed files.
  131. When enabled, you should use the cache for performance reasons which
  132. will be obvious if you try!  When enabled, the cache will get filled
  133. with entries for files which are *not* found in a compressed version.
  134. It will probably be rather slow until the cache is warmed up...")
  135.  
  136. (defvar zload::cache nil
  137.   "An EDBM structure used for keeping the cache.")
  138.  
  139. (defvar zload::loadlevel 0
  140.   "Used to handle recursive loads, ie. an autoloaded file that requires 
  141. another file")
  142.  
  143. ;; Load edbm if it is needed
  144.  
  145. (if zload:use-cache (require 'edbm))
  146.  
  147. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  148. ;; Retrieving information on compressed files
  149. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  150.  
  151. (defmacro zload::foreach (var list &rest body) 
  152.   "Set VAR to each element of LIST in turn, evaluating BODY for each
  153. value."
  154.  
  155.   (` (let ((zload:::list (, list))
  156.        ((, var) nil))
  157.        (while (setq (, var) (car zload:::list))
  158.      (setq zload:::list (cdr zload:::list))
  159.      (,@ body)))))
  160.  
  161. (defun zload::compressed-p (file)
  162.   "*Determine if FILE is compressed in a method known to zload.
  163. Returns an element of zload:suffix-alist or nil."
  164.  
  165.   (and (string-match "\\.[^.]$" file)
  166.        (assoc (substring file (match-beginning 0)) zload:suffix-alist)))
  167.  
  168. (defun zload::decompression-program (file)
  169.   "Return the program needed to decompress FILE, as a list of which
  170. the first elemnt is the program, and the rest are arguments."
  171.  
  172.   (let ((program (zload::compressed-p file)))
  173.     (if program
  174.     (if (listp (cdr program))
  175.         (cdr program)
  176.       (list (cdr program)))
  177.       (error "zload doesn't know how to decompress %s" file))))
  178.  
  179. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  180. ;; zload -- `load' substitute that can handle compressed files
  181. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  182.  
  183. (defun zload (file &optional noerror nomessage nosuffix)
  184.   "Execute a file of Lisp code named FILE.
  185. First tries FILE with .elc appended, then tries with .el,
  186.  then tries FILE unmodified.  Searches directories in  load-path.
  187. If optional second arg NOERROR is non-nil,
  188.  report no error if FILE doesn't exist.
  189. Print messages at start and end of loading unless
  190.  optional third arg NOMESSAGE is non-nil.
  191. If optional fourth arg NOSUFFIX is non-nil, don't try adding
  192.  suffixes .elc or .el to the specified name FILE.
  193. Return t if file exists.
  194.  
  195. Modified to work with compressed elisp files using zload"
  196.  
  197.   ;; if the file ends with `zfile:compressed-suffix', try loading it
  198.   ;; right away...
  199.   (if (zload::compressed-p file)
  200.       (let ((full-path (zload::find-path-exact file)))
  201.     (if full-path
  202.         (zload::load-file full-path nomessage)
  203.       (if noerror
  204.           nil
  205.         (signal 'file-error (list "Cannot open load file" file)))))
  206.  
  207.     (if zload:search-compressed-first
  208.     (let ((full-path (zload::find-path file nosuffix nomessage)))
  209.       (if full-path
  210.           (zload::load-file full-path nomessage)
  211.         (zload::orig-load file noerror nomessage nosuffix)))
  212.  
  213.       ;; Else, see if the original (emacs) load can handle it...
  214.       (let ((noloaderror (zload::orig-load file t nomessage nosuffix)))
  215.     (if noloaderror
  216.         (progn
  217.           (if zload:use-cache
  218.           (if nosuffix
  219.               (edbm:remove zload::cache (concat "!" file "!"))
  220.             (edbm:remove zload::cache file)))
  221.           noloaderror)
  222.       
  223.       ;; Finally, go and have a look for it...
  224.       (let ((full-path (zload::find-path file nosuffix nomessage)))
  225.         (if full-path
  226.         (zload::load-file full-path nomessage)
  227.           (if noerror
  228.           nil
  229.         (signal 'file-error (list "Cannot open load file" file))))))))))
  230.  
  231. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  232. ;; zload::find-path -- find absolute path of file appending .Z
  233. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  234.  
  235. (defun zload::find-path (file nosuffix nomessage)
  236.   "Find the absolute path of FILE which is compressed.
  237. FILE as given is supposed *not* to end in a `compressed suffix.'
  238. If NOSUFFIX is non-nil, don't try appending .el and .elc.
  239. If NOMESSAGE is non-nil, don't print error messages."
  240.  
  241.   (if (or nosuffix (string-match "\\.elc?$" file))
  242.       (setq nosuffix t))
  243.  
  244.   (let ((elfile (concat file ".el"))
  245.     (elcfile (concat file ".elc"))
  246.     (resfile))
  247.  
  248.     (let* ((dont-cache-it nil)
  249.        (result
  250.         (catch 'return
  251.  
  252.         (if zload:use-cache
  253.         (let ((cfile
  254.                (if nosuffix
  255.                (edbm:get zload::cache (concat "!" file "!"))
  256.              (edbm:get zload::cache file))))
  257.  
  258.           (if (and cfile
  259.                (or (null (cdr cfile))
  260.                    (file-exists-p (cdr cfile))))
  261.               (throw 'return (cdr cfile))
  262.  
  263.             ;; the cache is out of date -- remove that entry
  264.             (if nosuffix
  265.             (edbm:remove zload::cache (concat "!" file "!"))
  266.               (edbm:remove zload::cache file)))))
  267.  
  268.         ;; if file is absolute, it is simple... (and fast)
  269.         (if (file-name-absolute-p file)
  270.         (progn
  271.           (setq dont-cache-it t)
  272.           (zload::foreach encoding zload:suffix-alist
  273.                 (let ((sfx (car encoding)))
  274.  
  275.               ;; don't check .el & .elc versions if NOSUFFIX
  276.               (or nosuffix
  277.               (cond
  278.                ((file-exists-p (concat elcfile sfx))
  279.                 (throw 'return (concat elcfile sfx)))
  280.                ((file-exists-p (concat elfile sfx))
  281.                 (throw 'return (concat elfile sfx)))))
  282.               
  283.               ;; the bare-bone .z version
  284.               (if (file-exists-p (concat file sfx))
  285.               (throw 'return (concat file sfx))
  286.             (throw 'return nil))))))
  287.  
  288.         ;; otherwise, we must try all possibilities.  This is a bit
  289.         ;; clumsy, but fast in the case of byte compiled files...
  290.  
  291.         (zload::foreach encoding zload:suffix-alist
  292.           (let ((elczfile (concat file ".elc" (car encoding)))
  293.             (elzfile (concat file ".el" (car encoding)))
  294.             (zfile (concat file (car encoding))))
  295.         
  296.         (or nosuffix        ; id .elc/.el is wanted
  297.             
  298.             (zload::foreach path load-path    ; find .elc.Z file
  299.               (if (file-exists-p (setq resfile 
  300.                            (expand-file-name elczfile path)))
  301.               (throw 'return resfile)))
  302.           
  303.             (zload::foreach path load-path    ; find .el.Z file
  304.               (if (file-exists-p (setq resfile 
  305.                            (expand-file-name elzfile path)))
  306.               (throw 'return resfile))))
  307.     
  308.         (zload::foreach path load-path    ; append only .Z
  309.           (if (file-exists-p (setq resfile 
  310.                        (expand-file-name zfile path)))
  311.               (throw 'return resfile)))))
  312.     
  313.         nil)))
  314.  
  315.       ;; Save the newly found file to the cache
  316.       (if (and zload:use-cache
  317.            (not dont-cache-it))
  318.       (if nosuffix
  319.           (edbm:set zload::cache (concat "!" file "!") result)
  320.         (edbm:set zload::cache file result)))
  321.  
  322.       result)))
  323.  
  324. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  325. ;; zload::find-path-exact -- find absolute path of file
  326. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  327.  
  328. (defun zload::find-path-exact (file)
  329.   "Find the absolute path of FILE which is compressed.
  330. FILE is supposed to end in a `compressed suffix'."
  331.  
  332.   (let ((resfile))  
  333.  
  334.     (catch 'return
  335.  
  336.       ;; first check if the file is absolute
  337.       (if (file-name-absolute-p file)
  338.       (if (file-exists-p file)
  339.           (throw 'return file)
  340.         (throw 'return nil)))
  341.  
  342.       ;; else loop through load-path...
  343.       (zload::foreach path load-path
  344.     
  345.     (if (file-exists-p (setq resfile (expand-file-name file path)))
  346.         (throw 'return resfile)))
  347.       
  348.       nil)))
  349.     
  350.  
  351. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  352. ;; zload::load-file -- uncompress and execute a file
  353. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  354.  
  355. (defun zload::load-file (file nomessage)
  356.   "Execute a compressed lisp FILE.
  357. If second argument NOMESSAGE is non-nil, writes a message in the echo area.
  358. FILE is supposed to exist, and it must be the full name including the suffix"
  359.  
  360.   (let* ((tmp-buf (get-buffer-create (format "*zload<%d>*" zload::loadlevel)))
  361.      (orig-buf (current-buffer))
  362.      (full-prog (zload::decompression-program file))
  363.      (program (car full-prog))
  364.      (args (cdr full-prog)))
  365.  
  366.     (save-excursion
  367.       (or nomessage
  368.       (message "Loading %s..." (file-name-nondirectory file)))
  369.       (set-buffer tmp-buf)
  370.       (delete-region (point-min) (point-max))
  371.  
  372.       ;; We'll have to `apply' if there are arguments
  373.       (if args
  374.       (apply 'call-process program file tmp-buf nil args)
  375.     (call-process program file tmp-buf nil))
  376.  
  377.       ;; Execute it
  378.       (setq zload::loadlevel (1+ zload::loadlevel))
  379.       (eval-current-buffer)
  380.       (setq zload::loadlevel (1- zload::loadlevel))
  381.  
  382.       ;; go home again..
  383.       (set-buffer orig-buf)
  384.       (kill-buffer tmp-buf)
  385.  
  386.       ;; notify user
  387.       (or nomessage
  388.       (message "Loading %s...done" (file-name-nondirectory file)))))
  389.   t)
  390.  
  391. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  392. ;; zrequire -- load an option package
  393. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  394.  
  395. (defun zrequire (feature &optional file)
  396.   "If FEATURE is not present in Emacs (ie (featurep FEATURE) is false),
  397. load FILENAME.  FILENAME is optional and defaults to FEATURE.
  398.  
  399. Modified to work with compressed files using zload."
  400.  
  401.   (or (featurep feature)
  402.       (progn
  403.     (load (or file (symbol-name feature)) nil t nil)
  404.     (or (featurep feature)
  405.         (error "Required feature %s was not provided." feature))))
  406.   feature)
  407.  
  408. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  409. ;; zload::autoload -- load a package when function is invoked
  410. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  411.  
  412. (defun zautoload (__zload_fun __zload_file 
  413.           &optional __zload_doc __zload_int __zload_macro)
  414.   "Define FUNCTION to autoload from FILE.
  415. FUNCTION is a symbol; FILE is a file name string to pass to  load.
  416. Third arg DOCSTRING is documentation for the function.
  417. Fourth arg INTERACTIVE if non-nil says function can be called interactively.
  418. Fifth arg MACRO if non-nil says the function is really a macro.
  419. Third through fifth args give info about the real definition.
  420. They default to nil.
  421. If FUNCTION is already defined other than as an autoload,
  422. this does nothing and returns nil.
  423.  
  424. Modified to work with compressed files using zload."
  425.  
  426.   (or (and (fboundp __zload_fun)
  427.        (not (zload::autoload-p __zload_fun)))
  428.       (fset __zload_fun
  429.         (append (and __zload_macro '(macro))
  430.             '(lambda (&rest __autoload_args__))
  431.             (list __zload_doc)
  432.             (and __zload_int '((interactive)))
  433.             (list (list 'zload::do-autoload
  434.                 (list 'quote __zload_fun)
  435.                 __zload_file
  436.                 (and __zload_int '(interactive-p))
  437.                 __zload_macro
  438.                 '__autoload_args__))))))
  439.  
  440. (defun zload::autoload-p (__zload_fun)
  441.   "Determine if FUNCTION is an autoload function"
  442.   (and (fboundp __zload_fun)
  443.        (let ((def (symbol-function __zload_fun)))
  444.      (if (listp def)
  445.          (if (eq (car def) 'autoload) 
  446.          ;; it's an ordinary autoload
  447.          t     
  448.            
  449.            ;; strip 'macro if present
  450.            (if (eq (car def) 'macro)
  451.            (setq def (cdr def)))
  452.            
  453.            (let ((args (car (cdr def))))
  454.          (eq (car-safe (cdr-safe args))
  455.              '__autoload_args__)))))))
  456.  
  457. (defun zload::do-autoload (__zload_fun __zload_file __zload_int 
  458.                        __zload_macro __zload_args)
  459.   "Function called to actually evaluate an autoload function"
  460.  
  461.   ;; load it
  462.   (load __zload_file nil (not __zload_int) nil)
  463.  
  464.   (and (or (not (fboundp __zload_fun))
  465.        (zload::autoload-p __zload_fun))
  466.        (error "Autoloading file %s failed to define %s"
  467.           __zload_file __zload_fun))
  468.  
  469.   (if __zload_int
  470.       (call-interactively __zload_fun)
  471.     (if __zload_macro
  472.     (macroexpand (cons __zload_fun __zload_args))
  473.       (apply __zload_fun __zload_args))))
  474.  
  475. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  476. ;; Install zload in place of load
  477. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  478.  
  479. (if (fboundp 'zload::orig-load)
  480.     nil
  481.   (fset 'zload::orig-load (symbol-function 'load)))
  482.  
  483. (if (fboundp 'zload::orig-autoload)
  484.     nil
  485.   (fset 'zload::orig-autoload (symbol-function 'autoload)))
  486.  
  487. (fset 'load (symbol-function 'zload))
  488. (fset 'require (symbol-function 'zrequire))
  489. (fset 'autoload (symbol-function 'zautoload))
  490.  
  491. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  492. ;; load the cache if the user wants it
  493. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  494.  
  495. (if (and zload:use-cache
  496.      (file-name-absolute-p zload:cache-file))
  497.     (setq zload::cache (edbm:init zload:cache-file))
  498.   (setq zload:use-cache nil))
  499.  
  500.