% 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 \section{Introduction} \def\LPtopC{Introduction} \def\LPtopD{~} \def\LPtopF{~} \begin{verbatim} 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 \def\LPbotC{VieGOPHER 2.00.00 1993-06-16 18:00} #set SENDFIX NO \end{verbatim} \section{Help messages} \def\LPtopC{Help messages} \def\LPtopD{~} \def\LPtopF{~} \subsection{{\sl{help}} {\tt TEST\_and\_HACK}} \def\LPtopD{{\sl{help}} {\tt TEST\_and\_HACK}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt TEST\_dd\_binsel}} \def\LPtopD{{\sl{help}} {\tt TEST\_dd\_binsel}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt Compactor}} \def\LPtopD{{\sl{help}} {\tt Compactor}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt MAKE\_SERVER}} \def\LPtopD{{\sl{help}} {\tt MAKE\_SERVER}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt LOGFILE}} \def\LPtopD{{\sl{help}} {\tt LOGFILE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt SELECTORFILE}} \def\LPtopD{{\sl{help}} {\tt SELECTORFILE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt GROUPFILE}} \def\LPtopD{{\sl{help}} {\tt GROUPFILE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt EAIFILE}} \def\LPtopD{{\sl{help}} {\tt EAIFILE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt DSKGRPFILE}} \def\LPtopD{{\sl{help}} {\tt DSKGRPFILE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt ROOTINDEX}} \def\LPtopD{{\sl{help}} {\tt ROOTINDEX}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt GOPHER\_PORT}} \def\LPtopD{{\sl{help}} {\tt GOPHER\_PORT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt FINGER\_PORT}} \def\LPtopD{{\sl{help}} {\tt FINGER\_PORT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt COOKIE\_PORT}} \def\LPtopD{{\sl{help}} {\tt COOKIE\_PORT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt USE\_SRE}} \def\LPtopD{{\sl{help}} {\tt USE\_SRE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt SRE\_PORT}} \def\LPtopD{{\sl{help}} {\tt SRE\_PORT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt SRV\_WAKEUP\_SYSTEM}} \def\LPtopD{{\sl{help}} {\tt SRV\_WAKEUP\_SYSTEM}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt SRV\_TCP}} \def\LPtopD{{\sl{help}} {\tt SRV\_TCP}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt SRV\_ORGANIZATION}} \def\LPtopD{{\sl{help}} {\tt SRV\_ORGANIZATION}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt MAKE\_CLIENT}} \def\LPtopD{{\sl{help}} {\tt MAKE\_CLIENT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt STARTUP\_DIR}} \def\LPtopD{{\sl{help}} {\tt STARTUP\_DIR}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_TCP}} \def\LPtopD{{\sl{help}} {\tt CL\_TCP}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_USE\_FDNS}} \def\LPtopD{{\sl{help}} {\tt CL\_USE\_FDNS}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_USE\_LOCALFETCH}} \def\LPtopD{{\sl{help}} {\tt CL\_USE\_LOCALFETCH}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_LOCALHOST}} \def\LPtopD{{\sl{help}} {\tt CL\_LOCALHOST}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_LANG}} \def\LPtopD{{\sl{help}} {\tt CL\_LANG}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_DEFAULT\_HOST}} \def\LPtopD{{\sl{help}} {\tt CL\_DEFAULT\_HOST}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_DEFAULT\_PORT}} \def\LPtopD{{\sl{help}} {\tt CL\_DEFAULT\_PORT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_DEFAULT\_TYPE}} \def\LPtopD{{\sl{help}} {\tt CL\_DEFAULT\_TYPE}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_TIME\_OUT}} \def\LPtopD{{\sl{help}} {\tt CL\_TIME\_OUT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_INFO\_LEVEL}} \def\LPtopD{{\sl{help}} {\tt CL\_INFO\_LEVEL}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_CONTACT}} \def\LPtopD{{\sl{help}} {\tt CL\_CONTACT}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_TELNET\_8}} \def\LPtopD{{\sl{help}} {\tt CL\_TELNET\_8}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \subsection{{\sl{help}} {\tt CL\_TELNET\_T}} \def\LPtopD{{\sl{help}} {\tt CL\_TELNET\_T}} \def\LPtopF{~} \begin{verbatim} #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 \end{verbatim} \section{Multi-Threaded Server} \def\LPtopC{Multi-Threaded Server} \def\LPtopD{~} \def\LPtopF{~} \subsection{Module {\tt GOPHERDD EXEC}} \def\LPtopD{Module {\tt GOPHERDD EXEC}} \def\LPtopF{~} \begin{verbatim} #erase GOPHERDD EXEC #module GOPHERDD EXEC \end{verbatim} \subsubsection{main init} \def\LPtopF{main init} \begin{verbatim} /* ------------------------------------------------------------------- */ 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 \end{verbatim} \subsubsection{main loop} \def\LPtopF{main loop} \begin{verbatim} /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 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); \end{verbatim} \subsubsection{startserver} \def\LPtopF{startserver} \begin{verbatim} /* ------------------------------------------------------------------- */ /* 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; \end{verbatim} \subsubsection{gopherselector} \def\LPtopF{gopherselector} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{gophersend} \def\LPtopF{gophersend} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{gopherspecial} \def\LPtopF{gopherspecial} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{gophersys} \def\LPtopF{gophersys} \begin{verbatim} /* ------------------------------------------------------------------- */ 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); \end{verbatim} \subsubsection{gopher\_eai} \def\LPtopF{gopher\_eai} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{reaccess} \def\LPtopF{reaccess} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{readselectors} \def\LPtopF{readselectors} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{readgroups} \def\LPtopF{readgroups} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{read\_eai} \def\LPtopF{read\_eai} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{read\_disk\_groups} \def\LPtopF{read\_disk\_groups} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{xconnstat} \def\LPtopF{xconnstat} \begin{verbatim} #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 \end{verbatim} \subsubsection{logtext} \def\LPtopF{logtext} \begin{verbatim} /* ------------------------------------------------------------------- */ logtext: parse arg txt 'EXECIO 1 DISKW' logfile '(FINIS STRING' date(sorted) time() txt say date(sorted) time() txt return rc; \end{verbatim} \subsubsection{prepare\_command} \def\LPtopF{prepare\_command} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{check\_group} \def\LPtopF{check\_group} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{check\_disk\_group} \def\LPtopF{check\_disk\_group} \begin{verbatim} /* ------------------------------------------------------------------- */ 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; \end{verbatim} \subsubsection{EBCDIC to ASCII conversion} \def\LPtopF{EBCDIC to ASCII conversion} \begin{verbatim} /* 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); \end{verbatim} \subsubsection{ASCII to EBCDIC conversion} \def\LPtopF{ASCII to EBCDIC conversion} \begin{verbatim} /* 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 \end{verbatim} \section{Client} \def\LPtopC{Client} \def\LPtopD{~} \def\LPtopF{~} \subsection{Module {\tt GOPHERC EXEC}} \def\LPtopD{Module {\tt GOPHERC EXEC}} \def\LPtopF{~} \begin{verbatim} #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); \end{verbatim} \subsection{Module {\tt GOPHER XEDIT}} \def\LPtopD{Module {\tt GOPHER XEDIT}} \def\LPtopF{~} \begin{verbatim} #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*/ \end{verbatim} \subsection{Module {\tt GOPHER HELPCMS}} \def\LPtopD{Module {\tt GOPHER HELPCMS}} \def\LPtopF{~} \begin{verbatim} #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. \end{verbatim} \subsection{Module {\tt GOPHFTCH EXEC}} \def\LPtopD{Module {\tt GOPHFTCH EXEC}} \def\LPtopF{~} \begin{verbatim} #erase GOPHFTCH EXEC #module GOPHFTCH EXEC \end{verbatim} \subsubsection{main} \def\LPtopF{main} \begin{verbatim} /***********************************************************************/ /* 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 the name of the server */ /* rsrv is later on the ip address or some */ /* other data structure containing the */ /* address of the server. */ if fm_opt='' then 'ERASE' fnm; #switch CL_TCP #case REXTCPIP 'REXTCPIP'; #switch CL_USE_FDNS #case NO if verify(rsrv,'0123456789.')/=0 then do; ip= GetIPAddr(rsrv); if (ip='') then do; 'NUCXDROP REXTCPIP' x= report(fnm, fm_opt, "can't get" rsrv"'s IP address"); exit(1001); end; rsrv=ip end; #endswitch #case RXSOCKET # V1/V2 1993-04-21 parse value Socket('Initialize', 'Gopher') with x x_rest if x_rest='' then do; RXSOCKV= 1; if x>0 then x= 0; /* well... this could cause problems... */ end; else RXSOCKV= 2; if x/=0 then do; ERR: 'NUCXDROP RXSOCKET' # "EXECIO 1 DISKW" fnm "(FINIS STRING can't start TCP using RXSOCKET" x= report(fnm, fm_opt, "can't start TCP using RXSOCKET"); exit(1001); end; if RXSOCKV=1 then sock= Socket('Socket', 'AF_INET', 'Sock_Stream'); if RXSOCKV=2 then do; parse value Socket('Socket', 'AF_INET', 'Sock_Stream') with rc sock . if rc/=0 then sock= -1; end; if sock=-1 then signal ERR; if verify(rsrv,'0123456789.')=0 then do; /* call a converter function */ ip= INet_Addr(rsrv); /* doesn't work ... */ end; else do; if RXSOCKV=1 then ip= Socket('GetHostByName', rsrv); if RXSOCKV=2 then do; parse value Socket('GetHostByName', rsrv) with rc ip . if rc/=0 then ip=-1; end; # if ip=-1 then ip= GetHostByName(rsrv); Huh???? what's this?? if ip=-1 then do; 'NUCXDROP RXSOCKET' x= report(fnm, fm_opt, "MSG can't resolve hostname" rsrv); exit(1001); end; rsrv= ip; end; #endswitch #switch CL_USE_LOCALFETCH #case YES /* this is here to fix a problem with REXTCPIP, where */ /* local connections are not handled propperly... */ if rsrv_name=localhost & rprt=localgopher then do; if fm_opt='PUSH' then push('TOP'); if fm_opt='QUEUE' then queue('TOP'); x= localfetch(fnm, fm_opt, ty, rsel); #switch CL_TCP #case REXTCPIP 'NUCXDROP REXTCPIP' #case RXSOCKET 'NUCXDROP RXSOCKET' #endswitch if fm_opt='PUSH' then push('BOT'); if fm_opt='QUEUE' then queue('BOT'); exit(0); end; #endswitch if (inXedit=1) then do; address XEDIT 'MSG trying to open' rsrv_name '(timeout is' tout 'seconds)' address XEDIT 'REFRESH' end; #switch CL_TCP #case REXTCPIP gc= TCPOPEN(rsrv,rprt,tout); if (gc/=0 & TCPRC=0) then do; /* say 'rsel=['rsel']' */ TCPSEND(gc,rsel); if ty='2' then TCPSEND(gc,'quit'); #case RXSOCKET if RXSOCKV=1 then rc=Socket('Connect', sock, AF_INET || Htons(rprt) || rsrv); if RXSOCKV=2 then do; parse value Socket('Connect', sock, AF_INET rprt rsrv) with rc . if rc/=0 then rc= -1; end; if rc/=-1 then do; if RXSOCKV=1 then do; bytes_out= Socket('Write', sock, E2A(rsel)||crlf); if ty='2' then bytes_out= bytes_out + Socket('Write', sock, E2A('quit')||crlf); end; if RXSOCKV=2 then do; parse value Socket('Write', sock, E2A(rsel)||crlf) with rc bytes_out if ty='2' then do; parse value Socket('Write', sock, E2A('quit')||crlf) with rc bytes_inc bytes_out= bytes_out + bytes_inc end; end; xline=''; #endswitch if (inXedit=1) then do; address XEDIT 'MSG please hold on while receiving data...' address XEDIT 'REFRESH' end; receivecnt=0; xrececnt=1; if fm_opt='PUSH' then push('TOP'); if fm_opt='QUEUE' then queue('TOP'); do forever; #switch CL_TCP #case REXTCPIP connstat(gc); if (conn_stat/='Connected') then leave; if (is_text) then TCPRECEIVE(gc,'WAITKB'); else TCPRECEIVE(gc,'WAITKB',bin); select; when (TCPRC=0) then do; do i=1 to TCPLINE.0; line= TCPLINE.i #case RXSOCKET if RXSOCKV=1 then do; bytes_read= Socket('Read', sock, 'data'); if bytes_read=-1 then rc=999; else rc=0; end; if RXSOCKV=2 then do; parse value Socket('Read', sock) with rc bytes_read data end; if rc/=0 then do; x= report(fnm, fm_opt, 'error...'); 'NUCXDROP RXSOCKET' exit(0); end; if bytes_read=0 then leave; xline= xline||data; do forever; if (is_text) then do; xli= index(xline,lf); if xli=0 then leave; line= substr(xline,1,xli-1); if substr(line,1,1)=cr then line= substr(line,2); ll= length(line); if ll>0 then if substr(line,ll)=cr then line= substr(line,1,ll-1); line= A2E(line); xline= substr(xline,xli+1); end; else do; line= xline; /* no modification */ end; #endswitch if (is_text) then do; if (line='.') then leave; /* end of transmission */ if (substr(line,1,1)='.') then line= substr(line,2); x= prclin(fnm, fm_opt, ty, line); end; else do; /* binary, just write to file, no processing */ x= emmit(fnm, fnm_opt, line); end; receivecnt= receivecnt+1; if (inXedit=1 &, ((receivecnt =16)|, (receivecnt =32)|, (receivecnt =64)|, (receivecnt//128=0))) then do; xrececnt= xrececnt*2; if fm_opt='XEDIT' then address XEDIT ':1' address XEDIT 'MSG please hold on; receiving more data:', receivecnt 'lines sofar...'; address XEDIT 'REFRESH'; if fm_opt='XEDIT' then address XEDIT 'BOT' end; #switch CL_TCP #case REXTCPIP end; end; when (TCPRC=9) then do; /* wait, just a little bit longer ... */ if (inXedit=1) then do; address XEDIT 'MSG waiting for' rsrv_name 'to respond...' address XEDIT 'REFRESH' end; 'SLEEP 1 SEC' end; otherwise leave; end;/*select*/ #case RXSOCKET if (is_text=0) then leave; /* binary, only one record... */ /* (is *THIS* true???????) */ end/*do forever */ #endswitch end;/*do forever*/ end; if fm_opt='PUSH' then push('BOT'); if fm_opt='QUEUE' then queue('BOT'); #switch CL_TCP #case REXTCPIP 'NUCXDROP REXTCPIP' #case RXSOCKET 'NUCXDROP RXSOCKET' #endswitch if fm_opt='' then 'FINIS' fnm exit(0); \end{verbatim} \subsubsection{prclin} \def\LPtopF{prclin} \begin{verbatim} /* ------------------------------------------------------------------- */ prclin: parse arg fnm, fm_opt, ty, line select; when ty=1 then do; /* directory ... */ dty=substr(line,1,1) parse var line dtxt '05'x dsel '05'x dhst '05'x dprt '05'x nhit longline=0; if length(dtxt)>72 then longline=1; dtxt= substr(dtxt,2,72); if nhit/='' then dtxt=substr('('nhit')' dtxt,1,72); /* if length(dtxt)>72 then longline=1; */ if longline/=0 then dtxt= dtxt'**'; dtxt= substr(dtxt,1,75); select; /* initially defined types: - - - - - - - - - - - - - - - - - - */ when dty='0' then xdty=''; /* normal file */ when dty='1' then xdty=''; /* normal directory */ when dty='2' then xdty='CSO'; /* CSO phonebook */ when dty='7' then xdty=''; /* search request: -> directory */ when dty='8' then xdty=''; /* telnet session */ /* common extensions: - - - - - - - - - - - - - - - - - - - - - */ when dty='T' then xdty=''; /* 3270 telnet session */ when dty='w' then xdty=''; /* search request: -> file */ /* panda extensions: - - - - - - - - - - - - - - - - - - - - - - */ when dty='i' then xdty='***'; /* information entry */ /* local extensions: - - - - - - - - - - - - - - - - - - - - - - */ when dty='B' then xdty=''; /* BSS encoded */ when dty='H' then xdty='>H<'; /* BESt encoded HyperText (HYX) */ otherwise xdty='>'dty'<'; end;/*select*/ if (dtxt/='' & dhst/='' & dprt/='') | xdty='***' then do; x= emmit(fnm, fm_opt, xdty dtxt dty dhst dprt dsel); end; end; when ty='2' then do; parse var line resp line2 parse var resp resp1':'resp2':' if resp1='-200' then do; if resp2/=CSO_n then do; x= emmit(fnm, fm_opt, copies('-',64)); CSO_n= resp2; end; end; else line2= line; x= emmit(fnm, fm_opt, line2); end; otherwise x= emmit(fnm, fm_opt, line); end;/*select*/ return 0; \end{verbatim} \subsubsection{emmit} \def\LPtopF{emmit} \begin{verbatim} /* ------------------------------------------------------------------- */ emmit: parse arg fnm, fm_opt, line select; when fm_opt='PUSH' | fm_opt='QUEUE' then do; do while length(line)>250; subline= substr(line,1,250); line= substr(line,251); if fm_opt='PUSH' then push('+'subline); else queue('+'subline); end; if fm_opt='PUSH' then push('-'line); else queue('-'line); end; when fm_opt='XEDIT' then do; if line='' then line=' '; address xedit 'INPUT' line; end; otherwise do; XL.0= 1; XL.1= line; 'EXECIO 1 DISKW' fnm '(STEM XL.' end/*otherwise*/ end/*select*/ return 0; \end{verbatim} \subsubsection{report} \def\LPtopF{report} \begin{verbatim} /* ------------------------------------------------------------------- */ report: parse arg fnm, fm_opt, line return emmit(fnm, fm_opt, '*ERROR*' line); \end{verbatim} \subsubsection{localfetch} \def\LPtopF{localfetch} \begin{verbatim} #switch CL_USE_LOCALFETCH #case YES /* LOCAL ACCESS ====================================================== */ localfetch: parse arg fn ft fm, fm_opt, ty, sel':'xfrom':'xto select; when sel='' then return localget(fn ft fm, fm_opt, ty, indexfile gopherdisk); when sel='SPEC-COOKIE' then do; 'ERASE' fn 'SPEC'ft fm 'COOKIE' fn 'SPEC'ft fm return localget(fn ft fm, fm_opt, ty, fn 'SPEC'ft fm); end; when left(sel,11)='SPEC-FINGER' then do; sel=substr(sel,13); 'ERASE' fn 'SPEC'ft fm 'FINGERXX' fn 'SPEC'ft fm '(0' sel return localget(fn ft fm, fm_opt, ty, fn 'SPEC'ft fm); end; otherwise do; /* try to find it from the file */ 'LISTFILE' selectorfile gopherdisk '(ALLOC LIFO NOHEAD' if rc/=0 then return -1; pull . . . . . xsiz . parse var sel xsfn'.'xsft'.'xsfm 'FINIS' selectorfile do i=1 to xsiz; 'EXECIO 1 DISKR' selectorfile '(LIFO' parse pull xfntm . . xsel if (xfntm/='*') then do; parse var xfntm xfn'.'xft'.'xfm if xft='*' & xfn=xsfn then do; x=localget(fn ft fm, fm_opt, ty, xfn xsft xfm, xfrom xto); 'FINIS' selectorfile return x; end; if xsel=sel then do; x=localget(fn ft fm, fm_opt, ty, xfn xft xfm, xfrom xto); 'FINIS' selectorfile return x; end; end; end; 'FINIS' selectorfile end; end;/*select*/ return -1; /* selector not found */ #endswitch \end{verbatim} \subsubsection{localget} \def\LPtopF{localget} \begin{verbatim} #switch CL_USE_LOCALFETCH #case YES /* ------------------------------------------------------------------- */ localget: parse arg fn ft fm, fm_opt, ty, xfn xft xfm, xfrom xto . if xfm='' then xfm=gopherdisk; xfm='*'; /* ##### */ q=queued(); 'LISTFILE' xfn xft xfm '(ALLOC LIFO NOHEAD' if rc/=0 then return -1; q=queued()-q; do i=1 to q; pull . . xfm . . ysiz . end; if xfrom=''|xfrom<0 then xfrom=0; if xto=''|xto>ysiz then xto=ysiz; 'FINIS' xfn xft xfm do i2=xfrom+1 to xto; 'EXECIO 1 DISKR' xfn xft xfm i2 '(STEM XL.' x=prclin(fn ft fm, fm_opt, ty, XL.1); end; 'FINIS' xfn xft xfm return 0; #endswitch \end{verbatim} \subsubsection{xconnstat} \def\LPtopF{xconnstat} \begin{verbatim} #switch CL_TCP #case REXTCPIP /* DEBUGGING ========================================================= */ xconnstat: parse arg gcn msg say msg connstat(gcn); say 'open_time_wait='open_time_wait say 'bytes_to_read='bytes_to_read say 'unacked_bytes='unacked_bytes say 'local_addr='local_addr say 'local_port='local_port say 'foreign_addr='foreign_addr say 'foreign_port='foreign_port say 'conn_stat='conn_stat return 0; #endswitch #switch CL_TCP #case RXSOCKET \end{verbatim} \subsubsection{EBCDIC to ASCII conversion} \def\LPtopF{EBCDIC to ASCII conversion} \begin{verbatim} /* 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); \end{verbatim} \subsubsection{ASCII to EBCDIC conversion} \def\LPtopF{ASCII to EBCDIC conversion} \begin{verbatim} /* 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 \end{verbatim} \subsection{Module {\tt GOPHRING XEDIT}} \def\LPtopD{Module {\tt GOPHRING XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHRING XEDIT #module GOPHRING XEDIT /***********************************************************************/ /* File GOPHRING XEDIT ## ## ## */ /* */ /* present XEDIT ring list on screen */ /* */ /* written: 1992-07-23 */ /* latest update: 1993-05-23 <> */ /***********************************************************************/ 'EXTRACT /RING/'; ringfile= 'GOPHTMP RING A' outc= 0; do i=2 to ring.0; parse var ring.i fni fti . if fni='GOPHTMP' then do; /* @@@ 'GLOBALXV SELECT GOPHER STACK GI1:'fti @@@ */ address '' 'GLOBALV SELECT GOPHER STACK GI1:'fti parse pull seli outc= outc+1; XOUT.outc= substr(seli,1,80)||fti end; end; XOUT.0=outc; 'ERASE' ringfile 'EXECIO' outc 'DISKW' ringfile '(FINIS STEM XOUT.'; /* first: call the editor to get the right file */ 'XEDIT' ringfile '(PROFILE GOPHER' /* second: tell the editor to use the right profile mode */ 'GOPHER RNG' exit(0); \end{verbatim} \subsection{Module {\tt GOPHRNG3 XEDIT}} \def\LPtopD{Module {\tt GOPHRNG3 XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHRNG3 XEDIT #module GOPHRNG3 XEDIT /***********************************************************************/ /* File GOPHRNG3 XEDIT ## ## ## */ /* */ /* display selected item from the XEDIT ring */ /* */ /* written: 1992-07-23 */ /* latest update: 1993-05-21 <> */ /***********************************************************************/ /* get line of cursor */ 'EXTRACT /CURSOR/FLSCREEN/FTYPE/'; if (CURSOR.3 < 0) then exit(0); ':'CURSOR.3 'EXTRACT /CURLINE/' ':'FLSCREEN.1 /* analyze item to be fetched */ lin= curline.3 lin= substr(lin,80); 'QUIT' 'XEDIT GOPHTMP' lin exit(0); \end{verbatim} \subsection{Module {\tt GOPHSCRN XEDIT}} \def\LPtopD{Module {\tt GOPHSCRN XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHSCRN XEDIT #module GOPHSCRN XEDIT /***********************************************************************/ /* File GOPHSCRN XEDIT ## ## ## */ /* */ /* screen handling: splitting and joining */ /* */ /* written: 1992-07-23 */ /* last update: 1992-10-23 <> */ /***********************************************************************/ parse arg subcmd . upper subcmd; 'EXTRACT /SCREEN/' x=words(screen.1)-1; select; when subcmd='SPLIT' then 'COMMAND SET SCREEN' x+1; when subcmd='JOIN' then 'COMMAND SET SCREEN' x-1; otherwise do; /* nothing -- just ignore that ... */ end; end; /* error checking?? plausibility test??? -- nah! XEDIT does that :)) */ exit(0); \end{verbatim} \subsection{Module {\tt GOPHSRCH XEDIT}} \def\LPtopD{Module {\tt GOPHSRCH XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHSRCH XEDIT #module GOPHSRCH XEDIT /***********************************************************************/ /* File GOPHSRCH XEDIT ## ## ## */ /* */ /* Setup search screen */ /* usually called by GOPHXFTC.XEDIT (*) search() */ /* */ /* written: 1992-04-13 */ /* latest update: 1993-04-14 <> */ /***********************************************************************/ 'EXTRACT /LSCREEN/' p0=lscreen.1-2; parse pull SRrprt parse pull SRrhst parse pull SRrsel parse pull SRdis parse pull SRrty parse pull SRcso_opts 'set tofeof off' 'set scale off' 'set prefix off' 'set reserved 1 nohigh', ' ***** ***** Gopher Search Form ***** *****' 'set reserved 2 nohigh line 2' 'set reserved' p0 'nohigh PF3=Quit PF5=Enter (Enter=Enter too...)' 'set ctlchar % protect red high' 'set ctlchar $ noprotect blue high' 'set ctlchar " escape' ph='"%' nh='"$' /* mask */ 'set reserved 3 nohigh' SRdis 'set reserved 4 nohigh I will search on' SRrhst'('SRrprt') for' z.5= ph'Search item:' z.6= nh||copies(' ',76)||ph if SRrsel/='' then do; 'set reserved 7 nohigh and present the search string' 'set reserved 8 nohigh' SRrsel end; /* 'EXTRACT /LSCREEN/' the same as above? (1993-04-14) */ do sline= 1 to lscreen.1 if symbol('Z.SLINE')='VAR' then 'set reserved' sline 'nohigh' z.sline end /* readout data */ 'cursor screen 6 2' 'read all tag' 'qquit' \end{verbatim} \subsection{Module {\tt GOPHXBMC XEDIT}} \def\LPtopD{Module {\tt GOPHXBMC XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXBMC XEDIT #module GOPHXBMC XEDIT /***********************************************************************/ /* File GOPHXBMC XEDIT ## ## ## */ /* */ /* save item pointed to by cursor into the bookmark file */ /* */ /* written: 1992-02-28 */ /* latest update: 1993-03-28 <> */ /***********************************************************************/ /* get line of cursor */ 'EXTRACT /CURSOR/FLSCREEN/' if (CURSOR.3 < 0) then exit(0); ':'CURSOR.3 'EXTRACT /CURLINE/' ':'FLSCREEN.1 'GLOBALV SELECT GOPHER STACK BOOKMARK' pull bookmark if bookmark='' then bookmark='GOPHER BOOKMARK A'; 'EXECIO 1 DISKW' bookmark '(FINIS STRING' CURLINE.3 exit(0); \end{verbatim} \subsection{Module {\tt GOPHXBMI XEDIT}} \def\LPtopD{Module {\tt GOPHXBMI XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXBMI XEDIT #module GOPHXBMI XEDIT /***********************************************************************/ /* File GOPHXBMI XEDIT ## ## ## */ /* */ /* save pointer to item currently displayed into the bookmark file */ /* */ /* written: 1992-02-28 */ /* last update: 1993-05-23 <> */ /***********************************************************************/ 'EXTRACT /FTYPE/'; /* identify the item being displayed */ /* @@@ 'GLOBALXV SELECT GOPHER STACK GI1:'FTYPE.1 @@@ */ address '' 'GLOBALV SELECT GOPHER STACK GI1:'FTYPE.1 parse pull gitem1 /* @@@ 'GLOBALXV SELECT GOPHER STACK GI2:'FTYPE.1 @@@ */ address '' 'GLOBALV SELECT GOPHER STACK GI2:'FTYPE.1 parse pull gitem2 /* find out where the bookmark is */ 'GLOBALV SELECT GOPHER STACK BOOKMARK' pull bookmark if bookmark='' then bookmark='GOPHER BOOKMARK A'; /* save the bookmark entry */ 'EXECIO 1 DISKW' bookmark '(FINIS STRING' gitem1||gitem2 exit(0); \end{verbatim} \subsection{Module {\tt GOPHXBMS XEDIT}} \def\LPtopD{Module {\tt GOPHXBMS XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXBMS XEDIT #module GOPHXBMS XEDIT /***********************************************************************/ /* File GOPHXBMS XEDIT ## ## ## */ /* */ /* display current bookmark file */ /* */ /* written: 1992-02-28 */ /* last update: 1993-05-26 <> */ /***********************************************************************/ 'GLOBALV SELECT GOPHER STACK BOOKMARK' pull bookmark if bookmark='' then bookmark='GOPHER BOOKMARK A'; parse var bookmark . fty . 'IDENTIFY (LIFO' parse pull user . node . uid='Bookmark list of <'user'@'node'>' /* @@@ 'GLOBALXV SELECT GOPHER SETLP GHOST bookmark file:' uid bookmark @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI1:'fty uid @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI2:'fty bookmark @@@ */ address '' 'GLOBALV SELECT GOPHER SETL GHOST bookmark file:' uid bookmark address '' 'GLOBALV SELECT GOPHER SETL GI1:'fty uid address '' 'GLOBALV SELECT GOPHER SETL GI2:'fty bookmark 'XEDIT' bookmark '(PROFILE GOPHER' exit(0); \end{verbatim} \subsection{Module {\tt GOPHXFTC XEDIT}} \def\LPtopD{Module {\tt GOPHXFTC XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXFTC XEDIT #module GOPHXFTC XEDIT /***********************************************************************/ /* File GOPHXFTC XEDIT ## ## ## */ /* */ /* Fetch item pointed to by the cursor and display it */ /* */ /* written: 1992-02-26 */ /* 1993-03-21 <> redesign/paraemter exchange via stack */ /* latest update: 1993-05-26 <> */ /***********************************************************************/ parfile='GOPHTMP FETCHPAR A'; /* get line of cursor */ 'EXTRACT /CURSOR/FLSCREEN/FTYPE/'; if (CURSOR.3 < 0) then exit(0); ':'CURSOR.3 'EXTRACT /CURLINE/' ':'FLSCREEN.1 /* analyze item to be fetched */ lin= curline.3 dis= strip(substr(lin,1,78)); lin= substr(lin,80); parse var lin rty rhst rprt rsel rrty= rty; select; when rty='0' | rty='1' | rty='2' | rty='7' | rty='B' | rty='w' then do; if rty='2' | rty='7' | rty='w' then do; if (search(rty,dis,rsel,rhst,rprt)/=0) then exit(0); if rty='7' then rty='1'; /* now treat it like a directory */ if rty='w' then rty='0'; /* now treat it like a file */ end; /* @@@ 'GLOBALXV SELECT GOPHER STACK TMPCNT' @@@ */ address '' 'GLOBALV SELECT GOPHER STACK TMPCNT' pull tmpcnt . if tmpcnt='' then tmpcnt=1; 'GLOBALV SELECT GOPHER SETL L'FTYPE.1 cursor.1 cursor.2 /* @@@ 'GLOBALXV SELECT GOPHER SETLP TMPCNT' tmpcnt+1 @@@ */ address '' 'GLOBALV SELECT GOPHER SETL TMPCNT' tmpcnt+1 tmpfile='GOPHTMP TMP'tmpcnt 'A'; fty='TMP'tmpcnt /* @@@ 'GLOBALXV SELECT GOPHER SETLP GHOST' rhst':'rprt '<'rty'>' rsel @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI1:'fty substr(curline.3,1,80); @@@ */ /* @@@ 'GLOBALXV SELECT GOPHER SETLP GI2:'fty substr(curline.3,81); @@@ */ address '' 'GLOBALV SELECT GOPHER SETL GHOST' rhst':'rprt '<'rty'>' rsel address '' 'GLOBALV SELECT GOPHER SETL GI1:'fty substr(curline.3,1,80); address '' 'GLOBALV SELECT GOPHER SETL GI2:'fty substr(curline.3,81); /* CSO integration here is ... ugly ... */ if rty='2' then do; XL.0=4; XL.1=rty; XL.2=rhst; XL.3=rprt; XL.4=rsel; if rty='2' then do; XL.0= 5; XL.4= 'query name='rsel; XL.5= 'quit'; end; 'ERASE' parfile 'EXECIO' XL.0 'DISKW' parfile '(FINIS STEM XL.' 'GOPHFTCH' tmpfile 'FILE' parfile 'XEDIT' tmpfile '(PROFILE GOPHER' 'ERASE' tmpfile exit(0); /* brrr.... */ /* or maybe it's not even that bad ... */ /* ... anyway, I keep an eye on this! */ end; queue('Host='rhst); queue('Port='rprt); queue('Type='rty); xrsel=rsel; do while length(xrsel)>240; queue('Path='substr(xrsel,1,240)); xrsel= substr(xrsel,241); end; queue('Path='xrsel); queue('End'); if rrty='7' then do; /* type 7 items go via file because I don't know how to */ /* hanle lines longer than 250 chars with xedit input. */ 'GOPHFTCH' tmpfile 'PULL' 'XEDIT' tmpfile '(PROFILE GOPHER' 'ERASE' tmpfile end; else do; if 'all'='all' then do; if rty='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 PULL' ':1' 'SET ALT 0 0' end; if 'this'='not used' then do; /* move this to trash later on... */ 'GOPHFTCH' tmpfile'(QUEUE PULL' /* read the data from the stack */ linecnt= 1; linewidth= 0; XLINE.1= ''; q= queued(); do q; parse pull line; xcode= substr(line,1,1); line= substr(line,2); select; when xcode='-' then do; XLINE.linecnt=XLINE.linecnt||line ll= length(XLINE.linecnt); linecnt= linecnt+1; XLINE.linecnt= ''; if (ll>linewidth) then linewidth= ll; end; when xcode='+' then XLINE.linecnt= XLINE.linecnt||line; otherwise nop; end; end; linecnt= linecnt-1; 'XEDIT' tmpfile '(WIDTH' linewidth 'PROFILE GOPHER' 'ERASE' tmpfile /* get the data into the Xedit buffer */ 'RECFM V' 'LRECL' linewidth 'TRUNC' linewidth 'SET LINEND OFF' ':1' do i=1 to linecnt; if XLINE.i='' then XLINE.i=' '; address xedit 'INPUT' XLINE.i; end; drop XLINE.; ':1' end; end; end; when rty='8' | rty='T' then do; /* TELNET or 3270 Session */ 'GLOBALV SELECT GOPHER STACK TELNET-'rty pull telnet_program if telnet_program='' then do; select; when rty='8' then telnet_program=##; when rty='T' then telnet_program=##; otherwise telnet_program='TELNET MODULE'; /* Note: the otherwise path should never be taken anyway... */ end/*selelct*/ end; 'STATE' telnet_program if rc/=0 then do; telnet_program='('telnet_program')' 'COMMAND MSG sorry... you need TELNET'telnet_program 'to use this' exit(0); end; say '>>>> >>>> TELNET SESSION STARTING UP <<<< <<<<' 'IDENTIFY (LIFO' pull me . if (rsel/='') then do; 'TELL' me 'Enter' rsel 'on the login prompt' say 'Enter' rsel 'on the login prompt' end; if rprt<=0 then rprt=''; word(telnet_program,1) rhst rprt; end; when rty='H' then do; 'command msg sorry... cant''t handle this kind of HyperText yet' 'command msg contact for more info!' end; when rty='i' then do; /* nothing (panda info item) */ 'command msg That''s just info text; sorry...' end; when rty='3' then do; /* error return item */ 'command msg Hmmm... this was just an error message; sorry...' end; otherwise do; 'command msg sorry... cant''t handle this request currently' end; end; exit(0); /* ------------------------------------------------------------------- */ search: parse arg SRrty,SRdis,SRrsel,SRrhst,SRrprt; rv=0; q=queued(); push 'name'; /* CSO options */ push SRrty push SRdis push SRrsel push SRrhst push SRrprt 'XEDIT GOPHER TMPSRCH A (PROFILE GOPHSRCH' q=queued()-q; do q; parse pull tag tline tcolumn ttext ttext=strip(ttext); if (tag='PFK' & tline='3') then return -1; if (tline=6) & (tcolumn=2) then do; if rsel/='' then rsel = rsel||'05'x||ttext; else rsel= ttext; end; end;/*do q*/ /* if (rsel='') then return -1; ... empty search string *IS* allowed */ return rv; \end{verbatim} \subsection{Module {\tt GOPHXQIT XEDIT}} \def\LPtopD{Module {\tt GOPHXQIT XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXQIT XEDIT #module GOPHXQIT XEDIT /***********************************************************************/ /* File GOPHXQIT XEDIT ## ## ## */ /* */ /* leave a file and restore some parameters */ /* */ /* written: 1992-06-29 */ /* latest update: 1993-03-28 <> */ /***********************************************************************/ 'QQUIT'; 'COMMAND EXTRACT /FTYPE/'; if FTYPE.1/='FTYPE.1' then do; 'GLOBALV SELECT GOPHER STACK L'FTYPE.1 pull xl 'COMMAND CURSOR SCREEN' xl end; exit(0); \end{verbatim} \subsection{Module {\tt GOPHXSVI XEDIT}} \def\LPtopD{Module {\tt GOPHXSVI XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHXSVI XEDIT #module GOPHXSVI XEDIT /***********************************************************************/ /* File GOPHXSVI XEDIT ## ## ## */ /* */ /* save current item into file GOPHER SAVEFILE */ /* */ /* written: 1992-02-28 */ /* latest update: 1993-05-23 <> */ /***********************************************************************/ 'EXTRACT /FLSCREEN/FTYPE/'; 'GLOBALV SELECT GOPHER STACK SAVEFILE' pull savefile if savefile='' then savefile='GOPHER SAVEFILE A'; /* @@@ 'GLOBALXV SELECT GOPHER STACK GI1:'FTYPE.1 @@@ */ 'GLOBALV SELECT GOPHER STACK GI1:'FTYPE.1 parse pull gitem1 /* @@@ 'GLOBALXV SELECT GOPHER STACK GI2:'FTYPE.1 @@@ */ 'GLOBALV SELECT GOPHER STACK GI2:'FTYPE.1 parse pull gi2a gi2b gi2c gi2d ds=date(sorted); ds=substr(ds,1,4)'-'substr(ds,5,2)'-'substr(ds,7,2); /* write header for saved item */ 'EXECIO 1 DISKW' savefile '(STRING' copies('*',72); 'EXECIO 1 DISKW' savefile '(STRING saved GOPHER Item' ds time() 'EXECIO 1 DISKW' savefile '(STRING Name='gitem1 'EXECIO 1 DISKW' savefile '(STRING Type='gi2a 'EXECIO 1 DISKW' savefile '(STRING Host='gi2b 'EXECIO 1 DISKW' savefile '(STRING Port='gi2c 'EXECIO 1 DISKW' savefile '(STRING Path='gi2d 'EXECIO 1 DISKW' savefile '(FINIS STRING' copies('-',72); /* write the item to be saved */ ':1' 'PUT *' savefile ':'FLSCREEN.1 'command msg item saved to file' savefile exit(0); \end{verbatim} \subsection{Module {\tt GOPHSTP EXEC}} \def\LPtopD{Module {\tt GOPHSTP EXEC}} \def\LPtopF{~} \begin{verbatim} #erase GOPHSTP EXEC #module GOPHSTP EXEC /* ******************************************************************* */ /* File GOPHSTP.EXEC ## ## ## */ /* */ /* Edit Gopher setup parameters using a mask */ /* Take also a look at the documentation: (*) User Setup */ /* */ /* written: 1993-05-20 */ /* latest update: 1993-05-31 <> */ /* ******************************************************************* */ 'MAKEBUF' group='GOPHER' address '' 'GLOBALV SELECT' group 'STACK HOST PORT ITYPE SEL DNAME' parse pull host . parse pull port . parse pull itype . parse pull sel parse pull dname address '' 'GLOBALV SELECT' group 'STACK BOOKMARK SAVEFILE INFOLEVEL' parse pull bookmark parse pull savefile parse pull infolevel . address '' 'GLOBALV SELECT' group 'STACK TELNET-8 TELNET-T' parse pull telnet_8 parse pull telnet_T do forever; /* init variables */ update= 1; q=queued(); push host push port push itype push sel push dname push bookmark push savefile push infolevel push telnet_8 push telnet_T 'XEDIT GOPHER SETUP A (PROFILE GOPHSTP' q=queued()-q; do q; parse pull tag tline tcol ttxt; ttxt= strip(ttxt); if (tag='PFK' & tline='3') then do; done= 'YES'; valid= 0; leave; end; if (tag='PFK' & tline='10') then do; dname= ''; host= ''; port= ''; sel= ''; itype= ''; bookmark= ''; savefile= ''; telnet_8= ''; telnet_t= ''; infolevel= ''; address '' 'GLOBALV SELECT' group 'SETLP DNAME' dname; address '' 'GLOBALV SELECT' group 'SETLP HOST' host; address '' 'GLOBALV SELECT' group 'SETLP PORT' port; address '' 'GLOBALV SELECT' group 'SETLP SEL' sel; address '' 'GLOBALV SELECT' group 'SETLP ITYPE' itype; address '' 'GLOBALV SELECT' group 'SETLP BOOKMARK' bookmark; address '' 'GLOBALV SELECT' group 'SETLP SAVEFILE' savefile; address '' 'GLOBALV SELECT' group 'SETLP TELNET-8' telnet_8; address '' 'GLOBALV SELECT' group 'SETLP TELNET-T' telnet_t; address '' 'GLOBALV SELECT' group 'SETLP INFOLEVEL' infolevel; end; if (tag='RES' & tline='4' & tcol='8') then do; dname= ttxt; address '' 'GLOBALV SELECT' group 'SETLP DNAME' dname; end; if (tag='RES' & tline='5' & tcol='8') then do; host= ttxt; address '' 'GLOBALV SELECT' group 'SETLP HOST' host; end; if (tag='RES' & tline='6' & tcol='8') then do; port= ttxt; address '' 'GLOBALV SELECT' group 'SETLP PORT' port; end; if (tag='RES' & tline='7' & tcol='8') then do; sel= ttxt; address '' 'GLOBALV SELECT' group 'SETLP SEL' sel; end; if (tag='RES' & tline='8' & tcol='8') then do; itype= ttxt; address '' 'GLOBALV SELECT' group 'SETLP ITYPE' itype; end; if (tag='RES' & tline='10' & tcol='18') then do; bookmark= ttxt; address '' 'GLOBALV SELECT' group 'SETLP BOOKMARK' bookmark; end; if (tag='RES' & tline='11' & tcol='18') then do; savefile= ttxt; address '' 'GLOBALV SELECT' group 'SETLP SAVEFILE' savefile; end; if (tag='RES' & tline='13' & tcol='30') then do; telnet_8= ttxt; address '' 'GLOBALV SELECT' group 'SETLP TELNET-8' telnet_8; end; if (tag='RES' & tline='14' & tcol='30') then do; telnet_t= ttxt; address '' 'GLOBALV SELECT' group 'SETLP TELNET-T' telnet_t; end; if (tag='RES' & tline='16' & tcol='14') then do; infolevel= ttxt; address '' 'GLOBALV SELECT' group 'SETLP INFOLEVEL' infolevel; end; end/*do q*/; upper done; if (left(done,1)='J' | left(done,1)='Y') then leave; end/*do forever*/; 'DROPBUF'; exit(0); \end{verbatim} \subsection{Module {\tt GOPHSTP XEDIT}} \def\LPtopD{Module {\tt GOPHSTP XEDIT}} \def\LPtopF{~} \begin{verbatim} #erase GOPHSTP XEDIT #module GOPHSTP XEDIT /* ******************************************************************* */ /* File GOPHSTP.XEDIT ## ## ## */ /* */ /* Edit Gopher setup, parameter mask */ /* */ /* written: 1993-05-20 */ /* latest update: 1993-05-31 <> */ /* ******************************************************************* */ 'set tofeof off' 'set scale off' 'set prefix off' 'set reserved 1 nohigh', ' ***** ***** GOPHER SETUP EDITOR ***** *****' /* 'set reserved 2 nohigh line 2' */ /* 'set reserved 20 nohigh PF2=add PF3=Quit PF5=Enter (replace entry)'*/ 'set ctlchar % protect blue high' 'set ctlchar $ noprotect red high' 'set ctlchar { noprotect yellow high' 'set ctlchar } noprotect green high' 'set ctlchar " escape' 'set pf4 =' parse pull tn_T parse pull tn_8 parse pull infolevel parse pull savefnm parse pull bmkfnm parse pull dname parse pull sel parse pull itype parse pull port parse pull host tn_T= substr(tn_t, 1, 52); tn_8= substr(tn_8, 1, 52); infolevel= substr(infolevel, 1, 2); savefnm= substr(savefnm, 1, 20); bmkfnm= substr(bmkfnm, 1, 20); dname= substr(dname, 1, 74); sel= substr(sel, 1, 74); itype= substr(itype, 1, 1); port= substr(port, 1, 5); host= substr(host, 1, 74); ph= '"%' nh= '"$' yh= '"{' gh= '"}' 'EXTRACT /LSCREEN/'; last_line= lscreen.1-2; /* mask */ z.3= ph'Default Coordinates in Gopher Space to start from:' z.4= ph'Name='||nh||dname||ph z.5= ph'Host='||nh||host||ph z.6= ph'Port='||nh||port||ph z.7= ph'Path='||nh||sel||ph z.8= ph'Type='||nh||itype||ph z.10= ph'Bookmark file: '||nh||bmkfnm||ph z.11= ph'Save file: '||nh||savefnm||ph z.13= ph'Telnet program for Type=8: '||nh||tn_8||ph z.14= ph'Telnet program for Type=T: '||nh||tn_t||ph z.16= ph'Infolevel: '||nh||infolevel||ph||'0..nothing 5..normal 9..much' z.last_line= 'PF: 2=...... 3=quit 5=...... '||, '6=....... 7=..... 8=.... 9=..... 10=clear'; do zc= 1 to last_line; if symbol('Z.ZC') = 'VAR' then, 'set reserved' zc 'nohigh' z.zc end /* readout data */ 'cursor screen 4 8' 'read all tag' 'qquit' \end{verbatim} \subsection{Module {\tt XG EXEC}} \def\LPtopD{Module {\tt XG EXEC}} \def\LPtopF{~} \begin{verbatim} #erase XG EXEC #module XG EXEC /***********************************************************************/ /* File XG EXEC ## ## ## */ /* */ /* display a file in GOPHER fashion */ /* */ /* written: 1992 02 29 */ /* last update: 1992 10 23 <> */ /***********************************************************************/ parse arg fn ft fm . 'SET EMSG OFF' 'XEDIT' fn ft fm '(PROFILE GOPHER' 'SET EMSG ON' exit(rc); # endswitch # this ends the block of fixes for the client modules \end{verbatim} \section{Utilities for Server Functions} \def\LPtopC{Utilities for Server Functions} \def\LPtopD{~} \def\LPtopF{~} \subsection{Module {\tt COOKIE EXEC}} \def\LPtopD{Module {\tt COOKIE EXEC}} \def\LPtopF{~} \begin{verbatim} #erase COOKIE EXEC #module COOKIE EXEC /* ------------------------------------------------------------------- */ /* File COOKIE EXEC ## ## ## */ /* */ /* a COOKIE (or FORTUNE) message generator */ /* */ /* written: 1992-05-08 */ /* last update: 1992-10-23 <> */ /* ------------------------------------------------------------------- */ parse upper arg fnm '('option 'MAKEBUF'; readdisk='*'; fortune_txt='FORTUNE TXT' readdisk fortune_idx='FORTUNE IDX' readdisk 'LISTFILE' fortune_idx '(ALLOC LIFO NOHEAD' if rc/= 0 then do; push 'there is no fortune cookie for you!' q=1; end; else do; pull . . . . . fortune_size . r=random(1,fortune_size); 'EXECIO 1 DISKR' fortune_idx r '(FINIS LIFO' pull from to size=to-from+1; /* say 'r='r 'from='from 'to='to 'size='size */ q=queued(); 'EXECIO' size 'DISKR' fortune_txt from '(FINIS FIFO' q=queued()-q; end; if fnm='' then do; do q; parse pull line say line end; end; else; do; 'EXECIO' q 'DISKW' fnm '(FINIS' end; 'DROPBUF' exit(0); \end{verbatim} \subsection{Module {\tt FINGER EXEC}} \def\LPtopD{Module {\tt FINGER EXEC}} \def\LPtopF{~} \begin{verbatim} #erase FINGER EXEC #module FINGER EXEC /* ------------------------------------------------------------------- */ /* FINGER EXEC */ /* */ /* Interface to contact RUI (remote user information) servers on */ /* remote hosts or to get user information about local users. */ /* For local user information, the program FINGERXX.EXEC is called. */ /* */ /* written: 1992 05 08: */ /* last update: 1992 06 19: ditto */ /* ------------------------------------------------------------------- */ parse arg req last_at=0; do forever; i=index(req,'@',last_at+1); if (i=0) then leave; last_at=i; end; if last_at>0 then do; usr=substr(req,1,last_at-1); node=substr(req,last_at+1); end; else do; usr=req; node=''; end; localnode.1='awiwuw11.wu-wien.ac.at' if node='' then do; say '['localnode.1']' 'FINGERXX (0' usr exit(0); end; say '['node']' remote=node; 'REXTCPIP' if verify(node,'0123456789.') /= 0 then do; remote=GetIPAddr(node); if remote='' then do; say 'unknown host:' node 'NUCXDROP R