#verbatim % File viegophr.product % \documentstyle[lpr,appleps]{article} \pagestyle{myheadings} \begin{document} \def\LPtopA{{\tt VieGOPHER}} \def\LPtopB{{\sl The Code}} \pagenumbering{roman} %\thispagestyle{empty} \title{VieGOPHER\\---\\A Gopher System for VM/CMS\\---\\{\small The Code}} \author{Gerhard Gonter} \date{\today} \maketitle \begin{abstract} The Internet Gopher Protocol provides a simple, yet very effective method for distributed document search and retrieval. This document contains a documented source listing of the VieGOPHER system. Included are the sources of the server, the client and the VieGOPHER specific support programs and files. The printed version was generated from a Rose product file ({\tt VIEGOPHR.PRODUCT}). The product file itself was generated by linearizing a portion of a NED hypertext file that contains the documentation, the code and all extra information. The hypertext editor NED as well as the hypertext file for the VieGOPHER system can be obtained from {\tt Gerhard.Gonter@wu-wien.ac.at}. \end{abstract} \pagebreak[4] \begin{normalsize} \def\LPtopC{Contents} \tableofcontents \end{normalsize} \pagebreak[4] \setcounter{page}{1} \pagenumbering{arabic} %\begin{small} \small #endverbatim #section Introduction ROSE file: viegophr.product This file contains most files related to the VieGOPHER system. To install the product enter the command: ROSE VIEGOPHR % NOTE: This is not the original version of the document! % Instead, this is the sequenced and stripped version of a % hypertext file. % The linear ROSE version is provided for your convenience. % For more information contact the author. #set RELEASE VieGOPHER Ver 2.00.00 #set RELEASE_DATE 1993-06-16 #set RELEASE_TIME 18:00 #verbatim \def\LPbotC{VieGOPHER 2.00.00 1993-06-16 18:00} #endverbatim #set SENDFIX NO #section Help messages #help TEST_and_HACK There might be a number of Test and Hack options that can be configured. Usually you should select the predefined value. No further details about any option is given. #endhelp #set TEST_and_HACK YES #prompt TEST_and_HACK #help TEST_dd_binsel TEST_dd_binsel: BIN ... REXTCPIP, selector string is read binary and converted #endhelp #set TEST_dd_binsel NO #prompt TEST_dd_binsel #help Compactor Compactor allows the installation procedure to remove unrelevant parts from the EXEC and XEDIT files. Removed are comments, extra spaces, and lines. This reduces loading time a bit and might bring a little bit of performance. Compacting will only be executed if the program REXXCPCX.MODULE is present. Possible options are: NONE ... to compact nothing at all (the default) ALL ... to compact everything C ... to compact just client modules S ... to compact server modules T ... to compact tools Answering CU allows the compacting of the client and the utilities alone, the server will not be modified. CST is equivalent to ALL. #endhelp #set Compactor NONE #prompt Compactor #help MAKE_SERVER MAKE_SERVER defines if server modules will be generated. The preset value is YES. #endhelp #set MAKE_SERVER YES # currently no prompting, because ROSE doesn't do it anyway... # prompt MAKE_SERVER # This is a block of definitions for the server code. # switch MAKE_SERVER # case YES #help LOGFILE LOGFILE defines the file where server activities are logged. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set LOGFILE 'GOPHERD LOGFILE A' #prompt LOGFILE #help SELECTORFILE SELECTORFILE defines the file where all known selector strings are stored. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set SELECTORFILE 'GOPHERD SELECTOR A' #prompt SELECTORFILE #help GROUPFILE GROUPFILE defines the file where access group information is stored. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set GROUPFILE 'GOPHERD GROUPS A' #prompt GROUPFILE #help EAIFILE EAIFILE defines the file where handlers for embedded applications are declard. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set EAIFILE 'GOPHERD EAI A' #prompt EAIFILE #help DSKGRPFILE DSKGRPFILE names the file where assignments between disks and access groups are defined. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set DSKGRPFILE 'GOPHERD DSKGRP A' #prompt DSKGRPFILE #help ROOTINDEX ROOTINDEX defines the file that contains the root index or root menu. This file is sent out whenever an empty selector string is presented to the GOPHER server. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set ROOTINDEX 'GOPHERD INDEX A' #prompt ROOTINDEX #help GOPHER_PORT GOPHER_PORT is used in the server module to define the TCP port number of the gopher server. It is typically port number 70. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set GOPHER_PORT 70 #prompt GOPHER_PORT #help FINGER_PORT FINGER_PORT is used in the server module to define the TCP port number of the finger server. It is typically port number 79. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set FINGER_PORT 79 #prompt FINGER_PORT #help COOKIE_PORT COOKIE_PORT is used in the server module to define the TCP port number of the cookie server. It is typically port number 17. The preset value can be overridden by a global variable of the same name in the GOPHERD group. #endhelp #set COOKIE_PORT 17 #prompt COOKIE_PORT #help USE_SRE SRE means: Structured Representation Engine This is an experimental protocol and usually you would not select it. If you want to activate it, define the value YES, default is NO. #endhelp #set USE_SRE NO #prompt USE_SRE #help SRE_PORT SRE_PORT is used in the server module to define the TCP port number of the SRE server. It is typically port number 150. #endhelp #set SRE_PORT 150 #prompt SRE_PORT #help SRV_WAKEUP_SYSTEM SRV_WAKEUP_SYSTEM defines if or if not messages (SMSG, MSG) from other virtual machines or from RSCS are accepted and processed. Typically, messages are trapped and processed. In this case the variable value is set to 'YWAKEUP' or to 'WAKEUP', depending on the preference for either method. Trapping messages with option YWAKEUP requires YWAKEUP.MODULE, for WAKEUP you need IBM's WAKEUP module etc. If no message trapping is wanted, enter NO. If you have trouble using either method, select also NO. #endhelp #set SRV_WAKEUP_SYSTEM YWAKEUP #prompt SRV_WAKEUP_SYSTEM #help SRV_TCP SRV_TCP defines the TCP transport package to be used. Allowed value is REXTCPIP (the original and default package). RXSOCKET might be an option in the future. #endhelp #set SRV_TCP REXTCPIP # prompt SRV_TCP #help SRV_ORGANIZATION SRV_ORGANIZATION is the short name of your organization. Don't use single quotes in the string. #endhelp #set SRV_ORGANIZATION Univ. of Economics, Vienna, Austria #prompt SRV_ORGANIZATION # endswitch # this ends the block of fixes for the server modules #help MAKE_CLIENT MAKE_CLIENT defines if client modules will be generated. The preset value is YES. #endhelp #set MAKE_CLIENT YES # currently no prompting, because ROSE doesn't do it anyway... # prompt MAKE_CLIENT # This is a block of definitions for the client code. # switch MAKE_CLIENT # case YES #help STARTUP_DIR STARTUP_DIR defines a string that is displayed as a title for the first Gopher menu. Note: This value is *not* entered a quoted string. This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set STARTUP_DIR WU-Wien Gopher Information Service: Main Menu #prompt STARTUP_DIR #help CL_TCP CL_TCP defines the TCP transport package to be used by the client. Allowed values are REXTCPIP (the original package) and RXSOCKET (now the default). Please report any problem that you encounter with alternative TCP transport support. #endhelp #set CL_TCP RXSOCKET #prompt CL_TCP #help CL_USE_FDNS CL_USE_FDNS defines if the FDNS hack should be used to resolve a gopher hosts IP address or not. Default is NO. YES should be selected if you find it absolutely necessary to circumvent the GetIPAddr() function in REXTCPIP. If RXSOCKET is used, this variable must be set to NO. #endhelp #set CL_USE_FDNS NO # we don't allow this option any longer, it's not necessary ... # prompt CL_USE_FDNS #help CL_USE_LOCALFETCH CL_USE_LOCALFETCH defines if the client should be allowed to access data on the Gopher server's disk, if the server is on the very same host as the client. Localfetch is mainly a trade-off between data-privacy (whatever this means when we speak about a server) and performance. Using localfetch you can also setup a pseudo-server, that is, you provide only gopher data and don't run the server code. YES works if individual clients have read access to the gopher data files. This is needed for pseude servers. NO should work in most cases. Default is NO. #endhelp #set CL_USE_LOCALFETCH NO #prompt CL_USE_LOCALFETCH #help CL_LOCALHOST CL_LOCALHOST containts the IP address of the local machine. This information is only relevant when the option CL_USE_LOCALFETCH is answered with YES. In this case the client will try to read the gopher servers files direclty from his minidisk(s) without establishing a real TCP connection to the server. As fix value enter your machine's IP address. If you don't intend to allow direct access to the servers data you will not need to give the real IP address. Minor bug: if you're using RXSOCKET you should enter your hostname instead of the IP address #endhelp #set CL_LOCALHOST 999.99.9.99 #prompt CL_LOCALHOST #help CL_LANG CL_LANG defines the language that the client uses for messages to the user and help screens. Defined values are: ENGLISH and GERMAN, default is ENGLISH If you want another language included in the client, contact the author. #endhelp #set CL_LANG ENGLISH #prompt CL_LANG #help CL_DEFAULT_HOST CL_DEFAULT_HOST defines the Internet name of the default Gopher server that should be contacted on startup. Usually you will want to declare one of your favorite servers next door but you can of course also use the default value 'gopher.wu-wien.ac.at'. Note: Enter the host name as a quoted string! Default: 'gopher.wu-wien.ac.at' This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_DEFAULT_HOST 'gopher.wu-wien.ac.at' #prompt CL_DEFAULT_HOST #help CL_DEFAULT_PORT CL_DEFAULT_PORT defines the TCP port number at which your Internet Gopher server listens. The 'official' port number is now 70 and so is the default value. Note: the port number must be entered as a quoted string. Default: '70' This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_DEFAULT_PORT '70' #prompt CL_DEFAULT_PORT #help CL_DEFAULT_TYPE CL_DEFAULT_TYPE defines the code for the item type that you'll receive from your Gopher server. According to the protocol, a Gopher server should return a directory, if an empty selector string is sent to the server. The code for directory is '1' and so is the default value for this variable. Note: Values other than '1' are not recommended. Default: '1' This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_DEFAULT_TYPE '1' #prompt CL_DEFAULT_TYPE #help CL_TIME_OUT CL_TIME_OUT defines the time in seconds that the client will wait for the server to respond. After this timeout, the client will simply display a message about the unavailable server. Default value is 15. #endhelp #set CL_TIME_OUT 15 #prompt CL_TIME_OUT #help CL_INFO_LEVEL CL_INFO_LEVEL defines the default level of information that is in effect when the gopher client displays menus or files etc. Currently defined values are: 0 ... no function keys 5 ... two lines of function key description (DEFAULT) 9 ... four lines of function key description This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_INFO_LEVEL 5 #prompt CL_INFO_LEVEL #help CL_CONTACT CL_CONTACT defines the email address of a person that might be able to answer to typical questions of users. The default value 'Gerhard.Gonter@wu-wien.ac.at'. #endhelp #set CL_CONTACT Gerhard.Gonter@wu-wien.ac.at or GONTER@AWIWUW11.bitnet #prompt CL_CONTACT #help CL_TELNET_8 CL_TELNET_8 defines the name of the program for use with Telnet session pointed to by gopher item type 8. Basically, the gopher protocol does not specify, which terminal emulation should be used and it would be a nice feature if the Telnet program could automatically switch to VT100 or 3270 mode as appropriate. Since this is not done by IBM's Telnet you have to trade off between the various disadvantages. A possible choice for CL_TELNET_8 is 'TNVT100 EXEC', Arthur Ecock's VT100 emulater. This program requires TCPIP Version 2 and it might conflict with REXTCPIP in some (most?) installations. Note: Enter the name of the program as a quoted string and specify the filename *AND* the filetype Default: 'TELNET MODULE' This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_TELNET_8 'TELNET MODULE' #prompt CL_TELNET_8 #help CL_TELNET_T CL_TELNET_T defines the name of the program for use with Telnet session pointed to by gopher item type T. Basically, the gopher protocol does not specify, which terminal emulation should be used and it would be a nice feature if the Telnet program could automatically switch to VT100 or 3270 mode as appropriate. Since this is not done by IBM's Telnet you have to trade off between the various disadvantages. Type T is now an official extension to the original Gopher type list and is mostly used for 3270 based telnet sessions. Thus, in most situations the default value 'TELNET MODULE' would be ok. But you have the choice... Note: Enter the name of the program as a quoted string and specify the filename *AND* the filetype Default: 'TELNET MODULE' This setting can be re-defined by the user with the SETUP function in Gopher or by calling GOPHSTP. #endhelp #set CL_TELNET_T 'TELNET MODULE' #prompt CL_TELNET_T # endswitch # this ends the block of fixes for the client modules # This is a block of definitions for the server code. # switch MAKE_SERVER # case YES #section Multi-Threaded Server #erase GOPHERDD EXEC #module GOPHERDD EXEC #subsubsection main init /* ------------------------------------------------------------------- */ REVISION='GOPHERDD EXEC ## ## ##' /* */ /* an experimental multi-threaded multi-protocol daemon for VM/CMS: */ /* 1. GOPHER (protocol description spring 1992) */ /* 2. FINGER (RFC1288 compliant) */ /* 3. COOKIE (RFC865 compliant Quote-of-the-Day) */ #switch USE_SRE #case YES /* 4. S-REP ENGINE (experimental) */ #endswitch /* */ /* Uses: */ /* REXTCPIP MODULE */ #switch SRV_WAKEUP_SYSTEM #case YWAKEUP /* YWAKEUP MODULE */ #case WAKEUP /* WAKEUP MODULE */ #endswitch /* FINGERXX EXEC */ /* COOKIE EXEC */ /* REACCESS EXEC */ /* */ /* written: 1992-05-03: */ /* 1992-06-13: <> SRE: Structured Representation Engine */ /* 1993-04-14: <> EAI: Embedded Application Interface */ /* latest update: 1993-05-27 */ /* ------------------------------------------------------------------- */ 'GLOBALV SELECT GOPHERD STACK LOGFILE SELECTORFILE GROUPFILE EAIFILE', 'DSKGRPFILE GOPHERPORT FINGERPORT COOKIEPORT SREPORT ROOTINDEX' pull logfile pull selectorfile pull groupfile pull eaifile pull dskgrpfile pull gopherport pull fingerport pull cookieport pull sreport pull rootindex /* to be configured at installation time by ROSE: - - - - - - - - - - */ if logfile='' then logfile= ##; if selectorfile='' then selectorfile= ##; if groupfile='' then groupfile= ##; if eaifile='' then eaifile= ##; if dskgrpfile='' then dskgrpfile= ##; if rootindex='' then rootindex= ##; if gopherport='' then gopherport= ##; if fingerport='' then fingerport= ##; if cookieport='' then cookieport= ##; #switch USE_SRE #case YES if sreport='' then sreport= ##; #endswitch /* - - - - - - - - - - - - - - - - - - - end of configuration section */ stopflag=0; /* 1 -> stop execution ... */ rwdisk='A'; /* minidisk for R/W access */ selcnt=0; /* number of known GOPHER selectors */ selid.='' /* list of known selectors */ selfil.=''; /* associated file for the selector */ selgrp.=''; /* group which can access the given file */ grpcnt=0; /* number of known gropus */ grpid.=''; /* access group name */ grpaddr.=''; /* IP addresses that are members of a group */ dskgrpcnt= 0; /* number of known disk groups */ dskgrpnam.= ''; /* group name for this disk group */ dskgrppri.= 0; /* priority for this disk group */ dskgrpdsk.= ''; /* disk mode for this group */ TTW= 600; /* time to wait (10 minutes) */ MAXSQ= 16384; /* maximum output queue size */ recsblk= 64; /* records sent in one block */ dosleep= 0; /* to sleep or not to sleep? ... */ gsh_wait_exists= 0; /* Gopher Server Hook for WAIT */ gsh_wait_delay= 600; /* number of idle cycles before executing */ /* Gopher Server Wait hook code */ gsh_wait_ticks= 0; /* wait ticks elapsed sofar */ CRLF= '0D'x; TAB= '05'x; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #switch SRV_WAKEUP_SYSTEM #case YES ----> this mode is invalid, select either YWAKEUP or WAKEUP #case YWAKEUP 'set msg off' 'set smsg off' 'set wng off' 'YWAKEUP BEGIN *MSG SMSG' 'set msg iucv' 'set smsg iucv' #case WAKEUP 'set msg off' 'set smsg off' 'set wng off' 'WAKEUP +0 ( IUCVMSG' 'set msg iucv' 'set smsg iucv' #endswitch /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ nucx_loaded= 0; RECOVER: mtmax= 0; /* number of initialized servers */ if nucx_loaded/=0 then 'NUCXDROP REXTCPIP'; x= logtext('***** starting multi-threaded GOPHERDD daemon *****'); 'REXTCPIP' /* initialize REXTCPIP MODULE */ nucx_loaded= 0; x= reaccess(); # x= readselectors(selectorfile); /* initialize selector list */ # x= readgroups(groupfile); /* initialize group list */ # x= read_eai(eaifile); /* initialize EAI handler list */ should_listen=, /* slightly modified Gopher */ startserver('GOPHER', gopherport, '', ''); should_listen= should_listen+, /* almost normal Finger server */ startserver('FINGER', fingerport, '', ''); should_listen= should_listen+, /* simple Cookie server */ startserver('COOKIE', cookieport, '', ''); #switch USE_SRE #case YES should_listen= should_listen+, /* exper. S-REP Engine */ startserver('SRE', sreport, '', ''); #endswitch #subsubsection main loop /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ do forever; /* GOPHER ... a neverending story (hopefully) ... */ if dosleep=1 then do; 'SLEEP 1 SEC' /* Gopher Server Wait hook code */ gsh_wait_ticks= gsh_wait_ticks+1; if gsh_wait_exists=1 & gsh_wait_ticks=gsh_wait_delay then do; say date() time() 'GSH_WAIT'; 'GSH_WAIT'; gsh_wait_ticks= 0; end; end; dosleep=1; deadcount=0; emptycount=0; listencount=0; do mtact=1 to mtmax; sta=mtsta.mtact; gcn=mtgcn.mtact; select; /* scheduler :)) */ when sta=0 then do; /* unassigned slot */ emptycount=emptycount+1; end; when sta=1 then do; /* dead slot; recycle */ deadcount=deadcount+1; mtsta.mtact=0; say 'recycling dead slot['mtact']' end; /* listen as gopher, finger or cookie srv */ when sta=1000|sta=1001 then do; CONNSTAT(gcn); if CONN_STAT='Listening' then do; listencount=listencount+1; /* nothing else ... */ end; else do; if sta=1000 then do; /* fork() CMS style */ x= startserver(mttyp.mtact, mtprt.mtact, mtsel1.mtact, mtsel2.mtact); x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', 'status changed from listening to' CONN_STAT); listencount=listencount+1; end; select; when CONN_STAT='Connected' then do; /* we have contact! */ x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', 'connected to' foreign_addr':'foreign_port); if mttyp.mtact='COOKIE' then do; x= gopherspecial(mtact 'SPEC-COOKIE'); mtsta.mtact= 1020; end; else mtsta.mtact= 1010; mtttw.mtact= TTW; mtfad.mtact= foreign_addr; dosleep= 0; end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact= 0; end; when CONN_STAT='Trying to open' then do; say 'Hey, someone''s trying to open!' /* nothing */ mtsta.mtact= 1005; mtttw.mtact= TTW; end; when CONN_STAT='Receiving only' |, CONN_STAT='Sending only' then do; x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; end; otherwise do; x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/; end/*else do*/; end/*when sta=1000*/; when sta=1005 then do; /* after listening, the slot changed */ CONNSTAT(gcn); /* to trying to open... */ select; when CONN_STAT='Trying to open' then do; /* wait a \LaTeX\ \LaTeX\ little bit longer ... */ say 'sta=1005, still trying to open...' mtttw.mtact=mtttw.mtact-1; if mtttw.mtact=0 then mtsta.mtact= 1; end; when CONN_STAT='Listening' then mtsta.mtact= 1; otherwise mtsta.mtact= 1001; end/*select*/; end; when sta=1010 then do; /* waiting for a selector string */ connstat(gcn); select; when CONN_STAT='Listening' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpectedly listening!'); mtsta.mtact=1000; end; when CONN_STAT='Connected' then do; mtttw.mtact=mtttw.mtact-1; yy=0; say '1010: bytes_to_read='bytes_to_read 'ttw='mtttw.mtact; if bytes_to_read>0 then do; mtttw.mtact=mtttw.mtact+TTW; #switch TEST_dd_binsel #case BIN tcpreceive(gcn,bin); /* ## BINARY ## */ #default tcpreceive(gcn); #endswitch tl0=TCPLINE.0; say 'gcn='gcn' TCPRC='TCPRC' TCPLINE.0='TCPLINE.0 'tl0='tl0 say 'length(tcpline.1)='length(tcpline.1) if TCPRC = 0 &, tl0 /= 'TCPLINE.0' &, tcpline.0 >= 1 then do; /* strange error (REXTCPIP): if we get a line with the */ /* length of exactly 8228 byte we really only get JUNK */ /* cure: a) either deliver a message and tell the user */ /* to try it again, b) deliver the root menu ... */ /* I choose a) for now... */ if length(tcpline.1)>3000 then do; yy= 3; end; else do; #switch TEST_dd_binsel #case BIN sel=A2E(tcpline.1); /* ## BINARY ## */ parse var sel sel '0D'x . say 'sel='sel sel=strip(sel); #default sel=strip(tcpline.1); #endswitch say 'bytes_to_read='bytes_to_read 'sel='sel if TCPRC=0 then mtsel.mtact=sel; yy= gopherselector(mtact sel); dosleep= 0; end; end; end; if mtttw.mtact=0 then yy= 99; select; when yy= 0 then do; /* just wait ... */ end; when yy= 1 then do; TCPSEND(gcn, mtmsg.mtact); mtsta.mtact= 1090; end; when yy=2 then mtsta.mtact= 1020; when yy=3 then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') trouble 1010!'); mtsta.mtact= 1090; end; otherwise do; x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') crash, waited too long!'); mtsta.mtact= 1; end; end; end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact= 0; end; when CONN_STAT='Receiving only' |, CONN_STAT='Sending only' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact= 1; end; when CONN_STAT='Trying to open' then do; /* nothing */ say 'sta=1010, trying to open???? very strange...' end; otherwise do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/ end/*when sta=1010*/; when sta=1020 then do; /* in the process of sending a file */ CONNSTAT(gcn); select; when CONN_STAT='Listening' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpectedly listening!'); mtsta.mtact=1000; end; when CONN_STAT='Connected' then do; if unacked_bytes < MAXSQ then do; rec= mtrec.mtact; recs= recsblk; if rec+recs >= mtsiz.mtact then do; recs= mtsiz.mtact-rec; mtsta.mtact= 1090; /* the last block of the file */ /* say '['mtact']' mttyp.mtact'('mtprt.mtact')' sta '-> 1090'; */ end; 'EXECIO' recs 'DISKR' mtfil.mtact rec+1 '(FINIS STEM LIN.' if LIN.0 < recs then do; /* problems? */ recs= LIN.0; mtsta.mtact= 1090; /* the last block of the file */ end; do i=1 to recs; line= LIN.i; if substr(line,1,1)='.' then line='.'line tcpsend(gcn,line); end; mtrec.mtact= rec+recs; dosleep= 0; end; /* else say '('mtact')unacked_bytes='unacked_bytes */ end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact= 0; end; when CONN_STAT='Receiving only' |, CONN_STAT='Trying to open' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; end; when CONN_STAT='Sending only' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') connection ended by other side'); /* say '*** ('mtact') status: 1020->1090' */ mtsta.mtact=1090; dosleep=0; end; otherwise do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/ end/*when sta=1020*/; when sta=1090 then do; /* trying to close the connection */ CONNSTAT(gcn); x=xconnstat(gcn 'Status 1090'); select; when CONN_STAT='Listening' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpectedly listening!'); mtsta.mtact=1000; end; when CONN_STAT='Connected' |, CONN_STAT='Sending only' then do; i=100; if mttyp.mtact='GOPHER' then tcpsend(gcn,'.'); do while (CONN_STAT='Connected' & bytes_to_read > 0); tcpreceive(gcn); say 'TCPRC='TCPRC 'TCPLINE.0='TCPLine.0 if tcpline.0 <= 0 then leave; say 'TRASH:' tcpline.1 i=i-1; if i<= 0 then leave; CONNSTAT(gcn); end; tcpclose(gcn); /* now close the tcp connection */ x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact') closing'); mtsta.mtact=0; dosleep=0; 'CP Q TIME' end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact=0; end; when CONN_STAT='Receiving only' |, CONN_STAT='Trying to open' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; end; otherwise do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/ end/*when sta=1090*/; #switch USE_SRE #case YES /* SRE: S-REP Engine (structured representation engine) **********/ when sta=2000 then do; /* SRE: listening */ CONNSTAT(gcn); select; when CONN_STAT='Listening' then do; listencount=listencount+1; /* nothing... */ end; when CONN_STAT='Connected' then do; /* we have contact! */ /* fork() CMS style */ x= startserver(mttyp.mtact, mtprt.mtact, mtsel1.mtact, mtsel2.mtact); x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', 'connected to' foreign_addr':'foreign_port); listencount= listencount+1; mtsta.mtact= 2010; mtttw.mtact= TTW; mtfad.mtact= foreign_addr; dosleep=0; end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x= logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact=0; end; when CONN_STAT='Trying to open' then do; /* nothing */ end; when CONN_STAT='Receiving only' |, CONN_STAT='Sending only' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; end; otherwise do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/ end/*when sta=2000*/; when sta=2010 then do; /* SRE: connected */ CONNSTAT(gcn); select; when CONN_STAT='Listening' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpectedly listening!'); mtsta.mtact=2000; end; when CONN_STAT='Connected' then do; mtttw.mtact=mtttw.mtact-1; yy= 0; if bytes_to_read>0 then do; mtttw.mtact=mtttw.mtact+TTW; tcpreceive(gcn); tl0=TCPLINE.0; say 'gcn='gcn' TCPRC='TCPRC' TCPLINE.0='TCPLINE.0 'tl0='tl0, 'bytes_to_read='bytes_to_read if TCPRC = 0 &, tl0 /= 'TCPLINE.0' &, tcpline.0 >= 1 then do; do i=1 to tcpline.0; say 'tcpline.'i'='tcpline.i end; dosleep=0; end; end; end; when CONN_STAT='Closed' |, CONN_STAT='Non-existant' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:' CONN_STAT); mtsta.mtact=0; end; when CONN_STAT='Receiving only' |, CONN_STAT='Sending only' then do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unexpected connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; end; when CONN_STAT='Trying to open' then do; /* nothing */ end; otherwise do; x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown connstat:', CONN_STAT '-> now dead'); mtsta.mtact=1; /* now officially declared dead */ end; end/*select*/ end/*when sta=2010*/; #endswitch otherwise do; /* unknown slot status */ x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')', '('sta') unknown status -> now dead'); mtsta.mtact=1; end; end/*select*/ end/*do mtact=1 to mtmax*/; /* now check if we should commit suicide: */ if (deadcount>100) then do; /* so many dead slots? */ x=logtext('***** too many dead slots! deadcount=', deadcount 'leaving *****'); leave; end; if (listencount<=0 & emptycount+deadcount>=mtmax) then do; x=logtext('***** no-one is listening! listencount=', listencount 'leaving *****'); leave; end; if mtmax>1 & mtsta.mtmax=0 then do; mtmax=mtmax-1; say 'mtmax is now down to' mtmax end; #switch SRV_WAKEUP_SYSTEM #case YES ----> this mode is invalid, select either YWAKEUP, WAKEUP or NO #case YWAKEUP if dosleep then do; 'YWAKEUP (*MSG SMSG NOWAIT' select; when ywakeup.1='NOWAIT' then do; /* nothing */ end; when ywakeup.1='*MSG' then do; # do i=0 to ywakeup.0; # say 'ywakeup.'i'='ywakeup.i # end; if ywakeup.5='SMSG' & ywakeup.4='SYS-REACCESS' then do; x= reaccess(); 'MSG' ywakeup.3 'I''ve reaccessed my mini disks'; end; end; otherwise do; /* nothing */ end; end; end; #case WAKEUP if dosleep then do; q=queued(); 'WAKEUP +0 (IUCVMSG' ret=rc; do queued()-q; pull text select; when ret=5 then do; parse var text '*' wakeup1 wakeup3 wakeup4 select; when wakeup1='NOWAIT' then nop; when wakeup1='MSG' then nop; when wakeup1='SMSG' & wakeup4='SYS-REACCESS' then do; x= reaccess(); 'MSG' wakeup3 'I''ve reaccessed my mini disks'; end; otherwise do; /* nothing */ end; end/*select*/; end; otherwise do; /* nothing */ end; end/*select*/; end/* do queued()-q */; end; #endswitch end/*do forever*/; x=logtext('***** stopping *****') 'NUCXDROP REXTCPIP'; exit(0); #subsubsection startserver /* ------------------------------------------------------------------- */ /* start a protocol handler of a particular type on a given port */ /* RETURNS: 0 .. no server started, 1 .. server started */ startserver: parse arg type, port, sel1, sel2 say 'startserver type='type 'port='port 'sel1='sel1 'sel2='sel2 if port <= 0 then return 0; xsta= 1; select; when type='GOPHER' | type='FINGER' | type='COOKIE' then do; xsta= 1000; end; when type='SRE' then do; xsta= 2000; end; otherwise return 0; end; do i=1 to mtmax; if mtsta.i=0 then leave; end; if i=mtmax & mtsta.i/=0 then i= i+1; if i>mtmax then do; /* create a new slot */ mtmax= i; end; mtgcn.i= LISTEN(port,0); mttyp.i= type; mtprt.i= port; mtsel1.i= sel1; mtsel2.i= sel2; x= logtext('['i']' type'('port') started; TCPRC='TCPRC); if TCPRC=0 then mtsta.i= xsta; else mtsta.i= 1; return 1; #subsubsection gopherselector /* ------------------------------------------------------------------- */ gopherselector: parse arg nr full_selector x= logtext('['nr']' mttyp.nr'('mtprt.nr') sel='full_selector); if mttyp.nr='EXTRA' then do x= mtsel1.nr; if full_selector/='' && x/='' then x= x||TAB; full_selector= x||full_selector||mtsel2.nr; end; parse var full_selector selector':'lfrom':'lto if lfrom='' then lfrom= 0; if lto ='' then lto = 32700; /* catch all */ if mttyp.nr='FINGER' then do if selector='' then selector='NAMES' return gopherspecial(nr 'SPEC-FINGER' selector) end; /* empty selector means, return root index */ if selector='' then do return gophersend(nr lfrom lto rootindex) end; /* fast access to files */ if left(selector,5)='FILE-' then do; parse var selector fn'.'ft'.'.; q= queued(); address '' 'LISTFILE' fn ft '*' '(LIFO ALLOC NOHEAD' q= queued()-q; use= 0; do i=1 to q; parse pull . . xfm . . xsiz . xuse= check_disk_group(xfm, mtfad.mtact); if xuse>use then do; use= xuse; /* mark the priority of this disk item */ fm= xfm; /* record the file mode for later */ end; end; if use>0 then return gophersend(nr lfrom lto fn ft fm); end; /* embedded applications */ if left(selector,3)='EA-' then do return gopher_eai(nr substr(selector,4)) end; /* system items */ if left(selector,4)='SYS-' then do return gophersys(nr selector) end; /* special items */ if left(selector,5)='SPEC-' then do return gopherspecial(nr selector':'lfrom':'lto); end; /* according to gopher specs: optionally, a date specification can */ /* follow the selector string, seperated by a tab character. */ /* ... trash this thing, if present. */ parse var selector selector '05'x . selector= strip(selector); /* go down the list of known selectors and see if we care ... */ do i=1 to selcnt; parse upper var selfil.i fn'.'ft'.'fm parse upper var selector sfn'.'sft'.'sfm use= check_group(selgrp.i, mtfad.mtact); /* check for wildcards in the filename */ if (use & fn='*' & ft=sft) then do; say 'wildcardfile: selector='selector 'fnm='fn sft fm return gophersend(nr lfrom lto sfn ft fm); end; /* check for wildcards in the filetype */ if (use & ft='*' & fn=sfn) then do; say 'wildcardfile: selector='selector 'fnm='fn sft fm return gophersend(nr lfrom lto fn sft fm); end; if (use & selid.i=selector) then do; parse upper var selfil.i fn'.'ft'.'fm return gophersend(nr lfrom lto fn ft fm); end; end; mtmsg.nr= '3 sorry... this item is unknown here'; return 1; #subsubsection gophersend /* ------------------------------------------------------------------- */ gophersend: parse arg nr lfrom lto fn ft fm /* check out irregularities */ if lfrom<0 then lfrom=0; if lto>32500 then lto=32500; if lfrom>lto then do; lfrom=0; lto=32500; end; upper fn ft fm; 'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD' if rc/=0 then do; mtmsg.nr= '3 sorry... this item is currently unavailable'; return 1; end; pull . . . . . filesize . /* say 'send:' fn ft fm 'size='filesize */ if filesize<=0 then do; mtmsg.nr= '3 sorry... this item is no ok'; return 1; end; mtfil.nr= fn ft fm; if lto 0 then mtrec.nr= lfrom; else mtrec.nr= 0; return 2; #subsubsection gopherspecial /* ------------------------------------------------------------------- */ gopherspecial: parse arg nr sel':'lfrom':'lto xfnm= 'GOPHERDD TMP'nr rwdisk address command 'STATEW' xfnm if rc=0 then 'ERASE' xfnm; qtxt= 'QUERY LOGMSG' select; when left(sel,11)='SPEC-COOKIE' then do; 'COOKIE' xfnm return gophersend(nr 0 32700 xfnm); end; when word(sel,1)='SPEC-FINGER' then do; parse var sel . sel 'FINGERXX' xfnm '(0' sel return gophersend(nr 0 32700 xfnm); end; when substr(sel,1,11)='SPEC-FINGER' /* &, substr(sel,12,1)='05'x */ then do; 'FINGERXX' xfnm '(0' substr(sel,13); return gophersend(nr 0 32700 xfnm); end; otherwise do; parse var sel s1||'05'x||s2 if s1='SPEC-THUMB' & s2/='' then do; 'FINGERXX' xfnm '(1' s2 return gophersend(nr 0 32700 xfnm); end; mtmsg.nr= '3 sorry... this SPEC-item is unknown here'; return 1; end; end; return 0; #subsubsection gophersys /* ------------------------------------------------------------------- */ gophersys: parse arg nr sel xfnm= 'GOPHERDD TMP'nr rwdisk address command 'STATEW' xfnm if rc=0 then 'ERASE' xfnm; qtxt= 'QUERY LOGMSG' select; when left(sel,10)='SYS-QUERY ' then qtxt='QUERY' substr(sel,10); when left(sel,8)='SYS-STAT' then do; push 'data-to=Gopher@awiwuw11.wu-wien.ac.at' push 'links-to=Gopher@awiwuw11.wu-wien.ac.at' push 'bugs-to=Gerhard.Gonter@wu-wien.ac.at' push 'contact=##' 'IDENTIFY (LIFO'; parse pull z push 'server='z push 'revision='REVISION push 'note=Implemented using ## by', '' push 'note=##' push 'note=This is a VM/CMS GOPHER Server of the', 'EXECIO 9 DISKW' xfnm '(FINIS' return gophersend(nr 0 32700 xfnm); end; when selector='SYS-REACCESS' then do; x= reaccess(); mtmsg.nr= '3 I''ve reaccessed my mini disks'; return 1; end; when sel='SYS-STOP' then do; say 'stopping...' /* stopflag=1; */ mtmsg.nr= '3 I''m *not* stopping now, honey...'; return 1; end; otherwise do; mtmsg.nr= '3 sorry... this SYS-item is unknown here'; return 1; end; end; 'MAKEBUF' upper qtxt 'EXECIO * CP (FIFO STR' qtxt 'EXECIO' queued() 'DISKW' xfnm '(FINIS' 'DROPBUF' return gophersend(nr 0 32700 xfnm); #subsubsection gopher\_eai /* ------------------------------------------------------------------- */ gopher_eai: parse arg nr selector '05'x keyword xfnm= 'GOPHERDD TMP'nr rwdisk address command 'STATEW' xfnm if rc=0 then 'ERASE' xfnm; do i=1 to eaicnt; use= check_group(eaigrp.i, mtfad.mtact); if (use & eaiid.i=selector) then do; cmdline= prepare_command(eaipgm.i, xfnm, keyword); say 'cmd:' cmdline interpret cmdline; return gophersend(nr 0 32700 xfnm); end; end; mtmsg.nr= '3 sorry... this item is unknown here'; return 1; #subsubsection reaccess /* ------------------------------------------------------------------- */ reaccess: parse arg . x=logtext('*** reaccessing disks'); 'REACCESS' x= readselectors(selectorfile); /* initialize selector list */ x= readgroups(groupfile); /* initialize group list */ x= read_eai(eaifile); /* initialize EAI handler list */ x= read_disk_groups(dskgrpfile); /* initialize list of disk groups */ address '' 'STATE GSH_REAC EXEC' if rc=0 then do; say 'executing gopher server reaccess hook code' 'GSH_REAC' end; address '' 'STATE GSH_WAIT EXEC' if rc=0 then gsh_wait_exists= 1; else gsh_wait_exists= 0; return 0; #subsubsection readselectors /* ------------------------------------------------------------------- */ readselectors: parse arg fn ft fm upper fn ft fm; 'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD' if (rc/=0) then return -1; pull . . . . . filesize . say 'selector file has' filesize 'entries' selcnt=0; selid.='' selfil.=''; selgrp.=''; do i=1 to filesize; 'EXECIO 1 DISKR' fn ft fm '(LIFO' parse pull fil . grp sel /*say '['i'] ' fil sel */ if (fil/='*' & fil/='#') then do; selcnt=selcnt+1; selid.selcnt= strip(sel); selfil.selcnt= fil; selgrp.selcnt= grp; end; end/*do*/; x=logtext('***' selcnt 'selectors found'); 'FINIS' fn ft fm; return 0; #subsubsection readgroups /* ------------------------------------------------------------------- */ readgroups: parse arg fn ft fm upper fn ft fm; address '' 'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD' if (rc/=0) then return -1; pull . . . . . filesize . say 'group file has' filesize 'entries' grpcnt= 0; grpid.= '' grpaddr.= ''; do i=1 to filesize; 'EXECIO 1 DISKR' fn ft fm '(LIFO' parse pull grp addrs /*say '['i'] ' fil sel */ if (grp/='*' & grp/='#') then do; do while addrs/=''; parse var addrs addr addrs grpcnt= grpcnt+1; grpid.grpcnt= grp; grpaddr.grpcnt= addr; end/*while*/ end; end/*do*/; x= logtext('***' grpcnt 'groups found'); 'FINIS' fn ft fm; return 0; #subsubsection read\_eai /* ------------------------------------------------------------------- */ read_eai: parse arg fnm upper fnm; 'LISTFILE' fnm '(ALLOC LIFO NOHEAD' if (rc/=0) then return -1; pull . . . . . filesize . say 'EAI file has' filesize 'entries' eaicnt= 0; eaiid.= '' eaipgm.= ''; eaigrp.= ''; do i=1 to filesize; 'EXECIO 1 DISKR' fnm '(LIFO' parse pull hdl . grp pgm say '['i'] ' hdl grp pgm if (hdl/='*' & hdl/='#') then do; eaicnt= eaicnt+1; eaiid.eaicnt= strip(hdl); eaipgm.eaicnt= pgm; eaigrp.eaicnt= grp; end; end/*do*/; x= logtext('***' eaicnt 'EAI handlers found'); 'FINIS' fnm; return 0; #subsubsection read\_disk\_groups /* ------------------------------------------------------------------- */ read_disk_groups: parse arg fnm upper fnm; address '' 'LISTFILE' fnm '(ALLOC LIFO NOHEAD' if (rc/=0) then return -1; pull . . . . . filesize . say 'disk group file has' filesize 'entries' dskgrpcnt= 0; dskgrpnam.= '' dskgrpdsk.= ''; do i=1 to filesize; 'EXECIO 1 DISKR' fnm '(LIFO' parse pull dsk pri grps /*say '['i'] ' fil sel */ if (dsk/='*' & dsk/='#') then do; do while grps/=''; parse var grps grp grps dskgrpcnt= dskgrpcnt+1; dskgrpdsk.dskgrpcnt= dsk; dskgrppri.dskgrpcnt= pri; dskgrpnam.dskgrpcnt= grp; end/*while*/ end; end/*do*/; x= logtext('***' dskgrpcnt 'disk groups found'); 'FINIS' fnm; return 0; #subsubsection xconnstat #switch SRV_TCP #case REXTCPIP /* ------------------------------------------------------------------- */ xconnstat: parse arg gcn msg say msg connstat(gcn); say 'conn_stat='conn_stat' bytes_to_read='bytes_to_read' ', 'unacked_bytes='unacked_bytes say 'connection:' local_addr':'local_port ' -> ', foreign_addr':'foreign_port return 0; #endswitch #subsubsection logtext /* ------------------------------------------------------------------- */ logtext: parse arg txt 'EXECIO 1 DISKW' logfile '(FINIS STRING' date(sorted) time() txt say date(sorted) time() txt return rc; #subsubsection prepare\_command /* ------------------------------------------------------------------- */ prepare_command: parse arg template, tmpfn tmpft tmpfm, keyw outline= ''; ll= length(template); j= 1; do while j<=ll; select; when substr(template, j, 3)='@fn' then do; outline= outline||tmpfn; j= j+3; end; when substr(template, j, 3)='@ft' then do; outline= outline||tmpft; j= j+3; end; when substr(template, j, 3)='@fm' then do; outline= outline||tmpfm; j= j+3; end; when substr(template, j, 3)='@kw' then do; outline= outline||keyw; j= j+3; end; otherwise do; outline= outline||substr(template, j, 1); j= j+1; end; end/*select*/; end/*do*/; return outline; #subsubsection check\_group /* ------------------------------------------------------------------- */ check_group: parse arg xxgrp, fad if (xxgrp='*') then return 1; do j=1 to grpcnt; if (grpid.j=xxgrp &, grpaddr.j=substr(fad,1,length(grpaddr.j)) ) then return 1; end;/*do*/ return 0; #subsubsection check\_disk\_group /* ------------------------------------------------------------------- */ check_disk_group: parse arg xxfm, fad do j=1 to dskgrpcnt; if dskgrpdsk.j=xxfm then do; u= check_group(dskgrpnam, mtfad.mtact); if u>0 then return dskgrppri.j; end; end; return 0; #subsubsection EBCDIC to ASCII conversion /* E2A EXEC simple ebcdic to ascii translate by JCA ------------------ */ E2A: return translate(arg(1), , '000102030009007F0000000B0C0D0E0F'x||, '1011121300000800181900001C1D1E1F'x||, '00000000000A171B0000000000050607'x||, '0000160000000004000000001415001A'x||, '20000000000000000000002E3C282B7C'x||, '2600000000000000000021242A293B7E'x||, '2D2F0000000000000000002C255F3E3F'x||, '005E00000000000000603A2340273D22'x||, '00616263646566676869007B00000000'x||, '006A6B6C6D6E6F707172007D00000000'x||, '007E737475767778797A0000005B0000'x||, '000000000000000000000000005D0000'x||, '7B414243444546474849000000000000'x||, '7D4A4B4C4D4E4F505152000000000000'x||, '5C00535455565758595A000000000000'x||, '30313233343536373839000000000000'x); #subsubsection ASCII to EBCDIC conversion /* A2E EXEC simple ascii to ebcdic translate by JCA ------------------ */ A2E: return translate(arg(1), , '00010203372D2E2F1605250B0C0D0E0F'x||, '101112133C3D322618193F271C1D1E1F'x||, '405A7F7B5B6C507D4D5D5C4E6B604B61'x||, 'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x||, '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x||, 'D7D8D9E2E3E4E5E6E7E8E9ADE0BD716D'x||, '79818283848586878889919293949596'x||, '979899A2A3A4A5A6A7A8A9C04FD05F07'x||, '00010203372D2E2F1605250B0C0D0E0F'x||, '101112133C3D322618193F271C1D1E1F'x||, '405A7F7B5B6C507D4D5D5C4E6B604B61'x||, 'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x||, '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x||, 'D7D8D9E2E3E4E5E6E7E8E9ADE0BD716D'x||, '79818283848586878889919293949596'x||, '979899A2A3A4A5A6A7A8A98B4F9B5F07'x); # endswitch # this ends the block of fixes for the server modules # This is a block of definitions for the client code. # switch MAKE_CLIENT # case YES #section Client #erase GOPHERC EXEC #module GOPHERC EXEC /***********************************************************************/ /* File GOPHERC EXEC ## ## ## */ /* */ /* The main module of the GOPHER Client for VM/CMS */ /* */ /* written: 1992 02 26: */ /* latest update: 1993-05-26 */ /***********************************************************************/ parse arg host port ity sel /* address command ... not yet ... */ /* @@@ 'GLOBALVV SELECT GOPHER STACK HOST' @@@ */ address '' 'GLOBALV SELECT GOPHER STACK HOST' parse pull dhost . /* @@@ 'GLOBALVV SELECT GOPHER STACK SEL' @@@ */ address '' 'GLOBALV SELECT GOPHER STACK SEL' parse pull dsel . 'GLOBALV SELECT GOPHER STACK ITYPE PORT DNAME' pull dity . pull dport . parse pull dname /* use default values from lasting globalv */ if host='' | host='=' then host= dhost if port='' | port='=' then port= dport if ity='' then ity= dity if sel='' then sel= dsel /* if nothing is specified use preset values: */ /* to be configured at installation time by ROSE: - - - - - - - - - - */ if host='' | host='=' then host= ##; if port='' | port='=' then port= ##; if ity='' then ity=##; /* default: directory */ /* - - - - - - - - - - - - - - - - - - - end of configuration section */ select; when ity='0' then xity=''; when ity='1' then xity=''; when ity='7' then xity=''; when ity='B' then xity=''; /* BSS encoded binary data */ otherwise xity='>'ity'<'; end; if dname='' then dname= xity substr('##',1,76); /* @@@ 'GLOBALXV SELECT GOPHER SETLP TMPCNT 1' @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GHOST' host':'port '<'ity'>' sel */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI1:TMP0' dname; @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI2:TMP0' ity host port sel @@@ */ address '' 'GLOBALV SELECT GOPHER SETL TMPCNT 1' address '' 'GLOBALV SELECT GOPHER SETL GHOST' host':'port '<'ity'>' sel address '' 'GLOBALV SELECT GOPHER SETL GI1:TMP0' dname; address '' 'GLOBALV SELECT GOPHER SETL GI2:TMP0' ity host port sel 'SET EMSG OFF' tmpfile='GOPHTMP TMP0 A'; if 'THIS'/='JUNK' then do; 'GOPHFTCH' tmpfile ity host port sel 'XEDIT' tmpfile '(PROFILE GOPHER' end; else do; if ity='1' then linewidth=250; else linewidth=80; 'XEDIT' tmpfile '(WIDTH' linewidth 'PROFILE GOPHER' 'RECFM V' 'LRECL' linewidth 'TRUNC' linewidth 'SET LINEND OFF' ':1' 'GOPHFTCH' tmpfile'(XEDIT' ity host port sel ':1' 'SET ALT 0 0' end; say 'The Internet GOPHER service wishes you a nice and pleasant day.' say 'We hope we can welcome you back on board of the VieGOPHER system.' say 'For suggestions, complaints or questions contact' say '##.' /* @@@ 'ERASE LASTING GLOBALXV' @@@ */ 'ERASE GOPHTMP TMP0' 'SET EMSG ON' exit(0); #erase GOPHER XEDIT #module GOPHER XEDIT /***********************************************************************/ /* File GOPHER XEDIT ## ## ## */ /* */ /* Setup for the GOPHER Client */ /* */ /* written: 1992-02-26: */ /* latest update: 1993-05-31 */ /***********************************************************************/ parse upper arg modus /* modus defines a 'prefix' code for alternative keyboard setups */ /* The idea is from Mary Posey, I herewith steal it :) */ /* Here are the details: */ /* STD ... (or nothing) means normal operation mode */ /* 12K ... normal 12 key setup */ /* RNG ... setup for ring display */ /* 12R ... setup for ring display with only 12 keys */ if modus=''| words(modus)>1 then modus='STD'; /* set a few parameters ... */ 'SET NUMBER ON' 'SET NULLS ON' 'SET CURLINE ON 3' 'SET SCALE OFF' 'SET TOFEOF ON' 'SET CMDLINE TOP' 'SET PREFIX OFF' 'SET HEX ON' 'SET CASE MIXED IGNORE' /* ':1' ... this should only be done when current line is 0 */ /* install the gopher specific keys */ 'SET PF7 command backward' 'SET PF8 command forward' 'SET PF10 command msg undefined PF key!' select; when modus='12K' | modus='12R' then do; 'SET PF1 command xedit GOPHTMP TMP0 (PROFILE GOPHER' 'SET PF3 cancel' 'SET PF4 command msg undefined PF key!' 'SET PF5 command macro gophscrn split' 'SET PF6 command macro gophscrn join' 'SET PF9 command macro gophring' 'SET PF11 command exec gophstp' 'SET PF12 ignore command cursor cmdline 1 priority 30' end; when modus='STD' | modus='RNG' then do; 'SET PF1 command help gopher' 'SET PF3 command macro gophxqit' 'SET PF4 command macro gophxbmi' 'SET PF5 command macro gophxbmc' 'SET PF6 command macro gophxbms' 'SET PF9 command macro gophxsvi' 'SET PF12 command xedit' end; otherwise nop; end/*select*/; 'SET PF13 command xedit GOPHTMP TMP0 (PROFILE GOPHER' 'SET PF14 command macro gopher STD' 'SET PF15 cancel' 'SET PF16 command msg undefined PF key!' 'SET PF17 command macro gophscrn split' 'SET PF18 command macro gophscrn join' 'SET PF19 command backward' 'SET PF20 command forward' 'SET PF21 command macro gophring' 'SET PF22 command msg undefined PF key!' 'SET PF23 command exec gophstp' 'SET PF24 ignore command cursor cmdline 1 priority 30' select; when modus='STD' then do; 'SET PF2 command macro gopher 12K' 'SET PF11 command macro gophxftc'; 'SET ENTER command macro gophxftc' end; when modus='12K' then do; 'SET PF2 command macro gopher STD' end; when modus='RNG' then do; 'SET PF2 command macro gopher 12R' 'SET PF11 command macro gophrng3'; 'SET ENTER command macro gophrng3'; end; when modus='12R' then do; 'SET PF2 command macro gopher 12R' end; otherwise nop; end/*select*/ /* define possible function key display strings */ if modus='12K'|modus='12R' then do; T5L1=' 1= First 2=(basepfs) 3= Exit 4= ', ' 5= Split 6= Join '; T5L0=' 7= PageUP 8= PageDN 9= ShowRing 10= ', '11= Setup 12= Execute'; end; else do; T5L1=' 1= Help 2=(altpfs) 3= GoBack 4= Disp->BMK ', ' 5= Curs->BMK 6= BMK '; T5L0=' 7= PageUP 8= PageDN 9= Save 10= ', '11= Display 12= Circle'; end; /* detailed function key descriptions */ /*************** T9L1=' 1= Help 2= 3= GoBack 4= Disp->BMK ', ' 5= Curs->BMK 6= BMK '; T9L2=' 7= PageUP 8= PageDN 9= Save 10= ', '11= Display 12= Circle'; *****************/ T9L3='13= First 14= 15= Exit 16= ', '17= Split 18= Join '; T9L4='19= PageUP 20= PageDN 21= ShowRing 22= ', '23= Setup 24= Execute'; T9X1=' are files, are menus'; T9X2='place the cursor in the line of an interesting item and press ENTER'; /* display extra information on the screen */ 'EXTRACT /LSCREEN/SCREEN/FTYPE/'; pl0=LSCREEN.1; /* @@@ 'GLOBALXV SELECT GOPHER STACK GHOST' @@@ */ address '' 'GLOBALV SELECT GOPHER STACK GHOST' parse pull ghost /* @@@ 'GLOBALXV SELECT GOPHER STACK GI1:'FTYPE.1 @@@ */ address '' 'GLOBALV SELECT GOPHER STACK GI1:'FTYPE.1 parse pull dirname /* default info level */ 'GLOBALV SELECT GOPHER STACK INFOLEVEL' parse pull infolevel if infolevel='' then infolevel=##; if words(screen.1)>3 then infolevel=0; select; when infolevel>=1 & infolevel<=3 then do; end; when infolevel=4 | infolevel=5 then do; 'SET RESERVED' PL0-1 'BLUE NONE NOHIGH' T5L1 'SET RESERVED' PL0 'BLUE NONE NOHIGH' T5L0 end; when infolevel=6 then do; end; when infolevel=7 | infolevel=8 then do; end; when infolevel=9 then do; 'SET RESERVED 5 RED NONE HIGH' T9X1 'SET RESERVED 6 RED NONE HIGH' T9X2 'SET RESERVED' PL0-3 'BLUE NONE NOHIGH' T5L1 'SET RESERVED' PL0-2 'BLUE NONE NOHIGH' T5L0 'SET RESERVED' PL0-1 'BLUE NONE NOHIGH' T9L3 'SET RESERVED' PL0 'BLUE NONE NOHIGH' T9L4 end; otherwise nop; /* especially when infolevel=0 then */ end/*select*/; select; when modus='RNG' | modus='12R' then do; 'SET RESERVED 3 BLUE NONE NOHIGH list of currently active items; '||, 'select one ...' end; otherwise do; 'SET RESERVED 3 BLUE NONE NOHIGH' substr(dirname,1,79); 'SET RESERVED 4 BLUE NONE NOHIGH' substr(ghost,1,79); end; end/*select*/ #erase GOPHER HELPCMS #module GOPHER HELPCMS How to Use the VM Gopher at San Jose State ------------------------------------------------------------------------ *** Please note: this file was written by John Sroka, San Jose State *** *** University, and reflects the local installation there, most parts *** *** of this text, however, can be applied for other installations. -GG *** The VM gopher client/server at San Jose State University allows you to browse information files on the campus, or at any of the (175 and counting) other gopher sites around the world. Gopher was developed at the University of Minnesota. The CMS version was written by Gerhard Gonter at the University of Economics at Vienna, Austria (Gerhard.Gonter@wu-wien.ac.at, +43/1/31336/4111). Please direct questions or comments about the San Jose State VM gopher to John Sroka at Information Systems & Computing (sroka@sjsuvm1.sjsu.edu) phone (408) 924-2332. *** The Display *** A typical gopher display simply contains a list of information files, or directories which list more information files. The first character in the descriptor tells you what the item is: an information file a directory of more information files a text database where searching can be done # *** SJSU *** # a exec call (runs an external program) an item currently unavailable to VM gopher *** a line of information just on the screen *** The PF Keys *** You use PF keys to perform most of the gopher functions: PF1 calls this help file (main display only) PF3 'quits' to the previous screen, if at main display will exit gopher PF4 adds current display to your bookmark file (see below) PF5 adds item cursor is positioned on to your bookmark file (see below) PF6 retrieves your cumulative bookmark item list (from PF4 and PF5) PF7 page up ('back') in the current file PF8 page down ('forward') in the current file PF9 appends current display or item list to GOPHER SAVEFILE A # *** SJSU *** # PF10 spools the displayed item to your virtual printer PF11 displays item at current cursor position PF12 'jumps' to next available gopher screen, unless at main menu *** Using VM Gopher *** # *** SJSU *** # VM gopher may be initialized by either typing INFO or VMGOPHER at any # CMS command line. After invoking the VieGOPHER client, you will be presented with the 'Main Menu' from which you may select (with the cursor control and PF11 keys) a file or direct- ory to display. Bear in mind that some of the items must be 'gophered' from halfway around the world, so allow a few seconds after pressing the PF11 key for this process to happen. Occasionally a server may be unavailable, in which case no information items will be displayed. Simply press PF3 to drop to the previous screen and try again later. As you get 'deeper' into the gopher files, you may want to refer back to a previous screen, just press the PF12 (Next) key to toggle through the previous screens. Use the PF8 and PF7 keys to browse up and down the files, if you see an item you want to keep for later processing, pressing PF9 will append the current item onto file GOPHER SAVEFILE on your A disk. # *** SJSU *** # The Print key (PF10) will route the displayed item # to your virtual printer. See the CIC Bulletin Board item within # Computing and Network Services for information on setting up your # virtual printer. *** About Bookmarks *** This function allows you to build a customized screen of information files culled from your browsings through gopher. This is very handy if you find an information source you want to refer to often. To add an item to your customized list, simply press PF4 to add the currently displayed file to the list, or PF5 to add the item at the current cursor position. In the latter case, the cursor must be positioned on a valid file or directory descriptor. To retrieve your customized list, just press the PF6 (Bookmark) key. Note there are no keys to remove saved bookmarked items. Simply exit GOPHER and manually edit (XEDIT GOPHER BOOKMARK A) out the unwanted entries. *** Adding Information to Gopher *** We encourage departments who wish to publish information on the gopher to contact us at the e-mail address above. If you've a Macintosh or Unix box which is attached to the Internet, you may wish to build your own server, or the information may be added to the VM gopher. Your comments and questions are also welcomed. #erase GOPHFTCH EXEC #module GOPHFTCH EXEC #subsubsection main /***********************************************************************/ /* File GOPHFTCH EXEC ## ## ## */ /* */ /* Fetch an item from a GOPHER server */ /* */ /* Uses: REXTCPIP MODULE or RXSOCKET MODULE */ /* */ /* written: 1992-02-28: */ /* 1992-07-14: implemented type=w for search */ /* 1993-03-21: redesign/parameter exchance via STACK */ /* 1993-04-21: support for RXSOCKET V2 */ /* latest update: 1993-05-26 */ /***********************************************************************/ parse arg fn ft fm p1 p2 if fn='' then exit(28); /* get the GOPHER coordinates for item to fetch */ select; when p1='PULL' then do; /* gopher co-ordinates on stack */ rsel=''; do forever; parse pull aname'='avalue upper aname; if aname='END' then leave; select; when aname='TYPE' then ty=avalue; when aname='HOST' then rsrv=avalue; when aname='PORT' then rprt=avalue; when aname='PATH' then rsel=rsel||avalue; when aname='HTAP' then rsel=avalue||rsel; otherwise nop; /* do nothing; unknown attribute */ end/*select*/ end;/* do forever */ XL.0=4; XL.4=rsel; end; when p1='FILE' then do; /* gopher coordinates are in a file */ 'EXECIO 4 DISKR' p2 '(FINIS STEM XL.' if XL.0 < 4 then return -1; ty= XL.1; rsrv= XL.2; rprt= XL.3; rsel= XL.4; end; otherwise do; /* parameters are in the argument list */ ty=p1; parse var p2 rsrv rprt rsel XL.0=4; XL.4=rsel; end; end/*select*/; if ty='' then ty=0; /* to be configured at installation time by ROSE: - - - - - - - - - - */ if rsrv='' then rsrv=##; /* default server */ if rprt='' then rprt=##; /* default servers port */ tout=##; /* timeout for TCPOPEN (sec) */ #switch CL_USE_LOCALFETCH #case YES localhost='##'; /* IP where client runs */ localgopher='70'; /* Port where Gopher server listens on */ selectorfile='GOPHERD SELECTOR'; indexfile='GOPHERD INDEX'; gopherdisk='*'; #endswitch /* - - - - - - - - - - - - - - - - - - - end of configuration section */ tab='05'x; crlf='0D'x || '0A'x; cr='0D'x; lf='0A'x; 'SUBCOM XEDIT' if rc=0 then inXedit=1; else inXedit=0; CSO_n=0; if ty='9'||ty='I' then is_text=0; else is_text=1; #switch CL_USE_FDNS #case YES /* IP address resolution using FTP (see VMGOPHER.DOC for details) ******/ if verify(rsrv,'0123456789.')/=0 then do; 'FDNS' rsrv '(LIFO' pull code . ip . if (code/='AD') then do; exit(0); end; rsrv=ip end; #endswitch parse var fm fm'('fm_opt upper fm_opt; fnm=fn ft fm rsrv_name= rsrv; /* remember