summaryrefslogtreecommitdiff
path: root/example/virterm
blob: bab254b4d4d29952b65f439f2aa975a3413a5a38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
#!/bin/sh
# -*- tcl -*-
# The next line is executed by /bin/sh, but not tcl \
exec tclsh "$0" ${1+"$@"}

package require Expect

# Name: virterm - terminal emulator using Expect, v1.0, December, 1994
# Author: Adrian Mariano <adrian@cam.cornell.edu>
#
# Derived from Done Libes' tkterm

# This is a program for interacting with applications that use terminal
# control sequences.  It is a subset of Don Libes' tkterm emulator
# with a compatible interface so that programs can be written to work 
# under both.  
# 
# Internally, it uses arrays instead of the Tk widget.  Nonetheless, this
# code is not as fast as it should be.  I need an Expect profiler to go
# any further.
#
# standout mode is not supported like it is in tkterm.  
# the only terminal widget operation that is supported for the user
# is the "get" operation.  
###############################################
# Variables that must be initialized before using this:
#############################################
set rows 24		;# number of rows in term
set cols 80		;# number of columns in term
set term myterm		;# name of text widget used by term
set termcap 1		;# if your applications use termcap
set terminfo 0		;# if your applications use terminfo
			;# (you can use both, but note that
			;# starting terminfo is slow)
set term_shell $env(SHELL) ;# program to run in term

#############################################
# Readable variables of interest
#############################################
# cur_row		;# current row where insert marker is
# cur_col		;# current col where insert marker is
# term_spawn_id		;# spawn id of term

#############################################
# Procs you may want to initialize before using this:
#############################################

# term_exit is called if the associated proc exits
proc term_exit {} {
	exit
}

# term_chars_changed is called after every change to the displayed chars
# You can use if you want matches to occur in the background (a la bind)
# If you want to test synchronously, then just do so - you don't need to
# redefine this procedure.
proc term_chars_changed {} {
}

# term_cursor_changed is called after the cursor is moved
proc term_cursor_changed {} {
}

# Example tests you can make
#
# Test if cursor is at some specific location
# if {$cur_row == 1 && $cur_col == 0} ...
#
# Test if "foo" exists anywhere in line 4
# if {[string match *foo* [$term get 4.0 4.end]]}
#
# Test if "foo" exists at line 4 col 7
# if {[string match foo* [$term get 4.7 4.end]]}
#
# Return contents of screen
# $term get 1.0 end

#############################################
# End of things of interest
#############################################

set blankline ""
set env(LINES) $rows
set env(COLUMNS) $cols

set env(TERM) "tt"
if {$termcap} {
    set env(TERMCAP) {tt:
	:cm=\E[%d;%dH:
	:up=\E[A:
	:cl=\E[H\E[J:
	:do=^J:
	:so=\E[7m:
	:se=\E[m:
	:nd=\E[C:
    }
}

if {$terminfo} {
    set env(TERMINFO) /tmp
    set ttsrc "/tmp/tt.src"
    set file [open $ttsrc w]

    puts $file {tt|textterm|Don Libes' tk text widget terminal emulator,
	cup=\E[%p1%d;%p2%dH,
	cuu1=\E[A,
	cuf1=\E[C,
	clear=\E[H\E[J,
	ind=\n,
	cr=\r,
	smso=\E[7m,
	rmso=\E[m,
    }
    close $file

    set oldpath $env(PATH)
    set env(PATH) "/usr/5bin:/usr/lib/terminfo"
    if {1==[catch {exec tic $ttsrc} msg]} {
	puts "WARNING: tic failed - if you don't have terminfo support on"
	puts "your system, change \"set terminfo 1\" to \"set terminfo 0\"."
	puts "Here is the original error from running tic:"
	puts $msg
    }
    set env(PATH) $oldpath

    exec rm $ttsrc
}

log_user 0

# start a shell and text widget for its output
set stty_init "-tabs"
eval spawn $term_shell
stty rows $rows columns $cols < $spawn_out(slave,name)
set term_spawn_id $spawn_id

proc term_replace {reprow repcol text} {
  global termdata
  set middle $termdata($reprow) 
  set termdata($reprow) \
     [string range $middle 0 [expr $repcol-1]]$text[string \
       range $middle [expr $repcol+[string length $text]] end]
}


proc parseloc {input row col} {
  upvar $row r $col c
  global rows
  switch -glob -- $input \
    end { set r $rows; set c end } \
    *.* { regexp (.*)\\.(.*) $input dummy r c
           if {$r == "end"} { set r $rows }
        }
}

proc myterm {command first second args} {
  global termdata
  if {[string compare get $command]} { 
    send_error "Unknown terminal command: $command\r"
  } else {
    parseloc $first startrow startcol
    parseloc $second endrow endcol
    if {$endcol != "end"} {incr endcol -1}
    if {$startrow == $endrow} { 
      set data [string range $termdata($startrow) $startcol $endcol]
    } else {
      set data [string range $termdata($startrow) $startcol end]
      for {set i [expr $startrow + 1]} {$i < $endrow} {incr i} {
        append data $termdata($i)
      }
      append data [string range $termdata($endrow) 0 $endcol]
    }
    return $data
  }
}


proc scrollup {} {
  global termdata blankline
  for {set i 1} {$i < $rows} {incr i} {
    set termdata($i) $termdata([expr $i+1]) 
  }
  set termdata($rows) $blankline
}


proc term_init {} {
	global rows cols cur_row cur_col term termdata blankline
        
	# initialize it with blanks to make insertions later more easily
	set blankline [format %*s $cols ""]\n
	for {set i 1} {$i <= $rows} {incr i} {
             set termdata($i) "$blankline"
	}

	set cur_row 1
	set cur_col 0
}


proc term_down {} {
	global cur_row rows cols term

	if {$cur_row < $rows} {
		incr cur_row
	} else {
                scrollup
	}
}


proc term_insert {s} {
	global cols cur_col cur_row term

	set chars_rem_to_write [string length $s]
	set space_rem_on_line [expr $cols - $cur_col]

	##################
	# write first line
	##################

	if {$chars_rem_to_write <= $space_rem_on_line} {
           term_replace $cur_row $cur_col \
              [string range $s 0 [expr $space_rem_on_line-1]]
           incr cur_col $chars_rem_to_write
           term_chars_changed
           return
        }

	set chars_to_write $space_rem_on_line
	set newline 1

        term_replace $cur_row $cur_col\
            [string range $s 0 [expr $space_rem_on_line-1]]

	# discard first line already written
	incr chars_rem_to_write -$chars_to_write
	set s [string range $s $chars_to_write end]
	
	# update cur_col
	incr cur_col $chars_to_write
	# update cur_row
	if {$newline} {
		term_down
	}

	##################
	# write full lines
	##################
	while {$chars_rem_to_write >= $cols} {
                term_replace $cur_row 0 [string range $s 0 [expr $cols-1]]

		# discard line from buffer
		set s [string range $s $cols end]
		incr chars_rem_to_write -$cols

		set cur_col 0
		term_down
	}

	#################
	# write last line
	#################

	if {$chars_rem_to_write} {
                term_replace $cur_row 0 $s
		set cur_col $chars_rem_to_write
	}

	term_chars_changed
}

term_init

expect_before {
	-i $term_spawn_id
	-re "^\[^\x01-\x1f]+" {
		# Text
		term_insert $expect_out(0,string)
		term_cursor_changed
	} "^\r" {
		# (cr,) Go to to beginning of line
		set cur_col 0
		term_cursor_changed
	} "^\n" {
		# (ind,do) Move cursor down one line
		term_down
		term_cursor_changed
	} "^\b" {
		# Backspace nondestructively
		incr cur_col -1
		term_cursor_changed
	} "^\a" {
		# Bell, pass back to user
		send_user "\a"
	} "^\t" {
		# Tab, shouldn't happen
		send_error "got a tab!?"
	} eof {
		term_exit
	} "^\x1b\\\[A" {
		# (cuu1,up) Move cursor up one line
		incr cur_row -1
		term_cursor_changed
	} "^\x1b\\\[C" {
		# (cuf1,nd) Nondestructive space
		incr cur_col
		term_cursor_changed
	} -re "^\x1b\\\[(\[0-9]*);(\[0-9]*)H" {
		# (cup,cm) Move to row y col x
		set cur_row [expr $expect_out(1,string)+1]
		set cur_col $expect_out(2,string)
		term_cursor_changed
	} "^\x1b\\\[H\x1b\\\[J" {
		# (clear,cl) Clear screen
		term_init
		term_cursor_changed
	} "^\x1b\\\[7m" { # unsupported
		# (smso,so) Begin standout mode
		# set term_standout 1
	} "^\x1b\\\[m" {  # unsupported
		# (rmso,se) End standout mode
		# set term_standout 0
	}
}


proc term_expect {args} {
        global cur_row cur_col  # used by expect_background actions

	set desired_timeout [
	    uplevel {
		if {[info exists timeout]} {
			set timeout
		} else {
			uplevel #0 {
				if {[info exists timeout]} {
					set timeout
				} else {
					expr 10
				}
			}
		}
	    }
	]

        set timeout $desired_timeout

        set timeout_act {}

	set argc [llength $args]
	if {$argc%2 == 1} {
		lappend args {}
		incr argc
	}

	for {set i 0} {$i<$argc} {incr i 2} {
		set act_index [expr $i+1]
		if {[string compare timeout [lindex $args $i]] == 0} {
			set timeout_act [lindex $args $act_index]
			set args [lreplace $args $i $act_index]
			incr argc -2
			break
		}
	}

        set got_timeout 0
        
        set start_time [timestamp]

	while {![info exists act]} {
                expect timeout {set got_timeout 1}
                set timeout [expr $desired_timeout - [timestamp] + $start_time]
                if {! $got_timeout} \
                {
			for {set i 0} {$i<$argc} {incr i 2} {
				if {[uplevel [lindex $args $i]]} {
					set act [lindex $args [incr i]]
					break
				}
			}
		} else { set act $timeout_act }

                if {![info exists act]} {

                }
	}

	set code [catch {uplevel $act} string]
	if {$code >  4} {return -code $code $string}
	if {$code == 4} {return -code continue}
	if {$code == 3} {return -code break}
	if {$code == 2} {return -code return}
	if {$code == 1} {return -code error -errorinfo $errorInfo \
				-errorcode $errorCode $string}
	return $string
}	


# ======= end of terminal emulator ========

# The following is a program to interact with the Cornell Library catalog


proc waitfornext {} {
  global cur_row cur_col term
  term_expect {expr {$cur_col==15 && $cur_row == 24 &&
                         " NEXT COMMAND:  " == [$term get 24.0 24.16]}} {}
}

proc sendcommand {command} {
  global cur_col
  exp_send $command
  term_expect {expr {$cur_col == 79}} {}
}

proc removespaces {intext} {
  regsub -all " *\n" $intext \n intext
  regsub "\n+$" $intext \n intext
  return $intext
}

proc output {text} {
  exp_send_user $text
}



proc connect {} {
  global term
  term_expect {regexp {.*[>%]} [$term get 1.0 3.end]}
  exp_send "tn3270 notis.library.cornell.edu\r"
  term_expect {regexp "desk" [$term get 19.0 19.end]} {
                  exp_send "\r"
  	} 
  waitfornext
  exp_send_error "connected.\n\n"
}


proc dosearch {search} {
  global term
  exp_send_error "Searching for '$search'..."
  if {[string match ?=* "$search"]} {set typ ""} else {set typ "k="}
  sendcommand "$typ$search\r"
  waitfornext
  set countstr [$term get 2.17 2.35]
  if {![regsub { Entries Found *} $countstr "" number]} {
    set number 1
    exp_send_error "one entry found.\n\n"
    return 1
  }
  if {$number == 0} {
    exp_send_error "no matches.\n\n"
    return 0
  }
  exp_send_error "$number entries found.\n"
  if {$number > 250} {
    exp_send_error "(only the first 250 can be displayed)\n"
  }
  exp_send_error "\n"
  return $number
}


proc getshort {count} {
  global term
  output [removespaces [$term get 5.0 19.0]]
  while {[regexp "CONTINUED on next page" [$term get 19.0 19.end]]} {
    sendcommand "for\r"
    waitfornext
    output [removespaces [$term get 5.0 19.0]]
  }
}

proc getonecitation {} {
  global term
  output [removespaces [$term get 4.0 19.0]]
  while {[regexp "FORward page" [$term get 20.0 20.end]]} {
    sendcommand "for\r"
    waitfornext
    output [removespaces [$term get 5.0 19.0]]
  }
}


proc getcitlist {} {
  global term
  getonecitation
  set citcount 1
  while {[regexp "NEXt record" [$term get 20.0 21.end]]} {
    sendcommand "nex\r"
    waitfornext
    getonecitation
    incr citcount
    if {$citcount % 10 == 0} {exp_send_error "$citcount.."}
  }
}

proc getlong {count} {
  if {$count != 1} {
    sendcommand "1\r"
    waitfornext
  }
  sendcommand "lon\r"
  waitfornext
  getcitlist
}

proc getmed {count} {
  if {$count != 1} {
    sendcommand "1\r"
    waitfornext
  }
  sendcommand "bri\r"
  waitfornext
  getcitlist
}
  
#################################################################
#
set help {
libsearch version 1.0 by Adrian Mariano (adrian@cam.cornell.edu)

Invocation: libsearch [options] search text

 -i      : interactive
 -s      : short listing
 -l      : long listing
 -o file : output file (default stdout)
 -h      : print out list of options and version number
 -H      : print terse keyword search help

The search will be a keyword search.  
Example:  libsearch -i sound and arabic

}

#################################################################

proc searchhelp {} {
  send_error {
? truncation wildcard            default operator is AND

AND - both words appear in record
OR  - one of the words appears
NOT - first word appears, second words does not
ADJ - words are adjacent
SAME- words appear in the same field (any order)

.su. - subject   b.fmt. - books    eng.lng. - English  
.ti. - title     m.fmt. - music    spa.lng. - Spanish
.au. - author    s.fmt. - serials  fre.lng. - French

.dt. or .dt1. -- limits to a specific publication year.  E.g., 1990.dt.

}
}

proc promptuser {prompt} {
  exp_send_error "$prompt"
  expect_user -re "(.*)\n"
  return "$expect_out(1,string)"
}


set searchtype 1  
set outfile ""
set search ""
set interactive 0

while {[llength $argv]>0} {
  set flag [lindex $argv 0]
  switch -glob -- $flag \
   "-i" { set interactive 1; set argv [lrange $argv 1 end]} \
   "-s" { set searchtype 0; set argv [lrange $argv 1 end] } \
   "-l" { set searchtype 2; set argv [lrange $argv 1 end] } \
   "-o" { set outfile [lindex $argv 1]; set argv [lrange $argv 2 end] } \
   "-H" { searchhelp; exit } \
   "-h" { send_error "$help"; exit } \
   "-*" { send_error "\nUnknown option: $flag\n$help";exit }\
   default { set search [join $argv]; set argv {};}
}  
if { "$search" == "" } {
  send_error "No search specified\n$help"
  exit
}

exp_send_error "Connecting to the library..."

set timeout 200

trap { log_user 1;exp_send "\003";
       expect_before
       expect tn3270 {exp_send "quit\r"}
       expect "Connection closed." {exp_send "exit\r"}
       expect eof ; send_error "\n"; 
       exit} SIGINT


connect

set result [dosearch $search]

if {$interactive} {
  set quit 0
  while {!$quit} {
    if {!$result} {
      switch "[promptuser {(h)elp (n)ewsearch (q)uit? }]" {
        n { }
        h { searchhelp }
        q { set quit 1}
      }
    } else {
   switch "[promptuser {(s)hort (m)ed (l)ong (h)elp (n)ewsearch (q)uit? }]" {
        s { getshort $result; ;}
        l { getlong $result; ;}
        m { getmed $result; ; }
        n { research; }
        h { searchhelp }
        q { set quit 1; }
      }
    }
  }
} else {
  if {$result} {
    switch $searchtype {
      0 { getshort $result} 
      1 { getmed $result  } 
      2 { getlong $result }
    }
  }
}