diff options
Diffstat (limited to 'ipl/procs/getchlib.icn')
-rw-r--r-- | ipl/procs/getchlib.icn | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/ipl/procs/getchlib.icn b/ipl/procs/getchlib.icn new file mode 100644 index 0000000..e4ee2cc --- /dev/null +++ b/ipl/procs/getchlib.icn @@ -0,0 +1,338 @@ +############################################################################ +# +# File: getchlib.icn +# +# Subject: Procedures for getch for UNIX +# +# Author: Richard L. Goerwitz +# +# Date: May 2, 2001 +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 1.14 +# +############################################################################ +# +# Implementing getch() is a much, much more complex affair under UNIX +# than it is under, say, MS-DOS. This library represents one, +# solution to the problem - one which can be run as a library, and +# need not be compiled into the run-time system. Note that it will +# not work on all systems. In particular, certain Suns (with a +# screwy stty command) and the NeXT 1.0 OS (lacking the -g option for +# stty) do not run getchlib properly. See the bugs section below for +# workarounds. +# +# Four basic utilities are included here: +# +# getch() - waits until a keystroke is available & +# returns it without displaying it on the screen +# getche() - same as getch() only with echo +# getse(s) - like getche() only for strings. The optional +# argument s gives getse() something to start with. Use this +# if, say, you want to read single characters in cbreak mode, +# but get more input if the character read is the first part +# of a longer command. If the user backspaces over everything +# that has been input, getse() fails. Returns on \r or \n. +# reset_tty() - absolutely vital routine for putting the cur- +# rent tty line back into cooked mode; call it before exiting +# or you will find yourself with a locked-up terminal; use it +# also if you must temporarily restore the terminal to cooked +# mode +# +# Note that getse() *must* be used in place of read(&input) if you +# are planning on using getch() or getche(), since read(&input) +# assumes a tty with "sane" settings. +# +# Warning: The routines below do not do any sophisticated output +# processing. As noted above, they also put your tty line in raw +# mode. I know, I know: "Raw is overkill - use cbreak." But in +# a world that includes SysV, one must pick a lowest common denomi- +# nator. And no, icanon != cbreak. +# +# BUGS: These routines will not work on systems that do not imple- +# ment the -g option for the stty command. The NeXT workstation is +# an example of such a system. Tisk, tisk. If you are on a BSD +# system where the network configuration makes stty | more impossible, +# then substitute /usr/5bin/stty (or whatever your system calls the +# System V stty command) for /bin/stty in this file. If you have no +# SysV stty command online, then you can try replacing every instance +# of "stty -g 2>&1" below with "stty -g 2>&1 1> /dev/tty" or +# something similar. +# +############################################################################ +# +# Example program: +# +# The following program is a simple file viewer. To run, it +# needs to be linked with itlib.icn, iscreen.icn, and this file +# (getchlib.icn). +# +# procedure main(a) +# +# # Simple pager/file searcher for UNIX systems. Must be linked +# # with itlib.icn and iscreen.icn. +# +# local intext, c, s +# +# # Open input file +# intext := open(a[1],"r") | { +# write(&errout,"Can't open input file.") +# exit(1) +# } +# +# # Initialize screen +# clear() +# print_screen(intext) | exit(0) +# +# # Prompt & read input +# repeat { +# iputs(igoto(getval("cm"), 1, getval("li"))) +# emphasize() +# writes("More? (y/n or /search):") +# write_ce(" ") +# case c := getche() of { +# "y" : print_screen(intext) | break +# " " : print_screen(intext) | break +# "n" : break +# "q" : break +# "/" : { +# iputs(igoto(getval("cm"), 1, getval("li"))) +# emphasize() +# writes("Enter search string:") +# write_ce(" ") +# pattern := GetMoreInput() +# /pattern | "" == pattern & next +# # For more complex patterns, use findre() (IPL findre.icn) +# if not find(pattern, s := !intext) then { +# iputs(igoto(getval("cm"), 1, getval("li"))) +# emphasize() +# write_ce("String not found.") +# break +# } +# else print_screen(intext, s) | break +# } +# } +# } +# +# reset_tty() +# write() +# exit(0) +# +# end +# +# procedure GetMoreInput(c) +# +# local input_string +# static BS +# initial BS := getval("bc") | "\b" +# +# /c := "" +# if any('\n\r', chr := getch()) +# then return c +# else { +# chr == BS & fail +# writes(chr) +# input_string := getse(c || chr) | fail +# if any('\n\r', input_string) +# then fail else (return input_string) +# } +# +# end +# +# procedure print_screen(f,s) +# +# if /s then +# begin := 1 +# # Print top line, if one is supplied +# else { +# iputs(igoto(getval("cm"), 1, 1)) +# write_ce(s ? tab(getval("co") | 0)) +# begin := 2 +# } +# +# # Fill the screen with lines from f; clear and fail on EOF. +# every i := begin to getval("li") - 1 do { +# iputs(igoto(getval("cm"), 1, i)) +# if not write_ce(read(f) ? tab(getval("co") | 0)) then { +# # Clear remaining lines on the screen. +# every j := i to getval("li") do { +# iputs(igoto(getval("cm"), 1, j)) +# iputs(getval("ce")) +# } +# iputs(igoto(getval("cm"), 1, i)) +# fail +# } +# } +# return +# +# end +# +# procedure write_ce(s) +# +# normal() +# iputs(getval("ce")) | +# writes(repl(" ",getval("co") - *s)) +# writes(s) +# return +# +# end +# +############################################################################ +# +# Requires: UNIX +# +############################################################################ +# +# Links: itlib +# +############################################################################ + +link itlib + +global c_cc, current_mode # what mode are we in, raw or cooked? +record termio_struct(vintr,vquit,verase,vkill) + +procedure getse(s) + + # getse() - like getche, only for strings instead of single chars + # + # This procedure *must* be used instead of read(&input) if getch + # and/or getche are to be used, since these put the current tty + # line in raw mode. + # + # Note that the buffer can be initialized by calling getse with a + # string argument. Note also that, as getse now stands, it will + # fail if the user backspaces over everything that has been input. + # This change does not coincide with its behavior in previous ver- + # sions. It can be changed by commenting out the line "if *s < 1 + # then fail" below, and uncommenting the line "if *s < 1 then + # next." + + local chr + static BS + initial { + BS := getval("bc") | "\b" + if not getval("bs") then { + reset_tty() + stop("Your terminal can't backspace!") + } + } + + /s := "" + repeat { + case chr := getch() | fail of { + "\r"|"\n" : return s + c_cc.vkill : { + if *s < 1 then next + every 1 to *s do writes(BS) + s := "" + } + c_cc.verase : { + # if *s < 1 then next + writes(BS) & s := s[1:-1] + if *s < 1 then fail + } + default: writes(chr) & s ||:= chr + } + } + +end + + + +procedure setup_tty() + change_tty_mode("setup") + return +end + + + +procedure reset_tty() + + # Reset (global) mode switch to &null to show we're in cooked mode. + current_mode := &null + change_tty_mode("reset") + return + +end + + + +procedure getch() + + local chr + + # If the global variable current_mode is null, then we have to + # reset the terminal to raw mode. + if /current_mode := 1 then + setup_tty() + + chr := reads(&input) + case chr of { + c_cc.vintr : reset_tty() & stop() # shouldn't hard code this in + c_cc.vquit : reset_tty() & stop() + default : return chr + } + +end + + + +procedure getche() + + local chr + + # If the global variable current_mode is null, then we have to + # reset the terminal to raw mode. + if /current_mode := 1 then + setup_tty() + + chr := reads(&input) + case chr of { + c_cc.vintr : reset_tty() & stop() + c_cc.vquit : reset_tty() & stop() + default : writes(chr) & return chr + } + +end + + + +procedure change_tty_mode(switch) + + # global c_cc (global record containing values for kill, etc. chars) + local get_term_params, i + static reset_string + initial { + getval("li") # check to be sure itlib is set up + find("unix",map(&features)) | + stop("change_tty_mode: These routines must run under UNIX.") + get_term_params := open("/bin/stty -g 2>&1","pr") + reset_string := !get_term_params + close(get_term_params) + reset_string ? { + # tab upto the fifth field of the output of the stty -g cmd + # fields of stty -g seem to be the same as those of the + # termio struct, except that the c_line field is missing + every 1 to 4 do tab(find(":")+1) + c_cc := termio_struct("\x03","\x1C","\x08","\x15") + every i := 1 to 3 do { + c_cc[i] := char(integer("16r"||tab(find(":")))) + move(1) + } + c_cc[i+1] := char(integer("16r"||tab(0))) + } + } + + if switch == "setup" + then system("/bin/stty -echo raw") + else system("/bin/stty "||reset_string) + + return + +end |