Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
;;; eev.el --- embed shell/tcl/perl code and elisp hyperlinks in plain text.

;; Note: this file is obsolete! See:
;; <http://angg.twu.net/eev/README.html> (find-eev "README")

;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.

;; Author:     Eduardo Ochs <edrx@mat.puc-rio.br>
;; Maintainer: Eduardo Ochs <edrx@mat.puc-rio.br>
;; Version:    2001aug06
;; Keywords:   escripts, hyperlinks, shell, tcl, perl, tex
;; This file was copylefted to prevent against patent psychopaths; if
;; you want a version with any other license you'll have to write it
;; yourself. More formally,
;; This program 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 of the
;; License, or (at your option) any later version.

;;; Commentary:

;; This package provides support for e-scripts in Emacs. Look in
;; <http://angg.twu.net/emacs.html> for more information and in
;; <http://angg.twu.net/escripts.html> for examples of e-scripts... an
;; e-script is typically a text file in which certain parts are meant
;; to be executed by the shell or by other programs and certain
;; comments are in fact Emacs Lisp code; executing one of these
;; comments with C-x C-e (i.e., eval-last-sexp) brings up a file or an
;; info node, and thus works as a hyperlink. Many of the comments in
;; this file can be executed in this way.

;; See also the "eev Manifesto", at
;; <http://angg.twu.net/eev-manifesto.html>, the section named
;; "eev-howto", below, and whatever else seem important in
;; <http://angg.twu.net/>; that site is almost completely dedicated to
;; using eev.el.

;; «split»
;; 2001apr16: This file is in the process of being split in at least
;; three others, to comply with a request from RMS... here are
;; temporary links to the (unfinished!) parts:
;;   (find-angg "eev-all.el")
;;   (find-angg "eelinks.el")
;;   (find-angg "eev-save.el")
;;   (find-angg "eegdb.el")

;; Here's an index for the main sections of this file.
;; If you have ran the two `ee-invade-global-xxx' functions then you
;; should be able to follow the "to" hyperlinks with `M-e'.
;; Basic building blocks:
;; «.find-»		(to "find-")
;; «.variables»		(to "variables")
;; «.eev_pre»		(to "eev_pre")
;; The `eev' function and its closest friends:
;; «.eev-explanation»	(to "eev-explanation")
;; «.eev-howto»		(to "eev-howto")
;; «.eev-bash»		(to "eev-bash")
;; «.eev-tcsh»		(to "eev-tcsh")
;; «.eev»		(to "eev")
;; «.eeg»		(to "eeg")
;; Other, much stranger, `eexxx' functions:
;; «.eecd»		(to "eecd")
;; «.eeman»		(to "eeman")
;; «.eelatex»		(to "eelatex")
;; `eev-bounded' and many functions like it:
;; «.bounded»		(to "bounded")
;; «.bounded_support»	(to "bounded_support")
;; «.exxx-bounded»	(to "exxx-bounded")
;; «.find-w3»		(to "find-w3")
;; «.ee-arg»		(to "ee-arg")
;; A single call to `code-c-d' lets you define many `find-xxx'
;; hyperlink functions at once.
;; «.code-c-d»		(to "code-c-d")
;; «.code-c-d-explan1»	(to "code-c-d-explan1")
;; «.code-c-d-explan2»	(to "code-c-d-explan2")
;; «.code-c-d-examples»	(to "code-c-d-examples")
;; Strings within certain delimiters can be treated as "anchors",
;; i.e., special targets for `find-' functions.
;; «.find-anchor»	(to "find-anchor")
;; «.to_and_back»	(to "to_and_back")
;; Two functions to create hyperlinks.
;; «.inn»		(to "inn")
;; «.dff»		(to "dff")
;; Many examples of things that can be done with `eexxx' functions.
;; «.eexxx_examples»	(to "eexxx_examples")
;; To customize Emacs to make it more eev-friendly.
;; «.ee-invade-global-»	(to "ee-invade-global-")
;; «.M-e»		(to "M-e")

;;; History:

;; 2001aug06: added `find-package-in-Packages-file' and
;;            `find-available', and did some other cosmetic changes. 
;; 2001jan26: renamed `inn' and `dff', added `ee-to', `ee-back' and
;;            the `ee-invade-global-' functions; added an index and
;;            rewrote/rearranged great part of the comments.
;; 2001jan14: changed the last codepage850 chars in comments to latin1.
;; 2001jan07: added the optional argument `once' to bounded functions.
;; 2000oct30: changed `eelatex' to make it use the new variables
;;            `eelatex-post', `eelatex-file' and `eelatex-eevscript'.
;;            `inn' can now generate short info links for infofiles
;;            outside the standard info path; the long form for these
;;            links still doesn't include enough of the path.
;; 2000oct24: `ee-temp-bounded-function' is now marked as a
;;            "safe-local-variable" and thus it can now be set in the
;;            "Local Variables:" section of a file.
;; 2000sep21: fixed two little bugs in `find-anchor' and `inn'.
;; 2000sep08: added `ee-comment-format'.
;; 2000sep06: renamed `ee-default-bounded-function' to
;;            `ee-temp-bounded-function', cleaned the code that used
;;            it, and did some other renamings; now the variables that
;;            are set by `find-node2' and used by `inn' are called
;;            `ee-temp-infofile' and `ee-temp-code', for example.
;; 2000aug28: many more docstrings.
;; 2000aug22: renamed `find-tagh' to `find-anchor'; added
;;            `ee-anchor-string', `ee-delimiter-hash',
;;            `ee-delimiter-percent' and changed several functions to
;;            use them; added some more docstrings.
;; 2000aug04: corrected the instructions for tcsh; added the prefix
;;            `ee-' to some variables and internal functions created
;;            by `code-c-d'.
;; 2000jul04: added `find-w3' (finally!) and `ee-arg'.
;; 2000jun29: added `end-of-line-then-eval-last-sexp' and lots of
;;            comments.
;; 2000jun20: added `ee-default-bounded-function' and its friends.
;;            Added many `code-c-d's for Debian 2.2.
;; 2000may27: `find-___w3' now supports pos-specs, and `eecd' will now
;;            work in w3-mode.
;; 2000may26: added `find-status' and changed `dff' to use it.
;; 2000may25: Fixed some bugs in `inn' (but it will still require the
;;            exact case in the code-c-d to work right with an
;;            argument).
;; 2000may18: Added `find-fline-nosubst' and changed `code-c-d'; now
;;            the non-prefix part of a filename can have "~"s without
;;            them getting, huh, "expanded".
;; 2000may10: The `find-___' functions now accept any number of
;;            `pos-spec's. The modified `goto-position' is ugly and
;;            huge, so I haven't included it here; you'll have to
;;            fetch it from http://angg.twu.net/ and put it in your
;;            .emacs if you want to use it.
;; 2000may09: The generic interface. Removed all references to the
;;            `EE' environment variable (that used to hold the value
;;            of `ee-file'. The gdb interface now uses
;;            ee-file-generic.
;; 2000mar11: Got a review in Brave GNU World #13! (And maybe my first
;;            serious users... 8-)
;; 2000feb12: First announcement on FreshMeat.
;; late 1999: I started to have the impression that using elisp code
;;            for hyperlinks was not common practice.  Got in touch
;;            with the GNU people and they told me to start writing
;;            documentation.  Split eev.el from my .emacs.
;; early '99: `eeman', bounded `ee's, `inn', `dff'.  Started my
;;            e-scripts repository, at http://mat.puc-rio.br/~edrx/.
;;    1995-6: First functions: `find-fline', `eev', and then
;;            `find-node' and `code-c-d'.

;;; Code:

;;;; «find-»  (to ".find-")
;;;; Basic "find-..." functions.
;; Note that find-node and find-fline are the only functions that call
;; ee-goto-position directly, and they (and the calls to them from
;; code-c-d and find-node2) are already prepared to handle more than
;; one pos-spec argument. However, the simplistic version of
;; ee-goto-position below will scream if it receives more then one
;; argument.
;; There's an example of an enhanced ee-goto-position in my .emacs;
;; look for a link to it in <http://angg.twu.net/>.
;; (find-angg "eev-extras.el" "new_goto-position")

(defun ee-goto-position (&optional pos-spec)
  "Go to a certain position in the current buffer is POS-SPEC is not nil.
If POS-SPEC is a string, search for its first occurence, starting from
the beginning of the accessible area; if it is a number, go to that
line; if POS-SPEC if nil then do nothing and leave point where it is.\n
See the documentation for the functions `find-fline' and `find-node'."
  (if pos-spec (goto-char (point-min)))
  (if (numberp pos-spec)
      (forward-line (1- pos-spec)))
  (if (stringp pos-spec)
      (search-forward pos-spec)))

(autoload 'Info-goto-node "info")

(defun find-node (nodestr &rest pos-spec-list)
  "Open an Info node, and optionally go to a certain position in it.
This function is often used as a hyperlink: if you place the point
at the end of a line like one of those below\n
  # (find-node \"(emacs-lisp-intro)Run a Program\")
  # (find-node \"(emacs-e20)Lisp Eval\" \"C-x C-e\")
  # (find-node \"(emacs)Lisp Eval\")\n
and type `\\[eval-last-sexp]' Emacs will open the corresponding Info node.\n
For more information on the format of POS-SPEC-LIST, see the
documentation for the function `ee-goto-position'; for ways to omit the
\"(info-file-name)\" part of NODESTR, see the documentation for the
function `code-c-d'."
  (if (Info-goto-node nodestr)
      (apply 'ee-goto-position pos-spec-list)))

(defun find-fline (fname &rest pos-spec-list)
  "Visit a file, and optionally go to a certain position in it.
This function is often used as a hyperlink: if you place the point
at the end of a line like one of those below\n
  # (find-fline \"/etc/resolv.conf\")
  # (find-fline \"~root/\")
  # (find-fline \"$HOME/.emacs\" 25)\n
and type `\\[eval-last-sexp]' Emacs will open the corresponding file.\n
For more information on the format of POS-SPEC-LIST, see the
documentation for the function `ee-goto-position'; for ways to omit part of
the directory specification, see the documentation for the function
  (find-file (substitute-in-file-name fname))
  (apply 'ee-goto-position pos-spec-list))

(defun find-fline-nosubst (fname &rest pos-spec-list)
  "Like `find-fline', but without running `substitute-in-file-name' on FNAME."
  (find-file fname)
  (apply 'ee-goto-position pos-spec-list))

;;;; «variables»  (to ".variables")
;;;; (describe-variable 'ee-file)
;;;; (describe-variable 'ee-file-generic)
;;;; (describe-variable 'ee-temp-bounded-function)

(defvar ee-file "~/bin/ee.sh"
  "The file where `eev' saves its temporary shell script.
See the docs on the function `eev'.")

(defvar ee-file-generic "~/bin/ee.generic"
  "The file used for all \"ee\" commands based on the generic interface.
The command `eeg' saves the region in this file.\n
When you run an application through the \"eeg\" support script (written in
Expect) you are able to paste lines from this file to the application.")

(defvar ee-temp-bounded-function 'eev-bounded
  "The function that \\[ee-bounded] will execute.
All standard `eexxx-bounded' functions set this variable when they are
run; `eev-bounded' set it to `eev-bounded', `eeg-bounded' to
`eeg-bounded', and so on.")

(put 'ee-temp-bounded-function 'safe-local-variable t)

(defvar ee-delimiter-hash "\n#-\n"
  "The delimiter string used by `eev-bounded' and other similar functions.
All `eexxx-bounded' functions that are used for languages where
comments start with a `#' use this string.\n
The default value for this variable may change in future versions. For
more information on the best way to set a value for it, follow this
link: (find-enode \"File Variables\")")

(defvar ee-delimiter-percent "\n%-\n"
  "The delimiter string used by `eelatex-bounded' and similar functions.
All `eexxx-bounded' functions that are used for languages where
comments start with a `%' use this string.\n
The default value for this variable may change in future versions. For
more information on the best way to set a value for it, follow this
link: (find-enode \"File Variables\")")

(defvar ee-anchor-format "<<%s>>"
  "Hyperlink functions based on `find-anchor' use this string to make a string to search for.\n
The default value for this variable may change in future versions. For
more information on the best way to set a value for it, follow this
link: (find-enode \"File Variables\")")

(defvar ee-comment-format "# %s\n"
  "The format used by `inn' to insert \"find-xxxnode\" lines.")

;; Variables used by `eelatex':
(defvar eelatex-pre "\\documentclass{book}\n\\begin{document}\n")
(defvar eelatex-post "\n\\end{document}\n")
(defvar eelatex-file "~/tmp/tmp.tex")
(defvar eelatex-eevscript "cd ~/tmp/; latex tmp.tex")

;;;; «eev_pre»  (to ".eev_pre")
;;;; Some things needed by the `eexxx' functions.
;; (find-elnode "Writing to Files" "If START is a string")
;; (find-elnode "File Name Expansion" "substitute-in-file-name")
;; s: "start of region", or "string";
;; e: "end of region".

(defun ee-se-to-string (s e)
  "Return the part of the current buffer between S and E, or S if S is a string."
  (cond ((numberp s) (buffer-substring s e))
	((stringp s) s)))

(defun octal-to-num (str)
  "Interpret STR as an octal number and return the result."
  (let ((lastv (- (string-to-char (substring str -1)) ?0))
	(rest (substring str 0 -1)))
    (if (string= "" rest) lastv (+ lastv (* 8 (octal-to-num rest))))))

(defun ee-write-string (str &optional other-ee-file fmode)
  "Write STR into the specified file and optionally set the file's permissions.
If OTHER-EE-FILE is nil, use the value of the variable `ee-file' as
the file name; if not, use OTHER-EE-FILE.  In both cases
`substitute-in-file-name' will be applied to the file name, to make
things like \"$HURDDIR/root/bin/ee.sh\" be interpreted in the right
If FMODE is given then it will be converted to a number by
`octal-to-num' and used to set the permissions of the file.\n
Note that if ange-ftp is not disabled and the resulting filename is
something of the form \"/HOST:FILENAME\" or \"/USER@HOST:FILENAME\"
then Emacs will call an FTP program to write the string to a file on a
remote machine."
  (let ((fname (substitute-in-file-name (or other-ee-file ee-file))))
    (write-region str nil fname)	; a standard kludge
    (if fmode (set-file-modes fname (octal-to-num fmode)))))

(defun ee-write (s e pre post &optional other-ee-file fmode)
  "Same as `ee-write-string', but build the string from S, E, PRE and POST."
  (ee-write-string (concat pre (ee-se-to-string s e) post)
		   other-ee-file fmode))

;;;; «eev-explanation»  (to ".eev-explanation")
;;;; eev and friends.
;; "M-x eev" writes the "region" of the current buffer (i.e.,
;; everything between the cursor and the "mark") to a file, so that it
;; can be executed as a script. In fact it does better than that: the
;; region is saved surrounded by "set -v" and "set +v" and we source
;; it instead of running it as a script, to let it use and change the
;; environment, the directory and the pool of aliases and functions of
;; the current shell. Marking a block of text in emacs, typing "C-x
;; eev" and then going to a shell and running "ee" (which should be
;; aliased to `. ~/bin/ee.sh') is almost 100% equivalent to typing
;; each line to the shell.
;; «eev-howto»  (to ".eev-howto")
;; That was too technical... Here's the "quick eev how-to": copy
;; eev.el to your home directory, put these three lines in your .emacs
;; and restart Emacs,
;;   (load-library "~/eev.el")
;;   (ee-invade-global-namespace)
;;   (ee-invade-global-keymap)
;; or just execute them with C-x C-e if you already understand how; in
;; the latter case you won't need to restart Emacs. Also, put the
;; following line in your .zshrc (or .bashrc) and start a new
;; bash/zsh, or just execute it manually; the important thing is to
;; make this alias active.
;;   alias ee='. ~/bin/ee.sh'
;; Now write some shell commands in Emacs; mark a block of text around
;; them and run M-x eev. You should get a message like "wrote
;; ~/bin/ee.sh" in the echo area at the bottom of the screen. Switch
;; to a zsh/bash in which this alias is active (probably in another
;; virtual console or in another window) and run "ee". Voila`! Your
;; commands were executed, and each of them was be displayed (without
;; pause, and without a shell prompt) before being executed.
;; «eev-bash»  (to ".eev-bash")
;; Note: in bash the last line ("set +v") doesn't get overwritten by
;; the next shell prompt and remains on the screen. (Ugly. I need to
;; fix this, but I don't know how).
;; «eev-tcsh»  (to ".eev-tcsh")
;; If you use tcsh then things are a bit worse. This is what you
;; should put in your .cshrc:
;;   alias ee 'source ~/bin/ee.sh'
;; and as the commands to put the shell in verbose mode are different
;; in tcsh you should either use eevt instead of eev or, better, make
;; the definition of eevt overwrite the definition of eev with:
;;   (load-library "~/eev.el")
;;   (defalias
;;   (defun eev (s e &optional other-ee-file) (interactive "r")
;;     (ee-write s e "set verbose\n" "\nunset verbose" other-ee-file))
;; See:
;; (find-node "(zsh)Shell Builtin Commands" "`source'")
;; (find-node "(zsh)Shell Builtin Commands" "`set ")
;; (find-node "(zsh)Shell Builtin Commands" "`.")
;; (find-node "(zsh)Description of Options" "-v")
;; (find-node "(bash)Bourne Shell Builtins" "`.'")
;; (find-node "(bash)The Set Builtin" "  `-v'")
;; (find-elnode "Markers")

;;;; «eev»  (to ".eev")

(defun eev (s &optional e other-ee-file)
  "Save the region (plus \"set -v\"/\"set +v\") to a temporary script file.
If you have configured your shell properly (see below) then the shell
command \"ee\" will run the lines from the region similarly to as if
they were being typed at the keyboard -- each command will be echoed
before being executed, and commands like \"cd\", \"set\" and \"alias\" will
keep their effects after the script finishes.\n
See the documentation on `ee-se-to-string' on how S and E are
converted to the contents of the region. The name of the temporary
script file is taken from OTHER-EE-FILE if it is non-nil, else from
Configuration, and a simple example: if your shell is bash or zsh then
configuring it for eev means adding the following lines to your
  alias ee='. ~/bin/ee'
  export EE=~/bin/ee.sh
  export EEG=~/bin/ee.generic\n
Once this is done you can set the region to the block below and then
run `M-x eev' in Emacs and \"ee\" in a shell; the result should be easy
to guess.\n
# (find-fline \"~/eev.el\" \"hyperlinks are comments and are ignored\")
cd /tmp/
cat <<'---'
  (interactive "r")
  (ee-write s e "set -v\n" "\nset +v" other-ee-file))             ; zsh/bash

(defun eevt (s &optional e other-ee-file)
  "Save the region to a temporary script file (tcsh version).
See the comments in \"eev.el\"."
  (interactive "r")
  (ee-write s e "set verbose\n" "\nunset verbose\n" other-ee-file)) ; tcsh

;; eex: uses "-x" instead of "-v"
;; ees: "silent", i.e., non-verbose
(defun eex (s e)
  "A variant of `eev' that uses \"set -x\"/\"set +x\" instead of \"set -v\"/\"set +v\"."
  (interactive "r")
  (ee-write s e "set -x\n" "\nset +x"))

(defun ees (s e)
  "\"eev-silent\": like `eev', but doesn't set up any kind of verbose mode."
  (interactive "r")
  (ee-write s e "" "\n"))

;;;; «eeg»  (to ".eeg")
;;;; The "generic" version of eev.
;; The main use of this function is in connection with an Expect
;; script that is also called "eeg", and that can be found at:
;; <http://angg.twu.net/EXPECT/eeg>
;; (find-angg "EXPECT/eeg")
(defun eeg (s e)
  "Like `eev', but for the \"generic interface\".
Technically, this function just saves the region to the file whose
name is given by the value of `ee-file-generic' -- but you can use a
simple Expect script to paste lines from that file to any application
that takes its input from stdin, and so this gives you a sort of `eev'
that will work on programs where you can't implement an \"ee\"
The Expect script is at <http://angg.twu.net/EXPECT/eeg>; for more
information take a look at its comments and at the comments in the
file \"eev.el\".\n"
  (interactive "r")
  (ee-write s e "" "" ee-file-generic))

;;;; «eecd»  (to ".eecd")
;;;; eecd: change to the current buffer's directory.
;; In some modes like info-mode or dired-mode it is not immediately
;; obvious which is the current directory; use "M-x pwd" to learn.
(defun eecd ()
  "Like `eev', but saving a command that will \"cd\" to the current directory.
In most cases this function will get the directory from the
buffer-local variable `current-directory' (just like `pwd' does), but
for W3 buffers visiting local files a hack was introduced to make the
Right Thing happen."
  (if (not (eq major-mode 'w3-mode))
      (eev (concat "cd " default-directory) nil)
    ;; a hack for when we're visiting a local file in w3-mode
    (let ((url (url-view-url 0)))
      (if (string-match "^file:\\(.*/\\)[^/]*$" url)
	  (eev (concat "cd " (match-string 1 url)) nil)
	(error "Current url is %S, which is not a local file" url)))))

;;;; «eeman»  (to ".eeman")
;;;; eeman: linking to specific points on manpages
;; This function is a hybrid between the find-xxx's and the
;; eev's; it works as a link to a specific point on a manpage,
;; but you need to go to a shell and type "ee" to let it call man.
;; Examples of usage:
;; (eeman "man"  "  -P")
;; (eeman "less" "  \\+cmd")
;; (eeman "expect" 972)
(defun eeman (manpage &optional pos-spec)
"Write a call to \"man\" into the temporary script file.
This function is used to make hyperlinks to manpages. For example,
if you place the point at the end of one of the three lines below\n
  # (eeman \"etags\")
  # (eeman \"1 man\" \"-P pager, --pager=pager\")
  # (eeman \"expect\" 972)\n
and type `\\[eval-last-sexp]', Emacs will write into the temporary script file
(see `eev') a call to \"man MANPAGE\", optionally adding some extra
arguments to it make it start from the line given by POS-SPEC; if
POS-SPEC is a string then \"man\" will be called as\n
  man -P \"less '+/POS-SPEC'\" MANPAGE\n
which means: \"use `less' to display the manpage, but tell it to search
for the regexp POS-SPEC before giving control to the user; if POS-SPEC
is a number then interpret it as a line number, and tell \"less\" to go
to the POS-SPECth line instead of searching for a string."
  (cond ((numberp pos-spec)
	 (eev (format "man -P 'less +%d' %s\n" pos-spec manpage) nil))
	((stringp pos-spec)
	 (eev (format "man -P \"less '+/%s'\" %s\n" pos-spec manpage) nil))
	 (eev (format "man %s\n" manpage) nil))))

;;;; «eelatex»  (to ".eelatex")
;;;; A function to run LaTeX on a block of text.
(defun eelatex (s e)
  "An `eev'-like function to run LaTeX on a block of text.
This function will write the string obtained by concatenating
`eelatex-pre', the region, and `eelatex-post' to the temporary LaTeX
file whose name is given by the variable `eelatex-file', and will write
the commands in the variable `eelatex-eevscript' to the temporary
script file given by `ee-file' (the same that is used by `eev'). The
effect of all that is that by going to a properly-configured shell and
typing \"ee\" the temporary LaTeX file will be LaTeX-ed.\n
Note that you can put a \"Local Variables:\" section at the end of a
LaTeX file to make this command use special settings to compile pieces
of the file. For example:\n
  % Local Variables:
  % eelatex-file: \"~/LATEX/tmp.tex\"
  % eelatex-pre: \"\\input mythesis.sty\\n\\begin{document}\\n\"
  % eelatex-eevscript: \"cd ~/LATEX/; make tmp.dvi\"
  % End:"
  (interactive "r")
  (ee-write s e eelatex-pre eelatex-post eelatex-file)
  (eev eelatex-eevscript nil)
  (message "eelatex: wrote %s and %s" eelatex-file ee-file))

;;;; «bounded»  (to ".bounded")
;;;; "Bounded" versions for some eev commands; instead of working
;;;; with the region these functions select the text around point
;;;; until some fixed delimiter strings.
;;;; Note that `eev-bounded', `eelatex-bounded' and `eeg-bounded' set
;;;; the variable `ee-temp-bounded-function' to their function symbol
;;;; when they are run and this lets `ee-bounded' re-execute the last
;;;; eexxx-bounded command used.
;; «bounded_support»  (to ".bounded_support")
;; Some support functions,
;; (find-elnode "String Search")
(defun ee-search-backward (str)
  "Return the position after the previous occurrence of STR."
  (+ (save-excursion (search-backward str))
     (length str)))
(defun ee-search-forward (str)
  "Return the position before the next occurrence of STR."
  (- (save-excursion (search-forward str))
     (length str)))

(defun ee-bounded ()
  "Re-execute the last `eexxx-bounded' function called.
If you think you are going to use some of the `eexxx-bounded'
functions -- `eev-bounded' or `eelatex-bounded', say -- frequently
then this function certainly deserves to be bound to a key. See
  (funcall ee-temp-bounded-function))

(defun ee-meta-bounded (boundedfun fun sstr estr &rest rest)
  "A function that helps building `eexxx-bounded' functions.
It sets `ee-temp-bounded-function' to BOUNDEDFUN (for `ee-bounded')
and runs FUN with the string between SSTR and ESTR and extra arguments
If BOUNDEDFUN is nil then `ee-temp-bounded-function' is not set."
  (if boundedfun (setq ee-temp-bounded-function boundedfun))
  (apply fun
	 (ee-search-backward sstr)
	 (ee-search-forward estr)

;; «exxx-bounded»  (to ".exxx-bounded")
;; and the `eexxx-bounded' themselves:
(defun eev-bounded (&optional once)
  "Run `eev' on the region delimited by `ee-delimiter-hash'es.
Here's a simple example.  First execute the S-expression below, just
to make sure that the delimiter string is set to the right value:\n
  (set (make-local-variable 'ee-delimiter-hash) \"\\n#-\\n\")\n
Now place the cursor somewhere between the \"#-\"s below and run
`eev-bounded'; then go to a shell that was configured for `eev' (see
the documentation for the function `eev') and run \"ee\" as usual.\n
# Hyperlink comments are allowed as usual:
# (find-node \"(elisp)Creating Buffer-Local\")
# (find-node \"(bash)Redirections\" \"Here Documents\")
cat <<'---'
cd /tmp
See also the documentation on `\\[ee-default-strbounded]'."
  (ee-meta-bounded (if once nil 'eev-bounded)
		   'eev ee-delimiter-hash ee-delimiter-hash))

(defun eelatex-bounded (&optional once)
  "Run `eelatex' on the region delimited by `ee-delimiter-percent's.
See the documentation on `\\[ee-default-strbounded]'."
  (ee-meta-bounded (if once nil 'eelatex-bounded)
		   'eelatex ee-delimiter-percent ee-delimiter-percent))

(defun eeg-bounded (&optional once)
  "Run `eeg' on the region delimited by `ee-delimiter-hash'es.
See the documentation on `\\[ee-default-strbounded]'."
  (ee-meta-bounded (if once nil 'eeg-bounded)
		   'ee-write ee-delimiter-hash ee-delimiter-hash
		   "" "" ee-file-generic))

;;;; «find-w3»  (to ".find-w3")
;; «ee-arg»  (to ".ee-arg")
;; (to "M-e")
;; (find-elnode "Scope")
(setq ee-arg nil)

(defun find-w3 (localurl &rest pos-spec-list)
  "Like `find-fline', but will open LOCALURL using W3 or other browser.
If the value of `ee-arg' is 1 then use Lynx (via `eev') instead of W3.
The standard way to set `ee-arg' is to execute this function with
`\\[end-of-line-then-eval-last-sexp]' instead of with `\\[eval-last-sexp]'.\n
It is easy to add support for more browsers to this function by
redefining it at your .emacs with a richer `cond'. :-)"
  (cond ((eq ee-arg 1)
	 (ees (format "lynx %s\n" localurl) nil))
	(t (w3-open-local localurl)
	   (apply 'ee-goto-position pos-spec-list))))

;;;; «code-c-d»  (to ".code-c-d")
;;;; code-c-d, a factory of hypertext functions
;; (find-efile "etags.el")
'(load-library "etags")
(require 'etags)			; we need to set tags-add-tables to nil
(setq tags-add-tables nil)		; for find-___tags

(defun format-and-eval (formatstr &rest rest)
  "Apply `format' to FORMATSTR and REST and evaluate the result."
  (eval (read (apply 'format (concat "(progn " formatstr ")") rest))))

(defun find-node2 (infofile nodename &optional pos-spec-list  code)
  "Like `find-node', but saves CODE in `ee-temp-code' (to let `inn' use it)."
  (if code (setq ee-temp-code code ee-temp-infofile infofile))
  (apply 'find-node (format "(%s)%s" infofile nodename) pos-spec-list))

(defun code-c-d (c d &optional infofile)
  "Define an entire family of hyperlink functions at once.
A call to `code-c-d' with \"xxx\" as the value of C will define the
hyperlink functions `find-xxxfile', `find-xxxtag', `find-xxxw3' and
optionally `find-xxxnode', plus the auxiliary functions and variables
`ee-xxxdir', `ee-xxxtagsfile', `ee-xxxfile' and `ee-use-xxx-tags'.\n
To best way to understand this function is to make a call to
`icode-c-d' with the same parameters to see the code that `code-c-d'
will execute.  To see an example, copy the two lines below to a
non-read-only buffer and execute each of them with \\[eval-last-sexp]:\n
  # (icode-c-d \"grub\" \"/usr/src/grub-0.5.95/\")
  # (icode-c-d \"grub\" \"/usr/src/grub-0.5.95/\" \"grub\")\n
For more information (examples, tutorials, etc) see
  (format-and-eval "
    (setq ee-%sdir \"%s\")
    (setq ee-%stagsfile \"%sTAGS\")
    (defun ee-%sfile (str)
      (concat (substitute-in-file-name ee-%sdir) str))
    (defun ee-use-%s-tags ()
      (setq tags-file-name ee-%stagsfile))
    (defun find-%sfile (str &rest pos-spec-list)
      (apply 'find-fline-nosubst (ee-%sfile str) pos-spec-list))
    (defun find-%stag (str)
      (ee-use-%s-tags) (find-tag str))
    (defun find-%sw3 (furl &rest pos-spec-list)
      (apply 'find-w3 (ee-%sfile furl) pos-spec-list))
    (setq ee-temp-code %S ee-temp-infofile %S)
    " c d  c d  c c  c c  c c c  c c  c c  c infofile)
  (if infofile
     (format-and-eval "
       (defun find-%snode (nodename &rest pos-spec-list)
          (find-node2 %S nodename pos-spec-list  %S))" c infofile  c)))

;; «code-c-d-explan1»  (to ".code-c-d-explan1")
;; To understand exactly what a given "code-c-d" does, put an "i"
;; before the "code-c-d" and run it; the function "icode-c-d", defined
;; below, is a hack that will insert at point the block of code that
;; code-c-d would eval. For example,
;;   (icode-c-d "awk" "/usr/src/gawk-3.0.3/" "gawk")
;; gives, modulo indentation,
;; (progn
;;   (setq ee-awkdir "/usr/src/gawk-3.0.3/")
;;   (setq ee-awktagsfile "/usr/src/gawk-3.0.3/TAGS")
;;   (defun ee-awkfile (str)
;;     (concat (substitute-in-file-name ee-awkdir) str))
;;   (defun ee-use-awk-tags ()
;;     (setq tags-file-name ee-awktagsfile))
;;   (defun find-awkfile (str &rest pos-spec-list)
;;     (ee-use-awk-tags)
;;     (apply 'find-fline-nosubst (ee-awkfile str) pos-spec-list))
;;   (defun find-awktag (str)
;;     (ee-use-awk-tags) (find-tag str))
;;   (defun find-awkw3 (furl &rest pos-spec-list)
;;     (apply 'find-w3 (ee-awkfile furl) pos-spec-list))
;;   (setq ee-temp-code "awk" ee-temp-infofile "gawk"))
;; (progn
;;   (defun find-awknode (nodename &rest pos-spec-list)
;;     (find-node2 "gawk" nodename pos-spec-list  "awk")))
;; where the second progn block would not have been generated if the
;; third argument to code-c-d (the name of an info file) had been
;; suppressed.
;; «code-c-d-explan2»  (to ".code-c-d-explan2")
;; That is, the first argument to a code-c-d is the "prefix", the
;; second if the "path", the third is the "info file". A call to
;; code-c-d with a prefix of "xxx" defines ee-xxxdir, ee-xxxtagsfile,
;; ee-xxxfile, ee-use-xxx-tags, find-xxxfile, find-xxxtag, find-xxxw3
;; and maybe find-xxxnode; there are lots of examples of usage of
;; these functions in this file and in my e-scripts directory, which
;; is at <http://angg.twu.net/escripts.html>.

;; This is a quick hack that only serves dydactical purposes.
(defun icode-c-d (&rest args)
  "A variation of `code-c-d' that will insert the generated code at point instead of executing it."
  (defun format-and-eval (formatstr &rest rest)
    (insert (apply 'format (concat "\n(progn " formatstr ")\n") rest)))
  (apply 'code-c-d args)
  (defun format-and-eval (formatstr &rest rest)
    (eval (read (apply 'format (concat "(progn " formatstr ")") rest)))))

;;;; «code-c-d-examples»  (to ".code-c-d-examples")
;;;; Some examples of code-c-d's.
;;;; (given with Debian 2.2 in mind).
;; Note: if you run `(code-c-d PREFIX ...)' twice with the same
;; PREFIX, the second run overrides the first.  So it is quite trivial
;; to recycle the prefixes used in these examples...
;; Note also that a "typical" .emacs (like mine! :-) has over 150
;; calls to code-c-d:
;; (find-angg ".emacs" "code-c-ds")

(code-c-d "es" "$ES/")			; my e-scripts repository
(code-c-d "shttp" "$S/http/")		; local copies of pages from the web
(code-c-d "sftp" "$S/ftp/")		; same

(code-c-d "knuth" "$SCTAN/systems/knuth/") ; TeXbook, METAFONT book
(code-c-d "k2" "/usr/src/linux-2.0/")
(code-c-d "k22" "/usr/src/linux-2.2/")
(code-c-d "e" "/usr/share/emacs/20.7/lisp/" "emacs-e20")
(code-c-d "el" "/usr/share/emacs/20.7/lisp/" "elisp")
(code-c-d "eli" "/usr/src/emacs-lisp-intro-1.05/" "emacs-lisp-intro")
(code-c-d "eetc" "/usr/share/emacs/20.7/etc/" "emacs-e20")
(code-c-d "e19" "/usr/share/emacs/19.34/lisp/" "emacs-19/emacs")
;;(code-c-d "e" "/usr/share/emacs/19.34/lisp/" "emacs-19/emacs")
;;(code-c-d "el" "/usr/share/emacs/19.34/lisp/" "elisp")

(code-c-d "ud" "/usr/doc/")
(code-c-d "ht" "/usr/doc/HOWTO/")
(code-c-d "htet" "/usr/doc/HOWTO/en-txt/")
(code-c-d "drfc" "/usr/doc/doc-rfc/all-included-rfcs/")
(code-c-d "sagh" "/usr/share/doc/sysadmin-guide/sag.html/")
(code-c-d "nagh" "/usr/doc/ldp-nag/network-guide-1.0.html/")
(code-c-d "pack" "/usr/share/doc/packaging-manual/")
(code-c-d "packh" "/usr/share/doc/packaging-manual/packaging.html/")
(code-c-d "devref" "/usr/doc/developers-reference/")
(code-c-d "devrefh" "/usr/doc/developers-reference/developers-reference.html/")
(code-c-d "debfaq" "/usr/share/doc/debian/FAQ/")
(code-c-d "debfaqh" "/usr/share/doc/debian/FAQ/")
(code-c-d "maintg" "/usr/doc/maint-guide/")
(code-c-d "maintgh" "/usr/doc/maint-guide/")
(code-c-d "dpol" "/usr/doc/debian-policy/")
(code-c-d "vldi" "/var/lib/dpkg/info/")

;; These two are just to make some links shorter:
(defun find-devreftxt (&rest rest)
  "A hyperlink function to points of the \"Debian Developers Reference\" manual."
  (apply 'find-devreffile "developers-reference.txt.gz" rest))

(defun find-pl5pod (suffix &optional line)
  "A hyperlink function to the .pod files that make the manpages for Perl5."
  (find-fline (format "/usr/lib/perl5/5.004/pod/perl%s.pod" suffix) line))

;;;; «find-anchor»  (to ".find-anchor")
;;;; Find strings enclosed in angle quotation marks.
;;;; These functions are especially convenient for when you have to
;;;; convert your files with hyperlinks to html.
(defun find-anchor (fname &optional tag)
  "An auxiliary function to find \"anchor strings\" in text files."
  (find-fline fname)
  (if tag (ee-goto-position (format ee-anchor-format tag))))

;; I use these two functions to make hyperlinks to some files from my
;; home machine, when I want the hyperlinks to continue working after
;; converting the files to HTML. See <http://angg.twu.net/>.
(defun find-es (file &optional tag)
  "A hyperlink function that points to anchor strings in e-script files."
  (find-anchor (ee-esfile (concat file ".e")) tag))
(defun find-angg (file &optional tag)
  "A hyperlink function to anchors in files in my (edrx) home directory."
  (find-anchor (concat "~/" file) tag))

;; For info on Debian packages. See also `dff' below.
;; Here we use another kind of anchors.
(defun find-package-in-Packages-file (fname packagename)
  "An auxiliary function to find a package in a Debian \"Packages\" file."
  (find-fline fname)
  (ee-goto-position (format "\nPackage: %s\n" packagename)))

(defun find-status (packagename)
  "A hyperlink to the information in /var/lib/dpkg/status about a Debian package.
See the documentation on `dff'."
  (find-package-in-Packages-file "/var/lib/dpkg/status" packagename))

(defun find-available (packagename)
  "A hyperlink to the information in /var/lib/dpkg/available about a Debian package.
See the documentation on `dff'."
  (find-package-in-Packages-file "/var/lib/dpkg/available" packagename))

;;;; «to_and_back»  (to ".to_and_back")
;;;; Hyperlinks to anchors in the same buffer, with a "back" function.
;;;; The "ee" prefix is somewhat misleading: these functions are
;;;; related to following hyperlinks, not to saving blocks of text.
;; Note: the "back" list is currently buffer-local, and saves only line
;; numbers. Reason: this was easier to code.
;; (find-elnode "Intro to Buffer-Local" "`make-variable-buffer-local'")
(make-variable-buffer-local 'ee-back-list)
(setq-default ee-back-list nil)		; for XEmacs

(defun ee-save-position ()
  "Push an `(ee-goto-position LINENOW)' at the head of `ee-back-list'."
  (setq ee-back-list
	(cons (list 'ee-goto-position (count-lines (point-min) (point)))

(defun ee-to (anchor)
  (interactive "sAnchor: ")
  "Local hyperlink to ANCHOR, pushing the current line into `ee-back-list'."
  (ee-goto-position (format ee-anchor-format anchor)))

(defun ee-back ()
  "Pop `ee-back-list', going to the position stored in the popped element."
  (eval (car ee-back-list))
  (setq ee-back-list (cdr ee-back-list)))

;;;; «ee-insert-info-hyperlink»
;;;; Also called:         «inn»  (to ".inn")
;;;; Insert a hyperlink to the current info node.
;; (find-elnode "Interactive Examples")
;; (find-efile "info.el")
;; (find-efile "info.el" "Convert filename to lower case")
;; (find-etag "Info-set-mode-line")
(defun ee-infofile-to-code (infofile)
  "Try to convert INFOFILE to a \"code\" that can be used by `inn'.
This function is quite primitive at present: it just takes the
non-directory parts of INFOFILE and of `ee-temp-infofile', and if they
are equal it returns `ee-temp-code' (else it returns nil)."
  (if (and infofile
	   (string= (file-name-nondirectory infofile)
		    (file-name-nondirectory ee-temp-infofile)))

(defun ee-insert-info-hyperlink (arg)
  "Insert a hyperlink to the current info node.
If ARG is non-nil try to use the short form, like `# (find-elnode \"Top\")'.
Without an argument always use long forms, like `(find-node \"(elisp)Top\")'.
The infofile part (\"(elisp)\" in the example) is obtained by just taking
the non-directory part of `Info-current-file', and thus may be incomplete
if the infofile was at a non-standard directory; use `code-c-d' to work
around that."
  (interactive "P")
  (let (infofile infonode f code s)
    (if (get-buffer "*info*")
	    (set-buffer "*info*")
	    (setq infofile Info-current-file
		  infonode Info-current-node
		  f (file-name-nondirectory infofile)))
	  (if arg (setq code (ee-infofile-to-code infofile)))
	  ;; (insert (format "dbg: %S %S %S\n" infofile ee-temp-infofile f))
	   (format ee-comment-format
		   (if code (format "(find-%snode \"%s\")" code infonode)
		     (format "(find-node \"(%s)%s\")" f infonode)))))
      (error "No curent info node"))))

;;;; «ee-convert-to-debian-link»
;;;; Also called:          «dff»  (to ".dff")
;;;; This is a macro to create find-xxx hyperlinks for debian packages.
;; (find-elnode "Keyboard Macros")
(defun ee-convert-to-debian-link (N)
  "Convert N names of Debian packages into hyperlinks to the packages.
For example, if you have two lines like:\n
in a non-read-only buffer and the point is on the first one, then
running `M-2 M-x dff' will convert them to:\n
  # (find-status \"grub\")
  # (find-vldifile \"grub.list\")
  # (find-fline \"/usr/doc/grub/\")
  # (find-status \"oskit\")
  # (find-vldifile \"oskit.list\")
  # (find-fline \"/usr/doc/oskit/\")\n"
  (interactive "p")
   (read-kbd-macro "C-a NUL C-e C-w
                    # SPC (find-status SPC \" C-y \") RET
                    # SPC (find-vldifile SPC \" C-y .list\") RET
                    # SPC (find-fline SPC \"/usr/doc/ C-y /\") C-a <down>")

;;;; «ee-invade-global-»  (to ".ee-invade-global-")
;;;; Functions to customize Emacs for eev in convenient but ugly ways.
;;;; They will pollute your global key map and global namespace a bit.
(defun ee-invade-global-namespace ()
  "Define `dff' and `inn' as aliases to two hyperlink-making functions with long names."
  (defalias 'to 'ee-to)
  (defalias 'back 'ee-back)
  (defalias 'inn 'ee-insert-info-hyperlink)
  (defalias 'dff 'ee-convert-to-debian-link)
(defun ee-invade-global-keymap ()
  "Set some keys in the global to functions related to `eev'."
  (global-set-key [f3] 'ee-bounded)
  (global-set-key "\M-k" 'kill-buffer)
  (global-set-key "\M-E" 'end-of-line-then-eval-last-sexp)
  (global-set-key "\M-e" 'end-of-line-then-eval-last-sexp)
  (global-set-key "\M-A" 'back)

;;;; «M-e»  (to ".M-e")
;;;; A variant of `eval-last-sexp' that is able to pass an argument.
;;;; (to "ee-arg")
(defun end-of-line-then-eval-last-sexp (ee-arg)
  "Go to the end the line, then evaluate the sexp before point.
The argument to this command is made available to the sexp as the
variable `ee-arg', and some hyperlink functions will behave
differently is this variable is non-nil; `find-___w3', for example,
will call lynx or another browser instead of W3. See the source code
of eev.el for details.\n
This function is usually bound to a key by `ee-invade-global-keymap'."
  (interactive "P")
  (eval-last-sexp nil))

;;;; «eexxx_examples»  (to ".eexxx_examples")
;;;; These are just meant as examples, and are commented out with "'"s.
;; gdb: there used to be an `eegdb' function, but `eeg' replaced it.
;; The trick is that if you put something like
;;   define ee
;;     source ~/bin/ee.generic
;;   end
;; in your .gdbinit then you can use "ee" inside gdb to invoke the
;; saved block (and regard `eeg', defined above, as a sort of
;; `eegdb'). Note that this will work inside Emacs (i.e., inside GUD).
;; (find-angg ".gdbinit")
;; It is also possible to define functions to send lines of
;; ~/bin/ee.generic to GUD or other comint-based modes, one at a time;
;; there is an example at <http://angg.twu.net/eev-extras.el#eeg_in_emacs>.
;; (find-angg "eev-extras.el" "eeg_in_emacs")

;; perl, tcl/tk, expect:
'(defun eep (s e) (interactive "r")
  (ee-write s e "#!/usr/bin/perl\n" "" "~/bin/eep" "0755"))
'(defun eew (s e) (interactive "r")
  (ee-write s e "#!/usr/bin/wish\n" "" "$EET" "0755"))
'(defun eet (s e) (interactive "r")
  (ee-write s e "#!/usr/bin/tclsh\n" "" "$EET" "0755"))
'(defun eex (s e) (interactive "r")
  (ee-write s e "#!/usr/bin/expect\n" "" "$EET" "0755"))
'(defun eextk (s e) (interactive "r")
  (ee-write s e "#!/usr/bin/expectk\n" "" "$EET" "0755"))

;; eev to some remote machines, using ange-ftp:
;; (find-enode "Remote Files")
'(defun eevsaci (s e) (interactive "r")
  (eev s e "/edrx@$SACI:bin/ee.sh"))
'(defun eevp (s e) (interactive "r")
  (eev s e "/patna:/home/root/bin/ee.sh"))

;; eed, for dosemu. Aaargh!!
'(defun ee-crlfify (s)
  "Change each LF in S into a CRLF."
  (if (string-match "\\([^\n]*\\)\n\\(\\)" s)
      (concat (match-string 1 s) "\r\n"
	      (crlfify (substring s (match-beginning 2))))
'(defun eed (s e)
  "Write the region into \"/D/ee.bat\", changing the line terminators to CRLF."
  (interactive "r")
  (write-region (ee-crlfify (ee-se-to-string s e)) nil "/D/ee.bat"))

;; For lynx.
'(defun eel (s e) (interactive "r")
  (ee-write s e "lynx " "\n"))

;; Local Variables:
;; coding:               no-conversion
;; ee-anchor-format:     "«%s»"
;; ee-charset-indicator: "Ñ"
;; End:

;;; eev.el ends here