#!/usr/bin/expect --
# eeg4 and eeg: support for the "generic" interface for eev.
# Htmlized version of this file: <http://angg.twu.net/eev/eeg4.html>.
# Legal stuff:
#   Copyright (C) 2001 Free Software Foundation, Inc.
#   Author:     Eduardo Ochs <edrx@mat.puc-rio.br>
#   Maintainer: Eduardo Ochs <edrx@mat.puc-rio.br>
#   Version:    2002jan29
#   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.
#
# With this script almost any program that takes input from stdin
# becomes e-scriptable. The file $EEG (usually ~/bin/ee.generic)
# contains a sequence of "actions"; when we invoke a program through
# eeg4 or eeg, with
#
#        eeg4 [options] program [args]
#   or   eeg  [options] program [args]
#
# this script will run "program args" with a bit of keyboard (i.e.,
# stdin) translation: if we type a "M-l" ("meta-L", which is the Emacs
# terminology for an alt-L), eeg4 will capture it and instead of
# sending it to the program it will run an action from its list; for
# example, in
#
#   cat > $EEG <'---'
#   echo $shell
#   exit
#   ---
#   eeg tcsh
#
# the first action will be "send the string 'echo $shell' to the
# controlled program, followed by a newline", and the second will be
# "send the string 'exit' to the controlled program, followed by a
# newline", and so if we run the block above with M-x eev and "ee" and
# we type some "M-l"s to eeg we will see something like:
#
#   /home/edrx(edrx)# ee
#   cat > $EEG <<'---'
#   echo $shell
#   exit
#   ---
#   eeg tcsh
#   angg:~# echo $shell
#   /usr/bin/tcsh
#   angg:~# exit
#   exit
#   /home/edrx(edrx)#
#
# Compare with what we would see if we had run tcsh and typed "echo
# $shell" and "exit" by hand:
#
#   /home/edrx(edrx)# tcsh
#   angg:~# echo $shell
#   /usr/bin/tcsh
#   angg:~# exit
#   exit
#   /home/edrx(edrx)#
#
# but in the first case we only had to type "e e <enter> M-l M-l" in
# the shell.
#
# If this script is invoked as "eeg" then each line of $EEG becomes an
# action like "send the contents of this line, followed by a newline";
# if it is invoked by any other name (for example, eeg4) then $EEG is
# interpreted as a Tcl script; the block
#
#   cat > $EEG <'---'
#   l {echo $shell}
#   l {exit}
#   ---
#   eeg4 tcsh
#
# is equivalent to the block used in the example above.
#
# I have tried to keep this script as minimal as possible; it works
# and is useful enough, yet, plus, it has a mechanism for loading
# extensions! Take a look at the "loadeeglib" section below and at the
# "eeg libraries" that it refers to -- in Tcl it is trivial to replace
# a function by just redefining it, so some of the extensions redefine
# functions in this file by other, not so minimal, versions.


# .misc		(to "misc")
# .loadeeglib		(to "loadeeglib")
# .actions		(to "actions")
# .dointeraction	(to "dointeraction")
# .actionkey		(to "actionkey")
# .doreadscript	(to "doreadscript")
# .eeg		(to "eeg")
# .dospawn		(to "dospawn")
# .arguments		(to "arguments")
# .mainloop		(to "mainloop")



####
#
# misc  (to ".misc")
#
####

# I/O and generic library functions.

proc readfile {fname} {
  set ch [open $fname r]; set str [read $ch]; close $ch; return $str
}
proc writefile {fname str} {
  set ch [open $fname w]; puts -nonewline $ch $str; close $ch
}
proc getenv {key {defaultvalue {}}} {
  global env
  expr {[info exist env($key)]?$env($key):$defaultvalue}
}

proc ord {str} {scan $str %c n; return $n}
proc char {n} {format %c $n}
proc gset {args} { uplevel #0 set $args }	;# "global set"
# This is to remove a trailing newline, when there is one.
proc rmlastnewline {str} {
  regexp "^(.*)\n\$" $str -> str
  return $str
}


####
#
# loadeeglib  (to ".loadeeglib")
#
####

# (find-angg "eev/libeeg/k")
# (find-angg "eev/libeeg/term")
# (find-angg "eev/libeeg/msg")

# Just add the following to top of your eegscript if you want to load
# all the standard extensions and don't want to worry too much about
# the details:
#
#   loadeeglibs all ;# (find-angg "eev/libeeg/all")

proc toeeglibfname {stem} {
  return [getenv LIBEEGDIR [getenv HOME]/eev/libeeg]/$stem
}
proc loadeeglib {stem} { uplevel #0 source [toeeglibfname $stem] }
proc loadeeglibs {args} { foreach stem $args {loadeeglib $stem} }



####
#
# actions  (to ".actions")
#
####

set actions {}
set actions_i 0
proc doaction {} {
  global actions actions_i
  uplevel #0 [lindex $actions $actions_i]
  incr actions_i
}
proc appendaction {str} { global actions; lappend actions $str }
proc _str {str} { send $str }
proc str {str} { appendaction [list _str $str] }
proc _l {str} { send "$str\r" }
proc l {str} { appendaction [list _l $str] }


####
#
# dointeraction  (to ".dointeraction")
#
####

set action_re "\354|\033l"
proc dointeraction {} {
  global action_re
  interact -re $action_re {doaction; inter_return 1}
  return 0
}


####
#
# actionkey  (to ".actionkey")
#
####

proc meta_re {c} { format "%c|\033%s" [expr [ord $c]+128] $c }
proc actionkey {c} { gset action_re [meta_re $c] }


####
#
# doreadscript  (to ".doreadscript")
#
####

set EEG [getenv EEG [getenv HOME]/bin/ee.generic]
proc doreadscript {} { global EEG; uplevel #0 source $EEG }

# eeg  (to ".eeg")
# A hack: if this script is invoked as "eeg" it will interpret the
# $EEG file as a sequence of lines to be sent literally, instead of as
# a Tcl script.
proc doreadscript_eeg {} {
  global EEG
  foreach line [split [rmlastnewline [readfile $EEG]] "\n"] {l $line}
}
proc eeg {} { proc doreadscript {} { doreadscript_eeg } }
if {[file tail $argv0]=="eeg"} eeg


####
#
# dospawn  (to ".dospawn")
# arguments  (to ".arguments")
# mainloop  (to ".mainloop")
#
####

proc dospawn {} { global argv; uplevel #0 spawn -noecho $argv; stty raw }

set oldargv $argv
while 1 {
  if {[lindex $argv 0]=="-c"} {
    uplevel #0 [lindex $argv 1]
    set argv [lrange $argv 2 end]
  }
  if {[lindex $argv 0]=="-f"} {
    set EEG [lindex $argv 1]
    set argv [lrange $argv 2 end]
  }
  if {$oldargv==$argv} break else {set oldargv $argv}
}

doreadscript
dospawn

# The main loop:
while {[dointeraction]} {}




#
#  Local Variables:
#  mode:		 tcl
#  coding:               no-conversion
#  ee-anchor-format:     "%s"
#  ee-charset-indicator: ""
#  End:
