;;; eev.el --- embed shell/tcl/perl code and elisp hyperlinks in plain text. ;; Copyright (C) 1999 Free Software Foundation, Inc. ;; Author: Eduardo Ochs ;; Maintainer: Eduardo Ochs ;; Version: 1999jan20 ;; 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 ;; for more information and in ;; 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. ;; (find-elnode "Autoload" "magic autoload comment") ;; (find-elnode "Comment Tips") ;; (find-elnode "Library Headers") ;; (find-elnode "Documentation Tips") ;; (find-elfile "") ;;; Code: ;;;; ;;;; ®find-¯ ;;;; Basic "find-..." functions ;;;; ;;;###autoload (defun goto-position (pos-spec) (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") ;;;###autoload (defun find-node (nodestr &optional line) (if (Info-goto-node nodestr) (if line (goto-position line)))) ;;;###autoload (defun find-fline (fname &optional line) (find-file (substitute-in-file-name fname)) (goto-position line)) ;;;; ;;;; ®eev_pre¯ ;;;; The "ee" family. Basic building blocks, ;;;; ;; ;; (find-elnode "Writing to Files" "If START is a string") ;; "s": "start of region", or "string"; ;; "e": "end of region". (defun se-to-string (s e) (cond ((numberp s) (buffer-substring s e)) ((stringp s) s))) (defun octal-to-num (str) (let ((lastv (- (string-to-char (substring str -1)) ?0)) (rest (substring str 0 -1))) (if (string= "" rest) lastv (+ lastv (* 8 (octal-to-num rest)))))) (setq ee-file "$EE") (defun write-ee-string (str &optional other-ee-file fmode) (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 write-ee (s e pre post &optional other-ee-file fmode) (write-ee-string (concat pre (se-to-string s e) post) other-ee-file fmode)) ;;;; ;;;; ®eev¯ ;;;; eev and friends. ;;;; ;; ;; "C-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 `. $EE', where EE is usually set to ~/bin/emacs.ee, is ;; almost 100% equivalent to typing each line to the shell. ;; ;; ®eev-howto¯ ;; That was too technical... Here's the "quick eev how-to": put a line ;; like this in your .emacs: ;; ;; (load-library "~/eev.el") ;; ;; and put these two lines in your .zshrc: ;; ;; export EE=~/bin/emacs.ee ;; alias ee='. $EE' ;; ;; Restart zsh if needed, and enter Emacs. Write some shell commands; ;; mark a block of text and run C-x eev. You should get a message like ;; "wrote ~/bin/emacs.ee" in the echo area at the bottom of the ;; screen. Switch to a zsh (e.g., in another virtual console or in ;; another window) and run "ee". Voila`, your commands will be ;; executed, and each of them will be displayed (without pause) before ;; being executed. ;; ;; ®eev-bash¯ ;; If you use bash instead of zsh you'll have to put the ;; "export/alias" lines in your .bashrc instead; all the rest is the ;; same, except that the last line ("set +v") won't be overwritten by ;; the next shell prompt and will remain on the screen. (Ugly. I need ;; to fix this.) ;; ;; ®eev-tcsh¯ ;; If you use tcsh then things are a bit worse. This is what you ;; should put in your .cshrc: ;; ;; setenv EE ~/bin/emacs.ee ;; alias ee 'source $EE' ;; ;; 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, by dropping ;; the "t" in eevt's defun or by loading eev.el like this: ;; ;; (load-library "~/eev.el") ;; (defun eev (s e &optional other-ee-file) (interactive "r") ;; (write-ee 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") ;;;###autoload (defun eev (s e &optional other-ee-file) (interactive "r") (write-ee s e "set -v\n" "\nset +v" other-ee-file)) ; zsh/bash ;;;###autoload (defun eevt (s e &optional other-ee-file) (interactive "r") (write-ee s e "set verbose\n" "\nunset verbose" other-ee-file)) ; tcsh ;; eev to some remote machines, using ange-ftp: ;; (defun eevb (s e) (interactive "r") (eev s e "/edrx@$SACI:bin/emacs.ee")) (defun eevp (s e) (interactive "r") (eev s e "/patna:/home/root/bin/emacs.ee")) (defun eevi (s e) (interactive "r") (eev s e "/ifigenia:/home/root/bin/emacs.ee")) ;; eex: uses "-x" instead of "-v" ;; ees: "silent", i.e., non-verbose ;; (defun eex (s e) (interactive "r") (write-ee s e "set -x\n" "\nset +x")) (defun ees (s e) (interactive "r") (write-ee s e "" "\n")) ;; eecd: change to the current buffer's directory ;; Try "M-x pwd" to see it... ;; ;;;###autoload (defun eecd () (interactive) (eev (concat "cd " default-directory) nil)) '(defun eel (s e) (interactive "r") (write-ee s e "lynx " "\n")) ;; ®gdb¯ ;; Hey, you: use "ee" inside gdb to invoke the saved block. ;; You need to have something like ;; define ee ;; source ~/GDB/emacs.eeg ;; end ;; inside your .gdbinit. ;; (find-fline "~/.gdbinit") ;; (find-angg ".emacs" "gdb") ;; (defun eeg (s e) (interactive "r") (write-ee s e "" "" "~/GDB/emacs.eeg")) ;; perl, tcl/tk, expect ;; (defun eep (s e) (interactive "r") (write-ee s e "#!/usr/bin/perl\n" "" "~/bin/eep" "0777")) (defun eew (s e) (interactive "r") (write-ee s e "#!/usr/bin/wish\n" "" "$EET" "0777")) (defun eet (s e) (interactive "r") (write-ee s e "#!/usr/bin/tclsh\n" "" "$EET" "0777")) (defun eex (s e) (interactive "r") (write-ee s e "#!/usr/bin/expect\n" "" "$EET" "0777")) (defun eextk (s e) (interactive "r") (write-ee s e "#!/usr/bin/expectk\n" "" "$EET" "0777")) ;; (find-es "page" "latte") '(defun eel (s e) (interactive "r") (write-ee s e "latte-html -f - <<'--%%--' {\\include /home/root/latte/sty.latte}\n" "\n--%%--\n")) ;; eed, for dosemu. Aaargh!! ;; (defun crlfify (s) (if (string-match "\\([^\n]*\\)\n\\(\\)" s) (concat (match-string 1 s) "\r\n" (crlfify (substring s (match-beginning 2)))) s)) (defun eed (s e) (interactive "r") (write-region (crlfify (se-to-string s e)) nil "/D/ee.bat")) ;;;; ;;;; ®eeman¯ ;;;; eeman: linking to specific points on manpages ;;;; ;; ;; This function is a hybrid between the find-xxx's and the ;; code-c-d'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) ;; ;;;###autoload (defun eeman (str &optional line) (cond ((numberp line) (write-ee-string (format "man -P 'less +%d' %s\n" line str))) ((stringp line) (write-ee-string (format "man -P \"less '+/%s'\" %s\n" line str))) (t (write-ee-string (format "man %s\n" str))))) ;;;; ;;;; ®eelatex¯ ;;;; Things to run LaTeX on a block of text. ;;;; ;; ;; Run this on a block of text and then do a "ee" on a shell; a file ;; ~/LATEX/tmp.tex will be written and "make" will be called to ;; generate a tmp.dvi from it. ;; ;; (find-fline "~/LATEX/tese2.sty") ;; (defun eelatex (s e) (interactive "r") (write-ee s e "cat > ~/LATEX/tmp.tex <<'--%%--' \\input tese2.sty \\begin{document} % \\input mar99a.dnt % \\input mar99b.dnt\n" "\n\\end{document}\n--%%-- cd ~/LATEX rm tmp.dvi make tmp.dvi\n")) ;;;; ;;;; ®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. ;;;; ;; ;; Some support functions, ;; (find-elnode "String Search") ;; (defun ee-search-backward (str) (+ (save-excursion (search-backward str)) (length str))) (defun ee-search-forward (str) (- (save-excursion (search-forward str)) (length str))) (defun ee-strbounded (fun sstr estr &rest rest) (apply fun (ee-search-backward sstr) (ee-search-forward estr) rest)) (defun write-ee-bounded (sstr estr &rest rest) (apply 'ee-strbounded 'write-ee sstr estr rest)) ;; and the bounded ee's themselves: ;; (defun eelatex-bounded () (interactive) (ee-strbounded 'eelatex "\n%\n" "\n%\n")) (defun eegdb-bounded () (interactive) (ee-strbounded 'write-ee "\n%\n" "\n%\n" "" "" "~/GDB/emacs.eeg")) (defun eepf-bounded () (interactive) (ee-strbounded 'write-ee "%" "%" "" "" "/usr/src/pforth/ee.fth")) '(global-set-key [f3] 'eegdb-bounded) '(global-set-key [f3] 'eepf-bounded) '(global-set-key [f3] 'eelatex-bounded) ;;;; ;;;; ®code-c-d¯ ;;;; code-c-d, a factory of hypertext functions ;;;; ;; ;; (find-efile "etags.el") (load-library "etags") (setq tags-add-tables nil) ; for find-___tags (defun format-and-eval (formatstr &rest rest) (eval (read (apply 'format (concat "(progn " formatstr ")") rest)))) (defun find-node2 (ifile nodename &optional line code) (if code (setq ee-code-now code ee-ifile-now ifile)) (find-node (concat "(" ifile ")" nodename) line)) ;;;###autoload (defun code-c-d (c d &optional ifile) (format-and-eval " (setq %sdir \"%s\") (setq %stagsfile \"%sTAGS\") (defun %sfile (str) (concat %sdir str)) (defun set-%s-tags () (setq tags-file-name %stagsfile)) (defun find-%sfile (str &optional line) (set-%s-tags) (find-fline (%sfile str) line)) (defun find-%stag (str) (set-%s-tags) (find-tag str)) (defun find-%sw3 (furl) (w3-open-local (concat %sdir furl))) (setq ee-code-now %S ee-ifile-now %S) " c d c d c c c c c c c c c c c c ifile) (if ifile (format-and-eval " (defun find-%snode (nodename &optional line) (find-node2 %S nodename line %S))" c ifile c))) ;; ®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 awkdir "/usr/src/gawk-3.0.3/") ;; (setq awktagsfile "/usr/src/gawk-3.0.3/TAGS") ;; (defun awkfile (str) (concat awkdir str)) ;; (defun set-awk-tags () (setq tags-file-name awktagsfile)) ;; (defun find-awkfile (str &optional line) (set-awk-tags) ;; (find-fline (awkfile str) line)) ;; (defun find-awktag (str) (set-awk-tags) (find-tag str)) ;; (defun find-awkw3 (furl) (w3-open-local (concat awkdir furl)))) ;; (setq ee-code-now "awk" ee-ifile-now "awk") ;; ;; (progn ;; (defun find-awknode (nodename &optional line) ;; (find-node2 "gawk" nodename line))) ;; ;; 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¯ ;; ;; 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 xxxdir, xxxtagsfile, ;; xxxfile, set-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 ;; . ;; This is quite ridiculous but works. ;; (defun icode-c-d (&rest args) (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))))) ;; ®ccd-exs¯ ;; Some sample code-c-d's. ;; A typical .emacs (e.g., mine:-) has over 150 calls to code-c-d: ;; (find-angg ".emacs" "code-c-ds") (code-c-d "knuth" "$SCTAN/systems/knuth/") (code-c-d "vldi" "/var/lib/dpkg/info/") (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/19.34/lisp/" "emacs-19/emacs") (code-c-d "el" "/usr/share/emacs/19.34/lisp/" "elisp") (code-c-d "eli" "/usr/src/emacs-lisp-intro-1.05/" "emacs-lisp-intro") ;; Some little hacks... (defun find-pl5pod (radix &optional line) (find-pl5file (concat "pod/perl" radix ".pod") line)) ;; Find strings enclosed in angle quotation marks. ;; Some characters may look wrong on your screen... ;; (defun find-tagh (fname &optional tag) (if tag (find-fline fname (format "®%s¯" tag)) (find-fline fname))) (defun find-es (file &optional tag) (find-tagh (esfile (concat file ".e")) tag)) (defun find-angg (file &optional tag) (find-tagh (concat "~/" file) tag)) (defun find-slink (s) (deb-view (concat "/home/root/SLINK/" s))) ;;;; ;;;; Two extra functions for hypertext ;;;; ;; ;; (find-elnode "Buffer File Name") ;; (find-elnode "Interactive Examples") ;; (find-efile "info.el") ;; (find-etag "Info-set-mode-line") ;; (defun bfn (buffername) "Insert the full file name for a buffer." (interactive "bBuffer: ") (insert (buffer-file-name (get-buffer buffername)))) ;; ;; ®inn¯ (defun inn (arg) "Insert an hyperlink to the current info node. With an argument try to use the short form, like `# (find-elnode \"Top\")'. Without an argument always write something like `(find-node \"(elisp)Top\")'." (interactive "P") (let (f node ee code s) (save-excursion (set-buffer "*info*") (string-match "info/\\(.*\\)$" Info-current-file) (setq f (match-string 1 Info-current-file)) (setq node Info-current-node)) (insert (if (and arg (string= f ee-ifile-now)) (format "# (find-%snode \"%s\")\n" ee-code-now node) (format "# (find-node \"(%s)%s\")\n" f node))))) ;;;; ;;;; ®dff¯ ;;;; dff - a macro to create find-xxx ;;;; commands for debian packages ;;;; ;; ;; (find-node "(elisp)Keyboard Macros") ;; (defun dff (N) "Convert N lines with a name of a debian package in each to N ``# (find-vldifile \"/xxx.list\") # (find-fline \"/usr/doc/xxx/\")'' pairs." (interactive "p") (execute-kbd-macro (read-kbd-macro "C-a # SPC (find-vldifile SPC \" NUL C-e ESC w .list\") C-a RET # SPC (find-fline SPC \"/usr/doc/ C-y /\") C-a ") N)) ;;; eev.el ends here