system@asuvax.UUCP (Marc Lesure) (09/19/85)
Due to overwhelming demand for the 4.2 Window Management System, I've decided to post it to net.sources. The wms package is being sent in four parts. If your site decides on implementing wms, please send mail to the author so he can send updates (if any) in the future. Also, please send all bug reports, problems, fixes, etc. to the author rather than posting them to net.sources.bugs. This will allow him to maintain the system better. ------------------------------------------------- Marc Lesure System Manager Engineering Computer Center Arizona State University Tempe, Arizona UUCP: ...!{ucbvax,ihnp4}!arizona!asuvax!lesure ...!ihnp4!terak!asuvax!lesure CSNET: lesure@asu ARPA: lesure%asu@csnet-relay
system@asuvax.UUCP (Marc Lesure) (09/19/85)
wms part 1 of 4 If your site decides on implementing wms, please send mail to the author so he can send updates (if any) in the future. Also, please send all bug reports, problems, fixes, etc. to the author rather than posting them to net.sources.bugs. Marc Lesure System Manager Engineering Computer Center Arizona State University Tempe, Arizona UUCP: ...!{ucbvax,ihnp4}!arizona!asuvax!lesure ...!ihnp4!terak!asuvax!lesure CSNET: lesure@asu ARPA: lesure%asu@csnet-relay --------------------------------<cut here>---------------------------- # This is a shell archive. Remove all lines before this one. # Use 'sh <this file>' to unpack the contents. # # contents: # release # release/README # release/blurb # release/makefile # release/man7 # release/mant # release/mant/DS # release/mant/msh.t # release/mant/wms.t # release/mant/wty.t # echo x - release mkdir release echo x - release/README sed 's/^@@//' > "release/README" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ The window management system tape release consists of these files: README - this file. blurb - ... makefile - construct and install the system. man7 - directory for installed manuals. man7/msh.7 - installed manual for msh. man7/wms.7 - installed manual for wms. man7/wty.7 - installed manual for wty. mant - directory for uninstalled manuals. mant/DS - nroff string definitions. mant/msh.t - uninstalled manual for msh. mant/wms.t - uninstalled manual for wms. mant/wty.t - uninstalled manual for wty. public - public directory. public/msh - executable msh. public/termcap - sample terminal descriptions. public/wms - public wms directory. public/wms/max.msh.c - msh configuration parameters source variables. public/wms/max.msh.h - msh configuration parameters absolute maximum values. public/wms/max.msh.o - msh configuration parameters object code. public/wms/max.wty.c - wty configuration parameters source variables. public/wms/max.wty.h - wty configuration parameters absolute maximum values. public/wms/max.wty.o - wty configuration parameters object code. public/wms/msh.bind.o - msh main program binder object code. public/wms/msh.h - msh control codes. public/wms/msh.o - msh object code. public/wms/wty.bind.o - wty main program binder object code. public/wms/wty.o - wty object code. public/wty - executable wty. source - primary source code directory. source/NOTES - source code notes. source/msh - msh source directory. source/msh/max.msh.h - @ -> ../../public/wms/max.msh.h. source/msh/msh.bind.c - msh main program binder source. source/msh/msh.c - msh source. source/msh/msh.h - @ -> ../../public/wms/msh.h. source/msh/util.h - @ -> ../util.h. source/msh/relink - script to aid source code symbolic linking source/util.h - simple utilities. source/wty - wty source directory. source/wty/max.msh.h - @ -> ../../public/wms/max.msh.h. source/wty/max.wty.h - @ -> ../../public/wms/max.wty.h. source/wty/msh.h - @ -> ../../public/wms/msh.h. source/wty/util.h - @ -> ../util.h. source/wty/wty.bind.c - wty main program binder source. source/wty/wty.c - wty source. source/wty/wty.h - wty virtual terminal prototype codes. NOTES: The installation procedure is as follows: Since the default target directorys have been defined as subdirectorys of the current directory (this release directory), when you type 'make' nothing will be made. Everything is already installed. If you wish to install the files elsewhere, then you need to edit the 'makefile' and change the target directory macros as described in that file. After installing the manuals, it is a good idea to do a "man" on them. The wty manual is a good start. The shar version does not include object files or executables. Thus, typing 'make' will indeed make some files. Lastly, suitable access permissions may have to be set - try: chmod a+r <all files> chmod go-w <all files> chmod a+x <all executables, directorys> Please relay bugs, fixes, improvements, etc. back to ASU for future updates. George Nelan Engineering Research Center (ERC 207) Arizona State University Tempe, Arizona, 85287 (602)-965-2791 UUCP: ...{ucbvax,ihnp4}!arizona!asuvax!nelan ...ihnp4!noao!terak!asuvax!nelan CSNET: nelan@asu @@ END-OF-FILE echo x - release/blurb sed 's/^@@//' > "release/blurb" << '@@ END-OF-FILE' ----------------------------------------------------------------------------- WINDOW MANAGEMENT SYSTEM A window management system for 4.2bsd systems is being released by George Nelan at Arizona State University. The system, WMS, is in the public domain and consists of : - MSH, an executable meta-shell manager which resides on the host, manages sub-shells (csh or user) via ptys, and coordinates communications with an external window manager by multiplexing standard I/O; - WTY, a host-resident executable window manager built upon MSH, utilizing the TERMCAP database so that most programs can run in windows (vi,..); - TOOLS, a set of code fragments to assist the do-it-yourself types in developing their own [remote] window managers (PC,..) for linkage to MSH; - DOCUMENTATION, a complete set of user manuals in the form of three 'man' entries; and last, but not least, - SOURCE CODE. A brief description of WTY follows: WTY emulates a virtual windowing terminal on a variety of devices (vt100,..). The underlying actual device must support controllable scrolling regions and cursor motion as described by a [user-defined] TERMCAP entry. Windows are defined as multiplexed scrolling regions; all windows span the width of the display. Each window is truly scrollable (without redraws) with all visible windows the same size. WTY multiplexes sub-shell output into windows in a way that allows apparent concurrency. A 'select current input window' command may be used to direct user input to any visible window. Other commands allow window creation/deletion, opening/folding (making visible/invisible), labeling (windows are not bordered since context usually provides sufficient implicit discrimination clues - labels explicitly delimit borders), and buffer manipulation: create/delete (I/O recording), open/fold (enable/disable recording), and read selection (take a buffer as user input). Window size information is communicated to sub-shells by a wty-generated "setenv TERM <X>" message, where <X> identifies a virtual terminal related to the window size. A resize command allows retransmission of this message at any time. A 'nice' option on all commands which affect window size allows quiet dynamic manipulation of windows (no automatic "setenv" messages are generated). A help command provides a summary and the command character prefix may be user- defined. The system has been operational since June 1985 and seems to be fairly robust and relatively efficient. We will provide a copy of the system in 1600 BPI TAR format if you send us: - A mag tape (the system release is ~ 256KB). - Return postage or a check payable for same made out to: "A.S.U. Engineering Computer Center" Or indicate shipping collect. Shipping will be via UPS unless you specify otherwise. Upon special request, a shar version is available via UUCP. As this file is 123KB long (~20 min @ 1200B), we prefer tape transfers. Contact: George Nelan Engineering Research Center (ERC 207) Arizona State University Tempe, Arizona, 85287 (602)-965-2791 UUCP: ...{ucbvax,ihnp4}!arizona!asuvax!nelan ...ihnp4!noao!terak!asuvax!nelan CSNET: nelan@asu @@ END-OF-FILE echo x - release/makefile sed 's/^@@//' > "release/makefile" << '@@ END-OF-FILE' ############################################################################## # # The default target directorys are subdirectorys of the current directory. # You may wish to redefine the make macros something like: # # where the 'man' entries go: # L = /usr/man/man7 # # the 'man' # (as in L above): # M = 7 # # where msh, termcap, wms, and wty go: # P = /usr/public # # defaults: L = ./man7 M = 7 P = ./public # ############################################################################## # # # locals only (do not change): p = ./public s = ./source v = 1.7a # version # # all:: @csh -c 'if (-e $@) rm $@' # all:: $P/wms $L/msh.$M $L/wms.$M $L/wty.$M $P/msh $P/wty @csh -c 'if (-e $@) rm $@' @echo done # $P/wms: csh -c 'if (!(-e $@)) mkdir $@' # $L/msh.$M: mant/msh.t mant/DS cat mant/DS mant/msh.t > $@ # $L/wms.$M: mant/wms.t mant/DS cat mant/DS mant/wms.t > $@ # $L/wty.$M: mant/wty.t mant/DS cat mant/DS mant/wty.t > $@ # mant/DS: makefile echo '.ds V' $v > $@ echo '.ds P' $P >> $@ echo '.ds M' $M >> $@ # $P/msh: $P/wms/msh.bind.o $P/wms/msh.o $P/wms/max.msh.o cc -O $P/wms/msh.bind.o $P/wms/msh.o $P/wms/max.msh.o \ -o $@ strip $@ # $P/wms/msh.bind.o: $s/msh/msh.bind.c cc -O -c $s/msh/msh.bind.c mv msh.bind.o $@ # $P/wms/msh.o: $s/msh/msh.c $s/util.h $P/wms/msh.h $P/wms/max.msh.h cc -O -c $s/msh/msh.c mv msh.o $@ # $P/wms/max.msh.o: $P/wms/max.msh.c cc -O -c $P/wms/max.msh.c mv max.msh.o $@ # $P/wms/max.msh.c: $p/wms/max.msh.c cp $p/wms/max.msh.c $@ # $P/wms/max.msh.h: $p/wms/max.msh.h cp $p/wms/max.msh.h $@ # $P/wms/msh.h: $p/wms/msh.h cp $p/wms/msh.h $@ # $P/wty: $P/wms/wty.bind.o $P/wms/wty.o $P/wms/max.wty.o \ $P/wms/msh.o cc -O $P/wms/wty.bind.o $P/wms/wty.o $P/wms/max.wty.o \ $P/wms/msh.o -ltermcap -o $@ strip $@ # $P/wms/wty.bind.o: $s/wty/wty.bind.c $P/wms/max.wty.h cc -O -c $s/wty/wty.bind.c mv wty.bind.o $@ # $P/wms/wty.o: $s/wty/wty.c $s/util.h $s/wty/wty.h $P/wms/msh.h \ $P/wms/max.wty.h cc -O -c $s/wty/wty.c mv wty.o $@ # # $P/wms/max.wty.o: $P/wms/max.wty.c $P/wms/max.msh.c cc -O -c $P/wms/max.wty.c mv max.wty.o $@ # $P/wms/max.wty.c: $p/wms/max.wty.c cp $p/wms/max.wty.c $@ # $P/wms/max.wty.h: $p/wms/max.wty.h cp $p/wms/max.wty.h $@ @@ END-OF-FILE echo x - release/man7 mkdir release/man7 echo x - release/mant mkdir release/mant echo x - release/mant/DS sed 's/^@@//' > "release/mant/DS" << '@@ END-OF-FILE' .ds V 1.7a .ds P ./public .ds M 7 @@ END-OF-FILE echo x - release/mant/msh.t sed 's/^@@//' > "release/mant/msh.t" << '@@ END-OF-FILE' .TH msh \*M "1 July 1985" .SH NAME msh \- meta-shell manager (version \*V) .SH SYNOPSIS .B "\*P/msh" .SH DESCRIPTION .ad b .PP .I Msh is a stand-alone meta-shell manager intended for use with a remote window manager (such as a PC-resident program). It coordinates activities between independent host sub-shells and a remote windowing terminal. .PP .I Msh communicates with an external window manager over standard input and output. Input to .I msh (output from the window manager) includes normal user-input text along with commands generated by the window manager (in response to user actions). These commands may cause .I msh to create a new shell (for a window), delete a shell, select another shell for input, or terminate execution. All user-input text is directed towards the shell currently selected for input. .PP Output from .I msh (input to the window manager) consists of multiplexed outputs from sub-shells. The only command in this direction is a "change output window" sequence which preceeds and identifies each sub-shell's output buffer packet. .SH INTERFACE PROTOCOL The .I msh interface protocol is defined by the (extended BNF) syntax: .nf <msh_input> -> { <input_cmd> | TEXT } <input_cmd> -> <csi> <quit> | <csi> <set_input> | <csi> <create_csh> | <csi> <create_usr> | <csi> <resize> | <csi> <delete> | <csi> <stop> <csi> -> "\\0@" <quit> -> "A" <set_input> -> "B" <n> <create_csh> -> "C" <n> <tc> <create_usr> -> "D" <n> <tc> <sh> <resize> -> "E" <tc> <delete> -> "F" <n> <stop> -> "G" <f> <n> -> "1" | "2" | ... | "9" <tc> -> { ANY } ";" <sh> -> { ANY } "\\n" <f> -> "0" | "1" .fi .nf <msh_output> -> { <output_cmd> | TEXT } <output_cmd> -> "\\027" <n> .fi .PP Where TEXT is a non-ambiguous sequence of arbitrary characters, ANY is any non-ambiguous character, and "" delimits literal strings ("\\0" => null, "\\n" => newline, "\\027" => <CTRL-W>). The (input) command sequence introducer is <csi>. Shell (window) labels are given by <n>, and terminal identification code strings are given by <tc>. .PP The <quit> command terminates execution of .I "msh." .PP The <set_input> command changes the current input shell to that labeled by <n>. All succeeding input is directed towards that shell, until another <set_input> command. .PP The <create_csh> command creates a new c-shell (labeled by <n>) and sends a <resize> string of the form .nf setenv TERM <tc> .fi to the shell. .PP The <create_usr> command creates a new user-defined shell given by <sh>. .PP The <resize> command sends a string of the form .nf setenv TERM <tc> .fi to the current input shell. This command is useful for cases where a shell could not previously receive a <tc> string due to being in an improper state (e.g. in an editor). .PP The <delete> command deletes the current input shell and causes the shell labeled by <n> to become the new current input shell. .PP The <stop> command suspends execution and returns control to the parent shell of .I "msh." The <f> flag defines the tty state after suspension. With <f> = "0", the tty is reset to the state of the parent of .I "msh;" with <f> = "1", the state is left as is. This flag is useful as a debugging aid. .PP The single <output_cmd> is the packet header for each shell's output buffer. This command is indivisible (always two characters), but no such assumption can be made concerning indivisibility of buffer packets (the TEXT). This has ramifications for window managers which interpret virtual terminal command sequences. Shell output buffers are multiplexed on a prioritized polling basis so that echo response time is minimized while maintaining a high degree of output concurrency. .SH TTY INTERFACE .PP When .I msh begins, with one exception, the initial tty state is passed on to all sub-shells which are created. The exception is that "stty -tabs" is explicitly set for the sub-shells; this is because .I msh assumes that the remote window manager ignores incoming tabs from sub-shells. After the initial state is picked up, .I msh resets several tty modes ala "stty new cbreak -echo -tabs nl". All special characters are un-defined (i.e. stty <c> u) except for the flow control characters (^S ^Q usually). This is more-or-less equivalent to "stty raw" but allows automatic flow-control and delay-processing (heh-heh). .SH NOTES .PP .I Msh assumes that all input commands are syntactically and semantically correct \- it is the responsibililty of the remote window manager to assure this is so. Also, explicit initialization commands are required. For each initial shell, .I msh expects to receive a <create_csh> (or <create_usr>) command for each shell. .SH FILES .B "\*P/wms/*" .SH SEE ALSO .B "wty(\*M)" .br .B "wms(\*M)" .SH BUGS .PP It is possible that sub\-processes may still exist after .I msh is terminated. Usually this occurs if backround jobs are left running in a sub\-shell after quiting .I "msh." One should then do a ps command to check on stray processes. .PP The <break> key does not work. It is best to rely upon your <kill> character if you regularly use <break> as a <kill> alias. .PP The <start> and <stop> characters affect flow-control globally \- there is no way to get local (per shell) flow control. .PP If .I msh is executed for too long at a time, the operating system will renice it. As this will slow things down quite a bit, it is advisable to quit .I msh then restart it again. .SH AUTHOR George Nelan @@ END-OF-FILE echo x - release/mant/wms.t sed 's/^@@//' > "release/mant/wms.t" << '@@ END-OF-FILE' .TH wms \*M "1 July 1985" .SH NAME wms \- guide to the window management system (version \*V) .SH SYNOPSIS .B "\*P/wms/*" .SH DESCRIPTION .ad b .PP .I Wms consists of two executable programs, .I msh and .I "wty;" plus several code fragments which, when tied together in the correct manner, can provide the basis for "do-it-yourself" window managers. .PP .I Msh is a meta-shell manager that coordinates communication activities between sub-shells on the host and a window manager by means of a bi-directional communication-stream. The window manager may either reside on the host itself, acting as a virtual windowing terminal emulator, or it may reside on a remote machine (such as a PC). The code supplied with the system enables either configuration in a relatively simple and efficient way. .PP .I Wty is a host-resident window manager which has been integrated with .I msh in a virtual windowing terminal configuration. .I Wty utilizes the .B TERMCAP database so that many different kinds of terminals can potentially support the windowing emulation. Both .I msh and .I wty are documented as separate .I man entrys. .SH CODE FRAGMENTS .PP The following files are provided in \*P/wms/ as system code fragments: .nf max.msh.c max.msh.h max.msh.o max.wty.c max.wty.h max.wty.o msh.bind.o msh.h msh.o wty.bind.o wty.o .fi .PP The files "max.xxx.x" provide a mechanism for tuning variable parameters for .I msh and .I "wty." The ".h" files supply the absolute maximum values for the parameters, the ".c" files supply the corresponding variable definitions, and the ".o" files are the compiler outputs for the ".c" files. .PP The "msh.h" file provides the 'defines' required for command input/output by .I "msh." The file "msh.bind.o" is the object code binder for .I "msh." .I Msh is the result of linking "msh.o", "max.msh.o" and "msh.bind.o". .PP The file "wty.bind.o" is the object code binder for .I "wty." .I Wty is the result of linking "msh.o", "wty.o", "max.wty.o" and "wty.bind.o". .PP The following sections describe how to utilize the code fragments for the purposes of tuning or for the construction of customized window managers. .SH TUNING MSH .PP The following parameters, found in "max.msh.c", can be adjusted as desired: .nf MAXSLAVES MAXNOPOLLS MAXSHELL MAXTCODE MAXCBUF .fi .PP MAXSLAVES indicates the maximum number of sub-shells allowed to co-exist. .PP MAXNOPOLLS determines the maximum number of sub-shell output polls bypassed. Larger values will cause sub-shells which have been recently quiescent to have their first output burst delayed longer. After this initial delay, however, succeeding outputs will not be delayed. This effect serves to minimize excessive polls on sub-shells, thus providing a measure of echo response time optimization. This parameter does not apply to the current input shell when an echo is pending. .PP MAXSHELL defines an upper limit on the size of shell name strings which may be passed to .I msh via a <create_usr> command (q.v.). .PP MAXTCODE limits the size of terminal code strings which may be passed to .I msh via a <create_csh>, <create_usr>, or <resize> command (q.v.). .PP MAXCBUF is the maximum size of I/O character buffers in .I "msh." .PP Note: the default values for all parameters have been carefully chosen to provide near optimal performance. Also, recall that "max.msh.h" indicates the absolute maximum values for these parameters. .PP The procedure for retuning .I msh then consists of the following steps: .nf edit: max.msh.c compile: cc -c max.msh.c link: cc msh.o max.msh.o msh.bind.o -o msh .fi .SH TUNING WTY .PP The following parameters, found in "max.wty.c", can be adjusted as desired: .nf MAXWINDOWS MAXLABEL MAXQBUF .fi .PP MAXWINDOWS must be equal to MAXSLAVES in "max.msh.c". Thus, to increase the number of windows in .I "wty," MAXSLAVES and MAXWINDOWS must both be modified. .PP MAXLABEL must be the character equivalent of MAXWINDOWS. .PP MAXQBUF defines the size of I/O queues in .I "wty." Because of implementation considerations, this value should be set to about MAXCBUF << 2. Failure to adhere to this restriction may cause a run-time queue overflow in .I "wty" resulting in execution termination. .PP Note: the default values for all parameters have been carefully chosen to provide near optimal performance. Also, recall that "max.wty.h" indicates the absolute maximum values for these parameters. .PP The procedure for retuning .I wty then consists of the following steps: .nf edit: max.wty.c edit: max.msh.c (if necessary) compile: cc -c max.wty.c link: cc msh.o wty.o max.wty.o wty.bind.o \\ -ltermcap -o wty .fi .SH CONSTRUCTING A CUSTOM DISCREET WINDOW MANAGER .PP A remote window manager residing on (say) a PC may be interfaced quite simply with the host system. .I "Msh" provides all of the necessary work to coordinate and multiplex shell activity. Since .I msh communicates over standard input/output with the window manager, all that is necessary on the remote end is to initiate contact with The host, run .I msh on the host; then run the remote window manager. .PP This method of total window management is probably the most efficent and powerful way to implement the system. Advantages include significantly reduced host loading, not to mention the generally higher quality graphics capabilities of many PCs relative to standard terminals as described by TERMCAP. See the manual entry for .I msh for more information. .SH CONSTRUCTING A CUSTOM INTEGRATED WINDOW MANAGER .PP The procedure for implementing an integrated window manager with .I msh is somewhat more involved than the implementation of a discreet remote window manager. .I Wty is the result of such an implementation. .PP The primary consideration in the window manager / .I msh interface is how to convert the standard input/output calls of .I msh into a procedural interface with the window manager. This may be accomplished by substituting demand-driven procedure evaluation for IO calls. .PP The basic IO call flow of .I msh may be described as: .nf put(current_input_shell,get(stdin)) for_each_shell,put(stdout,get(shell)) .fi where necessary input command processing and output packet fragmentation is implicit. .PP The basic IO call flow of a window manager can be described as: .nf put(stdout,get(keyboard)) put(screen,get(stdin)) .fi where necessary input packet fragmentation and output command processing is implicit. .PP Thus the put(stdout) of .I msh is linked to the get(stdin) of the window manager, and the get(stdin) of .I msh is linked to the put(stdout) of the window manager. In other words, a bi-directional pipeline exists between .I msh and the window manager. .PP This relationship can be exploited so that the IO link between .I msh and the window manager is eliminated; being replaced by direct procedural evaluation. .PP Thus we have the following externally visible procedures in .I "msh:" .nf init_msh(); abort(); slave_driver(); .fi .PP .I Msh also requires that these procedures be linked with it: .nf get_master(string); put_master(string,length); .fi where string is (char *), and length is (int). Note: these are the procedures (along with main) which "msh.bind.o" supplys for "msh.o". .PP Thus, the window manager must provide a main program which essentially does the following: .nf ... init_msh(); ... for (;;) screen_driver(); ... .fi where screen_driver is part of the window manager and calls the slave_driver procedure of .I "msh." The slave_driver procedure, in turn, calls get_master which must be supplied by the window manager. This call can be directed to get input from the user and process any local commands. .PP When .I msh is ready to send an output packet, it calls put_master. This call may be used to satisfy the original demand from screen_driver. Thus one entire demand driven evaluation cycle can be completed upon a return to screen_driver. .PP Note: each call to "slave_driver" of .I msh results in a poll of the next slave in the polling sequence. .PP The entire demand evaluation cycle can be visualized as: .nf main: ... init_msh ... for (;;) screen_driver get msh slave_driver master_driver get_master get stdin (keyboard) put shell get shell put_master put stdout (screen) |____ .fi .PP The procedure for compiling and linking the integrated window manager is similiar to that for retuning .I "wty." .SH FILES .B "\*P/wms/*" .br .SH SEE ALSO .B "wty(\*M)" .br .B "msh(\*M)" .SH AUTHOR George Nelan @@ END-OF-FILE echo x - release/mant/wty.t sed 's/^@@//' > "release/mant/wty.t" << '@@ END-OF-FILE' .TH wty \*M "1 July 1985" .SH NAME wty \- window manager (version \*V) .SH SYNOPSIS .B "\*P/wty" [ n ] .SH DESCRIPTION .ad b .PP .I Wty provides window management capabilities allowing interaction with multiple concurrent shells in a simple and efficient way. The .B TERMCAP database is utilized so that a windowing terminal may be emulated on a number of different terminals. .PP When started, the program creates .I n windows and shells (the default is 2). Up to four windows (in this version) may be specified. .I Wty divides the screen into .I n equally sized, individually scrollable, horizontal regions (windows). Each window is assigned to an individual c-shell (csh), and is known to that shell by means of the .B TERM environment variable. Thus, most programs which use TERMCAP (including vi) may be run under .I "wty." .PP Output from all shells is made immediately available to their corresponding windows. However, at any one time, there is only one window which receives input from the user. This window is the .I "current input window," and may be changed at any time by the user. Except for .I wty operation commands, .B all inputs are presented to the current input window's shell as if the shell were communicating directly with the user. .PP Windows may have a buffer allocated for them allowing for recording of all input/output for the window. Buffers are implemented as named files so that they may be edited. Such a buffer may then be read as input to another window. .SH OPERATION .PP .I Wty accepts these commands for operation, each preceeded by <ctrl-A> (<n> is a numeric character window label): .nf <n> - change current input window c<n> - create new current input window cn<n> - create (nicely) d<n> - delete current input window dn<n> - delete (nicely) f<n> - fold current input window fn<n> - fold (nicely) o<n> - open another current input window on<n> - open (nicely) bc - create new buffer for current input window bd - delete buffer for current input window bf - fold buffer for current input window bo - open buffer for current input window br<n> - read buffer <n> into current input window brl<n> - read buffer <n> literally r - resize current input window l - label all windows h - help =<c> - set command character <ctrl-Z> - stop qy - quit .fi .PP Windows are labeled numerically from top to bottom starting at '1'. An input of <ctrl-A> <ctrl-A> results in one <ctrl-A> being passed to the input window's shell. An input of an invalid command rings the terminal's bell. .PP The change window command causes the current input window to become that labeled by <n>. .PP The create window command creates a new window labeled by <n> and attaches a shell to it. The new window becomes the current input window. All windows are cleared and resized equally \- a new "setenv TERM" command is sent to the attached shell. The nice form does not automatically resize other windows. .PP The delete window command deletes the current input window and detaches its shell. The current input window becomes that labeled by <n>. All windows are cleared and resized equally. The nice form does not automatically resize windows. .PP The fold window command temporarily removes the current input window from the display (until an open command). The current input window becomes that labeled by <n>. All windows are cleared and resized equally. The nice form does not automatically resize windows. .PP The open window command causes a previously folded window <n> to become visible again. This window becomes the new current input window. All windows are cleared and resized equally. The nice form does not automatically resize windows. Note: all output from a shell attached to a folded window is lost until it becomes open again (unless a buffer is recording). .PP The create buffer command allocates a named file to the current input window so that all suceeding input/output for the window may be recorded. The name of the file is "wty.buf.<n>" (where <n> is the label of the current input window), unless the environment variable .B WTYB has been defined; in this case, the WTYB variable defines the file name prefix and a suffix string of the form ".<n>" is generated by .I "wty." If the buffer file already exists, a new buffer is .B not created. .PP The delete buffer command closes the buffer file associated with the current input window and deletes it from the file space. .PP The fold buffer command temporarily inhibits the recording of input/output data for the current input window if it has a buffer attached to it. .PP The open buffer command causes a previously folded buffer (for the current input window) to become available for recording again. .PP The read buffer command reads the buffer associated with window <n> (if it exists) and transfers its contents to the current input window as input data. Any keyboard input during the transfer will terminate the transfer. All character strings of the form "\\r\\n" in the buffer file are transformed to the string "\\n" (this is a nominal transformation due to implementation considerations \- sub-shells normally echo inputted \\r's as "\\r\\n"). The read literal form of the command inhibits this transformation. .PP The resize command causes the current input window's .B TERM value to be sent again to its shell. It is assumed that the shell is in a state where a new .I setenv command can be accepted. Indeed, this command is useful in cases where a particular shell doesn't receive previous resizing data. .PP The label command causes the label of each window to appear in its upper right corner. This can be useful as a reminder since windows are not re-labeled after creation or deletion. For each window, if a buffer is allocated for it, a 'B' is displayed immediately following the label if the buffer is open; a 'b' is displayed if it is folded. .PP The help command clears the current input window then displays a summary of commands. .PP The set command character command changes the current command prefix character to the character <c>. The default character is <ctrl-A>. The new character may not be the same as the start/stop flow control characters. .PP The stop command temporarily suspends .I "wty" and returns control to UNIX. An 'fg' command can be given later to resume operation. .PP The quit (yes, do it!) command terminates execution of .I "wty." .SH ENVIRONMENT VARIABLES .PP .I Wty assumes that the TERMCAP file you use contains definitions for a master prototype entry. This prototype entry, named by the environment variable .B WTYP, defines the capabilities of a psuedo-device which .I wty requires for emulation. Thus these environment variables must be defined before you execute .I "wty:" .B "TERM," which names the actual terminal you are using; .B "TERMCAP," which should define the pathname of the file containing your TERMCAP descriptions; and .B "WTYP," which names the prototype entry within TERMCAP. (The .B WTYB buffer name variable is optional). .PP For those who use vt100 compatible terminals, .B TERM should be "vt100", .B TERMCAP should be "\*P/termcap" (or "/etc/termcap"), and .B WTYP should be "vwty_". The file "\*P/termcap" provides sufficient definitions for vt100 users. It may also be used as a guide in developing other definitions. If you .nf setenv TERMCAP /etc/termcap .fi it is assumed that "/etc/termcap" has been edited to contain a new vt100 definition and WTYP entry as given in the file "\*P/termcap". If this is not the case, or if you use a local TERMCAP file (which is faster anyway for programs that search for TERM entrys), then you need to edit your own local TERMCAP file. .PP A new .B TERM type entry is included in "\*P/termcap" for those who have access to ANSI compatible terminals (these are vt100 functional supersets which allow local editing functions). The entry name is "ansi": .nf setenv TERM ansi .fi and .B WTYP should be: .nf setenv WTYP wty_ .fi .br .SH TERMCAP INTERFACE .PP This section provides additional details about the .B WTYP prototype entry and how it relates to actual devices. This section is necessary reading if you do not have a vt100 or ANSI compatible terminal, or if you are writing a new TERMCAP entry. It is assumed that you are familiar with the material covered in .I "termcap(5)." .PP The primary capability required by .I wty for any terminal is a controllable scrolling region defined by .B "cs." The multiple scrolling regions of .I wty are controlled by multiplexing the scrolling region of .B "TERM." The .B cs cap requires a top and bottom row parameter defining the scrolling region. These parameters are encoded in the same way as the parameters are for .B "cm." .PP The specific caps required by .I wty for a given .B TERM definition depend, with two exceptions, upon the .B WTYP prototype definition. The exceptions are .B cs and .B "cm;" controllable scrolling region and cursor motion are the only two caps which .I must be provided in your .B TERM definition. .PP For most other caps, if the .B WTYP entry defines a particular cap, then the corresponding cap in the .B TERM entry must also be defined. Some caps which have reasonable defaults do not need an explicit definition at all. The caps: (bc, co, cr, do, is, ke, ks, li, nl, sg, ug, ve, vs, we, ws) have defaults (respectively) of: (^H, 80, ^M, ^J, ?, ?, ?, 24, ^J, 0, 0, ?, ?, ?, ?); where '?' means the empty string. .PP The .B WTYP prototype defines the specific caps required for all windows by .I "wty." Each differently sized window also requires a set of capabilities which define the size of the window. The caps .B co and .B li describe window size variables. .B Co should always be constant since windows are defined as scrolling regions which extend across the entire display. Thus the .B li cap is the only parameter determining window size. .PP Since each window's shell requires knowledge of its own window's size, via the .B TERM environment variable, this may be accomplished by extending the basic .B WTYP definition by defining a new entry for each different size window. The entry must be named according to this rule: The name of the .B WTYP entry is the prefix of the new name, and the row/column size of the window determines the postfix of the new name in the format: RxC, where R is the same as .B "li," and C is the same as .B "co." Thus, for a window of size 12x80, and (say) .B WTYP = "vwty_", then the name of the new entry would be "vwty_12x80". Note that there must be an entry for .B every window size. Window (row) sizes are computed by dividing the maximum number of rows by the number of windows. So, for example, if we have a 24-row host terminal, then we would need definitions for 24 rows (24/1), 12 rows (24/2), 8 rows (24/3), and 6 rows (24/4). .PP At this point, it may seem that the use of the .B TERM environment variable has been inconsistent \- this is not so. Recall that .I wty creates new .I shells for each window. It is these shells which require that their .I own version of the .B TERM variable identify the size of their attached window. The propagation of this information to each window's shell is handled automatically by .I wty. This is the purpose of forming a new TERMCAP entry by concatenating the .B WTYP prototype prefix and the RxC format postfix as described above. Since .I wty requires knowledge of the terminal it is controlling, the definition of .B TERM at the time of program initialization provides that knowledge. .SH TTY INTERFACE .PP This section is presented for informational purposes only \- understanding the details of the tty interface is not necessary in order to use .I wty. .PP When .I wty begins, with one exception, the initial tty state is passed on to the sub-shells which are created for all of the windows. In general, the actual setting of this state is more or less irrelevant to the correct operation of .I "wty." The exception is that "stty -tabs" is explicitly set for the sub-shells; this is because .I wty ignores incoming tabs (^I) from sub-shells. However, after this initial state is picked up, .I wty resets several tty modes ala "stty new cbreak -echo -tabs nl". Also, all special characters are un-defined (i.e. stty <c> u) except for the flow control characters (^S ^Q usually). This is essentially equivalent to "stty raw" but allows automatic flow-control and delay-processing (whew!). .SH SEE ALSO .B "msh(\*M)" .br .B "wms(\*M)" .SH BUGS .PP Not all programs which use TERMCAP can run under .I "wty." In particular, those programs which attempt to control .B cs themselves will have little luck. Fortunately, the number of such programs appears to be small. .PP It is possible that sub\-processes may still exist after .I wty is terminated. Usually this occurs if backround jobs are left running in a sub\-shell after quiting .I "wty." One should then do a ps command to check on stray processes. .PP The <break> key does not work. It is best to rely upon your <kill> character if you regularly use <break> as a <kill> alias. .PP The <start> and <stop> characters affect flow-control globally \- there is no way to get local (per window) flow control. .PP Due to the lack of internal buffering, when one resumes a stopped .I "wty," the individual windows are not automatically redrawn. For the same reason, creating or deleting windows automatically clears and homes all windows (likewise for folding and opening). .PP If .I wty is executed for too long at a time, the operating system will renice it. As this will slow things down quite a bit, it is advisable to quit .I wty then restart it again. Another alternative is to stop (<ctrl-A><ctrl-Z>) .I wty and use the original shell as often as feasible. This may delay the renice. .SH NOTES .PP The TERMCAP definitions as given in "\*P/termcap" make a reasonable basis for your own local TERMCAP file. Note two new capabilities: .B we and .B "ws." These (string) caps should be given to end and start a .I wty session (or when stopping and resuming). Typically, they would include the same information as .I is does but should explicitly set/reset the scrolling region. Note that .I is should .B not explicilty set/reset the scrolling region. .PP The .B cl and .B cd prototype caps are implemented by means of .B ce only (in the host TERMCAP definition). This means that cl and cd are not needed in the host definition. .PP The justification for pad counts in the TERMCAP prototypes is a little fuzzy. Generally, depending on your own terminal, no pad delays need be given. If .I wty seems to generate strange screen commands, a little delay time in things like .B cm in the prototype entry should fix things. The reason this seems to occur is that programs which use the padding information of TERMCAP (like vi) assume that they are talking directly to a terminal. Injecting a program like .I wty between vi and a terminal can introduce delay timing discontinuities into the output stream, not to mention the fact that .I wty itself is sending control codes to the screen (thus adding to the general chaos of control codes flocking to the terminal). .SH AUTHOR George Nelan @@ END-OF-FILE echo done
system@asuvax.UUCP (Marc Lesure) (09/19/85)
wms part 2 of 4 If your site decides on implementing wms, please send mail to the author so he can send updates (if any) in the future. Also, please send all bug reports, problems, fixes, etc. to the author rather than posting them to net.sources.bugs. Marc Lesure System Manager Engineering Computer Center Arizona State University Tempe, Arizona UUCP: ...!{ucbvax,ihnp4}!arizona!asuvax!lesure ...!ihnp4!terak!asuvax!lesure CSNET: lesure@asu ARPA: lesure%asu@csnet-relay -----------------------------------<cut here>----------------------------- # This is a shell archive. Remove all lines before this one. # Use 'sh <this file>' to unpack the contents. # # contents: # release/public # release/public/termcap # release/public/wms # release/public/wms/max.msh.c # release/public/wms/max.msh.h # release/public/wms/max.wty.c # release/public/wms/max.wty.h # release/public/wms/msh.h # echo x - release/public mkdir release/public echo x - release/public/termcap sed 's/^@@//' > "release/public/termcap" << '@@ END-OF-FILE' # # ANSI nominal wty prototype... WW|wty_|wty prototype :\ :am:al=\E@A:\ :bc=\E@B:\ :cd=\E@C:ce=\E@D:cl=\E@E:cm=15\E@F%3%3:cr=\E@G:\ :dc=\E@H:dl=\E@I:dm=:do=\E@J:\ :ed=:ei=:\ :ho=\E@K:\ :ic=\E@L:im=:is=\E@M:\ :k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\ :k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\ :kd=\EOB:ke=\E@N:kl=\EOD:kr=\EOC:ks=\E@O:ku=\EOA:\ :nd=\E@P:nl=\E@Q:\ :se=\E@R:sf=\E@S:sg#0:so=\E@T:sr=\E@U:\ :ue=\E@V:ug#0:up=\E@W:us=\E@X:\ :ve=\E@Y:vs=\E@Z:\ :we=\E@a:ws=\E@b: # # Other ansi wty window sizes # WW|wty_24x80|wty :li#24:co#80:tc=wty_: WW|wty_12x80|wty :li#12:co#80:tc=wty_: WW|wty_8x80|wty :li#8:co#80:tc=wty_: WW|wty_6x80|wty :li#6:co#80:tc=wty_: # # wide... WW|wtyw_24x132|wty :li#24:co#132:tc=wty_: WW|wtyw_12x132|wty :li#12:co#132:tc=wty_: WW|wtyw_8x132|wty :li#8:co#132:tc=wty_: WW|wtyw_6x132|wty :li#6:co#132:tc=wty_: # # # anemic vt100 wty entrys... # # vt100 nominal WW|vwty_|vt100 wty prototype :\ :al@:dc@:dm@:ed@:ei@:dl@:ic@:im@:sf@:ve@:vs@:tc=wty_: WW|vwty_24x80|vwty :li#24:co#80:tc=vwty_: WW|vwty_12x80|vwty :li#12:co#80:tc=vwty_: WW|vwty_8x80|vwty :li#8:co#80:tc=vwty_: WW|vwty_6x80|vwty :li#6:co#80:tc=vwty_: # # wide... WW|vwtyw_|vt100 wty prototype :\ :al@:dc@:dm@:ed@:ei@:dl@:ic@:im@:sf@:ve@:vs@:tc=wty_: WW|vwtyw_24x132|vwtyw :li#24:co#132:tc=vwtyw_: WW|vwtyw_12x132|vwtyw :li#12:co#132:tc=vwtyw_: WW|vwtyw_8x132|vwtyw :li#8:co#132:tc=vwtyw_: WW|vwtyw_6x132|vwtyw :li#6:co#132:tc=vwtyw_: # # # ansi - 0|ansi|ANSI|dec vt100 superset :\ :am:al=5\E[1L:\ :bc=2^H:\ :cd=50\E[J:ce=5\E[K:cl=50\E[;H\E[2J:cm=10\E[%i%d;%dH:co#80:cr=5^M:\ :cs=10\E[%i%d;%dr:\ :dc=5\E[1P:dl=5\E[1M:dm=:do=5^J:\ :ed=:ei=:\ :ho=10\E[H:\ :ic=5\E[1@:im=:is=50\E[;H\E[2J\E[1;1H:\ :k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\ :k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\ :kd=\EOB:ke=\E[?1l\E>:kl=\EOD:kr=\EOC:ks=\E[?1h\E=:ku=\EOA:\ :li#24:\ :nd=2\E[C:nl=5^J:\ :pt:\ :se=2\E[m:sf=5\ED:sg#0:so=2\E[7m:sr=5\EM:\ :ue=2\E[m:ug#0:us=2\E[4m:up=5\E[A:\ :ve=:vs=:\ :we=50\E[;H\E[2J\E[1;24r\E[1;1H:ws=50\E[;H\E[2J\E[1;24r\E[1;1H: # # nelan - vt100 - d0|vt100|vt100-am|dec vt100 :\ :am:\ :bc=2^H:\ :cd=50\E[J:ce=5\E[K:cl=50\E[;H\E[2J:cm=10\E[%i%d;%dH:co#80:cr=5^M:\ :cs=10\E[%i%d;%dr:\ :do=5^J:\ :ho=10\E[H:\ :is=50\E[;H\E[2J\E[1;1H:\ :k0=\EOP:k1=\EOQ:k2=\EOR:k3=\EOS:k4=\EOT:\ :k5=\EOU:k6=\EOV:k7=\EOW:k8=\EOX:k9=\EOY:\ :kd=\EOB:ke=\E[?1l\E>:kl=\EOD:kr=\EOC:ks=\E[?1h\E=:ku=\EOA:\ :li#24:\ :nd=2\E[C:nl=5^J:\ :pt:\ :se=2\E[m:sg#0:so=2\E[7m:sr=5\EM:\ :ue=2\E[m:ug#0:us=2\E[4m:up=5\E[A:\ :we=50\E[;H\E[2J\E[1;24r\E[1;1H:ws=50\E[;H\E[2J\E[1;24r\E[1;1H: # # wide d0|vt100w|vt100-amw|wide dec vt100 :\ :co#132:tc=vt100: # # cit alias d0|cit101|CIT101|cit101 ala vt100:\ :tc=vt100: # # wide cit alias d0|cit101w|CIT101w|wide cit101 ala vt100:\ :co#132:tc=vt100: @@ END-OF-FILE echo x - release/public/wms mkdir release/public/wms echo x - release/public/wms/max.msh.c sed 's/^@@//' > "release/public/wms/max.msh.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* all msh adjustable max parameters are here */ /* note that they all must be <= maximums as defined in "max.msh.h" */ int MAXSLAVES = 4; /* whatever */ int MAXNOPOLLS = 64; /* tuned! */ int MAXSHELL = 64; /* whatever */ int MAXTCODE = 64; /* whatever */ int MAXCBUF = 128; /* tuned! */ @@ END-OF-FILE echo x - release/public/wms/max.msh.h sed 's/^@@//' > "release/public/wms/max.msh.h" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /************************************************************************/ /* THIS FILE IS SUPPOSED TO BE READ-ONLY */ /************************************************************************/ /* all absolute maximums for adjustable max parameters are here */ #define AMAXSLAVES 9 #define AMAXNOPOLLS 1024 #define AMAXSHELL 128 #define AMAXTCODE 128 #define AMAXCBUF 512 /* see "max.msh.c" for declarations of following... */ extern int MAXSLAVES; extern int MAXNOPOLLS; extern int MAXSHELL; extern int MAXTCODE; extern int MAXCBUF; @@ END-OF-FILE echo x - release/public/wms/max.wty.c sed 's/^@@//' > "release/public/wms/max.wty.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* all wty adjustable max parameters are here */ /* note that they all must be <= maximums as defined in "max.wty.h" */ #include "max.msh.c" int MAXWINDOWS = 4; /* == MAXSLAVES */ char MAXLABEL = 4 + '0'; /* == MAXSLAVES + '0' */ /* be sure to update max.msh.c !! (MAXSLAVES) */ int MAXQBUF = 256; /* MAXCBUF << 2 */ @@ END-OF-FILE echo x - release/public/wms/max.wty.h sed 's/^@@//' > "release/public/wms/max.wty.h" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /************************************************************************/ /* THIS FILE IS SUPPOSED TO BE READ-ONLY */ /************************************************************************/ /* all absolute maximums for adjustable max parameters are here */ #include "max.msh.h" #define AMAXWINDOWS AMAXSLAVES #define AMAXLABEL (AMAXSLAVES + '0') #define AMAXQBUF (AMAXCBUF << 2) /* see "max.wty.c" for declarations of following... */ extern int MAXWINDOWS; extern char MAXLABEL; extern int MAXQBUF; @@ END-OF-FILE echo x - release/public/wms/msh.h sed 's/^@@//' > "release/public/wms/msh.h" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* command sequence introducer for msh input */ #define M_CSI "\0@" /* commands */ #define M_QX "A" /* quit execution */ #define M_SS "B" /* set shell */ #define M_CC "C" /* create csh shell */ #define M_CU "D" /* create user shell */ #define M_RS "E" /* reset shell size */ #define M_DS "F" /* delete shell */ #define M_SX "G" /* stop execution (4.2bsd only) */ /* msh output windowing operation: (atomic command) */ #define W_WG "\027" /* ^W: Window: Goto window */ @@ END-OF-FILE echo done
system@asuvax.UUCP (Marc Lesure) (09/20/85)
wms part 3 of 4 If your site decides on implementing wms, please send mail to the author so he can send updates (if any) in the future. Also, please send all bug reports, problems, fixes, etc. to the author rather than posting them to net.sources.bugs. Marc Lesure System Manager Engineering Computer Center Arizona State University Tempe, Arizona UUCP: ...!{ucbvax,ihnp4}!arizona!asuvax!lesure ...!ihnp4!terak!asuvax!lesure CSNET: lesure@asu ARPA: lesure%asu@csnet-relay ------------------------------------<cut here>------------------------- # This is a shell archive. Remove all lines before this one. # Use 'sh <this file>' to unpack the contents. # # contents: # release/source # release/source/NOTES # release/source/msh # release/source/msh/max.msh.h # release/source/msh/msh.bind.c # release/source/msh/msh.c # release/source/msh/msh.h # release/source/msh/util.h # echo x - release/source mkdir release/source echo x - release/source/NOTES sed 's/^@@//' > "release/source/NOTES" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ ------------------------------------------------------------------------------ General notes & philosophy... ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ The source codes reference include files which they think are in their own local directory. These files are symbolically linked to where they are relative to the distribution directory structure. This needs to be considered if the structure is disturbed (a relink may be necessary - see the 'relink' script in this directory). ------------------------------------------------------------------------------ For wty, the requirement that terminals possess a controllable scrolling region can be extended to cover tvi-925 and other types. This would require either a unique format for the 'cs' cap (of the actual device), or perhaps more appropriately, a new cap for wty only (so that other programs do not get confused by the non-standard 'cs' format). This would allow wty to distinguish between various methods of 'cs'. ------------------------------------------------------------------------------ The initial version of wty used the *curses* library and was found to be unsatisfactory for these reasons: o Programs which do cursor motion usually optimize such motion themselves, possibly via curses itself. No redundancy in this area was desired. o Curses' idea of scrolling windows means to redraw a portion of the screen. For arbitrary size windows on a vanilla terminal, there is no other way. Simply by restricting window dimensions however, it is possible to efficiently implement truly scrollable windows via multiplexing of the scrolling region. Hence the lower level termcap library was used instead. However, not using curses makes it more difficult to implement internal buffering & window refresh. It would be nice to be able to scroll and refresh internal buffers. ------------------------------------------------------------------------------ The method of communicating the current window size to a sub-shell needs to be thought about more carefully. The "setenv" message is the simplest way, but leaves much to be desired. ------------------------------------------------------------------------------ Msh has a hook in it to keep roots from using it. When our own local root tested it, we found strange things happening to various sockets. Since I am not the root here, it is difficult for me to validate possible solutions. I believe such problems have been resolved, but I cannot be sure. Therefore, remove this hook at your own risk. (The hook is in init_msh(), the problem seemed to be in do_QX() and/or do_DS()). ------------------------------------------------------------------------------ Signal handling is minimal. Some thought needs to be done about putting msh to sleep when IO inactivity exceeds some threshold, waking up of course on resumed IO activity. ------------------------------------------------------------------------------ In both wty & msh, input command procedures are intentionally split into duals so that one procedure manages input parameters (if any), checking syntax and semantics; and the other manages the function itself. This method introduces redundancy in some cases; but for the sake of consistency, all future expansions should follow the same style. At least this allows the second procedure to be used independently of the first. ------------------------------------------------------------------------------ The following graph illustrates the primary flow of control for msh: main: init_msh loop slave_driver (for next slave i) master_driver get_master, read tty exec_cmd | put_slave, write pty get_slave(i), read pty(i) put_master |__ ------------------------------------------------------------------------------ The following graph illustrates the primary flow of control for wty/msh: main: init_msh init_wty loop screen_driver get_port (if pq empty) slave_driver (for next slave i) master_driver get_master (if mq empty) keyboard_driver get_keyboard, read tty put_port put_slave, write pty get_slave(i), read pty(i) put_master put_screen(i), write tty |__ @@ END-OF-FILE echo x - release/source/msh mkdir release/source/msh echo x - release/source/msh/max.msh.h ln -s ../../public/wms/max.msh.h release/source/msh/max.msh.h echo x - release/source/msh/msh.bind.c sed 's/^@@//' > "release/source/msh/msh.bind.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* AUTHOR: GEORGE NELAN */ /* discreet version msh binder */ #include <stdio.h> extern init_msh(); extern slave_driver(); #include <sys/ioctl.h> /****************************************************************************/ main (argc,argv) int argc; char *argv[]; { init_msh(); for (;;) { slave_driver(); } } /* main */ /****************************************************************************/ int get_master(chp) register char *chp; /* return > 0 if std input ready, * else return <= 0 if not ready or error. */ { int tlen; if (ioctl(0,FIONREAD,&tlen) == (-1)) return(-1); if ((tlen > 0) && ((tlen = read(0,chp,1)) == (-1))) return(-1); return(tlen); } /* get_master */ /****************************************************************************/ put_master(chp,len) register char *chp; register int len; { write(1,chp,len); } /* put_master */ @@ END-OF-FILE echo x - release/source/msh/msh.c sed 's/^@@//' > "release/source/msh/msh.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* msh: meta-shell manager */ /* author: George Nelan */ #include <stdio.h> #include <sys/file.h> #include <sys/types.h> #include <sys/timeb.h> #include <signal.h> #include <sgtty.h> #include <errno.h> extern int errno; #include "max.msh.h" #include "msh.h" #include "util.h" #define sigmask(m) (1 << ((m)-1)) /* everything for the new tty driver */ typedef struct { int f; struct sgttyb b; struct tchars c; int w; struct ltchars l; int d; } tty_t; /* one master... */ static int master_pid; static tty_t master_tty; /* a slave for each shell... */ typedef struct { int pid; /* == NIL => inactive */ tty_t pty; /* comm. link to master */ int nopolls; /* #times slave poll bypassed */ int maxnopolls; /* relative max #times poll bypassed */ char label; /* constant window label */ bool rs_pending; /* initial ReSize kludge */ char rs_tc[AMAXTCODE]; /* term code for ^ */ } slave_t; /* all slaves... */ static slave_t slave[AMAXSLAVES]; /* current input slave */ static slave_t *islave = 0; /* this must be explicitly initialized by a SS command */ /* current output window label */ static char olabel = '0'; /* result flag for shell creation (CC/CU) */ static bool C_result; /****************************************************************************/ #define read_master(chp) {while(get_master(chp) <= 0);} /****************************************************************************/ #define msg_master(msg) {put_master(msg,sizeof msg);} /****************************************************************************/ #define put_slave(sp,chp,len) {\ write(sp->pty.f,chp,len);\ } /****************************************************************************/ /* define current output window (can't use currency) */ #define goto_win(winchp) {\ olabel = (*winchp);\ put_master(W_WG,1);\ put_master(winchp,1);\ } /****************************************************************************/ public abort(msg) char *msg; { char str[128]; strcpy(str, msg); if (errno) { strcat(str, " possibly"); perror(str); } else puts(str); puts("\r"); res_m_tty(); exit(-1); } /****************************************************************************/ public master_driver() /* Poll master. * If input is there then see if it is an msh command. * If so, execute the command; * else send the input to the current slave. * Note: islave is 0 until explicitly inited via SS command. */ { int result; char ch0; reg char *ch0p = &ch0; char ch1; reg bool isacmd; if (get_master(ch0p) > 0) { isacmd = FALSE; if (*ch0p == M_CSI[0]) { read_master(&ch1); if (!(isacmd = (ch1 == M_CSI[1]))) { /* we consume M_CSI[0] */ *ch0p = ch1; } } if (isacmd) { read_master(ch0p); m_exec(*ch0p); } else if (islave) { put_slave(islave,ch0p,1); /* try to get echo asap */ islave->maxnopolls = (-1); } } } /* master_driver */ /****************************************************************************/ public bool isactive(sl) char sl; /* slave label */ { return (slave[sl - '1'].pid != NIL); } /* isactive */ /****************************************************************************/ /* slave cycle index for slave_driver */ static int slave_index = 0; /****************************************************************************/ public slave_driver() /* Poll slave 'slave_index'. Send any and all input thus received to the * remote window manager for output to its window. * Attempt to optimize io: if a slave has just output, give it higher * polling priority. * As long as no output is pending, keep giving it lower priority * (to a limit: MAXNOPOLLS). * This tends to delay output from quiet slaves, while active slaves are * tended to more quickly. Hopefully, total io is minimized, * thus decreasing echo response time. * Note the call to master_driver at start. This also tends to minimize * echo response time since input is tended to more often. */ { int result; reg slave_t *sp; char chb[AMAXCBUF]; reg char *chbp = chb; master_driver(); sp = &slave[slave_index]; /* bypass inactive slaves */ if (sp->pid == NIL) goto done; /* If the (active) slave has not been polled since * (maxnopolls) then go ahead and poll it now. */ if (sp->nopolls > sp->maxnopolls) { sp->nopolls = 0; if ((result = get_slave(sp,chbp,MAXCBUF)) > 0) { /* KLUDGE: If the slave has just been created, * now is the time to tell it about TERM */ if (sp->rs_pending) { slave_t *tsp; /* for do_RS */ tsp = islave; islave = sp; sp->rs_pending = FALSE; do_RS(sp->rs_tc); islave = tsp; } /* here is the output fragment packet header */ goto_win(&(sp->label)); put_master(chbp,result); /* Since we just got some input from the * slave, chances are we'll need some * more (asap). */ sp->maxnopolls = (-1); } else { /* limit the number of nopolls */ if ((sp->maxnopolls)++ > MAXNOPOLLS) sp->maxnopolls = MAXNOPOLLS; } } else (sp->nopolls)++; done: /* here is where we cycle for the next slave */ slave_index = ( (++slave_index) < MAXSLAVES ? slave_index : 0 ); } /* slave_driver */ /****************************************************************************/ /* execute msh cmd (assume valid!) */ static m_exec(ch) char ch; { if (ch == M_QX[0]) m_QX(); else if (ch == M_SS[0]) m_SS(); else if (ch == M_CC[0]) m_CC(); else if (ch == M_CU[0]) m_CU(); else if (ch == M_RS[0]) m_RS(); else if (ch == M_DS[0]) m_DS(); else if (ch == M_SX[0]) m_SX(); } /* m_exec */ /****************************************************************************/ /* quit execution */ static m_QX() { do_QX(); } /* m_QX */ static do_QX() { char sl; res_m_tty(); for (sl = '1'; sl <= MAXSLAVES + '0'; sl++ ) do_DS(sl); kill(0,SIGTERM); exit(0); } /* do_QX */ /****************************************************************************/ /* set new current shell to 'n' */ static m_SS() { char n; read_master(&n); do_SS(n); } /* m_SS */ static do_SS(n) char n; { islave = &slave[n - '1']; } /* do_SS */ /****************************************************************************/ /* TRUE if can create new current (csh) shell n code tc */ static m_CC() { char n,tc[AMAXTCODE]; short i; read_master(&n); i = 0; do { read_master(&tc[i]); } while (tc[i++] != ';' && i < MAXTCODE-1); tc[i] = '\0'; do_CC(n,tc); if (!C_result) msg_master("Sorry, shell not created...(ptys?)"); } /* m_CC */ static do_CC(n,tc) char n,*tc; { do_CU(n,tc,"/bin/csh"); } /* do_CC */ /****************************************************************************/ /* TRUE if can create new current (user) shell n code tc */ static m_CU() { char n,tc[AMAXTCODE]; char shell[AMAXSHELL]; short i; read_master(&n); i = 0; do { read_master(&tc[i]); } while (tc[i++] != ';' && i < MAXTCODE-1); tc[i] = '\0'; for (i = 0; i < MAXSHELL; i++) { read_master(&shell[i]); if (shell[i] == '\n') { shell[i] = '\0'; break; } } do_CU(n,tc,shell); if (!C_result) msg_master("Sorry, shell not created...(ptys?)"); } /* m_CU */ static do_CU(n,tc,shell) char n,*tc; char *shell; { int pid; int on; tty_t *pty; slave_t *sp; char tid[16]; /* the following code is pretty muchly standard pty to stdio stuff */ C_result = TRUE; sp = &slave[n - '1']; sp->nopolls = 0; sp->maxnopolls = (-1); pty = &(sp->pty); if (fail(ptmalloc(pty,tid))) return(C_result = FALSE); on = 1; ioctl(pty->f,FIONBIO,&on); set_s_pty(pty); if ((sp->pid = pid = vfork()) < 0) return(C_result = FALSE); else if (pid == 0) { /* slave */ tty_t tty_s; tty_t *tty = &tty_s; int t,cpid; close(pty->f); setpgrp(0,getpid()); if ((t = open("/dev/tty",O_RDWR)) >= 0) { ioctl(t,TIOCNOTTY,0); close(t); } close(2); if (fail(ptsalloc(tty,tid))) { msg_master("msh: bad pty slave alloc"); _exit(-1); } cpid = getpid(); setpgrp(0,cpid); ioctl(2,TIOCSPGRP,&cpid); close(0); close(1); dup(2); dup(2); set_s_tty(tty); execl(shell,shell,0); msg_master("msh: bad exec"); _exit(-1); } /* master */ do_SS(n); islave->rs_pending = TRUE; strcpy(islave->rs_tc,tc); } /* do_CU */ /****************************************************************************/ /* reset size of current slave */ /* we assume that the slave shell is in a state which can receive this */ static m_RS() { char tc[AMAXTCODE]; short i; i = 0; do { read_master(&tc[i]); } while (tc[i++] != ';' && i < MAXTCODE-1); tc[i] = '\0'; do_RS(tc); } /* m_RS */ static do_RS(tc) char *tc; { static char rs_msg[AMAXTCODE+16]; strcpy(rs_msg,"setenv TERM "); strcat(rs_msg,tc); rs_msg[strlen(rs_msg) - 1] = '\n'; /* wipe out ; terminator */ rs_msg[strlen(rs_msg) ] = '\0'; put_slave(islave,rs_msg,strlen(rs_msg)); } /* do_RS */ /****************************************************************************/ /* delete current shell, make n current */ static m_DS() { char n; read_master(&n); do_DS(n); } /* m_DS */ static do_DS(n) char n; { int gid; ioctl(islave->pty.f,TIOCGPGRP,&gid); /* I think this test solves the problem discussed in ../NOTES */ if (islave->pid != (-1) && gid != (-1)) { killpg(gid,SIGKILL); killpg(islave->pid,SIGKILL); } close(islave->pty.f); islave->pid = NIL; do_SS(n); } /* do_DS */ /****************************************************************************/ /* stop execution (bsd only) */ static m_SX() { char f; /* debug flag */ read_master(&f); do_SX(f); } /* m_SX */ static do_SX(f) char f; { if (f != '1') res_m_tty(); killpg(master_pid,SIGTSTP); set_m_tty(); } /* do_SX */ /****************************************************************************/ /* Return > 0 if ptyp ready, * else return <= 0 if not ready or error. * Note: if a shell has prematurely died on us, we will get an I/O error now. * So mark this slave as inactive. Admitedly this is a bit crude, but it * is simpler than waiting for child status deltas and it does seem to work. */ static int get_slave(sp,chp,len) reg slave_t *sp; reg char *chp; reg int len; { if (fail(len = read(sp->pty.f,chp,len)) && errno != EWOULDBLOCK) { len = (-1); sp->pid = NIL; msg_master("Shell has died..."); } return(len); } /* get_slave */ /****************************************************************************/ /* Allocate master side of psuedo-terminal pair (return 1 else -1 if fail) */ static int ptmalloc(ptyp,name) tty_t *ptyp; char *name; { static char pnmap[] = "0123456789abcdef"; static char ptypn[] = "/dev/pty??"; int i,j; int p; for (i = 'p'; i <= 's'; i++) { ptypn[8] = (char) i; for (j = 0; j <= 15; j++) { ptypn[9] = pnmap[j]; if (!fail(p = open(ptypn,O_RDWR,0700))) goto openok; } } return(-1); openok: ptyp->f = p; strcpy(name,ptypn); return(1); } /* ptalloc */ /****************************************************************************/ /* Allocate slave side of psuedo-terminal pair (return 1 else -1 if fail) */ static int ptsalloc(ttyp,name) tty_t *ttyp; char *name; { int t; name[strlen("/dev/")] = 't'; if (fail(t = open(name,O_RDWR,0700))) return(-1); ttyp->f = t; return(1); } /* ptalloc */ /*****************************************************************************/ /* Original flags for tty state stuff */ static int o_flags; /*****************************************************************************/ /* Save original master tty state */ sav_m_tty() { get_state(master_tty.f,&master_tty); /* save original flags */ o_flags = master_tty.b.sg_flags; /* make sure tabs are space-converted! */ master_tty.b.sg_flags |= XTABS; } /* sav_m_tty */ /*****************************************************************************/ /* Reset master tty to original state */ res_m_tty() { /* restore original flags */ master_tty.b.sg_flags = o_flags; set_state(master_tty.f,&master_tty); } /* res_t_tty */ /*****************************************************************************/ /* Set master tty to new state */ set_m_tty() { tty_t news; news.b.sg_ispeed = master_tty.b.sg_ispeed; news.b.sg_ospeed = master_tty.b.sg_ospeed; news.b.sg_erase = (-1); news.b.sg_kill = (-1); news.b.sg_flags = master_tty.b.sg_flags; news.b.sg_flags |= XTABS; news.b.sg_flags &= (~CRMOD); news.b.sg_flags &= (~ECHO); news.b.sg_flags |= CBREAK; news.c.t_intrc = (-1); news.c.t_quitc = (-1); news.c.t_startc = master_tty.c.t_startc; news.c.t_stopc = master_tty.c.t_stopc; news.c.t_eofc = (-1); news.c.t_brkc = (-1); news.w = master_tty.w; news.l.t_suspc = (-1); news.l.t_dsuspc = (-1); news.l.t_rprntc = (-1); news.l.t_flushc = (-1); news.l.t_werasc = (-1); news.l.t_lnextc = (-1); news.d = master_tty.d; set_state(master_tty.f,&news); } /* set_m_tty */ /*****************************************************************************/ /* Set slave tty to original state of master (may not be necessary) */ set_s_tty(ttyp) tty_t *ttyp; { set_state(ttyp->f,&master_tty); } /* set_s_tty */ /*****************************************************************************/ /* Set slave pty to original state of master */ set_s_pty(ptyp) tty_t *ptyp; { set_state(ptyp->f,&master_tty); } /* set_s_pty */ /*****************************************************************************/ /* Get state of f into ttyp */ static get_state(f,ttyp) int f; tty_t *ttyp; { ioctl(f,TIOCGETP,&(ttyp->b)); ioctl(f,TIOCGETC,&(ttyp->c)); ioctl(f,TIOCLGET,&(ttyp->w)); ioctl(f,TIOCGLTC,&(ttyp->l)); ioctl(f,TIOCGETD,&(ttyp->d)); } /* get_state */ /*****************************************************************************/ /* Set state of f from ttyp */ static set_state(f,ttyp) int f; tty_t *ttyp; { ioctl(f,TIOCSETP,&(ttyp->b)); ioctl(f,TIOCSETC,&(ttyp->c)); ioctl(f,TIOCLSET,&(ttyp->w)); ioctl(f,TIOCSLTC,&(ttyp->l)); ioctl(f,TIOCGETD,&(ttyp->d)); } /* set_state */ /****************************************************************************/ /* initialize... */ public init_msh() { int i; char *w; /* must be interactive */ if (!isatty()) { puts("Sorry, msh requires interactive control."); exit(-1); } /* may be bad for root to do */ if (geteuid() == 0) { puts("Sorry root, this program may be unsafe for you to run."); exit(-1); } /* disable SIGTTIN,SIGTTOU for self & descendents */ sigblock(sigmask(SIGTTIN)); sigblock(sigmask(SIGTTOU)); master_pid = getpid(); master_tty.f = STDOUT; sav_m_tty(); set_m_tty(); /* for CBREAK mode, ignore <break> => intr */ signal(SIGINT,SIG_IGN); for (i = 0; i < MAXSLAVES; i++) { slave[i].pid = NIL; slave[i].label = (char)(i + '1'); slave[i].rs_pending = FALSE; } } /* init_msh */ @@ END-OF-FILE echo x - release/source/msh/msh.h ln -s ../../public/wms/msh.h release/source/msh/msh.h echo x - release/source/msh/util.h ln -s ../util.h release/source/msh/util.h echo done
system@asuvax.UUCP (Marc Lesure) (09/20/85)
wms part 4 of 4 If your site decides on implementing wms, please send mail to the author so he can send updates (if any) in the future. Also, please send all bug reports, problems, fixes, etc. to the author rather than posting them to net.sources.bugs. Marc Lesure System Manager Engineering Computer Center Arizona State University Tempe, Arizona UUCP: ...!{ucbvax,ihnp4}!arizona!asuvax!lesure ...!ihnp4!terak!asuvax!lesure CSNET: lesure@asu ARPA: lesure%asu@csnet-relay ------------------------------------<cut here>------------------------- # This is a shell archive. Remove all lines before this one. # Use 'sh <this file>' to unpack the contents. # # contents: # release/source/relink # release/source/util.h # release/source/wty # release/source/wty/max.msh.h # release/source/wty/max.wty.h # release/source/wty/msh.h # release/source/wty/util.h # release/source/wty/wty.bind.c # release/source/wty/wty.c # release/source/wty/wty.h # echo x - release/source/relink sed 's/^@@//' > "release/source/relink" << '@@ END-OF-FILE' # source this script to aid source code symbolic linking for files in .../wms. # we assume ./util.h doesn't move. # if link targets move, update this variable (relative to msh,wty subdirs): set W = '../../public/wms' # cd msh rm max.msh.h ln -s $W/max.msh.h max.msh.h rm msh.h ln -s $W/msh.h msh.h cd ../wty rm max.msh.h ln -s $W/max.msh.h max.msh.h rm max.wty.h ln -s $W/max.wty.h max.wty.h rm msh.h ln -s $W/msh.h msh.h cd .. @@ END-OF-FILE echo x - release/source/util.h sed 's/^@@//' > "release/source/util.h" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ #define STDIN 0 #define STDOUT 1 #define STDERR 2 #define reg register typedef char bool; #define TRUE 1 #define FALSE 0 #define NIL (-1) #define fail(x) ((x)==(-1)) #define public @@ END-OF-FILE echo x - release/source/wty mkdir release/source/wty echo x - release/source/wty/max.msh.h ln -s ../../public/wms/max.msh.h release/source/wty/max.msh.h echo x - release/source/wty/max.wty.h ln -s ../../public/wms/max.wty.h release/source/wty/max.wty.h echo x - release/source/wty/msh.h ln -s ../../public/wms/msh.h release/source/wty/msh.h echo x - release/source/wty/util.h ln -s ../util.h release/source/wty/util.h echo x - release/source/wty/wty.bind.c sed 's/^@@//' > "release/source/wty/wty.bind.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* wty.bind: integrated version wty binder */ /* author: George Nelan */ #include "max.wty.h" /* from msh */ extern init_msh(); extern abort(); extern slave_driver(); /* from wty */ extern init_wty(); extern keyboard_driver(); extern screen_driver(); typedef struct { char buf[AMAXQBUF]; char *fp,*rp,*beg,*end; } queue; #define initq(q) {\ q.fp = q.rp = q.buf;\ q.beg = q.buf + 1;\ q.end = q.buf + MAXQBUF - 1;\ } #define nullq(q) (q.fp == q.buf) #define fullq(q) (q.fp == q.rp) int qresult; #define insertq(q,e) {\ qresult = 1;\ q.rp = (q.rp == q.end ? q.beg : q.rp + 1);\ if (fullq(q)) qresult = 0;\ else {\ *(q.rp) = (e);\ if (q.fp == q.buf) q.fp = q.beg;\ }\ } #define deleteq(q,ep) {\ qresult = 1;\ if (nullq(q)) qresult = 0;\ else {\ *(ep) = *q.fp;\ if (q.fp == q.rp) q.fp = q.rp = q.buf;\ else q.fp = (q.fp == q.end ? q.beg : q.fp + 1);\ }\ } static queue pq,mq; /* wty port, msh master queues */ /****************************************************************************/ main (argc,argv) int argc; char *argv[]; { initq(pq); initq(mq); init_msh(); init_wty(argc,argv); for (;;) screen_driver(); } /* main */ /****************************************************************************/ int get_master(chp) register char *chp; /* return > 0 if wty character ready, * else return <= 0 if not ready or error. */ { if (nullq(mq)) keyboard_driver(); if (!nullq(mq)) { deleteq(mq,chp); return (1); } return (0); } /* get_master */ /****************************************************************************/ put_port(chp,len) register char *chp; register int len; { register int i; for (i = 0; i < len; i++) { insertq(mq,*(chp + i)); if (!qresult) abort("wty: put_port q o/v"); } } /* put_port */ /****************************************************************************/ int get_port(chp) register char *chp; /* return > 0 if msh character ready, * else return <= 0 if not ready or error. */ { if (nullq(pq)) slave_driver(); if (!nullq(pq)) { deleteq(pq,chp); *chp &= 0177; /* make sure 7-bits */ return (1); } return (0); } /* get_port */ /****************************************************************************/ put_master(chp,len) register char *chp; register int len; { register int i; for (i = 0; i < len; i++) { insertq(pq,*(chp + i)); if (!qresult) abort("msh: put_master q o/v"); } } /* put_master */ @@ END-OF-FILE echo x - release/source/wty/wty.c sed 's/^@@//' > "release/source/wty/wty.c" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* wty: virtual windowing terminal emulator */ /* author: George Nelan */ #include <stdio.h> #include <sys/file.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <sgtty.h> #include <errno.h> extern int errno; #include "max.wty.h" #include "msh.h" #include "util.h" #include "wty.h" #define BELL '\07' static char cmdchr = '\01'; /* ^A */ /* initialization kludge for init_wty,do_CC */ static bool initflag; /* nice flag for inhibiting auto-resizes */ static bool niceflag = FALSE; static char WTYB[128]; /* buffer name prefix env. var. */ static char WTYP[AMAXTCODE]; /* wty proto. env. var. */ /* buffer stuff */ /* set by 'br' - tells keyboard_driver to take input from b_fpending */ static bool b_rpending = FALSE; /* reset @ EOF */ static FILE *b_fpending; static char b_label; /* saved during 'br' reads */ static bool b_nlitflag = TRUE; /* not-literal flag for 'brl' */ typedef struct { char label; /* constant == index + 1 */ bool isactive; /* are we attached to a shell... */ bool isvisible; /* are we not folded... */ /* buffer variables */ FILE *b_file; /* the buffer is a file */ bool b_isactive; /* are we attached to a file... */ bool b_isvisible; /* are we not folded... */ /* viewport variables */ int top0,bot0; /* 0-org wrt screen global CS */ int row0,col0; /* 0-org wrt window local CS */ int row0max; /* 0-org wrt window local CS MAX */ int col0max; /* 0-org wrt window local CS MAX */ /* state variables... (see screen_driver) */ short state; char s_code; short s_argc; short s_i; char s_argv[8]; } window_t; /* all windows... */ static window_t window[AMAXWINDOWS]; static window_t *iwindow = 0; /* current input window */ static window_t *owindow = 0; /* current output window */ static char ilabel; /* label of current input window */ static char olabel; /* label of current output window */ static char sav_olabel; /* saved olabel */ static bool new_olabel; /* new olabel due to do_SS */ /* note: the interaction between the keyboard driver side and the * screen driver side is indirect. see do_SS and screen_driver (new_olabel). */ static int nwindows = 0; /* n visible windows => row sizes */ static int ctop = (-1); /* current global CS cs limits */ static int cbot = (-1); /* current global CS cs limits */ /* screen driver not-ignore character table */ static bool s_nignore[128]; /* screen driver special character table (affects cursor other than by +1) */ static bool s_special[128]; /* screen driver function execution table - need speed here */ static int (*s_exec[128])(); /* screen driver function arguments */ /* since only CM needs arguments, this is quite an over-generalization */ static short s_argc[128]; static char *s_argvp; /* screen driver TERMCAP functions */ static int s_null(); static int s_AL(); static int s_BC(); static int s_CD(); static int s_CE(); static int s_CL(); static int s_CM(); static int s_CR(); static int s_DC(); static int s_DL(); static int s_DO(); static int s_HO(); static int s_IC(); static int s_IS(); static int s_KE(); static int s_KS(); static int s_ND(); static int s_NL(); static int s_SE(); static int s_SF(); static int s_SO(); static int s_SR(); static int s_UE(); static int s_UP(); static int s_US(); static int s_VE(); static int s_VS(); static int s_WE(); static int s_WS(); /* TERMCAP interface (extern stuff in termlib) */ static int put_screen(); static char t_nam[32]; static char t_buf[1024]; static char t_area[1024]; extern int tgetent(); extern int tgetnum(); extern int tgetflag(); extern char *tgetstr(); extern char *tgoto(); extern int tputs(); extern short ospeed; static char *AL; static int AM; extern char *BC; static char *CD; static char *CE; static char *CL; static char *CM; static int CO; static char *CR; static char *CS; static char *DC; static char *DL; static char *DO; static char *HO; static char *IC; static char *IS; static char *KE; static char *KS; static int LI; static char *ND; static char *NL; extern char PC; static char *SE; static char *SF; static int SG; static char *SO; static char *SR; static char *UE; static int UG; extern char *UP; static char *US; static char *VE; static char *VS; static char *WE; static char *WS; /****************************************************************************/ #define error() {put_screen(BELL);flush_screen();} /****************************************************************************/ #define atoi3(h,m,l) (((h)-'0')*100+((m)-'0')*10+((l)-'0')) /****************************************************************************/ /* send command string to msh */ #define exec_msh(str,len) {\ put_port(M_CSI,(sizeof M_CSI)-1);\ put_port(str,len);\ } /****************************************************************************/ #define read_port(chp) {\ while(get_port(chp)<=0);\ } /****************************************************************************/ #define read_keyboard(chp) {\ while(get_keyboard(chp)<=0);\ } /****************************************************************************/ #define tput(str,nla) {tputs(str,nla,put_screen);} /****************************************************************************/ /* keyboard driver routines */ /****************************************************************************/ /****************************************************************************/ public keyboard_driver() /* get input from keyboard|buffer. * process any local (e.g. function key) (not to mention msh) commands. * send to port. */ { char ch; reg char *chp = &ch; reg bool isacmd; /* take input from buffer if necessary */ if (b_rpending) { b_take(); if (b_rpending) return; } if (get_keyboard(chp) > 0) { isacmd = FALSE; if (*chp == cmdchr) { /* test for escape (two cmdchrs) */ read_keyboard(chp); isacmd = (*chp != cmdchr); } if (isacmd) k_exec(*chp); else put_port(chp,1); } } /* keyboard_driver */ /****************************************************************************/ /* read buffer input */ /* perform '\r''\n' => '\n' transformation unless b_nlitflag */ #define MAXB (MAXQBUF >> 4) static b_take() { int i; char ch; reg char *chp = &ch; /* if we are taking stuff out of a file buffer, any keyboard * input will terminate buffer input. */ for (i = 0; i < MAXB; i++) { if (get_keyboard(chp) > 0) { b_rpending = FALSE; b_nlitflag = TRUE; break; } if ((ch = getc(b_fpending)) != EOF) { if (b_nlitflag && ch == '\r') { char tch; tch = getc(b_fpending); i++; if (tch != EOF) { if (tch == '\n') put_port(&tch,1); else { put_port(&ch,1); put_port(&tch,1); } } else { /* EOF */ put_port(&ch,1); /* \r */ b_rpending = FALSE; b_nlitflag = TRUE; break; } } else /* not \r */ put_port(&ch,1); } else { /* EOF */ b_rpending = FALSE; b_nlitflag = TRUE; break; } } if (!b_rpending) { /* open up again (reset to BOF) due to previous read of EOF */ WTYB[strlen(WTYB) - 1] = b_label; if ((window[b_label - '1'].b_file = fopen(WTYB,"a")) == NULL) error(); } } /* b_take */ #undef MAXB /****************************************************************************/ /* keyboard wty command execution */ static k_exec(ch) reg char ch; { switch (ch) { case '=' : k_EQ(); break; case 'h' : k_HE(); break; case 'q' : k_QX(); break; case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : k_SS(ch); break; case 'c' : k_CC(); break; case 'u' : k_CU(); break; case 'r' : k_RS(); break; case 'd' : k_DS(); break; case 'l' : k_LS(); break; case 'f' : k_FW(); break; case 'o' : k_OW(); break; case 'b' : k_BU(); break; case '\032' : k_SX(); break; default : error(); break; } } /* k_exec */ /****************************************************************************/ /* keyboard driver execution routines */ /****************************************************************************/ /****************************************************************************/ /* set command prefix */ static k_EQ() { char newcmd; read_keyboard(&newcmd); do_EQ(newcmd); } /* k_EQ */ static do_EQ(nc) char nc; { cmdchr = nc; } /* do_EQ */ /****************************************************************************/ /* help */ static k_HE() { do_HE(); } /* k_HE */ static do_HE() { s_CL(); help("<n> - change current input window"); help("c<n> - create new current input window"); help("cn<n> - create (nicely)"); help("d<n> - delete current input window"); help("dn<n> - delete (nicely)"); help("f<n> - fold current input window"); help("fn<n> - fold (nicely)"); help("o<n> - open another current input window"); help("on<n> - open (nicely)"); help("bc - create new buffer for current input window"); help("bd - delete buffer for current input window"); help("bf - fold buffer for current input window"); help("bo - open buffer for current input window"); help("br<n> - read buffer <n> into current input window"); help("brl<n> - read buffer <n> literally"); help("r - resize current input window"); help("l - label all windows"); help("h - help"); help("=<c> - set command prefix"); help("<ctrl-Z> - stop"); help("qy - quit"); flush_screen(); } /* do_HE */ static help(msg) char *msg; { int i, j; j = strlen(msg); for (i = 0; i < j; i++) put_screen(msg[i]); s_CR(); s_NL(); } /* help */ /****************************************************************************/ /* quit execution */ static k_QX() { char ch; read_keyboard(&ch); if (ch == 'y') do_QX(); else error(); } /* k_QX */ static do_QX() { int i; s_WE(); flush_screen(); for (i = 0; i < MAXWINDOWS; i++) if (window[i].b_isactive) { fclose(window[i].b_file); WTYB[strlen(WTYB) - 1] = i + '1'; unlink(WTYB); } exec_msh(M_QX,(sizeof M_QX)-1); } /* do_QX */ /****************************************************************************/ /* set current input shell to <n> */ static k_SS(n) char n; { if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible) { error(); return; } do_SS(n); } /* k_SS */ static do_SS(n) char n; { iwindow = &window[n - '1']; ilabel = n; new_olabel = TRUE; exec_msh(M_SS,(sizeof M_SS)-1); put_port(&ilabel,1); } /* do_SS */ /****************************************************************************/ /* create new current input (csh) shell <n> */ static k_CC() { char n; read_keyboard(&n); if (n == 'n') { niceflag = TRUE; read_keyboard(&n); } if (n < '1' || n > MAXLABEL || window[n - '1'].isactive) { error(); niceflag = FALSE; return; } do_CC(n); niceflag = FALSE; } /* k_CC */ static do_CC(n) char n; { char nn,tc[AMAXTCODE]; int r,c; ++nwindows; r = (int) LI/nwindows; c = CO; nn = n; mk_TC(tc,r,c); exec_msh(M_CC,(sizeof M_CC) - 1); put_port(&nn,1); put_port(tc,strlen(tc)); do_CCCU(n,r,c); } /* do_CC */ /****************************************************************************/ /* create new current input (user) shell <n> */ static k_CU() { /* not implemented */ } /* k_CU */ static do_CU(n,shell) char n; char *shell; { char nn,tc[AMAXTCODE]; int r,c; ++nwindows; r = (int) LI/nwindows; c = CO; nn = n; mk_TC(tc,r,c); exec_msh(M_CC,(sizeof M_CC) - 1); put_port(&nn,1); put_port(tc,strlen(tc)); put_port(shell,strlen(shell)); /* must have \n at end */ do_CCCU(n,r,c); } /* do_CU */ do_CCCU(n,r,c) char n; int r,c; { int i,j; window_t *wp; wp = &window[n - '1']; wp->row0 = 0; wp->col0 = 0; wp->col0max = c - 1; wp->isactive = TRUE; wp->isvisible = TRUE; for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) { if (wp->isvisible) { wp->top0 = r * j; wp->bot0 = r * (j + 1) - 1; wp->row0max = window[i].bot0 - window[i].top0; if (!initflag) { wgoto(i + '1'); s_CL(); flush_screen(); } /* don't do our self - CC/CU did it already */ if (n != ((char) i + '1') && !initflag) { do_SS(i + '1'); /* needed by do_RS */ do_RS(wp->row0max + 1, wp->col0max + 1); } j++; } } do_SS(n); } /* do_CCCU */ /****************************************************************************/ /* Resize current input shell */ static k_RS() { do_RS(iwindow->row0max + 1, iwindow->col0max + 1); } /* k_RS */ static do_RS(r,c) int r,c; { char tc[AMAXTCODE]; if (niceflag) return; mk_TC(tc,r,c); exec_msh(M_RS,(sizeof M_RS) - 1); put_port(tc,strlen(tc)); } /* do_RS */ /****************************************************************************/ /* delete current input shell; make <n> new current */ static k_DS() { char n; read_keyboard(&n); /* new current iwindow */ if (n == 'n') { niceflag = TRUE; read_keyboard(&n); } if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible || n == ilabel) { error(); niceflag = FALSE; return; } do_DS(n); niceflag = FALSE; } /* k_DS */ static do_DS(n) char n; { int i,j,k; window_t *wp; char nn; nwindows--; nn = n; exec_msh(M_DS,(sizeof M_DS)-1); put_port(&nn,1); iwindow->isactive = FALSE; iwindow->isvisible = FALSE; do_SS(n); k = (int) LI/nwindows; for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) { if (wp->isvisible) { wp->top0 = k * j; wp->bot0 = k * (j + 1) - 1; wp->row0max = window[i].bot0 - window[i].top0; do_SS(i + '1'); /* for do_RS */ do_RS(wp->row0max + 1, wp->col0max + 1); wgoto(i + '1'); s_CL(); flush_screen(); j++; } } do_SS(n); } /* do_DS */ /****************************************************************************/ /* label all shells */ static k_LS() { do_LS(); } /* k_LS */ static do_LS() { short i; window_t *wp; char ilab; ilab = ilabel; for (i = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) { if (wp->isvisible) { int r,c; wgoto(wp->label); r = wp->row0; c = wp->col0; cmove(0,(wp->col0max) - 2*SG - 2); tput(SO,1); put_screen(wp->label); if (wp->b_isactive) if (wp->b_isvisible) put_screen('B'); else put_screen('b'); else put_screen(' '); tput(SE,1); flush_screen(); cmove(r,c); } } wgoto(ilab); flush_screen(); } /* do_LS */ /****************************************************************************/ /* fold current input window, make <n> new current */ static k_FW() { char n; int i,j,k; window_t *wp; read_keyboard(&n); /* new current iwindow */ if (n == 'n') { niceflag = TRUE; read_keyboard(&n); } if (n < '1' || n > MAXLABEL || !window[n - '1'].isvisible || n == ilabel) { error(); niceflag = FALSE; return; } do_FW(n); niceflag = FALSE; } /* k_FW */ static do_FW(n) char n; { int i,j,k; window_t *wp; char nn; nwindows--; iwindow->isvisible = FALSE; do_SS(n); k = (int) LI/nwindows; for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) { if (wp->isvisible) { wp->top0 = k * j; wp->bot0 = k * (j + 1) - 1; wp->row0max = window[i].bot0 - window[i].top0; do_SS(i + '1'); /* for do_RS */ do_RS(wp->row0max + 1, wp->col0max + 1); wgoto(i + '1'); s_CL(); flush_screen(); j++; } } do_SS(n); } /* do_FW */ /****************************************************************************/ /* open new current input window <n> */ static k_OW() { char n; read_keyboard(&n); if (n == 'n') { niceflag = TRUE; read_keyboard(&n); } if (n < '1' || n > MAXLABEL || !window[n - '1'].isactive || window[n - '1'].isvisible) { error(); niceflag = FALSE; return; } do_OW(n); do_SS(n); niceflag = FALSE; } /* k_OW */ static do_OW(n) char n; { int i,j; window_t *wp; int r,c; nwindows++; r = (int) LI/nwindows; c = CO; wp = &window[n - '1']; wp->row0 = 0; wp->col0 = 0; wp->col0max = c - 1; wp->isvisible = TRUE; for (i = 0, j = 0, wp = &window[i]; i < MAXWINDOWS; i++, wp++) { if (wp->isvisible) { wp->top0 = r * j; wp->bot0 = r * (j + 1) - 1; wp->row0max = window[i].bot0 - window[i].top0; wgoto(i + '1'); s_CL(); do_SS(i + '1'); /* for do_RS */ do_RS(wp->row0max + 1, wp->col0max + 1); flush_screen(); j++; } } } /* do_OW */ /****************************************************************************/ /* Buffer operations */ static k_BU() { char op,arg; read_keyboard(&op); if (op == 'r') { read_keyboard(&arg); if (arg == 'l' /* ell */ ) { b_nlitflag = FALSE; read_keyboard(&arg); } if (arg < '1' || arg > MAXLABEL) { error(); return; } } do_BU(op,arg); } /* k_BU */ do_BU(op,arg) char op,arg; { struct stat sbuf; switch (op) { case 'c' : /* create buffer (new: must not exist) */ WTYB[strlen(WTYB) - 1] = ilabel; if (iwindow->b_isactive || stat(WTYB,&sbuf) != (-1) || (iwindow->b_file = fopen(WTYB,"w")) == NULL) { error(); break; } iwindow->b_isactive = TRUE; iwindow->b_isvisible = TRUE; break; case 'd' : /* delete buffer */ WTYB[strlen(WTYB) - 1] = ilabel; if (iwindow->b_isactive) { fclose(iwindow->b_file); unlink(WTYB); iwindow->b_isactive = FALSE; iwindow->b_isvisible = FALSE; } else error(); break; case 'f' : /* fold buffer */ if (iwindow->b_isvisible) { fflush(iwindow->b_file); iwindow->b_isvisible = FALSE; } else error(); break; case 'o' : /* open buffer */ if (iwindow->b_isactive && !iwindow->b_isvisible) iwindow->b_isvisible = TRUE; else error(); break; case 'r' : /* read buffer (close then open) */ if (!window[arg - '1'].b_isactive) { error(); break; } /* temp - see b_take for re-open at EOF */ b_label = arg; fclose(window[b_label - '1'].b_file); WTYB[strlen(WTYB) - 1] = arg; if ((b_fpending = fopen(WTYB,"r")) == NULL) { error(); break; } b_rpending = TRUE; break; default : error(); break; } } /* do_BU */ /****************************************************************************/ /* stop execution (of msh) (bsd) */ static k_SX() { do_SX(); } /* k_SX */ static do_SX() { tput(WE,1); exec_msh(M_SX,(sizeof M_SX)-1); put_port("0",1); tput(WS,1); } /* do_SX */ /****************************************************************************/ /* screen driver routines */ /****************************************************************************/ /****************************************************************************/ /* upon W_WG, save state, resume to indicated window at its state */ #define s_resume(resume) {\ char n;\ owindow->state = resume;\ read_port(&n);\ wgoto(n);\ continue;\ } /****************************************************************************/ /* send and cursor-process an output character to the screen */ #define s_put(ch) {\ if (s_nignore[ch]) \ if (s_special[ch]) {\ switch (ch) {\ case '\000' :\ case '\007' : put_screen(ch); break;\ case '\010' : s_BC();break;\ case '\012' : s_NL();break;\ case '\015' : s_CR();break;\ }\ } else {\ put_screen(ch);\ if (owindow->col0 < owindow->col0max)\ ++owindow->col0;\ else if (AM) {\ owindow->col0 = 0;\ if (owindow->row0 < owindow->row0max)\ ++owindow->row0;\ }\ }\ } /****************************************************************************/ #define next {owindow->state=0;continue;} /****************************************************************************/ #define b_write(chr) {\ if (owindow->b_isvisible)\ fprintf(owindow->b_file,"%c",chr);\ } /****************************************************************************/ /* Get input from port. * Process any control sequences. * Send to term. * Don't hog time. * Be careful of fragmented slave messages (use of s_resume). * Maintain cursor for each window. * Uh, BTW, this is a coroutine. */ public screen_driver() { char ch; reg char *chp = &ch; int i; bool first; first = TRUE; for (;;) { switch (owindow->state) { case 0 : if (get_port(chp) <= 0) goto done; if (first) { /* the cursor was at the point of expected * input, so lets get it back */ first = FALSE; wgoto(sav_olabel); } if (*chp == W_WG[0]) { s_resume(0); } if (*chp != W_CSI[0]) { /* normal case */ b_write(*chp); s_put(*chp); next; } /* we probably have a CSI */ case 1 : read_port(chp); if (*chp == W_WG[0]) { s_resume(1); } if (*chp != W_CSI[1]) { next; } /* we definitely have a CSI */ case 2 : read_port(chp); if (*chp == W_WG[0]) { s_resume(2); } /* pickup args if necess. */ owindow->s_argc = s_argc[owindow->s_code = *chp]; owindow->s_i = 0; case 3 : loop: if (owindow->s_i < owindow->s_argc) { read_port((char *) (chp = ((owindow->s_argv) + owindow->s_i))); if (*chp == W_WG[0]) { s_resume(3); } (owindow->s_i)++; goto loop; } s_argvp = (owindow->s_argv); b_write(W_CSI[0]); b_write(W_CSI[1]); b_write(owindow->s_code); for (i = 0; i < owindow->s_argc; i++) b_write(s_argvp[i]); (*(s_exec[owindow->s_code]))(); next; default : abort("wty: screen_driver state"); } /* switch */ } /* for (;;) */ done: if (!first || new_olabel) { /* move cursor back to point of expected input */ new_olabel = FALSE; sav_olabel = olabel; wgoto(ilabel); flush_screen(); } } /* screen_driver */ /****************************************************************************/ /* screen driver TERMCAP functions */ /****************************************************************************/ /****************************************************************************/ /* dummy */ static s_null() {} /****************************************************************************/ /* Add Line */ static s_AL() { tput(AL,1); } /* s_AL */ /****************************************************************************/ /* Backspace Character */ static s_BC() { if (owindow->col0 > 0) { --owindow->col0; tput(BC,1); } } /* s_BC */ /****************************************************************************/ /* Clear to end of Display */ static s_CD() { short i,r,c; tput(CE,1); r = owindow->row0; c = owindow->col0; for (i = r + 1; i <= owindow->row0max; i++) { cmove(i,0); tput(CE,1); } cmove(r,c); } /* s_CD */ /****************************************************************************/ /* Clear to End of line */ static s_CE() { reg int r,c; r = owindow->row0; c = owindow->col0; tput(CE,1); cmove(r,c); } /* s_CE */ /****************************************************************************/ /* CLear display */ static s_CL() { short i; for (i = 0; i <= owindow->row0max; i++) { cmove(i,0); tput(CE,1); } cmove(0,0); } /* s_CL */ /****************************************************************************/ /* Cursor Motion */ static s_CM() { reg int r,c; r = atoi3(*(s_argvp ), *(s_argvp+1), *(s_argvp+2)); c = atoi3(*(s_argvp+3), *(s_argvp+4), *(s_argvp+5)); cmove(r,c); } /* s_CM */ /****************************************************************************/ /* Carraige Return */ static s_CR() { tput(CR,1); owindow->col0 = 0; } /* s_CR */ /****************************************************************************/ /* Delete Character */ static s_DC() { tput(DC,1); } /* s_DC */ /****************************************************************************/ /* Delete Line */ static s_DL() { tput(DL,1); } /* s_DL */ /****************************************************************************/ /* DOwn line */ static s_DO() { tput(DO,1); if (owindow->row0 < owindow->row0max) ++owindow->row0; } /* s_DO */ /****************************************************************************/ /* HOme cursor */ static s_HO() { cmove(0,0); } /* s_HO */ /****************************************************************************/ /* Insert Character */ static s_IC() { tput(IC,1); } /* s_IC */ /****************************************************************************/ /* Initialization String */ static s_IS() { tput(IS,1); } /* s_IS */ /****************************************************************************/ /* Keypad End */ static s_KE() { tput(KE,1); } /* s_KE */ /****************************************************************************/ /* Keypad Start */ static s_KS() { tput(KS,1); } /* s_KS */ /****************************************************************************/ /* NonDestructive space */ static s_ND() { tput(ND,1); if (owindow->col0 < owindow->col0max) ++owindow->col0; else if (AM) { owindow->col0 = 0; if (owindow->row0 < owindow->row0max) ++owindow->row0; } } /* ND */ /****************************************************************************/ /* NewLine */ static s_NL() { tput(NL,1); if (owindow->row0 < owindow->row0max) ++owindow->row0; } /* NL */ /****************************************************************************/ /* Standout End */ static s_SE() { tput(SE,1); if (SG) { owindow->col0 += SG; if (owindow->col0 > owindow->col0max) if (AM) { owindow->col0 = 0; if (owindow->row0 < owindow->row0max) ++owindow->row0; } else owindow->col0 = owindow->col0max; } } /* s_SE */ /****************************************************************************/ /* Scroll Forwards (normal/up) */ static s_SF() { tput(SF,1); } /* s_SF */ /****************************************************************************/ /* StandOut start */ static s_SO() { tput(SO,1); if (SG) { owindow->col0 += SG; if (owindow->col0 > owindow->col0max) if (AM) { owindow->col0 = 0; if (owindow->row0 < owindow->row0max) ++owindow->row0; } else owindow->col0 = owindow->col0max; } } /* s_SO */ /****************************************************************************/ /* Scroll Reverse (backwords/down) */ static s_SR() { tput(SR,1); } /* s_SR */ /****************************************************************************/ /* Underscore End */ static s_UE() { tput(UE,1); if (UG) { owindow->col0 += UG; if (owindow->col0 > owindow->col0max) if (AM) { owindow->col0 = 0; if (owindow->row0 < owindow->row0max) ++owindow->row0; } else owindow->col0 = owindow->col0max; } } /* s_UE */ /****************************************************************************/ /* UP one line */ static s_UP() { tput(UP,1); if (owindow->row0 > 0) --owindow->row0; } /* s_UP */ /****************************************************************************/ /* Underscore start */ static s_US() { tput(US,1); if (UG) { owindow->col0 += UG; if (owindow->col0 > owindow->col0max) if (AM) { owindow->col0 = 0; if (owindow->row0 < owindow->row0max) ++owindow->row0; } else owindow->col0 = owindow->col0max; } } /* s_US */ /****************************************************************************/ /* Visual End */ static s_VE() { tput(VE,1); } /* s_VE */ /****************************************************************************/ /* Visual Start */ static s_VS() { tput(VS,1); } /* s_VS */ /****************************************************************************/ /* Wty End */ static s_WE() { tput(WE,1); } /* s_WE */ /****************************************************************************/ /* Wty Start */ static s_WS() { tput(WS,1); } /* s_WS */ /****************************************************************************/ /* Return > 0 if input ready else <= 0 */ static get_keyboard(chp) reg char *chp; { int tlen; if (fail(ioctl(STDIN,FIONREAD,&tlen))) { abort("wty: get_keyboard"); } if ((tlen > 0) && (fail(tlen = read(STDIN,chp,1)))) { abort("wty: get_keyboard[read]"); } return(tlen); } /* get_keyboard */ /****************************************************************************/ static char s_buf[AMAXCBUF]; /* screen output buffer */ static char *s_bufap = s_buf; /* add ptr */ static int s_len = 0; /* length */ /****************************************************************************/ /* Add one char to output buffer, flushing if necessary. * Also called from TERMCAP tputs routine. */ static put_screen(ch) char ch; { if (owindow->isvisible) { /* see if we need to flush our buffer first */ if (s_len >= MAXCBUF) flush_screen(); *s_bufap++ = ch; s_len++; } } /* put_screen */ /****************************************************************************/ /* this is the final output routine... */ static flush_screen() { if (s_len > 0) { write(STDOUT,s_buf,s_len); s_bufap = s_buf; s_len = 0; } } /* flush_screen */ /****************************************************************************/ /* goto new output window at its current row0,col0 */ wgoto(n) reg char n; { owindow = &window[n - '1']; olabel = n; if (owindow->bot0 != cbot || owindow->top0 != ctop) { cbot = owindow->bot0; ctop = owindow->top0; tput(tgoto(CS,cbot,ctop),1); } cmove(owindow->row0,owindow->col0); } /* wgoto */ /****************************************************************************/ /* move cursor absolute wrt current output window local CS (clipped) */ /* update window row0,col0 */ /* can't use currencies... */ cmove(r,c) reg int r,c; { reg int rr; rr = r + owindow->top0; rr = (rr > owindow->bot0 ? owindow->bot0 : rr); tput(tgoto(CM,c,rr),1); owindow->row0 = r; owindow->col0 = c; } /* cmove */ /****************************************************************************/ /* make terminal code string of form: "<WTYP>RxC;" */ static mk_TC(tc,r,c) char *tc; int r,c; { char cc[16]; strcpy(tc,WTYP); sprintf(cc,"%dx",r); strcat(tc,cc); sprintf(cc,"%d;",c); strcat(tc,cc); } /* mk_TC */ /****************************************************************************/ /* Initialize... */ public init_wty(argc,argv) int argc; char *argv[]; { int i; char *a,*p; bool f = FALSE; char *M_AL, *M_CD, *M_CE, *M_CL, *M_DC, *M_DL, *M_IC; char *M_ND, *M_SE, *M_SF, *M_SO, *M_SR, *M_UE, *M_US, *M_UP; int M_AM; struct sgttyb sgb; for (i = 0; i < 128; i++) { s_exec[i] = s_null; s_argc[i] = 0; s_nignore[i] = TRUE; s_special[i] = FALSE; } for (i = 1; i < 040; i++) s_nignore[i] = FALSE; /* ^A..^_ */ s_nignore[007] = TRUE; /* ^G */ s_nignore[010] = TRUE; /* ^H */ /* no ^I */ s_nignore[012] = TRUE; /* ^J */ s_nignore[015] = TRUE; /* ^M */ s_special[000] = TRUE; /* ^@ */ s_special[007] = TRUE; /* ^G */ s_special[010] = TRUE; /* ^H */ /* no ^I */ s_special[012] = TRUE; /* ^J */ s_special[015] = TRUE; /* ^M */ /* define TERMCAP execution entry points */ s_exec[W_AL[0]] = s_AL; s_exec[W_BC[0]] = s_BC; s_exec[W_CD[0]] = s_CD; s_exec[W_CE[0]] = s_CE; s_exec[W_CL[0]] = s_CL; s_exec[W_CM[0]] = s_CM; s_argc[W_CM[0]] = 6; s_exec[W_CR[0]] = s_CR; s_exec[W_DC[0]] = s_DC; s_exec[W_DL[0]] = s_DL; s_exec[W_DO[0]] = s_DO; s_exec[W_HO[0]] = s_HO; s_exec[W_IC[0]] = s_IC; s_exec[W_IS[0]] = s_IS; s_exec[W_KE[0]] = s_KE; s_exec[W_KS[0]] = s_KS; s_exec[W_ND[0]] = s_ND; s_exec[W_NL[0]] = s_NL; s_exec[W_SE[0]] = s_SE; s_exec[W_SF[0]] = s_SF; s_exec[W_SO[0]] = s_SO; s_exec[W_SR[0]] = s_SR; s_exec[W_UE[0]] = s_UE; s_exec[W_UP[0]] = s_UP; s_exec[W_US[0]] = s_US; s_exec[W_VE[0]] = s_VE; s_exec[W_VS[0]] = s_VS; s_exec[W_WE[0]] = s_WE; s_exec[W_WS[0]] = s_WS; /* get buffer name prefix */ if (strlen(strcpy(WTYB,getenv("WTYB"))) == 0) strcpy(WTYB,"wty.buf"); strcat(WTYB,".?"); /* setup TERMCAP interface */ #define bad(msg) {f=TRUE;printf("no %s in TERMCAP \n\r",msg);} strcpy(WTYP,getenv("WTYP")); /* verify that host terminal is at least as functional as prototype */ if ((i = tgetent(t_buf,WTYP)) < 0) {abort("wty: can't find TERMCAP database");} else if (i == 0) {abort("wty: can't find TERMCAP entry for wty prototype");} a = t_area; M_AL = tgetstr("al",&a); M_AM = tgetflag("am"); M_CD = tgetstr("cd",&a); M_CE = tgetstr("ce",&a); M_CL = tgetstr("cl",&a); M_DC = tgetstr("dc",&a); M_DL = tgetstr("dl",&a); M_IC = tgetstr("ic",&a); M_ND = tgetstr("nd",&a); M_SE = tgetstr("se",&a); M_SF = tgetstr("sf",&a); M_SO = tgetstr("so",&a); M_SR = tgetstr("sr",&a); M_UE = tgetstr("ue",&a); M_UP = tgetstr("up",&a); M_US = tgetstr("us",&a); /* load host TERMCAP entry */ strcpy(t_nam,getenv("TERM")); if ((i = tgetent(t_buf,t_nam)) < 0) {abort("wty: can't find TERMCAP database");} else if (i == 0) {abort("wty: can't find TERMCAP entry");} ioctl(STDOUT,TIOCGETP,&sgb); ospeed = sgb.sg_ospeed; a = t_area; if (!(AL = tgetstr("al",&a)) && M_AL) {bad("al");} if (!(AM = tgetflag("am")) && M_AM) {bad("am");} BC = a;if (!tgetstr("bc",&a)) {*a++ = '\010';*a++ = '\0';} if (!(CD = tgetstr("cd",&a)) && M_CD) {bad("cd");} if (!(CE = tgetstr("ce",&a)) && M_CE) {bad("ce");} if (!(CL = tgetstr("cl",&a)) && M_CL) {bad("cl");} if (!(CM = tgetstr("cm",&a))) {bad("cm");} if (!(CO = tgetnum("co"))) CO = 80; CR = a;if (!tgetstr("cr",&a)) {*a++ = '\015';*a++ = '\0';} if (!(CS = tgetstr("cs",&a))) {bad("cs");} if (!(DC = tgetstr("dc",&a)) && M_DC) {bad("dc");} if (!(DL = tgetstr("dl",&a)) && M_DL) {bad("dl");} DO = a;if (!tgetstr("do",&a)) {*a++ = '\012';*a++ = '\0';} HO = a;if (!tgetstr("ho",&a)) {*a++ = '\0';} if (!(IC = tgetstr("ic",&a)) && M_IC) {bad("ic");} IS = a;if (!tgetstr("is",&a)) {*a++ = '\0';} KE = a;if (!tgetstr("ke",&a)) {*a++ = '\0';} KS = a;if (!tgetstr("ks",&a)) {*a++ = '\0';} if (!(LI = tgetnum("li"))) LI = 24; if (!(ND = tgetstr("nd",&a)) && M_ND) {bad("nd");} NL = a;if (!tgetstr("nl",&a)) {*a++ = '\012';*a++ = '\0';} PC = ((p = tgetstr("pc",&a)) ? *p : '\0'); if (!(SE = tgetstr("se",&a)) && M_SE) {bad("se");} if (!(SF = tgetstr("sf",&a)) && M_SF) {bad("sf");} if (!(SG = tgetnum("sg"))) SG = 0; if (!(SO = tgetstr("so",&a)) && M_SO) {bad("so");} if (!(SR = tgetstr("sr",&a)) && M_SR) {bad("sr");} if (!(UE = tgetstr("ue",&a)) && M_UE) {bad("ue");} if (!(UG = tgetnum("ug"))) UG = 0; if (!(UP = tgetstr("up",&a)) && M_UP) {bad("up");} if (!(US = tgetstr("us",&a)) && M_US) {bad("us");} VE = a;if (!tgetstr("ve",&a)) {*a++ = '\0';} VS = a;if (!tgetstr("vs",&a)) {*a++ = '\0';} WE = a;if (!tgetstr("we",&a)) {*a++ = '\0';} WS = a;if (!tgetstr("ws",&a)) {*a++ = '\0';} if (f) {abort("wty: insufficent TERMCAP definition");} if (argc == 1) nwindows = 2; else if (argc == 2 && '0' < argv[1][0] && argv[1][0] <= MAXLABEL) nwindows = atoi(argv[1]); else abort("Usage: wty [n]"); s_WS(); flush_screen(); for (i = 0; i < MAXWINDOWS; i++) { window[i].state = 0; window[i].label = i + '1'; window[i].isactive = FALSE; window[i].isvisible = FALSE; window[i].b_isactive = FALSE; window[i].b_isvisible = FALSE; } initflag = TRUE; /* kludge: see do_CCCU */ for (i = 0; i < nwindows; i++) { --nwindows; /* kludge: see do_CC */ do_CC(i + '1'); } initflag = FALSE; do_SS('1'); wgoto('1'); sav_olabel = '1'; } /* init_wty */ @@ END-OF-FILE echo x - release/source/wty/wty.h sed 's/^@@//' > "release/source/wty/wty.h" << '@@ END-OF-FILE' /****************************************************************************\ * Copyright 1985 by George Nelan, Arizona State University. * * All rights reserved. Permission to use, modify, and copy these programs * * and documentation is granted, provided that the copy is not sold and * * that this copyright and permission notice appear on all copies. * \****************************************************************************/ /* Command Sequence Introducer */ #define W_CSI "\033@" /* ESC @ */ /* * ANSI function compatible TERMCAP entries for wty_. * Note: Capabilities marked with [*] are optional in the host definition. * Capabilities marked with (*) are optional only if the corresponding * wty_ TERMCAP capabilities are edited out of the wty_ entry. */ #define W_AL "A" /* Add Line (*) */ /* AM (Automatic Margin) (*) */ #define W_BC "B" /* Backspace Character [*] */ #define W_CD "C" /* Clear to end of Display (*) */ #define W_CE "D" /* Clear to End of line (*) */ #define W_CL "E" /* CLear display (*) */ #define W_CM "F" /* Cursor Motion */ /* CO# (# COlumns) [*] */ #define W_CR "G" /* Carriage Return [*] */ #define W_DC "H" /* Delete Character (*) */ #define W_DL "I" /* Delete Line (*) */ #define W_DO "J" /* DOwn line [*] */ #define W_HO "K" /* HOme cursor (*) */ #define W_IC "L" /* Insert Character (*) */ #define W_IS "M" /* Initialization String [*] */ #define W_KE "N" /* Keypad End [*] */ #define W_KS "O" /* Keypad Start [*] */ /* LI# (# LInes) [*] */ #define W_ND "P" /* NonDestructive space (*) */ #define W_NL "Q" /* NewLine [*] */ #define W_SE "R" /* Standout End (*) */ #define W_SF "S" /* Scroll Forwards (up) (*) */ /* SG# [*] */ #define W_SO "T" /* StandOut start (*) */ #define W_SR "U" /* Scroll Reverse (down) (*) */ #define W_UE "V" /* Underscore End (*) */ /* UG# [*] */ #define W_UP "W" /* UP Line (*) */ #define W_US "X" /* Underscore Start (*) */ #define W_VE "Y" /* Visual End [*] */ #define W_VS "Z" /* Visual Start [*] */ #define W_WE "a" /* Wty End [*] */ #define W_WS "b" /* Wty Start [*] */ @@ END-OF-FILE echo done