goer@ellis.uchicago.edu (Richard L. Goerwitz) (05/29/91)
---- Cut Here and feed the following to sh ---- #!/bin/sh # this is yahtz.02 (part 2 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file iolib.icn continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 2; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping iolib.icn' else echo 'x - continuing file iolib.icn' sed 's/^X//' << 'SHAR_EOF' >> 'iolib.icn' && X# suggesting things like letting drive specifications appear in DOS X# TERMCAP environment variables, and for finding several bugs (e.g. X# the lack of support for %2 and %3 in cm). Although he is loathe X# to accept this credit, I think he deserves it. X# X######################################################################### X# X# Contents: X# X# setname(term) X# Use only if you wish to initialize itermlib for a terminal X# other than what your current environment specifies. "Term" is the X# name of the termcap entry to use. Normally this initialization is X# done automatically, and need not concern the user. X# X# getval(id) X# Works something like tgetnum, tgetflag, and tgetstr. In the X# spirit of Icon, all three have been collapsed into one routine. X# Integer valued caps are returned as integers, strings as strings, X# and flags as records (if a flag is set, then type(flag) will return X# "true"). Absence of a given capability is signalled by procedure X# failure. X# X# igoto(cm,destcol,destline) - NB: default 1 offset (*not* zero)! X# Analogous to tgoto. "Cm" is the cursor movement command for X# the current terminal, as obtained via getval("cm"). Igoto() X# returns a string which, when output via iputs, will cause the X# cursor to move to column "destcol" and line "destline." Column and X# line are always calculated using a *one* offset. This is far more X# Iconish than the normal zero offset used by tgoto. If you want to X# go to the first square on your screen, then include in your program X# "iputs(igoto(getval("cm"),1,1))." X# X# iputs(cp,affcnt) X# Equivalent to tputs. "Cp" is a string obtained via getval(), X# or, in the case of "cm," via igoto(getval("cm"),x,y). Affcnt is a X# count of affected lines. It is completely irrelevant for most X# modern terminals, and is supplied here merely for the sake of X# backward compatibility with itlib, a UNIX-only version of these X# routines (one which handles padding on archaic terminals). X# X########################################################################## X# X# Notes for MS-DOS users: X# X# There are two basic reasons for using the I/O routines X# contained in this package. First, by using a set of generalized X# routines, your code will become much more readable. Secondly, by X# using a high level interface, you can avoid the cardinal X# programming error of hard coding things like screen length and X# escape codes into your programs. X# X# To use this collection of programs, you must do two things. X# First, you must add the line "device=ansi.sys" (or the name of some X# other driver, like zansi.sys, nansi.sys, or nnansi.sys [=new X# nansi.sys]) to your config.sys file. Secondly, you must add two X# lines to your autoexec.bat file: 1) "set TERM=ansi-mono" and 2) X# "set TERMCAP=\location\termcap." The purpose of setting the TERM X# variable is to tell this program what driver you are using. If you X# have a color system, you could use "ansi-color" instead of X# "ansi-mono," although for compatibility with a broader range of X# users, it would perhaps be better to stick with mono. The purpose X# of setting TERMCAP is to make it possible to determine where the X# termcap database file is located. The termcap file (which should X# have been packed with this library as termcap.dos) is a short X# database of all the escape sequences used by the various terminal X# drivers. Set TERMCAP so that it reflects the location of this file X# (which should be renamed as termcap, for the sake of consistency X# across UNIX and MS-DOS spectra). If desired, you can also try X# using termcap2.dos. Certain games work a lot better using this X# alternate file. To try it out, rename it to termcap, and set X# the environment variable TERMCAP to its location. X# X# Although the authors make no pretense of providing here a X# complete introduction to the format of the termcap database file, X# it will be useful, we believe, to explain a few basic facts about X# how to use this program in conjunction with it. If, say, you want X# to clear the screen, add the line, X# X# iputs(getval("cl")) X# X# to your program. The function iputs() outputs screen control X# sequences. Getval retrieves a specific sequence from the termcap X# file. The string "cl" is the symbol used in the termcap file to X# mark the code used to clear the screen. By executing the X# expression "iputs(getval("cl"))," you are 1) looking up the "cl" X# (clear) code in the termcap database entry for your terminal, and X# the 2) outputting that sequence to the screen. X# X# Some other useful termcap symbols are "ce" (clear to end of X# line), "ho" (go to the top left square on the screen), "so" (begin X# standout mode), and "se" (end standout mode). To output a X# boldfaced string, str, to the screen, you would write - X# X# iputs(getval("so")) X# writes(str) X# iputs(getval("se")) X# X# You can also write "writes(getval("so") || str || getval("se")), X# but this would make reimplementation for UNIX terminals that X# require padding rather difficult. X# X# It is also heartily to be recommended that MS-DOS programmers X# try not to assume that everyone will be using a 25-line screen. X# Most terminals are 24-line. Some 43. Some have variable window X# sizes. If you want to put a status line on, say, the 2nd-to-last X# line of the screen, then determine what that line is by executing X# "getval("li")." The termcap database holds not only string-valued X# sequences, but numeric ones as well. The value of "li" tells you X# how many lines the terminal has (compare "co," which will tell you X# how many columns). To go to the beginning of the second-to-last X# line on the screen, type in: X# X# iputs(igoto(getval("cm"), 1, getval("li")-1)) X# X# The "cm" capability is a special capability, and needs to be output X# via igoto(cm,x,y), where cm is the sequence telling your computer X# to move the cursor to a specified spot, x is the column, and y is X# the row. The expression "getval("li")-1" will return the number of X# the second-to-last line on your screen. X# X########################################################################## X# X# Requires: UNIX or MS-DOS, co-expressions X# X# See also: itlib.icn, iscreen.icn X# X########################################################################## X X Xglobal tc_table, isDOS Xrecord true() X X Xprocedure check_features() X X initial { X X if find("UNIX",&features) then X isDOS := &null X else if find("MS-DOS", &features) then X isDOS := 1 X else stop("check_features: OS not (yet?) supported.") X X find("expressi",&features) | X er("check_features","co-expressions not implemented - &$#!",1) X } X X return X Xend X X X Xprocedure setname(name) X X # Sets current terminal type to "name" and builds a new termcap X # capability database (residing in tc_table). Fails if unable to X # find a termcap entry for terminal type "name." If you want it X # to terminate with an error message under these circumstances, X # comment out "| fail" below, and uncomment the er() line. X X #tc_table is global X X check_features() X X tc_table := table() X tc_table := maketc_table(getentry(name)) | fail X # er("setname","no termcap entry found for "||name,3) X return "successfully reset for terminal " || name X Xend X X X Xprocedure getname() X X # Getname() first checks to be sure we're running under DOS or X # UNIX, and, if so, tries to figure out what the current terminal X # type is, checking successively the value of the environment X # variable TERM, and then (under UNIX) the output of "tset -". X # Terminates with an error message if the terminal type cannot be X # ascertained. DOS defaults to "mono." X X local term, tset_output X X check_features() X X if \isDOS then { X term := getenv("TERM") | "mono" X } X else { X if not (term := getenv("TERM")) then { X tset_output := open("/bin/tset -","pr") | X er("getname","can't find tset command",1) X term := !tset_output X close(tset_output) X } X } X X return \term | X er("getname","can't seem to determine your terminal type",1) X Xend X X X Xprocedure er(func,msg,errnum) X X # short error processing utility X write(&errout,func,": ",msg) X exit(errnum) X Xend X X X Xprocedure getentry(name, termcap_string) X X # "Name" designates the current terminal type. Getentry() scans X # the current environment for the variable TERMCAP. If the X # TERMCAP string represents a termcap entry for a terminal of type X # "name," then getentry() returns the TERMCAP string. Otherwise, X # getentry() will check to see if TERMCAP is a file name. If so, X # getentry() will scan that file for an entry corresponding to X # "name." If the TERMCAP string does not designate a filename, X # getentry() will scan the termcap file for the correct entry. X # Whatever the input file, if an entry for terminal "name" is X # found, getentry() returns that entry. Otherwise, getentry() X # fails. X X local isFILE, f, getline, line, nm, ent1, ent2 X static slash, termcap_names X initial { X if \isDOS then { X slash := "\\" X termcap_names := ["termcap","termcap.dos","termcap2.dos"] X } X else { X slash := "/" X termcap_names := ["/etc/termcap"] X } X } X X X # You can force getentry() to use a specific termcap file by cal- X # ling it with a second argument - the name of the termcap file X # to use instead of the regular one, or the one specified in the X # termcap environment variable. X /termcap_string := getenv("TERMCAP") X X if \isDOS then { X if \termcap_string then { X if termcap_string ? ( X not ((tab(any(&letters)), match(":")) | match(slash)), X pos(1) | tab(find("|")+1), =name) X then return termcap_string X else isFILE := 1 X } X } X else { X if \termcap_string then { X if termcap_string ? ( X not match(slash), pos(1) | tab(find("|")+1), =name) X then return termcap_string X else isFILE := 1 X } X } X X # The logic here probably isn't clear. The idea is to try to use X # the termcap environment variable successively as 1) a termcap en- X # try and then 2) as a termcap file. If neither works, 3) go to X # the /etc/termcap file. The else clause here does 2 and, if ne- X # cessary, 3. The "\termcap_string ? (not match..." expression X # handles 1. X X if \isFILE # if find(slash, \termcap_string) X then f := open(termcap_string) X /f := open(!termcap_names) | X er("getentry","I can't access your termcap file. Read iolib.icn.",1) X X getline := create read_file(f) X X while line := @getline do { X if line ? (pos(1) | tab(find("|")+1), =name, any(':|')) then { X entry := "" X while (\line | @getline) ? { X if entry ||:= 1(tab(find(":")+1), pos(0)) X then { X close(f) X # if entry ends in tc= then add in the named tc entry X entry ?:= tab(find("tc=")) || X # recursively fetch the new termcap entry X (move(3), getentry(tab(find(":"))) ? X # remove the name field from the new entry X (tab(find(":")+1), tab(0))) X return entry X } X else { X \line := &null # must precede the next line X entry ||:= trim(trim(tab(0),'\\'),':') X } X } X } X } X X close(f) X er("getentry","can't find and/or process your termcap entry",3) X Xend X X X Xprocedure read_file(f) X X # Suspends all non #-initial lines in the file f. X # Removes leading tabs and spaces from lines before suspending X # them. X X local line X X \f | er("read_tcap_file","no valid termcap file found",3) X while line := read(f) do { X match("#",line) & next X line ?:= (tab(many('\t ')) | &null, tab(0)) X suspend line X } X X fail X Xend X X X Xprocedure maketc_table(entry) X X # Maketc_table(s) (where s is a valid termcap entry for some X # terminal-type): Returns a table in which the keys are termcap X # capability designators, and the values are the entries in X # "entry" for those designators. X X local k, v X X /entry & er("maketc_table","no entry given",8) X if entry[-1] ~== ":" then entry ||:= ":" X X /tc_table := table() X X entry ? { X X tab(find(":")+1) # tab past initial (name) field X X while tab((find(":")+1) \ 1) ? { X &subject == "" & next X if k := 1(move(2), ="=") then { X # Get rid of null padding information. Iolib can't X # handle it (unlike itlib.icn). Leave star in. It X # indicates a real dinosaur terminal, and will later X # prompt an abort. X str := ="*" | ""; tab(many(&digits)) X tc_table[k] := Decode(str || tab(find(":"))) X } X else if k := 1(move(2), ="#") X then tc_table[k] := integer(tab(find(":"))) X else if k := 1(tab(find(":")), pos(-1)) X then tc_table[k] := true() X else er("maketc_table", "your termcap file has a bad entry",3) X } X } X X return tc_table X Xend X X X Xprocedure getval(id) X X /tc_table := maketc_table(getentry(getname())) | X er("getval","can't make a table for your terminal",4) X X return \tc_table[id] | fail X # er("getval","the current terminal doesn't support "||id,7) X Xend X X X Xprocedure Decode(s) X X # Does things like turn ^ plus a letter into a genuine control X # character. X X new_s := "" X X s ? { X X while new_s ||:= tab(upto('\\^')) do { X chr := move(1) X if chr == "\\" then { X new_s ||:= { X case chr2 := move(1) of { X "\\" : "\\" X "^" : "^" X "E" : "\e" X "b" : "\b" X "f" : "\f" X "n" : "\n" X "r" : "\r" X "t" : "\t" X default : { X if any(&digits,chr2) then { X char(integer("8r"||chr2||move(2 to 0 by -1))) | X er("Decode","bad termcap entry",3) X } X else chr2 X } X } X } X } X else new_s ||:= char(ord(map(move(1),&lcase,&ucase)) - 64) X } X new_s ||:= tab(0) X } X X return new_s X Xend X X X Xprocedure igoto(cm,col,line) X X local colline, range, increment, padding, str, outstr, chr, x, y X X if col > (tc_table["co"]) | line > (tc_table["li"]) then { X colline := string(\col) || "," || string(\line) | string(\col|line) X range := "(" || tc_table["co"]-1 || "," || tc_table["li"]-1 || ")" X er("igoto",colline || " out of range " || (\range|""),9) X } X X # Use the Iconish 1;1 upper left corner & not the C-ish 0 offsets X increment := -1 X outstr := "" X X cm ? { X while outstr ||:= tab(find("%")) do { X tab(match("%")) X if padding := integer(tab(any('23'))) X then chr := (="d" | "d") X else chr := move(1) X if case \chr of { X "." : outstr ||:= char(line + increment) X "+" : outstr ||:= char(line + ord(move(1)) + increment) X "d" : { X str := string(line + increment) X outstr ||:= right(str, \padding, "0") | str X } X } X then line :=: col X else { X case chr of { X "n" : line := ixor(line,96) & col := ixor(col,96) X "i" : increment := 0 X "r" : line :=: col X "%" : outstr ||:= "%" X "B" : line := ior(ishift(line / 10, 4), line % 10) X ">" : { X x := move(1); y := move(1) X line > ord(x) & line +:= ord(y) X &null X } X } | er("goto","bad termcap entry",5) X } X } X return outstr || tab(0) X } X Xend X X X Xprocedure iputs(cp, affcnt) X X # Writes cp to the screen. Use this instead of writes() for X # compatibility with itlib (a UNIX-only version which can handle X # albeit inelegantly) terminals that need padding. X X static num_chars X initial num_chars := &digits ++ '.' X X type(cp) == "string" | X er("iputs","you can't iputs() a non-string value!",10) X X cp ? { X if tab(many(num_chars)) & ="*" then X stop("iputs: iolib can't use terminals that require padding.") X writes(tab(0)) X } X X return X Xend SHAR_EOF echo 'File iolib.icn is complete' && true || echo 'restore of iolib.icn failed' rm -f _shar_wnt_.tmp fi # ============= termcap.dos ============== if test -f 'termcap.dos' -a X"$1" != X"-c"; then echo 'x - skipping termcap.dos (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting termcap.dos (Text)' sed 's/^X//' << 'SHAR_EOF' > 'termcap.dos' && Xansi|color|ansi-color|ibm|ibmpc|ANSI.SYS color:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[H\E[2J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :ti=\E[0;44m:te=\E[0m:\ X :so=\E[1;35;44m:se=\E[0;44m:\ X :us=\E[1;31;44m:ue=\E[0;44m:\ X :mb=\E[5m:md=\E[1m:me=\E[0;44m: Xmono|ansi-mono|ANSI.SYS:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[H\E[2J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\ X :mb=\E[5m:md=\E[1m:me=\E[m: Xnnansi-mono|NNANSI.SYS:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[2J:cd=\E[J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :so=\E[7m:se=\E[2m:\ X :us=\E[4m:ue=\E[24m:\ X :mb=\E[5m:md=\E[1m:mh=\E[2m:mr=\E[7m:me=\E[m:\ X :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: Xnnansi|nnansi-color|NNANSI.SYS color:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[2J:cd=\E[J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :ti=\E[0;44m:te=\E[0m:\ X :so=\E[1;35;44m:se=\E[2;37m:\ X :us=\E[4m:ue=\E[24m:\ X :mb=\E[5m:md=\E[1m:mh=\E[2m:mr=\E[7m:me=\E[0;44m:\ X :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: Xnansi-mono|zansi-mono|N/ZANSI.SYS:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[2J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :ti=\E[0m:te=\E[0m:\ X :so=\E[7;35m:se=\E[0m:\ X :us=\E[1;31m:ue=\E[0m:\ X :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[m:\ X :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: Xnansi|zansi|nansi-color|zansi-color|N/ZANSI.SYS color:\ X :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ X :cl=\E[2J:ce=\E[K:\ X :ho=\E[H:cm=\E[%i%d;%dH:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :ti=\E[0;44m:te=\E[0m:\ X :so=\E[1;35;44m:se=\E[0;44m:\ X :us=\E[1;31;44m:ue=\E[0;44m:\ X :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[0;44m:\ X :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: XAX|ANSI X3.64|full ANSI X3.64 (1977) standard:\ X :co#80:li#25:bs:pt:am:mi:bl=^G:le=^H:\ X :cl=\E[2J:ce=\E[K:cd=\E[J:\ X :ho=\E[H:cm=\E[%i%d;%dH:cs=\E[%i%d;%dr:\ X :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ X :UP=\E[%dA:DO=\E[%dB:LE=\E[%dC:RI=\E[%dD:\ X :so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\ X :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[m:as=^N:ae=^O:\ X :ku=\E[A:kd=\E[B:kl=\E[C:kr=\E[D:kb=^H:\ X :kn#4:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:\ X :im=\E[4h:ei=\E[4l:al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P:sf=\ED:sr=\EM: SHAR_EOF true || echo 'restore of termcap.dos failed' rm -f _shar_wnt_.tmp fi # ============= Makefile.dist ============== if test -f 'Makefile.dist' -a X"$1" != X"-c"; then echo 'x - skipping Makefile.dist (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting Makefile.dist (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile.dist' && X# Don't change this unless you're sure of what you're doing. SHAR_EOF true || echo 'restore of Makefile.dist failed' fi echo 'End of part 2' echo 'File Makefile.dist is continued in part 3' echo 3 > _shar_seq_.tmp exit 0 -- -Richard L. Goerwitz goer%sophist@uchicago.bitnet goer@sophist.uchicago.edu rutgers!oddjob!gide!sophist!goer