# Copyright 2000, 2004 by Paul Mattes. # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, # provided that the above copyright notice appear in all copies and that # both that copyright notice and this permission notice appear in # supporting documentation. # # x3270, c3270, s3270 and tcl3270 are distributed in the hope that they will # be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE # for more details. # Glue functions between 'expect' and x3270 # Usage: source x3270_glue.expect namespace eval x3270 { variable verbose 0 variable pid 0 # Start function: Start ?-nohup? ?program? ?options? # # Sets up the 'expect' environment correctly and spawns a 3270 # interface process. # # The 'program' and 'options' can be: # "x3270 -script" to drive an x3270 session # "s3270" to drive a displayless 3270 session # "x3270if -i" to run as a child script of x3270 (via the Script() # action) # # If "args" is empty, or starts with an option besides '-nohup', # guesses which process to start. # It will only guess "x3270if -i" or "s3270"; if you want to start # x3270, you need to specify it explicitly. # # Returns the process ID of the spawned process. proc Start {args} { global stty_init timeout spawn_id env variable verbose variable pid if {$pid != 0} {return -code error "Already started."} # If the first argument is "-nohup", remember that as an # argument to 'spawn'. if {[lindex $args 0] == "-nohup"} { set nohup {-ignore HUP} set args [lrange $args 1 end] } { set nohup {} } # If there are no arguments, or the first argument is an # option, guess what to start. # If X3270INPUT is defined in the environment, this must be a # child script; start x3270if. Otherwise, this must be a peer # script; start s3270. if {$args == {} || [string index [lindex $args 0] 0] == "-"} { if {[info exists env(X3270INPUT)]} { set args [concat x3270if -i $args] } { set args [concat s3270 $args] } } # Set up the pty initialization default. set stty_init -echo # Spawn the process. if {$verbose} { set pid [eval [concat spawn $nohup $args]] } { set pid [eval [concat spawn -noecho $nohup $args]] log_user 0 } # Set the 'expect' timeout. set timeout -1 return $pid } # Basic interface command. Used internally by the action functions # below. proc cmd {cmd} { variable verbose variable pid if {$pid==0} { return -code error "Not started yet." } if {$verbose} {puts "+$cmd"} send "$cmd\r" expect { -re "data: (.*)\r\n.*\r\nok\r\n$" { set r $expect_out(buffer) } "*ok\r\n" { return {} } -re "(.*)\r\n.*?\r\nerror\r\n" { return -code error "$expect_out(1,string)" } "*error\r\n" { return -code error \ "$cmd failed: $expect_out(buffer)" } eof { set pid 0; error "process died" } } # Convert result to a list. set ret {} set iter 0 while {1} { if {! [regexp "data: (.*?)\r\n" $r dummy elt]} {break} if {$iter==1} {set ret [list $ret]} set r [string range $r [expr [string length $elt]+7] \ end] if {$iter > 0} { set ret [linsert $ret end $elt] } { set ret $elt } set iter [expr $iter + 1] } if {$verbose} {puts "ret $iter"} return $ret } # Convert an argument list to a comma-separated list that x3270 will # accept. proc commafy {alist} { set i 0 set a "" while {$i < [llength $alist]} { if {$i > 0} { set a "$a,[lindex $alist $i]" } { set a [lindex $alist $i] } incr i } return $a } # Quote a text string into x3270-acceptable format. proc stringify {text} { set a "\"" set i 0 while {$i < [string len $text]} { set c [string range $text $i $i] switch -- $c { "\n" { set a "$a\\n" } "\r" { set a "$a\\r" } " " { set a "$a\\ " } "\"" { set a "$a\\\"" } default { set a "$a$c" } } incr i } set a "$a\"" return $a } # User-accessible actions. # Some of these apply only to x3270 and x3270if, and not to s3270. proc AltCursor {} { return [cmd "AltCursor"] } proc Ascii {args} { return [cmd "Ascii([commafy $args])"] } proc AsciiField {} { return [cmd "AsciiField"] } proc Attn {} { return [cmd "Attn"] } proc BackSpace {} { return [cmd "BackSpace"] } proc BackTab {} { return [cmd "BackTab"] } proc CircumNot {} { return [cmd "CircumNot"] } proc Clear {} { return [cmd "Clear"] } proc CloseScript {} { return [cmd "CloseScript"] } proc Cols {} { return [lindex [Status] 7] } proc Compose {} { return [cmd "Compose"] } proc Connect {host} { return [cmd "Connect($host)"] } proc CursorSelect {} { return [cmd "CursorSelect"] } proc Delete {} { return [cmd "Delete"] } proc DeleteField {} { return [cmd "DeleteField"] } proc DeleteWord {} { return [cmd "DeleteWord"] } proc Disconnect {} { return [cmd "Disconnect"] } proc Down {} { return [cmd "Down"] } proc Dup {} { return [cmd "Dup"] } proc Ebcdic {args} { return [cmd "Ebcdic([commafy $args])"] } proc EbcdicField {} { return [cmd "EbcdicField"] } proc Enter {} { return [cmd "Enter"] } proc Erase {} { return [cmd "Erase"] } proc EraseEOF {} { return [cmd "EraseEOF"] } proc EraseInput {} { return [cmd "EraseInput"] } proc FieldEnd {} { return [cmd "FieldEnd"] } proc FieldMark {} { return [cmd "FieldMark"] } proc FieldExit {} { return [cmd "FieldExit"] } proc Flip {} { return [cmd "Flip"] } proc HexString {x} { return [cmd "HexString($x)"] } proc Home {} { return [cmd "Home"] } proc Info {text} { return [cmd "Info([stringify $text])"] } proc Insert {} { return [cmd "Insert"] } proc Interrupt {} { return [cmd "Interrupt"] } proc Key {k} { return [cmd "Key($k)"] } proc Keymap {k} { return [cmd "Keymap($k)"] } proc Left {} { return [cmd "Left"] } proc Left2 {} { return [cmd "Left2"] } proc MonoCase {} { return [cmd "MonoCase"] } proc MoveCursor {r c} { return [cmd "MoveCursor($r,$c)"] } proc Newline {} { return [cmd "Newline"] } proc NextWord {} { return [cmd "NextWord"] } proc PA {n} { return [cmd "PA($n)"] } proc PF {n} { return [cmd "PF($n)"] } proc PreviousWord {} { return [cmd "PreviousWord"] } proc Quit {} { exit } proc Reset {} { return [cmd "Reset"] } proc Right {} { return [cmd "Right"] } proc Right2 {} { return [cmd "Right2"] } proc Rows {} { return [lindex [Status] 6] } proc SetFont {font} { return [cmd "SetFont($font)"] } proc Snap {args} { return [cmd "Snap([commafy $args])"] } proc Status {} { variable verbose variable pid if {$pid==0} { return -code error "Not started yet." } if {$verbose} {puts "+(nothing)"} send "\r" expect { "*ok\r\n" { set r $expect_out(buffer) } eof { set pid 0; error "process died" } } return [string range $r 0 [expr [string length $r]-7]] } proc String {text} { return [cmd "String([stringify $text])"] } proc SysReq {} { return [cmd "SysReq"] } proc Tab {} { return [cmd "Tab"] } proc ToggleInsert {} { return [cmd "ToggleInsert"] } proc ToggleReverse {} { return [cmd "ToggleReverse"] } proc TemporaryKeymap {args} { return [cmd "TemporaryKeymap($args)"] } proc Transfer {args} { return [cmd "Transfer([commafy $args])"] } proc Up {} { return [cmd "Up"] } proc Wait {args} { return [cmd "Wait($args)"] } # Extra function to toggle verbosity on the fly. proc Setverbose {level} { variable verbose set verbose $level return } # Export all the user-visible functions. namespace export \[A-Z\]* } # Import all of the exported functions. namespace import x3270::*