;;; os2help.el --- invoke OS/2 on-line help, using an EPM index file

;; Copyright (C) 1992-1996 Eberhard Mattes

;; Author: Eberhard Mattes <mattes@azu.informatik.uni-stuttgart.de>
;; Keywords: emx

;; This file is part of GNU Emacs.

;; GNU Emacs 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.

;; GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:
;;;
;;; Version 1.1d
;;;
;;; Usage:
;;;
;;; 1. Put the following code into your .emacs file:
;;;
;;;        (autoload 'os2help "os2help" "OS/2 on-line help" t)
;;;        (setq os2help "c:/toolkt20/book/epmkwhlp.ndx")
;;;        (global-set-key [C-f1] 'os2help)
;;;
;;;    Insert the correct directory in the 2nd line. The example above
;;;    uses the EPM index file of the IBM Developer's Toolkit for OS/2 2.0.
;;;    If you want to get on-line help for IBM C Set/2 as well, use the
;;;    following code instead:
;;;
;;;        (setq os2help '("c:/ibmc/help/dde4.ndx"
;;;                        "c:/toolkt20/book/epmkwhlp.ndx"))
;;;
;;;    You might want to add the emx index file:
;;;
;;;        (setq os2help '("c:/emx/book/emxdoc.ndx"
;;;                        "c:/toolkt20/book/epmkwhlp.ndx"))
;;;
;;;
;;; 2. Optionally byte-compile os2help.el.
;;;
;;; 3. Load your .emacs file (for instance by restarting Emacs).
;;;
;;; 4. Place the cursor on the function name you want to get help about
;;;    and press C-f1. Either enter a topic name or press RETURN to use
;;;    the default.
;;;
;;; History
;;;
;;; 1.0a     Initial release
;;;
;;; 1.1a     Renamed (original name: toolkt-online.el)
;;;          Bug fixed: os2help-get-word failed on word at start of buffer
;;;          Support for multiple index files
;;;          Support for templates not ending with *
;;;          Preprocess index files
;;;          Search is case-sensitive. On failure, try again with case ignored
;;;
;;; 1.1b     Emacs 19
;;;          GPL
;;;
;;; 1.1c     Turn defconst into defvar
;;;
;;; 1.1d     Add patches from <Stefan_Gruendel@wue.maus.de>:
;;;          Obey the EXTENSIONS statement of index files
;;;          Ask if there are multiple matching entries
;;;

;;; Code:

(provide 'os2help)

(defvar os2help '("g:/emx/book/emxdoc.ndx" "g:/toolkt20/book/epmkwhlp.ndx")
  "Name of the EPM index file for on-line help.
To use multiple index files, assign a list of file names to `os2help'.")

(defvar os2help-old nil
  "Value of `os2help' for which `os2help-list' has been built.")

(defvar os2help-list nil
  "Contents of the EPM index files preprocessed into a list.
Each list element is a list of the form
\(EXTENSION-LIST REGEXP-LIST COMMAND\).
EXTENSION-LIST is is a list of file extensions that REGEXP-LIST is
relevant for or nil if REGEXP-LIST is valid for all buffers.  It
corresponds to the EXTENSIONS statement of an EPM index file.
REGEXP-LIST is a list of regular expressions that help is available
for with COMMAND.")

(defun os2help (topic)
  "Request OS/2 on-line help for TOPIC.
Before using `\\[os2help]', set `os2help' to the name of the index
file or to a list of names of index files."
  (interactive (let* ((default (os2help-get-word))
                      (input (read-string
                              (if default (format "Topic (default %s): "
                                                  default)
                                "Topic: "))))
                 (list (if (string= input "")
                           default
                         input))))
  (let ((ext (and buffer-file-name
                  (nth 2 (split-file-name buffer-file-name))))
        tmp template command args)
    (if (equal os2help os2help-old)
        nil
      (message "Reading EPM index files...")
      (setq os2help-list nil)
      (if (listp os2help)
          (mapcar 'os2help-parse-file os2help)
        (os2help-parse-file os2help))
      (message "")
      (setq os2help-old os2help))
    (and ext
         (setq ext (upcase (substring ext 1))))
    (let ((case-fold-search nil))
      (mapcar 'os2help-find os2help-list))
    (or tmp
        (let ((case-fold-search t))
          (mapcar 'os2help-find os2help-list)))
    (or tmp
        (error "Topic not found"))
    (setq template
          (if (= (length tmp) 1)
              (car tmp)
            (let ((unread-command-events '(?\t ?\t)))
              (completing-read "Command: "
                               (mapcar (function
                                        (lambda (node)
                                          (cons node nil)))
                                       tmp)
                               nil t))))
    (setq command
          (if (string-match "~" template)
              (concat
               (substring template 0 (match-beginning 0))
               topic
               (substring template (match-end 0)))
            template))
    (while (string-match "[ \t]+" command)
      (setq args
            (append args (list (substring
                                command 0 (match-beginning 0)))))
      (setq command (substring command (match-end 0))))
    (or (string= command "")
        (setq args (append args (list command))))
    (eval (append '(call-process "cmd.exe" nil 0 nil "/c" "start")
                  args))))

(defun os2help-find (help-list)
  "Examine HELP-LIST for OS/2 on-line help.
HELP-LIST is an element of `os2help-list'."
  (and (or (null ext)
           (null (car help-list))
           (member ext (car help-list)))
       (and (catch 'os2help-find-regexp
              (mapcar 'os2help-find-regexp (nth 1 help-list))
              nil)
            (setq tmp (cons (nth 2 help-list) tmp)))))

(defun os2help-find-regexp (regexp)
  "Examine REGEXP for OS/2 on-line help."
  (and (string-match regexp topic)
       (throw 'os2help-find-regexp t)))

(defun os2help-parse-file (fname)
  "Parse EPM index file FNAME and add patterns to `os2help-list'."
  (let ((extension-list '("*")) prefix prefix-list template last-template
        index-buffer limit (more t))
    (if (not (file-readable-p fname))
        (error "can't access EPM index file %s" fname))
    (setq index-buffer (get-buffer-create " *os2help-parse-file*"))
    (set-buffer index-buffer)
    (erase-buffer)
    (insert-file-contents fname nil)
    (set-buffer-modified-p nil)
    (while more
      (cond ((looking-at "^ *(")
             (end-of-line)
             (setq limit (point))
             (beginning-of-line)
             (while (re-search-forward
                     " *(\\([^,)]+\\),[ \t]*\\([^)]+\\))" limit t)
               (setq prefix (buffer-substring (match-beginning 1)
                                              (match-end 1)))
               (setq template (buffer-substring (match-beginning 2)
                                                (match-end 2)))
               (setq prefix (concat "^"
                                    (if (string= (substring prefix -1) "*")
                                        (regexp-quote (substring prefix 0 -1))
                                      (concat (regexp-quote prefix) "$"))))
               (if (equal last-template template)
                   (nconc prefix-list (list prefix))
                 (and prefix-list
                      (if os2help-list
                          (nconc os2help-list (list (list extension-list
                                                          prefix-list
                                                          last-template)))
                        (setq os2help-list (list (list extension-list
                                                       prefix-list
                                                       last-template)))))
                 (setq prefix-list (list prefix)
                       last-template template))))
            ((looking-at "^ *EXTENSIONS:")
             (setq extension-list '()
                   prefix (match-end 0))
             (end-of-line)
             (setq limit (point))
             (goto-char prefix)
             (while (re-search-forward " *\\([^\n ]+\\)" limit t)
               (if extension-list
                   (nconc extension-list
                          (list (upcase (buffer-substring (match-beginning 1)
                                                          (match-end 1)))))
                 (setq extension-list
                       (list (upcase (buffer-substring (match-beginning 1)
                                                       (match-end 1)))))))
             (and (equal extension-list '("*"))
                  (setq extension-list nil))))
      (setq more (zerop (forward-line 1))))
    (if os2help-list
        (nconc os2help-list (list (list extension-list
                                        prefix-list
                                        template)))
      (setq os2help-list (list (list extension-list
                                     prefix-list
                                     template))))
    (kill-buffer index-buffer)))

(defun os2help-get-word ()
  "Get text for OS/2 online help."
  (let ((pos (point)) start)
    (if (looking-at "[A-Za-z_0-9]+")
        (setq end (match-end 0))
      (setq end pos))
    (goto-char end)
    (if (re-search-backward "[^A-Za-z_0-9]" nil t)
        (setq start (match-end 0))
      (setq start (point-min)))
    (goto-char pos)
    (if (< start end)
        (buffer-substring start end)
      nil)))

;;; os2help.el ends here
