[CONTACT]

[ABOUT]

[POLICY]

verbatim File product tt sl The

Found at: gopher.meulie.net:70/gopher/VieGOPHER/viegophr.product


#verbatim
% File viegophr.product
%
\documentstyle[lpr,appleps]{article}
\pagestyle{myheadings}
\begin{document}
\def\LPtopA{{\tt VieGOPHER}}
\def\LPtopB{{\sl The Code}}

\pagenumbering{roman}
%\thispagestyle{empty}
\title{VieGOPHER\\---\\A Gopher System for VM/CMS\\---\\{\small The Code}}
\author{Gerhard Gonter}
\date{\today}
\maketitle

\begin{abstract}
The Internet Gopher Protocol provides a simple, yet very effective
method for distributed document search and retrieval.  This document
contains a documented source listing of the VieGOPHER system.  Included
are the sources of the server, the client and the VieGOPHER specific
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
\end{abstract}

\pagebreak[4]
\begin{normalsize}
\def\LPtopC{Contents}
\tableofcontents
\end{normalsize}
\pagebreak[4]
\setcounter{page}{1}
\pagenumbering{arabic}
%\begin{small}
\small
#endverbatim

#section Introduction
ROSE file: viegophr.product

This file contains most files related to the VieGOPHER system.
To install the product enter the command: ROSE VIEGOPHR

% NOTE: This is not the original version of the document!
%       Instead, this is the sequenced and stripped version of a
%       hypertext file.
%       The linear ROSE version is provided for your convenience.
%       For more information contact the author.


#set       RELEASE VieGOPHER Ver 2.00.00
#set  RELEASE_DATE 1993-06-16
#set  RELEASE_TIME 18:00
#verbatim
\def\LPbotC{VieGOPHER 2.00.00 1993-06-16 18:00}
#endverbatim

#set SENDFIX NO

#section Help messages


#help TEST_and_HACK
There might be a number of Test and Hack options that can be configured.
Usually you should select the predefined value.  No further details about
any option is given.
#endhelp

#set TEST_and_HACK YES
#prompt TEST_and_HACK

#help TEST_dd_binsel
TEST_dd_binsel:
BIN ... REXTCPIP, selector string is read binary and converted
#endhelp
#set TEST_dd_binsel NO
#prompt TEST_dd_binsel



#help Compactor
Compactor allows the installation procedure to remove unrelevant parts
from the EXEC and XEDIT files.  Removed are comments, extra spaces,
and lines.  This reduces loading time a bit and might bring a little
bit of performance.  Compacting will only be executed if the program
REXXCPCX.MODULE is present.

NONE ... to compact nothing at all (the default)
ALL  ... to compact everything
C    ... to compact just client modules
S    ... to compact server modules
T    ... to compact tools
Answering CU allows the compacting of the client and the utilities
alone, the server will not be modified.  CST is equivalent to ALL.
#endhelp

#set Compactor NONE
#prompt Compactor



#help MAKE_SERVER
MAKE_SERVER defines if server modules will be generated.
The preset value is YES.
#endhelp

#set MAKE_SERVER YES
# currently no prompting, because ROSE doesn't do it anyway...
# prompt MAKE_SERVER


# This is a block of definitions for the server code.
# switch MAKE_SERVER
# case YES


#help LOGFILE
LOGFILE defines the file where server activities are logged.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set LOGFILE 'GOPHERD LOGFILE A'
#prompt LOGFILE


#help SELECTORFILE
SELECTORFILE defines the file where all known selector strings are stored.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set SELECTORFILE 'GOPHERD SELECTOR A'
#prompt SELECTORFILE


#help GROUPFILE
GROUPFILE defines the file where access group information is stored.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set GROUPFILE 'GOPHERD GROUPS A'
#prompt GROUPFILE


#help EAIFILE
EAIFILE defines the file where handlers for embedded applications
are declard.
The preset value can be overridden by a global variable of the same
name in the GOPHERD group.
#endhelp

#set EAIFILE 'GOPHERD EAI A'
#prompt EAIFILE


#help DSKGRPFILE
DSKGRPFILE names the file where assignments between disks and
access groups are defined.
The preset value can be overridden by a global variable of the same
name in the GOPHERD group.
#endhelp

#set DSKGRPFILE 'GOPHERD DSKGRP A'
#prompt DSKGRPFILE


#help ROOTINDEX
ROOTINDEX defines the file that contains the root index or root menu.
This file is sent out whenever an empty selector string is presented
to the GOPHER server.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set ROOTINDEX 'GOPHERD INDEX A'
#prompt ROOTINDEX


#help GOPHER_PORT
GOPHER_PORT is used in the server module to define the TCP port number
of the gopher server.  It is typically port number 70.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set GOPHER_PORT 70
#prompt GOPHER_PORT


#help FINGER_PORT
FINGER_PORT is used in the server module to define the TCP port number
of the finger server.  It is typically port number 79.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set FINGER_PORT 79
#prompt FINGER_PORT


#help COOKIE_PORT
COOKIE_PORT is used in the server module to define the TCP port number
of the cookie server.  It is typically port number 17.
The preset value can be overridden by a global variable of the same name
n the GOPHERD group.
#endhelp

#set COOKIE_PORT 17
#prompt COOKIE_PORT


#help USE_SRE
SRE means: Structured Representation Engine
This is an experimental protocol and usually you would not select it.
#endhelp

#set USE_SRE NO
#prompt USE_SRE

#help SRE_PORT
SRE_PORT is used in the server module to define the TCP port number
of the SRE server.  It is typically port number 150.
#endhelp
#set SRE_PORT 150
#prompt SRE_PORT


#help SRV_WAKEUP_SYSTEM
SRV_WAKEUP_SYSTEM defines if or if not messages (SMSG, MSG) from other
virtual machines or from RSCS are accepted and processed.
Typically, messages are trapped and processed.  In this case the
variable value is set to 'YWAKEUP' or to 'WAKEUP', depending on the
#endhelp

#set SRV_WAKEUP_SYSTEM YWAKEUP
#prompt SRV_WAKEUP_SYSTEM


#help SRV_TCP
SRV_TCP defines the TCP transport package to be used.  Allowed value
s REXTCPIP (the original and default package).
RXSOCKET might be an option in the future.
#endhelp

#set SRV_TCP REXTCPIP
# prompt SRV_TCP


#help SRV_ORGANIZATION
SRV_ORGANIZATION is the short name of your organization.
Don't use single quotes in the string.
#endhelp

#set SRV_ORGANIZATION Univ. of Economics, Vienna, Austria
#prompt SRV_ORGANIZATION


# endswitch
# this ends the block of fixes for the server modules


#help MAKE_CLIENT
MAKE_CLIENT defines if client modules will be generated.
The preset value is YES.
#endhelp

#set MAKE_CLIENT YES
# currently no prompting, because ROSE doesn't do it anyway...
# prompt MAKE_CLIENT



# This is a block of definitions for the client code.
# switch MAKE_CLIENT
# case YES


#help STARTUP_DIR
STARTUP_DIR defines a string that is displayed as a title for the
first Gopher menu.

Note: This value is *not* entered a quoted string.

This setting can be re-defined by the user with the SETUP function
n Gopher or by calling GOPHSTP.
#endhelp

#set STARTUP_DIR WU-Wien Gopher Information Service: Main Menu
#prompt STARTUP_DIR


#help CL_TCP
CL_TCP defines the TCP transport package to be used by the client.
Allowed values are REXTCPIP (the original package) and
RXSOCKET (now the default).

alternative TCP transport support.
#endhelp

#set CL_TCP RXSOCKET
#prompt CL_TCP


#help CL_USE_FDNS
CL_USE_FDNS defines if the FDNS hack should be used to resolve a gopher
YES should be selected if you find it absolutely necessary to circumvent
the GetIPAddr() function in REXTCPIP.  If RXSOCKET is used, this variable
must be set to NO.
#endhelp

#set CL_USE_FDNS NO
# we don't allow this option any longer, it's not necessary ...
# prompt CL_USE_FDNS


#help CL_USE_LOCALFETCH
CL_USE_LOCALFETCH defines if the client should be allowed to access data
on the Gopher server's disk, if the server is on the very same host as
the client.

Localfetch is mainly a trade-off between data-privacy (whatever this
means when we speak about a server) and performance.  Using localfetch
you can also setup a pseudo-server, that is, you provide only gopher

YES     works if individual clients have read access to the gopher
        data files.  This is needed for pseude servers.
NO      should work in most cases.
Default is NO.
#endhelp

#set CL_USE_LOCALFETCH NO
#prompt CL_USE_LOCALFETCH


#help CL_LOCALHOST
CL_LOCALHOST containts the IP address of the local machine.  This
nformation is only relevant when the option CL_USE_LOCALFETCH is
answered with YES.  In this case the client will try to read the
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

Minor bug: if you're using RXSOCKET you should enter your hostname
nstead of the IP address
#endhelp

#set CL_LOCALHOST 999.99.9.99
#prompt CL_LOCALHOST


#help CL_LANG
CL_LANG defines the language that the client uses for messages to
the user and help screens.
Defined values are: ENGLISH and GERMAN, default is ENGLISH
#endhelp

#set CL_LANG ENGLISH
#prompt CL_LANG


#help CL_DEFAULT_HOST
CL_DEFAULT_HOST defines the Internet name of the default Gopher server
that should be contacted on startup.  Usually you will want to declare
one of your favorite servers next door but you can of course also use
the default value 'gopher.wu-wien.ac.at'.

Note:           Enter the host name as a quoted string!
Default:        'gopher.wu-wien.ac.at'

This setting can be re-defined by the user with the SETUP function
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_DEFAULT_HOST 'gopher.wu-wien.ac.at'
#prompt CL_DEFAULT_HOST


#help CL_DEFAULT_PORT
CL_DEFAULT_PORT defines the TCP port number at which your Internet 
Gopher server listens.  The 'official' port number is now 70 and

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
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_DEFAULT_PORT '70'
#prompt CL_DEFAULT_PORT


#help CL_DEFAULT_TYPE
CL_DEFAULT_TYPE defines the code for the item type that you'll receive
from your Gopher server.  According to the protocol, a Gopher server
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
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_DEFAULT_TYPE '1'
#prompt CL_DEFAULT_TYPE


#help CL_TIME_OUT
CL_TIME_OUT defines the time in seconds that the client will wait
for the server to respond.  After this timeout, the client will
Default value is 15.
#endhelp

#set CL_TIME_OUT 15
#prompt CL_TIME_OUT


#help CL_INFO_LEVEL
CL_INFO_LEVEL defines the default level of information that is in
effect when the gopher client displays menus or files etc.

Currently defined values are:

This setting can be re-defined by the user with the SETUP function
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_INFO_LEVEL 5
#prompt CL_INFO_LEVEL


#help CL_CONTACT
CL_CONTACT defines the email address of a person that might be able to
answer to typical questions of users.
The default value 'Gerhard.Gonter@wu-wien.ac.at'.
#endhelp

#set CL_CONTACT Gerhard.Gonter@wu-wien.ac.at or GONTER@AWIWUW11.bitnet
#prompt CL_CONTACT


#help CL_TELNET_8
CL_TELNET_8 defines the name of the program for use with Telnet session
feature if the Telnet program could automatically switch to VT100 or

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
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_TELNET_8 'TELNET MODULE'
#prompt CL_TELNET_8


#help CL_TELNET_T
CL_TELNET_T defines the name of the program for use with Telnet session
feature if the Telnet program could automatically switch to VT100 or

Type T is now an official extension to the original Gopher type list and
s 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
n Gopher or by calling GOPHSTP.
#endhelp

#set CL_TELNET_T 'TELNET MODULE'
#prompt CL_TELNET_T


# endswitch
# this ends the block of fixes for the client modules


# This is a block of definitions for the server code.
# switch MAKE_SERVER
# case YES


#section Multi-Threaded Server
#erase GOPHERDD EXEC
#module GOPHERDD EXEC
#subsubsection main init
/* ------------------------------------------------------------------- */
REVISION='GOPHERDD EXEC #<RELEASE># #<RELEASE_DATE># #<RELEASE_TIME>#'
/*                                                                     */
/* 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: <Gerhard.Gonter@wu-wien.ac.at>           */
/*                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'
 
/* to be configured at installation time by ROSE:  - - - - - - - - - - */
f logfile=''      then logfile=      #<LOGFILE>#;
f selectorfile='' then selectorfile= #<SELECTORFILE>#;
f groupfile=''    then groupfile=    #<GROUPFILE>#;
f eaifile=''      then eaifile=      #<EAIFILE>#;
f dskgrpfile=''   then dskgrpfile=   #<DSKGRPFILE>#;
f rootindex=''    then rootindex=    #<ROOTINDEX>#;
f gopherport=''   then gopherport=   #<GOPHER_PORT>#;
f fingerport=''   then fingerport=   #<FINGER_PORT>#;
f cookieport=''   then cookieport=   #<COOKIE_PORT>#;
#switch USE_SRE
#case YES
f sreport=''      then sreport=      #<SRE_PORT>#;
#endswitch
/* - - - - - - - - - - - - - - - - - - -  end of configuration section */
 




TTW= 600;               /* time to wait (10 minutes)                   */
MAXSQ= 16384;           /* maximum output queue size                   */
                        /* Gopher Server Wait hook code                */

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               */
f 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   */

  startserver('GOPHER', gopherport, '', '');
  startserver('FINGER', fingerport, '', '');
  startserver('COOKIE', cookieport, '', '');
#switch USE_SRE
#case YES
  startserver('SRE',    sreport,    '', '');
#endswitch 


#subsubsection main loop
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
  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;
      '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;
            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;
              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 .
                  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);
              if tcpline.0 <= 0 then leave;
              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;
    'bytes_to_read='bytes_to_read
              if TCPRC      = 0           &,
                 tl0       /= 'TCPLINE.0' &,
                 tcpline.0 >= 1           then do;
                do i=1 to tcpline.0;
                  say 'tcpline.'i'='tcpline.i
                end;
                dosleep=0;
              end;
            end;
          end;
          when CONN_STAT='Closed' |,
               CONN_STAT='Non-existant' then do;
            x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')',
                      '('sta') unexpected connstat:' CONN_STAT);
            mtsta.mtact=0;
          end;
          when CONN_STAT='Receiving only' |,
               CONN_STAT='Sending only' then do;
            x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')',
                      '('sta') unexpected connstat:',
                      CONN_STAT '-> now dead');
            mtsta.mtact=1;
          end;
          when CONN_STAT='Trying to open' then do; /* nothing */ end;
          otherwise do;
            x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')',
                      '('sta') unknown connstat:',
                      CONN_STAT '-> now dead');
            mtsta.mtact=1; /* now officially declared dead */
          end;
        end/*select*/
      end/*when sta=2010*/;
#endswitch

    otherwise do; /* unknown slot status */
      x=logtext('['mtact']' mttyp.mtact'('mtprt.mtact')',
                '('sta') unknown status -> now dead');
      mtsta.mtact=1;
    end;
    end/*select*/
 
  end/*do mtact=1 to mtmax*/;
 
  /* now check if we should commit suicide: */
  if (deadcount>100) then do;       /* so many dead slots? */
    x=logtext('***** too many dead slots! deadcount=',
              deadcount 'leaving *****');
    leave;
  end;
  if (listencount<=0 & emptycount+deadcount>=mtmax) then do;
    x=logtext('***** no-one is listening! listencount=',
              listencount 'leaving *****');
    leave;
  end;
  if mtmax>1 & mtsta.mtmax=0 then do;
    mtmax=mtmax-1;
    say 'mtmax is now down to' mtmax
  end;
 
#switch SRV_WAKEUP_SYSTEM
#case YES
----> this mode is invalid, select either YWAKEUP, WAKEUP or NO
#case YWAKEUP
  if dosleep then do;
     'YWAKEUP (*MSG SMSG NOWAIT'
     select;
       when ywakeup.1='NOWAIT' then do; /* nothing */ end;
       when ywakeup.1='*MSG' then do;
#        do i=0 to ywakeup.0;
#          say 'ywakeup.'i'='ywakeup.i
#        end;
         if ywakeup.5='SMSG' & ywakeup.4='SYS-REACCESS' then do;
           x= reaccess();
           'MSG' ywakeup.3 'I''ve reaccessed my mini disks';
         end;
       end;
       otherwise do; /* nothing */ end;
     end;
  end;
#case WAKEUP
  if dosleep then do;
    q=queued();
    'WAKEUP +0 (IUCVMSG'
    ret=rc;
    do queued()-q;
      pull text
      select;
        when ret=5 then do;
          parse var text '*' wakeup1 wakeup3 wakeup4
          select;
            when wakeup1='NOWAIT' then nop;
            when wakeup1='MSG' then nop;
            when wakeup1='SMSG' & wakeup4='SYS-REACCESS' then do;
              x= reaccess();
              'MSG' wakeup3 'I''ve reaccessed my mini disks';
            end;
            otherwise do; /* nothing */ end;
          end/*select*/;
        end;
        otherwise do; /* nothing */ end;
      end/*select*/;
    end/* do queued()-q */;
  end;
#endswitch

end/*do forever*/;
x=logtext('***** stopping *****')
 
'NUCXDROP REXTCPIP';
exit(0); 


#subsubsection startserver
/* ------------------------------------------------------------------- */
/* start a protocol handler of a particular type on a given port       */
/* RETURNS: 0 .. no server started, 1 .. server started                */

f port <= 0 then return 0;
xsta= 1;

  when type='GOPHER' | type='FINGER' | type='COOKIE' then do;
    xsta= 1000;
  end;
  when type='SRE' then do;
    xsta= 2000;
  end;
  otherwise return 0;
end;
 
  if mtsta.i=0 then leave;
end;
f i=mtmax & mtsta.i/=0 then i= i+1;
f 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);
f TCPRC=0 then mtsta.i= xsta; else mtsta.i= 1;


#subsubsection gopherselector
/* ------------------------------------------------------------------- */
x= logtext('['nr']' mttyp.nr'('mtprt.nr') sel='full_selector);

f mttyp.nr='EXTRA' then do
  x= mtsel1.nr;
  if full_selector/='' && x/='' then x= x||TAB;
  full_selector= x||full_selector||mtsel2.nr;
end;

f lfrom='' then lfrom= 0;
f lto  ='' then lto  = 32700;   /* catch all */

f mttyp.nr='FINGER' then do
  if selector='' then selector='NAMES'
  return gopherspecial(nr 'SPEC-FINGER' selector)
end;

/* empty selector means, return root index */
f selector='' then do
  return gophersend(nr lfrom lto rootindex)
end;

/* fast access to files */
f 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 */
f left(selector,3)='EA-' then do
  return gopher_eai(nr substr(selector,4))
end;

/* system items */
f left(selector,4)='SYS-' then do
  return gophersys(nr selector)
end;

/* special items */
f 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.                                   */

/* go down the list of known selectors and see if we care ... */
  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';


#subsubsection gophersend
/* ------------------------------------------------------------------- */
 
/* check out irregularities */
f lfrom<0     then lfrom=0;
f   lto>32500 then lto=32500;
f lfrom>lto   then do; lfrom=0; lto=32500; end;
 
upper fn ft fm;
'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD'
f rc/=0 then do;
  mtmsg.nr= '3 sorry... this item is currently unavailable';
  return 1;
end;
/* say 'send:' fn ft fm 'size='filesize */
f filesize<=0 then do;
  mtmsg.nr= '3 sorry... this item is no ok';
  return 1;
end;
 
mtfil.nr= fn ft fm;
f lto  <filesize then mtsiz.nr= lto;   else mtsiz.nr= filesize;
f lfrom>0        then mtrec.nr= lfrom; else mtrec.nr= 0;


#subsubsection gopherspecial
/* ------------------------------------------------------------------- */
xfnm= 'GOPHERDD TMP'nr rwdisk
address command 'STATEW' xfnm
f rc=0 then 'ERASE' xfnm;
qtxt= 'QUERY LOGMSG'
  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;
 


#subsubsection gophersys
/* ------------------------------------------------------------------- */
xfnm= 'GOPHERDD TMP'nr rwdisk
address command 'STATEW' xfnm
f rc=0 then 'ERASE' xfnm;
qtxt= 'QUERY LOGMSG'
  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=#<CL_CONTACT>#'
    'IDENTIFY (LIFO';
    parse pull z
    push 'server='z
    push 'revision='REVISION
    push 'note=Implemented using #<SRV_TCP># by',
               '<Gerhard.Gonter@wu-wien.ac.at>'
    push 'note=#<SRV_ORGANIZATION>#'
    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'


#subsubsection gopher\_eai
/* ------------------------------------------------------------------- */

xfnm= 'GOPHERDD TMP'nr rwdisk
address command 'STATEW' xfnm
f rc=0 then 'ERASE' xfnm;

  use= check_group(eaigrp.i, mtfad.mtact);
  if (use & eaiid.i=selector) then do;
    cmdline= prepare_command(eaipgm.i, xfnm, keyword);
    interpret cmdline;
    return gophersend(nr 0 32700 xfnm);
  end;
end;
mtmsg.nr= '3 sorry... this item is unknown here';


#subsubsection reaccess
/* ------------------------------------------------------------------- */
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'
f rc=0 then do;
  say 'executing gopher server reaccess hook code'
  'GSH_REAC'
end;

address '' 'STATE GSH_WAIT EXEC'
f rc=0 then gsh_wait_exists= 1;
else gsh_wait_exists= 0;



#subsubsection readselectors
/* ------------------------------------------------------------------- */
upper fn ft fm;
'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD'
f (rc/=0) then return -1;
  '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;


#subsubsection readgroups
/* ------------------------------------------------------------------- */
upper fn ft fm;
address '' 'LISTFILE' fn ft fm '(ALLOC LIFO NOHEAD'
f (rc/=0) then return -1;
  '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;


#subsubsection read\_eai
/* ------------------------------------------------------------------- */
upper fnm;
'LISTFILE' fnm '(ALLOC LIFO NOHEAD'
f (rc/=0) then return -1;
eaicnt= 0;
eaiid.= ''
eaipgm.= '';
eaigrp.= '';
  '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;


#subsubsection read\_disk\_groups
/* ------------------------------------------------------------------- */
upper fnm;
address '' 'LISTFILE' fnm '(ALLOC LIFO NOHEAD'
f (rc/=0) then return -1;
  '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;


#subsubsection xconnstat
#switch SRV_TCP
#case REXTCPIP 
/* ------------------------------------------------------------------- */
xconnstat: parse arg gcn msg
connstat(gcn);
    'unacked_bytes='unacked_bytes
    foreign_addr':'foreign_port
#endswitch


#subsubsection logtext
/* ------------------------------------------------------------------- */
logtext: parse arg txt
'EXECIO 1 DISKW' logfile '(FINIS STRING' date(sorted) time() txt


#subsubsection prepare\_command
/* ------------------------------------------------------------------- */
outline= '';
ll= length(template);
  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*/;


#subsubsection check\_group
/* ------------------------------------------------------------------- */
check_group: parse arg xxgrp, fad
f (xxgrp='*') then return 1;

  if (grpid.j=xxgrp &,
      grpaddr.j=substr(fad,1,length(grpaddr.j)) )
    then return 1;
end;/*do*/



#subsubsection check\_disk\_group
/* ------------------------------------------------------------------- */
check_disk_group: parse arg xxfm, fad
  if dskgrpdsk.j=xxfm then do;
    u= check_group(dskgrpnam, mtfad.mtact);
    if u>0 then return dskgrppri.j;
  end;
end;


#subsubsection EBCDIC to ASCII conversion
/* E2A EXEC simple ebcdic to ascii translate by JCA ------------------ */
E2A:
   return translate(arg(1), ,
     '000102030009007F0000000B0C0D0E0F'x||,
     '1011121300000800181900001C1D1E1F'x||,
     '00000000000A171B0000000000050607'x||,
     '0000160000000004000000001415001A'x||,
     '20000000000000000000002E3C282B7C'x||,
     '2600000000000000000021242A293B7E'x||,
     '2D2F0000000000000000002C255F3E3F'x||,
     '005E00000000000000603A2340273D22'x||,
     '00616263646566676869007B00000000'x||,
     '006A6B6C6D6E6F707172007D00000000'x||,
     '007E737475767778797A0000005B0000'x||,
     '000000000000000000000000005D0000'x||,
     '7B414243444546474849000000000000'x||,
     '7D4A4B4C4D4E4F505152000000000000'x||,
     '5C00535455565758595A000000000000'x||,
     '30313233343536373839000000000000'x);



#subsubsection ASCII to EBCDIC conversion
/* A2E EXEC simple ascii to ebcdic translate by JCA ------------------ */
A2E:
   return translate(arg(1), ,
     '00010203372D2E2F1605250B0C0D0E0F'x||,
     '101112133C3D322618193F271C1D1E1F'x||,
     '405A7F7B5B6C507D4D5D5C4E6B604B61'x||,
     'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x||,
     '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x||,
     'D7D8D9E2E3E4E5E6E7E8E9ADE0BD716D'x||,
     '79818283848586878889919293949596'x||,
     '979899A2A3A4A5A6A7A8A9C04FD05F07'x||,
     '00010203372D2E2F1605250B0C0D0E0F'x||,
     '101112133C3D322618193F271C1D1E1F'x||,
     '405A7F7B5B6C507D4D5D5C4E6B604B61'x||,
     'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x||,
     '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x||,
     'D7D8D9E2E3E4E5E6E7E8E9ADE0BD716D'x||,
     '79818283848586878889919293949596'x||,
     '979899A2A3A4A5A6A7A8A98B4F9B5F07'x);



# endswitch
# this ends the block of fixes for the server modules


# This is a block of definitions for the client code.
# switch MAKE_CLIENT
# case YES


#section Client
#erase GOPHERC EXEC
#module GOPHERC EXEC
/***********************************************************************/
/* File GOPHERC EXEC #<RELEASE># #<RELEASE_DATE># #<RELEASE_TIME># */
/*                                                                     */
/* The main module of the GOPHER Client for VM/CMS                     */
/*                                                                     */
/* written:       1992 02 26: <Gerhard.Gonter@wu-wien.ac.at>           */
/* latest update: 1993-05-26                                           */
/***********************************************************************/

/* address command ... not yet ... */
/* @@@ 'GLOBALVV SELECT GOPHER STACK HOST' @@@ */
address '' 'GLOBALV SELECT GOPHER STACK HOST'
/* @@@ 'GLOBALVV SELECT GOPHER STACK SEL' @@@ */
address '' 'GLOBALV SELECT GOPHER STACK SEL'
'GLOBALV SELECT GOPHER STACK ITYPE PORT DNAME'

/* use default values from lasting globalv */
f host='' | host='=' then host= dhost
f port='' | port='=' then port= dport
f ity='' then ity= dity
f sel='' then sel= dsel
 
/* if nothing is specified use preset values: */
 
/* to be configured at installation time by ROSE:  - - - - - - - - - - */
f host='' | host='=' then host= #<CL_DEFAULT_HOST>#;
f port='' | port='=' then port= #<CL_DEFAULT_PORT>#;
f ity='' then ity=#<CL_DEFAULT_TYPE>#;  /* default: directory */
/* - - - - - - - - - - - - - - - - - - -  end of configuration section */
 
  when ity='0' then xity='<F>';
  when ity='1' then xity='<D>';
  when ity='7' then xity='<S>';
  when ity='B' then xity='<B>'; /* BSS encoded binary data */
  otherwise xity='>'ity'<';
end;

f dname='' then dname= xity substr('#<STARTUP_DIR>#',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';

f '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;

/* @@@ 'ERASE LASTING GLOBALXV' @@@ */
'ERASE GOPHTMP TMP0'
'SET EMSG ON'
 
exit(0);


#erase GOPHER XEDIT
#module GOPHER XEDIT
/***********************************************************************/
/* File GOPHER XEDIT #<RELEASE># #<RELEASE_DATE># #<RELEASE_TIME># */
/*                                                                     */
/* Setup for the GOPHER Client                                         */
/*                                                                     */
/* written:       1992-02-26: <Gerhard.Gonter@wu-wien.ac.at>           */
/* latest update: 1993-05-31                                           */
/***********************************************************************/

/* 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                    */
f 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!'

  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'

  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 */
f 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='<F> are files, <D> 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/';
/* @@@ 'GLOBALXV SELECT GOPHER STACK GHOST' @@@ */
address '' 'GLOBALV SELECT GOPHER STACK GHOST'
/* @@@ 'GLOBALXV SELECT GOPHER STACK GI1:'FTYPE.1 @@@ */
address '' 'GLOBALV SELECT GOPHER STACK GI1:'FTYPE.1

/* default info level */
'GLOBALV SELECT GOPHER STACK INFOLEVEL'
f infolevel='' then infolevel=#<CL_INFO_LEVEL>#;
f words(screen.1)>3 then infolevel=0;

  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*/;

  when modus='RNG' | modus='12R' then do;
    'SET RESERVED 3 BLUE NONE NOHIGH list of currently active items; '||,
       'select one ...'
  end;
  otherwise do;
    'SET RESERVED 3 BLUE NONE NOHIGH' substr(dirname,1,79);
    'SET RESERVED 4 BLUE NONE NOHIGH' substr(ghost,1,79);
  end;
end/*select*/



#erase GOPHER HELPCMS
#module GOPHER HELPCMS
How to Use the VM Gopher at San Jose State
------------------------------------------------------------------------

***  Please note: this file was written by John Sroka, San Jose State   ***
***  University, and reflects the local installation there, most parts  ***
***  of this text, however, can be applied for other installations. -GG ***
 
The VM gopher client/server at San Jose State University allows you
to browse information files on the campus, or at any of the (175 and
counting) other gopher sites around the world.
 
Gopher was developed at the University of Minnesota.  The CMS version
Austria (Gerhard.Gonter@wu-wien.ac.at, +43/1/31336/4111).
 
to John Sroka at Information Systems & Computing (sroka@sjsuvm1.sjsu.edu)
 
 
*** The Display ***
 
A typical gopher display simply contains a list of information files,
or directories which list more information files.  The first character
n the descriptor tells you what the item is:
 
<F>  an information file
<D>  a directory of more information files
<S>  a text database where searching can be done
# *** SJSU ***
# <E>  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:
 
# *** SJSU ***
# PF10 spools the displayed item to your virtual printer
 
*** 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
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,
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
cursor position.  In the latter case, the cursor must be positioned
on a valid file or directory descriptor.  To retrieve your customized
list, just press the PF6 (Bookmark) key.
 
Note there are no keys to remove saved bookmarked items.  Simply exit
GOPHER and manually edit (XEDIT GOPHER BOOKMARK A) out the unwanted
entries.
 
*** Adding Information to Gopher ***
 
We encourage departments who wish to publish information on the gopher
to contact us at the e-mail address above.   If you've a Macintosh
or Unix box which is attached to the Internet, you may wish to build
your own server, or the information may be added to the VM gopher.
 
Your comments and questions are also welcomed.



#erase GOPHFTCH EXEC
#module GOPHFTCH EXEC
#subsubsection main
/***********************************************************************/
/* File GOPHFTCH EXEC  #<RELEASE># #<RELEASE_DATE># #<RELEASE_TIME># */
/*                                                                     */
/* Fetch an item from a GOPHER server                                  */
/*                                                                     */
/* Uses: REXTCPIP MODULE or RXSOCKET MODULE                            */
/*                                                                     */
/* written:       1992-02-28: <Gerhard.Gonter@wu-wien.ac.at>           */
/*                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                                          */
/***********************************************************************/
 
f fn='' then exit(28);

/* get the GOPHER coordinates for item to fetch */
  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*/;
 
f ty='' then ty=0;
 
/* to be configured at installation time by ROSE:  - - - - - - - - - - */
f rsrv='' then rsrv=#<CL_DEFAULT_HOST>#;      /* default server       */
f rprt='' then rprt=#<CL_DEFAULT_PORT>#;      /* default servers port */
tout=#<CL_TIME_OUT>#;                   /* timeout for TCPOPEN (sec)   */

#switch CL_USE_LOCALFETCH
#case YES
localhost='#<CL_LOCALHOST>#';  /* IP where client runs */
localgopher='70';              /* Port where Gopher server listens on  */
ndexfile='GOPHERD INDEX';
#endswitch
/* - - - - - - - - - - - - - - - - - - -  end of configuration section */
 
tab='05'x;
crlf='0D'x || '0A'x;
cr='0D'x;
lf='0A'x;

'SUBCOM XEDIT'
f rc=0 then inXedit=1; else inXedit=0;

CSO_n=0;
f 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) ******/
f verify(rsrv,'0123456789.')/=0 then do;
  'FDNS' rsrv '(LIFO'
  pull code . ip .
  if (code/='AD') then do;
    exit(0);
  end;
  rsrv=ip
end;
#endswitch

upper fm_opt;
fnm=fn ft fm






























AD:

NEW PAGES:

[ODDNUGGET]

[GOPHER]