Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#!/bin/bash function COPYRIGHT () { cat <<'---' This is the `eev-rctool' file of GNU eev. Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU eev. GNU eev 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 eev 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 eev; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Author: Eduardo Ochs <eduardoochs@gmail.com> Maintainer: Eduardo Ochs <eduardoochs@gmail.com> Version: 2008jan10 --- } ### Commentary: # This is the new (mar2005) installer script for eev. Strangely, its # main function is to modify rcfiles... Let me explain: # # Eev is able to send blocks of commands to many external interactive # programs, but it usually only does half of the job: it saves the # block of commands into a temporary file and only that, and then the # user needs to tell the external program something like "now read the # temporary file and execute its contents as commands". # # The trick is that we can abbreviate "now read the temporary file and # execute its contents as commands" to just "ee" if we define "ee" in # the right way for each one of these external programs; for example, # for Bash one possible definition for "ee" would be: # # function ee () { . ~/.eev/ee.sh; } # # and for GDB it could be: # # define ee # source ~/.eev/ee.gdb # end # # The best way(*) to make Bash and GDB recognize these "ee" commands # is to put these definitions in their initialition files, a.k.a. # "rcfiles": ~/.bashrc for Bash, ~/.gdbinit for GDB, ~/.emacs for # Emacs, etc. # # Changing rcfiles by hand is boring and error-prone; changing rcfiles # automatically is extremely rude; and some people would go to great # lengths to avoid actually changing their rcfiles. So this script has # to try to be everything for everybody: it allows automatic patching # of all or some rcfiles, it allows comparing the would-be-rcfiles # with the current ones, it keeps backups to allow unpatching, and it # permits executing the things that it does step-by-step. # # (*): "best" because it is technically simpler, easier to understand, # less likely to break and the idea is easier to adapt to other # programs; it's not "best" in the sense of "works transparently, # invisibly and miraculously and requires no extra keystrokes". function help_text () { echo 'Usage: Invocation Action ========== ====== eev-rctool prepare_rc DIR same as the next one but using EEVDIR:=DIR eev-rctool prepare_rc create the modified and backup rcfiles and the do_install.sh and do_uninstall.sh scripts inside BACKUPDIR, using EEVDIR:=THISDIR eev-rctool diff_rc show the differences between the modified and the backup rcfiles eev-rctool install_rc copy the modified rcfiles from BACKUPDIR to HOME (actually runs BACKUPDIR/do_install.sh) eev-rctool uninstall_rc copy the backup rcfiles from BACKUPDIR to HOME (actually runs BACKUPDIR/do_uninstall.sh) eev-rctool prepare_tmpdir create EEVTMPDIR and a few files in it eev-rctool prepare run both prepare_tmpdir and prepare_rc eev-rctool help show instructions eev-rctool new_block_emacs show the new eev block for .emacs eev-rctool current_block_emacs show the current eev block for .emacs DIFF="tkdiff" eev-rctool diff_rc like eev-rctool diff_rc, but using tkdiff Variable Default value Meaning ======== ============= ======= THISDIR ~/eev-current (the dir where this script is) EEVDIR ~/eev-current (the dir where the elisp files are) EEVTMPDIR ~/.eev (where the temporary scripts are stored) BACKUPDIR ~/.eev/backup (where the modified and backup rcfiles and the do_install.sh and do_uninstall.sh scripts are kept) DIFF "diff -N -c" (what to run for diff_rc) RCFILES ".emacs .bashrc .zshrc .gdbinit .tclshrc" (which rcfiles to modify or inspect)' } function show_vars () { echo "Variable Current value" echo "======== =============" echo "HOME = $HOME" echo "THISDIR = $THISDIR" echo "EEVDIR = $EEVDIR" echo "EEVTMPDIR = $EEVTMPDIR" echo "BACKUPDIR = $BACKUPDIR" echo "INSTALLSCRIPT = $INSTALLSCRIPT" echo "UNINSTALLSCRIPT = $UNINSTALLSCRIPT" echo "DIFF = \"$DIFF\"" echo "RCFILES = \"$RCFILES\"" } function help () { help_text; echo; show_vars; } function notes () { cat <<'%%%' Notes (technical & messy): I need to reimplement parts of the functionality of eev-rctool in elisp... the commands "install_rc" and "uninstall_rc" to eev-rctool don't deal well with symlinks... also, in BSDs the "cp" command doesn't have the option "-a", that in GNU cp means to try to preserve as most the possible the attributes of the original file; I replaced the "-a" by "-p", but I'm not sure if this is adequate. Suggestions? (find-node "(coreutils)cp invocation" "\n`-a'\n") (find-eev "eev-rctool") (find-eevsh "./eev-rctool") (find-eevsh "./eev-rctool help") (find-eevsh "./eev-rctool show_vars") (find-eevsh "./eev-rctool new_block_emacs") (find-eevsh "./eev-rctool current_block_emacs") (find-eevsh "./eev-rctool new_block_bashrc") (find-eevsh "./eev-rctool current_block_bashrc") (find-eevsh "./eev-rctool new_block_zshrc") (find-eevsh "./eev-rctool current_block_zshrc") (find-eevsh "./eev-rctool new_block_gdbinit") (find-eevsh "./eev-rctool current_block_gdbinit") (find-eevsh "./eev-rctool new_block_tclshrc") (find-eevsh "./eev-rctool current_block_tclshrc") (find-eevsh "./eev-rctool notes") (find-eev "eev-rctool" "notes") (find-eev "eev-rctool" "new_block_emacs") (find-eev "eev-rctool" "new_block_emacs") (find-eev "eev-rctool" "current_block_gdbinit") %%% } # (find-eev "Makefile") # (find-eevrc "change") # (find-eevrc "change.awk") # (find-bashnode "The Set Builtin" "`-e'") # (find-bashnode "Shell Parameter Expansion" "`${PARAMETER:=WORD}'") # (find-bashnode "Conditional Constructs" "`case'") # (find-efunction 'sh-maybe-here-document) # (remove-from-keymap sh-mode-map ?<) set -e function absolute () { case $1 in /*) echo $1;; *) echo $PWD/$1;; esac; } function semiabsolute () { absolute $1 | sed "s,^$HOME,~,"; } function noslash () { sed 's_\(.\)/$_\1_'; } # remove a trailing "/" function stem () { echo $1 | sed 's/\.//'; } # remove "."s function v () { echo $*; $*; } function vecho () { echo '#' $*; } ### Do I need noslash? or sed 's_//_/_g' ? ### h2so4 reported a bug involving a final slash in a Conectiva box # export HOME=$(echo $HOME | no_final_slash) THISSCRIPT=$(absolute $0) THISDIR=$(cd $(dirname $THISSCRIPT); echo $PWD) EEVDIR=$THISDIR : ${DIFF:="diff -N -c"} : ${RCFILES:=".emacs .bashrc .zshrc .gdbinit .tclshrc"} : ${EEVTMPDIR:=$HOME/.eev} : ${BACKUPDIR:=$EEVTMPDIR/backup} : ${EEVRCDIR:=$EEVDIR/rcfiles} INSTALLSCRIPT=$BACKUPDIR/do_install.sh UNINSTALLSCRIPT=$BACKUPDIR/do_uninstall.sh _EEVRCDIR=$(semiabsolute $EEVRCDIR) _EEVDIR=$(semiabsolute $EEVDIR) _EEVTMPDIR=$(semiabsolute $EEVTMPDIR) function substitute_paths () { # this sed used to be just a simple cat (back when I didn't have the # "@...@"s in the eev chunks) sed -e "s,@EEVRCDIR@,$EEVRCDIR,g" -e "s,@_EEVRCDIR@,$_EEVRCDIR,g" \ -e "s,@EEVDIR@,$EEVDIR,g" -e "s,@_EEVDIR@,$_EEVDIR,g" \ -e "s,@EEVTMPDIR@,$EEVTMPDIR,g" -e "s,@_EEVTMPDIR@,$_EEVTMPDIR,g" } function wrap_block () { echo "$1 Beginning of the eev block:" echo "$1 See: (find-eev \"eev-rctool\" \"new_block_$2\")" echo "$1 (find-eev-update-links)" echo "$1" new_block_no_wrappers_$2 | substitute_paths echo "$1" echo "$1 End of the eev block." } ##### # # generating the "eev blocks" that will be put in rcfiles # ##### function new_block_bashrc () { wrap_block '#' bashrc; } function new_block_no_wrappers_bashrc () { new_block_no_wrappers_zshrc; # same as the eev block for .zshrc, below } function new_block_zshrc () { wrap_block '#' zshrc; } function new_block_no_wrappers_zshrc () { cat <<'%%%' export EEVTMPDIR ;: ${EEVTMPDIR:=@_EEVTMPDIR@} export EE ;: ${EE:=$EEVTMPDIR/ee.sh} function ee () { set -v; . $EE$*; set +v; } # export EEVDIR ;: ${EEVDIR:=@_EEVDIR@} export EEVRCDIR ;: ${EEVRCDIR:=$EEVDIR/rcfiles} export EEG ;: ${EEG:=$EEVTMPDIR/ee.eeg} export EEGDB ;: ${EEGDB:=$EEVTMPDIR/ee.gdb} export EETEX ;: ${EETEX:=$EEVTMPDIR/ee.tex} # export EEC ;: ${EEC:=$EEVTMPDIR/ee.c} export EETMPC ;: ${EETMPC:=$EEVTMPDIR/tmp.c} export EEAOUT ;: ${EEAOUT:=$EEVTMPDIR/ee.aout} function eegcc () { cat $EETMPC - > $EEC gcc $* -o $EEAOUT $EEC } alias eec=$EEAOUT # # To define $S and psne uncomment the line below (or copy it, # uncommented, to outside of the eev block): # if [ -e $EEVTMPDIR/psne.sh ]; then . $EEVTMPDIR/psne.sh; fi # (find-eevtmpfile "psne.sh") %%% } function new_block_tclshrc () { wrap_block '#' tclshrc; } function new_block_no_wrappers_tclshrc () { cat <<'%%%' proc ee {} { global env; uplevel #0 source $env(EEVTMPDIR)/ee.tcl } %%% } function new_block_gdbinit () { wrap_block '#' gdbinit; } function new_block_no_wrappers_gdbinit () { cat <<'%%%' define ee source @_EEVTMPDIR@/ee.gdb end %%% } function new_block_emacs () { wrap_block ';;' emacs; } function new_block_no_wrappers_emacs () { cat <<'%%%' (add-to-list 'load-path "@_EEVDIR@") (require 'eev-all) ; (find-eev "eev-all.el") (eev-mode 1) %%% } # 2007dec21: I changed the function above to make it use "eev-all.el". # It used to do all this... # # function new_block_no_wrappers_emacs () { # cat <<'%%%' # (add-to-list 'load-path "@_EEVDIR@") # (load-library "eev.el") ; (find-eev "eev.el") # (eev-mode 1) ; (find-efunctiondescr 'eev-mode) # (load-library "eev-bounded.el") ; (find-eev "eev-bounded.el") # (load-library "eev-insert.el") ; (find-eev "eev-insert.el") # ;; (load-library "eev-steps.el") ; (find-eev "eev-steps.el") # (load-library "eev-mini-steps.el") ; (find-eev "eev-mini-steps.el") # (load-library "eev-glyphs.el") ; (find-eev "eev-glyphs.el") # (load-library "eev-compose.el") ; (find-eev "eev-compose.el") # (load-library "eev-langs.el") ; (find-eev "eev-langs.el") # (eev-set-aliases) ; (find-eev "eev.el") # (eev-set-default-glyphs) # %%% # } ##### # # the awk script # ##### # Usage: # # run_awk_script PATCHFILE < OLDFILE > NEWFILE # # What this does: it reads PATCHFILE and OLDFILE and produces NEWFILE, # which is OLDFILE with its "eev block" replaced by a new "eev block". # The new "eev block" is given by PATCHFILE. If OLDFILE didn't have an # "eev block" then we spit out a copy of OLDFILE, then a newline, then # a copy of PATCHFILE. # # That was too obscure, so now let me explain that in all the details. # PATCHFILE is typically like this (see `wrap_block', above): # # ;; Beginning of the eev block: # (stuff) # ;; End of the eev block. # # or: # # # Beginning of the eev block: # (stuff) # # End of the eev block. # # we read PATCHFILE and store its first line and its last line into # the variables `firstline' and `lastline'. The "eev block" of OLDFILE # is the part of OLDFILE that is between the first occurrence of # `firstline' in it and the first ocurrence of `lastline' after that; # what the awk script does is to replace that "eev block" by a new one # (or to just append the new block at the end). # # Now some technical explanations about the code below: after reading # PATCHFILE we set ARGV[1] to "-" so that awk will try to read from # stdin afterwards; when we are reading from stdin (i.e., from # OLDFILE) then state==0 means that we're before the first occurrence # of "firstline", state==1 means that we're inside its "eev block", # and state==2 means that we are past the "lastline". When state==0 or # state==2 we echo the input lines to NEWFILE, when state==1 we skip # lines, when we transition from state==1 to state==2 we print # PATCHFILE. Finishing reading stdin in state==0 means that we still # have to output a copy of PATCHFILE (preceded by a newline), # finishing in state==1 is an error, and finishing with state==2 is # ok. function run_awk_script () { awk ' BEGIN { patchfile = ARGV[1]; n = 0; while ((getline line < patchfile) > 0) patchlines[++n] = line; firstline = patchlines[1]; lastline = patchlines[n]; ARGV[1] = "-"; } function printpatchfile (i) { for (i=1; i<=n; ++i) print patchlines[i]; } function myerror (str) { print str > "/dev/stderr"; exit 1; } # state == 0 { if ($0 == firstline) state = 1; else print; } state == 1 { if ($0 == lastline) { printpatchfile(); state = 2; next; } } state == 2 { print } END { if (state == 0) { print ""; printpatchfile(); } if (state == 1) myerror("Input file ends in the middle of the patch block"); if (state == 2) { } }' $1 } # A quick hack: run_awk_script_2 is only used by the # `current_block_xxxrc' functions below. # function run_awk_script_2 () { awk ' BEGIN { patchfile = ARGV[1]; n = 0; while ((getline line < patchfile) > 0) patchlines[++n] = line; firstline = patchlines[1]; lastline = patchlines[n]; ARGV[1] = "-"; } state == 0 { if ($0 == firstline) state = 1 } state == 1 { print; if ($0 == lastline) state = 2 } state == 2 { } ' $1 } function current_block () { run_awk_script_2 <(new_block_$1) < $2; } function current_block_bashrc () { current_block bashrc ~/.bashrc; } function current_block_zshrc () { current_block zshrc ~/.zshrc; } function current_block_tclshrc () { current_block tclshrc ~/.tclshrc; } function current_block_gdbinit () { current_block gdbinit ~/.gdbinit; } function current_block_emacs () { current_block emacs ~/.emacs; } ##### # # more auxiliary words # ##### # (eev "eev-rctool for_rcfile .emacs prepare_backup") function for_rcfile () { set_vars_for_file $1; shift; $*; } # for tests function set_vars_for_file () { ORIGFILE=$HOME/$1 BACKUPFILE=$BACKUPDIR/$1.backup NEWFILE=$BACKUPDIR/$1.new STEM=$(stem $1) } # 2008jan10: I replaced "cp -a" by "cp -p" here (experimentally)... CP="cp -p" function prepare_backup () { if [ -e $ORIGFILE ]; then echo " Backup: $ORIGFILE -> $BACKUPFILE" $CP $ORIGFILE $BACKUPFILE echo "echo ' Restore: $ORIGFILE <- $BACKUPFILE'" >> $UNINSTALLSCRIPT echo "$CP $BACKUPFILE $ORIGFILE" >> $UNINSTALLSCRIPT else echo "echo ' Remove: $ORIGFILE'" >> $UNINSTALLSCRIPT echo "rm $ORIGFILE" >> $UNINSTALLSCRIPT fi } function transform () { if [ -e $ORIGFILE ]; then run_awk_script <(new_block_$STEM) < $BACKUPFILE > $NEWFILE else new_block_$STEM fi } function prepare_new () { transform $ORIGFILE > $NEWFILE echo "echo ' Install: $ORIGFILE <- $NEWFILE'" >> $INSTALLSCRIPT echo "cp $NEWFILE $ORIGFILE" >> $INSTALLSCRIPT } ##### # # creating the eev temp dir (usually ~/.eev) and some files in it # ##### function create_tmpdir_file_maybe () { if [ -e $EEVTMPDIR/$1 ]; then echo " Not changing: $EEVTMPDIR/$1" else echo " Creating: $EEVTMPDIR/$1" cat > $EEVTMPDIR/$1 fi } function create_help () { create_tmpdir_file_maybe HELP <<'---' M-? eev-help-page (*) Evaluation: C-_ undo C-x C-e eval-last-sexp C-g keyboard-quit M-e eek-eval-sexp-eol (*) M-x execute-extended-command M-E eek-eval-last-sexp (*) TAB indent/complete M-0 M-e, M-0 M-E just highlight (*) (* (+ 1 2) (+ 3 4)) C-w kill-region (cut) M-w kill-ring-save (copy) "Return from hyperlinks": C-y yank (paste) M-k kill-this-buffer (*) M-K bury-buffer (*) C-x C-s save-buffer C-x C-b buffer-menu C-x C-c save-buffers-kill-emacs C-x C-f find-file Main docs: (find-efunctiondescr 'eev-mode) C-x 0 delete-window (find-eevfile "article/eev.txt") C-x 1 delete-other-windows (find-eevfile "doc/keys.e") C-x 2 split-window-vertically C-x o other-window (find-node "(emacs)Top") C-x b switch-to-buffer (find-node "(emacs)Concept Index") C-x k kill-buffer (find-node "(emacs)Command Index") (find-node "(emacs)Key Index") (find-node "(elisp)Top") (find-node "(elisp)Index") (find-elinode "Index") [Used in a workshop about Emacs and eev:] (tool-bar-mode) (menu-bar-mode nil) http://www.paulgraham.com/lib/paulgraham/jmc.ps (code-ps "pgroots" "$S/http/www.paulgraham.com/lib/paulgraham/jmc.ps") (find-pgrootspage 8) (find-eev "eev-rctool" "create_help") --- } function create_psnesh () { create_tmpdir_file_maybe psne.sh <<'---' # -*- mode: sh -*- # This is the `rcfiles/psne.sh' file of GNU eev. # It was created by: (find-eev "eev-rctool" "create_psnesh") # Author and version: Eduardo Ochs, 2005jun12. Public Domain. # To activate the `psne' command (and to define the S variable if it # is not already defined), source this file; it works on zsh, and it # should also work on bash and on most sh-derivatives -- but note: I # don't use non-zsh shell very often, I might have skipped some bugs. # The installation scripts of eev add a few lines to your .bashrc and # .zshrc to make bash and zsh read this file on startup. # (find-eev "eev-rctool" "new_block_bashrc") # (find-eev "eev-rctool" "new_block_zshrc") # # This is a simple implementation of `psne' for bash and zsh. # Example: "psne http://www.foo.bar/mm" will run this: # # mkdir -p $S/http/www.foo.bar/ && \ # cd $S/http/www.foo.bar/ && \ # wget http://www.foo.bar/mm # # Note that after running that "psne" we are left at the directory # "~/snarf/http/www.foo.bar/". export S;: ${S:=~/snarf} function psne-sh-sed-snarfize () { sed 's,^\([a-z]*\)://,$S/\1/,'; } function psne-sh-urlp () { echo $1 | egrep -q '^(http|ftp)://'; } function psne-sh-meta () {( URL=$1 SURL=$(echo $1 | psne-sh-sed-snarfize) DIR=$(dirname $SURL) echo "mkdir -p $DIR/ && \\" echo "cd $DIR/ && \\" echo "wget $URL" );} function psne-sh () { if psne-sh-urlp $1; then eval "$(psne-sh-meta $1)" echo $1 >> ~/.psne.log else echo "Not an url: $1" fi } alias psne=psne-sh # (find-sh ". $EEVTMPDIR/psne.sh; psne-sh-meta http://www.foo.bar/mm") # (find-node "(bashref)The Set Builtin" "`e'") --- } function create_tmptex () { create_tmpdir_file_maybe tmp.tex <<'---' % This is the `$EEVTMPDIR/tmp.tex' file of GNU eev. % It was created by: (find-eev "eev-rctool" "prepare_tmpdir") % Author and version: Eduardo Ochs, 2005mar31. Public domain. % If you run `eelatex' in a block in Emacs and then go to a shell and % type `ee' you'll notice that this is the file that will be LaTeX'ed % (unless you've changed the defaults). This is a wrapper around % ee.tex. \documentclass{book} \usepackage[latin1]{inputenc} \usepackage{amsmath} \usepackage{graphicx} \begin{document} \input ee.tex \end{document} --- } function prepare_tmpdir () { echo " Creating dir: $EEVTMPDIR/" mkdir -p $EEVTMPDIR echo " Creating: $EEVTMPDIR/README" cat > $EEVTMPDIR/README <<'---' This is the `$EEVTMPDIR/README' file of GNU eev. It was created by: (find-eev "eev-rctool" "prepare_tmpdir") This directory is where eev saves most of its temporary scripts. If other people have access to your home directory then you should consider making this directory accessible only by you. The files with names starting with "ee" are temporary files created by eev. The files with names starting with "tmp" are usually wrappers. See tmp.tex. The file psne.sh is something more complicated. Look at its comments. The file HELP is used by `M-?'. See: (find-efunction 'eev-help-page) Eduardo Ochs 2005aug15 --- create_help ;# (find-eev "eev-rctool" "create_help") create_psnesh ;# (find-eev "eev-rctool" "create_psnesh") create_tmptex ;# (find-eev "eev-rctool" "create_tmptex") } ##### # # top-level words # ##### function prepare_rc () { if [ "$1" != "" ]; then EEVDIR=$1; fi echo " Creating dir: $BACKUPDIR/" mkdir -p $BACKUPDIR echo " Creating: $INSTALLSCRIPT" echo "# Automatically generated by eev-rctoll" > $INSTALLSCRIPT echo "# in $(date)" >> $INSTALLSCRIPT echo " Creating: $UNINSTALLSCRIPT" echo "# Automatically generated by eev-rctoll" > $UNINSTALLSCRIPT echo "# in $(date)" >> $UNINSTALLSCRIPT for i in $RCFILES; do set_vars_for_file $i prepare_backup done for i in $RCFILES; do set_vars_for_file $i echo " Creating: $NEWFILE" prepare_new done } function diff_rc () { for i in $RCFILES; do set_vars_for_file $i $DIFF $BACKUPFILE $NEWFILE || true done } function install_rc () { echo " Running: $INSTALLSCRIPT" if [ -e $INSTALLSCRIPT ]; then sh $INSTALLSCRIPT else echo Error: $INSTALLSCRIPT not found! fi } function uninstall_rc () { echo " Running: $UNINSTALLSCRIPT" if [ -e $UNINSTALLSCRIPT ]; then sh $UNINSTALLSCRIPT else echo Error: $UNINSTALLSCRIPT not found! fi } function prepare () { prepare_tmpdir; prepare_rc $1; } ##### # # Process the command-line arguments # ##### # (find-node "(bash)Conditional Constructs" "dog | cat") # This used to be just: # $* function __help () { help $*; } function _h () { help $*; } COMMAND=$(echo $1 | tr \\- _) : ${COMMAND:=help} shift || true $COMMAND $* # (find-node "(gawk)ARGC and ARGV") # (find-node "(gawk)Auto-set" "`ARGIND #'") # Local Variables: # ee-anchor-format: "\nfunction %s ()" # End: