home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD1.bin
/
new
/
util
/
edit
/
jade
/
lisp
/
info.jl
< prev
next >
Wrap
Lisp/Scheme
|
1994-10-03
|
19KB
|
563 lines
;;;; info.jl -- Info browser
;;; Copyright (C) 1994 John Harper <jsh@ukc.ac.uk>
;;; This file is part of Jade.
;;; Jade 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, or (at your option)
;;; any later version.
;;; Jade 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 Jade; see the file COPYING. If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
(provide 'info)
;;; Limitations:
;;; - Depends wholly on tag tables --- does no searching for nodes just looks
;;; up their position (except in the dir file).
;;; - No support for `*' node name.
;;; - Doesn't work 100% with info files formatted by emacs. For best results
;;; makeinfo has to be used.
;;; - No editing of nodes.
(defvar info-directory-list
(if (amiga-p) '("INFO:") '("/usr/info" "/usr/local/info/" "~/info"))
"List of directories to search for info files if they can't be found as-is.")
(defvar info-keymap (make-keytab)
"Keymap for Info.")
(defvar info-buffer (make-buffer "*Info*")
"Buffer in which Info nodes are displayed.")
(set-buffer-special info-buffer t)
(defvar info-tags-buffer (make-buffer "*Info tags*")
"Buffer for storing the current Info file's tag table.")
(set-buffer-special info-tags-buffer t)
(defvar info-history '()
"List of `(FILE NODE POS)' showing how we got to the current node.")
(defvar info-file-name nil
"The true name (in the filesystem) of the current Info file.")
(defvar info-node-name nil
"The name of the current Info node.")
(defvar info-file-modtime nil
"The modtime of file `info-file-name' last time we read something from it.")
(defvar info-indirect-list nil
"List of `(START-OFFSET . FILE-NAME)' saying where the current Info file
is split.")
(defvar info-has-tags-p nil
"t when we were able to load a tag table for this Info file.")
(defvar info-initialised nil
"Protection against being loaded multiple times.")
(unless info-initialised
(setq info-initialised t)
(put 'info-error 'error-message "Info")
(bind-keys info-keymap
"SPC" 'next-screen
"BS" 'prev-screen
"1" 'info-menu-nth
"2" 'info-menu-nth
"3" 'info-menu-nth
"4" 'info-menu-nth
"5" 'info-menu-nth
"6" 'info-menu-nth
"7" 'info-menu-nth
"8" 'info-menu-nth
"9" 'info-menu-nth
"b" 'goto-buffer-start
"d" '(info "(dir)Top")
"f" 'info-follow-ref
"h" '(info "(info)Help")
"g" 'info-goto-node
"l" 'info-last
"m" 'info-menu
"n" 'info-next
"p" 'info-prev
"q" 'bury-buffer
"u" 'info-up
"?" 'describe-mode
"HELP" 'describe-mode
"RET" 'info-goto-link
"LMB-CLICK2" 'info-goto-link
"TAB" 'info-next-link
"Meta-TAB" 'info-prev-link
"Shift-TAB" 'info-prev-link)
(with-buffer info-buffer
(setq keymap-path (cons 'info-keymap keymap-path)
major-mode 'info-mode
buffer-record-undo nil)
(set-buffer-read-only info-buffer t))
(with-buffer info-tags-buffer
(setq buffer-record-undo nil)))
;; Read the indirect list (if it exists) and tag table from the file FILENAME.
;; Indirect list ends up in `info-indirect-list', tag table is read into the
;; `info-tags-buffer' buffer. `info-has-tags-p' is set to t if a tags table
;; was loaded.
(defun info-read-tags (filename)
(let
((file (open filename "r"))
(dir (file-name-directory filename))
str)
(unless file
(signal 'info-error (list "Can't open info file" filename)))
(unwind-protect
(with-buffer info-tags-buffer
(clear-buffer)
(setq info-indirect-list nil
info-file-name nil
info-has-tags-p nil)
;; Read until we find the tag table or the indirect list.
(setq str (read-file-until file "^(Tag Table:|Indirect:) *\n$" t))
(when (and str (regexp-match "Indirect" str t))
;; Parse the indirect list
(while (and (setq str (read-line file))
(/= (aref str 0) ?\^_))
(setq info-indirect-list
(cons
(cons
(read-from-string (regexp-expand "^.*: ([0-9]+)\n$" str "\\1"))
(concat dir (regexp-expand "^(.*): [0-9]+\n$" str "\\1")))
info-indirect-list)))
(setq info-indirect-list (nreverse info-indirect-list))
;; Now look for the tag table
(setq str (read-file-until file "^Tag Table: *\n$" t)))
(when (and str (regexp-match "Tag Table" str t))
(read-buffer file)
(setq info-has-tags-p t))
(setq info-file-name filename
info-file-modtime (file-modtime filename))
t)
(close file))))
;; Read the `dir' file, if multiple `dir' files exist concatenate them
(defun info-read-dir ()
(let
((read-dir nil)
(path info-directory-list))
(clear-buffer)
(while path
(let
((name (file-name-concat (expand-file-name (car path)) "dir")))
(when (file-exists-p name)
(if read-dir
(let
((spos (cursor-pos)))
(insert (read-file name))
;; lose all text from the beginning of the file to the
;; first menu item
(when (find-next-regexp "^\\* Menu:" spos nil t)
(delete-area spos (next-line 1 (match-start)))))
(read-buffer name)
;; try to delete the file's preamble
(when (find-next-regexp "^File:" (buffer-start) nil t)
(delete-area (buffer-start) (match-start)))
(goto-buffer-end)
(setq read-dir t))
(unless (equal (cursor-pos) (line-start))
(split-line))))
(setq path (cdr path)))
(unless read-dir
(signal 'info-error '("Can't find `dir' file")))
(setq info-file-name "dir"
info-file-modtime 0
info-node-name "Top"
mode-name "(dir)")
(goto-buffer-start)
t))
;; Record the file, node and cursor-position in the `info-history' list
;; for the `info-last' command.
(defun info-remember ()
(when (and info-file-name info-node-name)
(setq info-history (cons (list info-file-name
info-node-name
(cursor-pos))
info-history))))
;; Find the actual file for the info-file FILENAME
(defun info-locate-file (filename)
(if (and info-file-name (or (not filename) (equal filename "")))
info-file-name
(let*
((filename-and-info (concat filename ".info"))
(lcase-name (translate-string (copy-sequence filename)
downcase-table))
(lcase-and-info (concat lcase-name ".info")))
(cond
((file-exists-p filename)
filename)
((file-exists-p filename-and-info)
filename-and-info)
((file-exists-p lcase-name)
lcase-name)
((file-exists-p lcase-and-info)
lcase-and-info)
(t
(catch 'foo
(let
((dir info-directory-list)
real)
(while dir
(setq real (expand-file-name (car dir)))
(cond
((file-exists-p (file-name-concat real filename))
(throw 'foo (file-name-concat real filename)))
((file-exists-p (file-name-concat real filename-and-info))
(throw 'foo (file-name-concat real filename-and-info)))
((file-exists-p (file-name-concat real lcase-name))
(throw 'foo (file-name-concat real lcase-name)))
((file-exists-p (file-name-concat real lcase-and-info))
(throw 'foo (file-name-concat real lcase-and-info))))
(setq dir (cdr dir)))
(signal 'info-error (list "Can't find file" filename)))))))))
;; Display the node NODENAME. NODENAME can contain a file name. If no node
;; is specified go to `Top' node.
;; This depends on some magic for locating the node text. It only works 100%
;; with `makeinfo' generated files.
(defun info-find-node (nodename)
(let
((filename (regexp-expand "^\\((.*)\\).*$" nodename "\\1"))
(inhibit-read-only t)
offset)
(when filename
(unless (setq nodename (regexp-expand "^\\(.*\\)(.+)$" nodename "\\1"))
(setq nodename "Top")))
(if (member filename '("dir" "DIR" "Dir"))
(info-read-dir)
(setq filename (info-locate-file filename))
(when (or (not (equal info-file-name filename)