#!/usr/local/bin/kermit ; r e m o t e a c c e s s ; ; How to conduct a dialog with a user when Kermit does not have a controlling ; terminal; for example, when Kermit has accepted an incoming Internet ("set ; host *") or modem ("answer") connection, or when a Kermit script is started ; under inetd. This script shows how to handle echoing and rudimentary ; keyboard editing: ; ; . Prints a prompt and waits for a line of text. ; . Ordinary characters echo. ; . Rubout, Delete, or Backspace removes the rightmost character from the line. ; . Ctrl-U removes the current line, if any. ; . Ctrl-R repaints the current line. ; . The prompt is protected. ; ; To make it more interesting, lines are interpreted as commands, validated, ; and executed. ; ; Note that when printing lines to the user's screen, both carriage (\13) ; and linefeed (\10) are included for when there is no terminal driver ; to supply them. In case there IS a terminal driver, the SET INPUT ECHO OFF ; command allows the script to work there too. ; ; F. da Cruz, Columbia University, January 2003 ; First make a command keyword table and then sort it for \tablelook(). ; declare \&k[] = exit:5 quit:5 help:2 ?:2 echo:1 send:3 list:4 directory:4 sort &k ; Make an array of help strings keyed on keyword value. ; dcl \&h[5] .\&h[1] = ECHO [text] echos its arguments. .\&h[2] = HELP prints this message; Synonym: "?". .\&h[3] = SEND filename [as-name] sends the given file under the given name. .\&h[4] = LIST [filespec] lists files; Synonym: DIRECTORY. .\&h[5] = EXIT exits; Synonym: QUIT. ; Define command service routines define DOEXIT { ; Service routine for EXIT command output "\13\10Bye.\13\10" sleep 1 exit } define DOSEND { ; Service routine for SEND command if not defined \%1 { output "Filename required\13\10" end 1 } else if not exist \%1 { output "File not found: "\%1"\13\10" end 1 } send \%1 \%2 } define DOLIST { ; Service routine for LIST command local \&d[] \%i \%f \%m directory /array:&d \%1 ; Get filenames into array &d[] output "\13\10" if not \fdim(&d) { output "(No files match)\13\10" } else { .\%m := 0 ; Quickly get length of longest name for \%i 1 \fdim(&d) 1 { .\%m := \fmax(\%m,\flen(\&d[\%i])) } incr \%m for \%i 1 \fdim(&d) 1 { ; Print listing .\%f := \&d[\%i] if not readable \%f continue output " \frpad(\%f,\%m) \flpad(\fsize(\%f),9) \fdate(\%f)\13\10" } } output "\13\10" } define DOHELP { ; Service routine for HELP command local \%i output "\13\10" output "Commands are:\13\10\13\10" for \%i 1 \fdim(&h) 1 { output " \&h[\%i]\13\10" } output "\13\{10}Commands may be abbreviated.\13\10" output "\13\10" } ; Command parser and dispatcher define DOCOMMAND { if not def \%1 return ; Ignore blank lines void \fsplit(\%1,&a,\32) ; Split line into words .\%k := \ftablelook(\&a[1],&k) ; Look up first word switch \%k { ; Handle lookup error :-1 output "Command not found: "\&a[1]"\13\10" output "Type HELP for help\13\10" continue :-2 output "Ambiguous: "\&a[1]"\13\10" output "Type HELP for help\13\10" continue } switch \fword(\&k[\%k],2,:) { ; Dispatch on keyword value :1 ; ECHO prints its arguments output "\fjoin(&a[2:])\13\10" break :2 ; HELP prints help text dohelp break :3 ; SEND sends a file dosend \&a[2] \&a[3] ; with an optional as-name output "\13\10" break :4 ; LIST prints a file list dolist \&a[2] break :5 ; QUIT and EXIT quit doexit } } set case off ; Commands are case-independent set input echo off ; In case there is a controlling tty set root . ; Restrict users to this directory tree define prompt "Command: " ; Define prompt output "\13\10" ; Start on a new line output "Type HELP for help.\13\10" ; Greet ; Loop to read lines and pass them to the command interpreter. ; This is where we handle echoing and editing. while true { ; Outer loop for lines undef buf ; Line buffer .\%n = 0 ; Line length output "\m(prompt)" ; Prompt set flag off ; Inner loop control while not flag { ; Inner loop for characters input -1 ; Get a character if fail stop 1 INPUT Error ; Make sure we did .\%c := \v(inchar) ; This is the character .\%x := \fcode(\%c) ; This is its code switch \%x { ; switch on its code :3 ; Ctrl-C doexit ; Exit immediately :13 ; Carriage Return :10 ; or Line Feed :12 ; or Form Feed output "\13\10" ; Echo CRLF set flag on ; Set inner-loop exit flag docommand "\m(buf)" ; Process the user's command continue :8 ; Backspace :127 ; or Rubout if not def buf { ; Nothing to delete output "\7" ; just beep continue } output "\8 \8" ; Erase char from user's screen decr \%n ; And from line buffer .buf := \s(buf[1:\%n]) continue :18 ; Ctrl-R output "\13\10" ; Refresh line output "\m(prompt)\m(buf)" continue :21 ; Ctrl-U for \%i 1 \%n 1 { ; Erase line from screen output "\8 \8" } .\%n = 0 undef buf continue :9 ; Ctrl-I (Tab) .\%c := \32 ; Convert to space .\%x := 32 ; and fall thru. :default ; Anything else if > \%x 31 { ; If char is printable output \%c ; echo it .buf := \m(buf)\%c ; add it to line buffer incr \%n ; and count it } else output "\7" ; Beep for nonprintables } } }