/*Last Modified:   8-MAR-1993 10:46:18.40, By: MARK */
/* Derived from an
* Example of server using TCP protocol
* pp 284-5 Stevens UNIX Network Programming
*/


#include "vmsgopherd.h"
void LOGGopher();

char Zehostname[128];
int GopherPort = GOPHER_PORT;

extern int vmserrno;
extern int socket_errno;

main(argc, argv)
 int   argc;
 char  *argv[];
{
    int sockfd;
    int newsockfd;
    int clilen;
    int childpid;
    int i, argcounter;
    char *vms_errno_string();
    int ret_status, write_len, equiv_len, lnm_index;
    unsigned int lnm_attrib;
    unsigned char acmode;
    char *LOGFile, *SecurityFile, *equiv_string, *datadir_def;

    struct sockaddr_in cli_addr;
    struct sockaddr_in serv_addr;

    /*** for getopt processing ***/
    int c;
    extern char *optarg;
    extern int optind;
    int errflag =0;
    char *colon_pos;

    $DESCRIPTOR( d_gopherroot, "GOPHER_ROOT" );
    $DESCRIPTOR( d_datadef, "DATA_DIRECTORY_DEF" );
    $DESCRIPTOR( d_logfile, "GOPHER_LOGFILE" );
    $DESCRIPTOR( d_SecurityFile, "GOPHER_SecurityFile" );
    $DESCRIPTOR( d_datadir, "GOPHER_DATADIR" );
    $DESCRIPTOR( d_tcpport, "GOPHER_TCPPORT" );
    $DESCRIPTOR( d_stable, "LNM$SYSTEM_TABLE" );
    $DESCRIPTOR( d_jtable, "LNM$JOB" );
    $DESCRIPTOR( d_equiv, equiv_string );

    acmode = PSL$C_EXEC;
    lnm_attrib = LNM$M_CONCEALED + LNM$M_TERMINAL;
    pname = argv[0];
    strcpy(Data_Dir, DATA_DIRECTORY);

/*      do logical name translations here to get port, data_dir, logfile,
*      and security file.  How do I determine if I got here from inetd?
*/

    LOGFile = ( char * ) malloc( 255 );
    SecurityFile = ( char * ) malloc( 255 );
    equiv_string = ( char * ) malloc( 255 );
    datadir_def = ( char * ) malloc( 255 );

/*
* Now we set up to translate the logical names if they exist for the logfile,
* the security file, the data directory, and the port.
*/

    strncpy( LOGFile, "\0", 255 );
    strncpy( SecurityFile, "\0", 255 );
    strncpy( equiv_string, "\0", 255 );
    strncpy( datadir_def, "\0", 255 );

    lnmlst[0].length = 255;
    lnmlst[0].code = LNM$_STRING;
    lnmlst[0].bufadr = equiv_string;
    lnmlst[0].retlen = &equiv_len;

    lnmlst[1].length = 4;
    lnmlst[1].code = LNM$_INDEX;
    lnmlst[1].bufadr = &lnm_index;
    lnmlst[1].retlen = 0;

    lnmlst[2].length = 0;
    lnmlst[2].code = 0;
    lnmlst[2].bufadr = 0;
    lnmlst[2].retlen = 0;

    lnm_index = 0;
    equiv_len = 0;
/*
* Translate the logical name for the log file if it exists
*/
    ret_status = sys$trnlnm( 0, &d_stable, &d_logfile, &acmode, &lnmlst );

    if ( ret_status == SS$_NORMAL )
       strncpy( LOGFile, equiv_string, equiv_len );

    strncpy( equiv_string, "\0", 255 );
    equiv_len = 0;
/*
* Translate the logical name for the security file if it exists
*/
    ret_status = sys$trnlnm( 0, &d_stable, &d_SecurityFile, &acmode, &lnmlst );

    if ( ret_status == SS$_NORMAL )
       strncpy( SecurityFile, equiv_string, equiv_len );

    strncpy( equiv_string, "\0", 255 );
    equiv_len = 0;
/*
* Translate the logical name for the data directory if it exists
*/
    ret_status = sys$trnlnm( 0, &d_stable, &d_datadir, &acmode, &lnmlst );

    if ( ret_status == SS$_NORMAL )
       strncpy( Data_Dir, equiv_string, equiv_len );

    strncpy( equiv_string, "\0", 255 );
    equiv_len = 0;
/*
* Translate the logical name for the tcpport to use if it exists
*/
    ret_status = sys$trnlnm( 0, &d_stable, &d_tcpport, &acmode, &lnmlst );

    if ( ret_status == SS$_NORMAL ) {
       d_equiv.dsc$w_length = equiv_len;
       d_equiv.dsc$a_pointer = equiv_string;
       ret_status = ots$cvt_ti_l( &d_equiv, &GopherPort, 4, 1 );
    }

    strncpy( equiv_string, "\0", 255 );
    equiv_len = 0;
/*
* Translate the logical name to create the logical name for the root
* directory of the gopher data
*/
    ret_status = sys$trnlnm( 0, &d_stable, &d_datadef, &acmode, &lnmlst );

    if ( ret_status != SS$_NORMAL ) {
      strncpy( equiv_string, "\0", 255 );
      equiv_len = 0;
      ret_status = sys$trnlnm( 0, &d_stable, &d_gopherroot, &acmode, &lnmlst );
    }

    lnmlst[0].length = 4;
    lnmlst[0].code = LNM$_ATTRIBUTES;
    lnmlst[0].bufadr = &lnm_attrib;
    lnmlst[0].retlen = 0;

    lnmlst[1].length = equiv_len;
    lnmlst[1].code = LNM$_STRING;
    lnmlst[1].bufadr = equiv_string;
    lnmlst[1].retlen = 0;

    lnmlst[2].length = 0;
    lnmlst[2].code = 0;
    lnmlst[2].bufadr = 0;
    lnmlst[2].retlen = 0;

    ret_status = sys$crelnm( 0, &d_jtable, &d_gopherroot, &acmode, &lnmlst );

    if (!RunFromInetd) {
         printf("Data directory is %s\n", Data_Dir);
         printf("Port is %d\n", GopherPort);
    }

    if (*LOGFile != '\0' && !RunFromInetd)
         fprintf(stdout,"Logging to File %s\n", LOGFile);

    if (*SecurityFile != '\0' && !RunFromInetd)
         fprintf(stdout,"Using Security file %s\n", SecurityFile);

    if (chdir(Data_Dir)) {
         fprintf(stderr, "Cannot change to data directory!! %s \n",Data_Dir);
         exit(-1);
    }

    /** Open up the LOGFile and the SecurityFile
        **Warning!** we have to do this over because the daemon_start
        function closes all fd's

        This part just checks to see if the files exist...
     **/

    if (*LOGFile != '\0') {
         LOGFileHandle = fopen(LOGFile, "a");

         if (LOGFileHandle == NULL) {
              printf("Can't open the logfile: %s\n", LOGFile);
              exit(-1);
         }

         fclose(LOGFileHandle);
    }

    if (*SecurityFile != '\0') {
         SECFileHandle = fopen(SecurityFile, "r");

         if (SECFileHandle == NULL) {
              fprintf(stderr,"Can't open the security file: %s\n", SecurityFile);
              exit(-1);
         }
         fclose(SECFileHandle);
    }

    /*** Hmmm, does this look familiar? :-) ***/

    if (LOGFile[0] != '\0') {
         LOGFileHandle = fopen(LOGFile, "a");

         if (LOGFileHandle == NULL) {
              fprintf(stderr,"Can't open the logfile: %s\n", LOGFile);
              exit(-1);
         }
         fclose(LOGFileHandle);
         LOGGopher(-1, "Starting gopher daemon\n");
    }

    if (*SecurityFile != '\0') {
         SECFileHandle = fopen(SecurityFile, "r");

         if (SECFileHandle == NULL) {
              fprintf(stderr,"Can't open the security file: %s\n", SecurityFile);
              exit(-1);
         }
    }

    /* Work out our fully-qualified name, for later use */

    if (gethostname(Zehostname, 128) != 0) {
         strcpy(Zehostname, "<<no-name>>");
    }
    strcat(Zehostname, DOMAIN_NAME);

    if (RunFromInetd) {
         while(do_command(0)!=0);      /* process the request */
         exit(0);
    }

    /** Open a TCP socket (an internet stream socket **/

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
         err_dump("server: can't open stream socket");

    /*
     * Bind our local address so that the client can send to us
     */

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family               = AF_INET;
    serv_addr.sin_addr.s_addr  = htonl(INADDR_ANY);
    serv_addr.sin_port         = htons(GopherPort);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
         err_dump("server: can't bind local address");

    listen(sockfd, 5);

    for ( ; ; ) {
         /*
          * Wait for a connection from a client process.
          * This is an example of a concurrent server.
          */

         clilen = sizeof(cli_addr);
         newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
                            &clilen);

         /*** weird.. with this thing here our gethostaddrs work.
           without it, it fails..... why?
          ***/

         if (newsockfd < 0)
              err_dump("server: accept error");

         while(do_command(newsockfd)!=0);{}    /* process the request */

#ifdef WOLLONGONG
         netclose(newsockfd);          /* parent process */
#endif

#ifdef MULTINET
         socket_close(newsockfd);              /* parent process */
#endif

#ifdef UCX
         close(newsockfd);             /* parent process */
#endif

    }
}

/*
*
*  Code stolen from nntp.....
*
* inet_netnames -- return the network, subnet, and host names of
* our peer process for the Internet domain.
*
*      Parameters:     "sock" is our socket, which we don't need.
*                      "sin" is a pointer to the result of
*                      a getpeername() call.
*                      "host_name"
*                      is filled in by this routine with the
*                      corresponding ASCII names of our peer.
*      Returns:        Nothing.
*      Side effects:   None.
*/
inet_netnames(sock, sin, host_name)
       int                     sock;
       struct sockaddr_in      *sin;
       char                    *host_name;
{
       u_long                  net_addr;
       struct hostent          *hp;
       struct netent           *np;

       net_addr = inet_netof(sin->sin_addr);   /* net_addr in host order */
       np = getnetbyaddr(net_addr, AF_INET);

       hp = gethostbyaddr((char *) &sin->sin_addr.s_addr,
               sizeof (sin->sin_addr.s_addr), AF_INET);

       if (hp != NULL)
               (void) strcpy(host_name, hp->h_name);
       else
               (void) strcpy(host_name, inet_ntoa(sin->sin_addr));
}

/*
* This finds the current peer and the time and  jams it into the
* logfile (if any) and adds the message at the end
*/
void
LOGGopher(sockfd, message)
 int sockfd;
 char *message;
{
    struct sockaddr sa;
    int             length;
    static char     host_name[256];
    time_t          Now;
    char *cp;

    host_name[0] = '\0';

    LOGFileHandle = fopen(LOGFile, "a");

    if (LOGFileHandle != NULL) {

         if (sockfd > -1) {
              length = sizeof (sa);
              getpeername(sockfd, &sa, &length);
              inet_netnames(sockfd, &sa, host_name);
         }

         time(&Now);
         cp = (char *) ctime(&Now);
         ZapCRLF(cp);

         fprintf(LOGFileHandle, "%s %s : %s\n", cp, host_name, message);
         fflush(LOGFileHandle);
         fclose(LOGFileHandle);
    }
}

process_mailfile(sockfd, Mailfname)
 int sockfd;
 char *Mailfname;
{
    FILE *Mailfile;
    char Zeline[MAXLINE];
    char outputline[MAXLINE];
    char Title[MAXLINE];
    long Startbyte=0, Endbyte, Bytecount;

    Mailfile = fopen(Mailfname, "r");

    if (Mailfile == NULL) {
         writestring(sockfd, "- Cannot access file\r\n.\r\n");
         return;
    }

    while (fgets(Zeline, MAXLINE, Mailfile) != NULL) {

         if (strncmp(Zeline, "Subject: ", 9)==0) {
              strcpy(Title, Zeline + 9);
              ZapCRLF(Title);
              if (DEBUG)
                   fprintf(stderr, "Found title %s", Title);
         }

         if (strncmp(Zeline, "From ", 5) == 0) {
              Endbyte = Bytecount;

              if (Endbyte != 0) {
                   sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
                           Title, Startbyte, Endbyte, Mailfname,
                           Zehostname, GopherPort);
                   writestring(sockfd, outputline);

                   Startbyte=Bytecount;
                   *Title = '\0';
              }
         }

         Bytecount += strlen(Zeline);
    }

    if (*Title != '\0') {
         sprintf(outputline, "0%s\tR%d-%d-%s\t%s\t%d\r\n",
                 Title, Startbyte, Endbyte, Mailfname,
                 Zehostname, GopherPort);
         writestring(sockfd, outputline);
    }

    writestring(sockfd, ".\r\n");
}

boolean
Can_Read(sockfd)
 int sockfd;
{
    struct sockaddr sa;
    int             length;
    char            host_name[256];
    char *cp;
    boolean         MatchReturn;
    char            inputline[MAXLINE];

    if (*SecurityFile == '\0')
         return(TRUE);

    length = sizeof (sa);
    getpeername(sockfd, &sa, &length);
    inet_netnames(sockfd, &sa, host_name);

    fseek(SECFileHandle, 0, 0);  /** Go to beginning of Security File **/

    while (fgets(inputline, MAXLINE, SECFileHandle) != NULL) {
         ZapCRLF(inputline);

         MatchReturn = TRUE;
         cp = inputline;

         if (*cp == '!') {
              MatchReturn = FALSE;
              cp++;
         }

         if (isdigit(*cp)) {
              /** Internet address x.x.x.x ..***/
              /** Check for a match from the beginning **/
              if (strstr(host_name,cp) == host_name)
                   return(MatchReturn);
         }

         else if (*cp == '*') {
              return(MatchReturn);
         }

         else if (*cp != '#') {
              /*** Not a comment, must be a host name **/
              if ((strstr(host_name, cp) + strlen(cp)) == (host_name + strlen(host_name))) {
                   return(MatchReturn);
              }
         }
    }

    /*** Hmmm, didn't find a match...  Let em have it***/

    return(TRUE);
}

int
do_command(sockfd)
 int sockfd;
{
    char inputline[MAXLINE];
    int length;                /* Length of the command line */
    char logline[MAXLINE];

    /*** Reopen the security file ***/

    if (*SecurityFile != '\0') {
         SECFileHandle = fopen(SecurityFile, "r");

         if (SECFileHandle == NULL) {
              LOGGopher(sockfd, "Security File dissappeared!");
              exit(-1);
         }
    }

    GDinit(&SortDir);

    length = readline(sockfd, inputline, MAXLINE); /** Get the line **/

    if (length <= 0)
         err_dump("getcommand: readline error");

    ZapCRLF(inputline);

    /*** With the funky new capability system we can just check the
         first letter, end decide what the object refers to. ***/

    switch (inputline[0]) {
    case '\0':
    case '\t':

         /*** The null capability, so it's not a file, probably wants
              to talk to a directory server ***/

         /*** we'll just do a "list" of the root directory, with no user
              capability.  ***/

         listdir(sockfd, "gopher_root:[000000]");
         LOGGopher(sockfd, "Root Connection");
         return(0);
         break;

    case '0':
         /*** It's a generic file capability ***/
         printfile(sockfd, inputline+1, 0, -1);

         /*** Log it ***/
         strcpy(logline, "retrieved file ");
         strcat(logline, inputline+1);
         LOGGopher(sockfd, logline);
         break;

    case '1':
         /*** It's a directory capability ***/
         listdir(sockfd, inputline+1);

         /** Log it **/
         strcpy(logline, "retrieved directory ");
         strcat(logline, inputline+1);
         LOGGopher(sockfd, logline);

         break;

    case '7':
         /*** It's an index capability ***/
         Do_IndexTrans(sockfd, inputline+1);

         /*** Log it **/
         strcpy(logline, "did a search ");
         strcat(logline, inputline+1);
         LOGGopher(sockfd, logline);

         break;

    case 's':
         /*** It's a sound capability ***/
         echosound(sockfd, inputline+1);

         /* Log it */
         strcpy(logline, "retrieved sound ");
         strcat(logline, inputline);
         LOGGopher(sockfd, logline);
         break;

    case 'm':
         /*** This is an internal identifier ***/
         /*** The m paired with an Objtype of 1 makes a mail spool file
              into a directory.
         ***/

         process_mailfile(sockfd, inputline + 1);
         break;

    case 'R':
         /*** This is an internal identifier ***/
         /*** The R defines a range  ****/
         /*** The format is R<startbyte>-<endbyte>-<filename> **/
    {
         int startbyte, endbyte;
         char *cp, *oldcp;

         cp = ( char * ) strchr(inputline+1, '-');

         if (cp == NULL) {
              writestring(sockfd, "Range specifier error.\r\n.\r\n");
              break;
         }

         *cp = '\0';
         startbyte = atoi(inputline+1);
         oldcp = cp+1;

         cp = ( char * ) strchr(oldcp, '-');

         if (cp == NULL) {
              writestring(sockfd, "Range specifier error.\r\n.\r\n");
              break;
         }

         *cp = '\0';
         endbyte = atoi(oldcp);
         oldcp = cp + 1;
         if (DEBUG)
              fprintf(stderr, "Start: %d, End: %d  File: %s\n", startbyte, endbyte, oldcp);

         printfile(sockfd, oldcp, startbyte, endbyte);

         break;
    }
    default:
         /*** Hmmm, must be an old link... Let's see if it exists ***/

         switch (isadir(inputline) == 1) {
         case -1:
              /* no such file */
              return(1);
         case 0:
              /* it's a file */
              printfile(sockfd, inputline, 0, -1);

              /* Log it... */
              strcpy(logline, "retrieved file ");
              strcat(logline, inputline);
              LOGGopher(sockfd, logline);

              break;
         case 1:
              /* it's a directory */
              listdir(sockfd, inputline);

              /* Log it */
              strcpy(logline, "retrieved directory ");
              strcat(logline, inputline);
              LOGGopher(sockfd, logline);

              break;
         }
    }

    return(0);
}

/*
* Returns true (1) for a directory
*         false (0) for a file
*         -1 for anything else
*/
boolean
isadir(path)
 char *path;
{
    struct stat buf;
    int result;

    result = stat(path, &buf);

    if (result != 0)
         return(-1);

#ifdef BROKENDIRS

#define S_ISDIR(m)      (((m)&S_IFMT) == S_IFDIR)
#define S_ISREG(m)      (((m)&S_IFMT) == S_IFREG)

#endif

    if (S_ISDIR(buf.st_mode))
         return(1);
    else if (S_ISREG(buf.st_mode))
         return(0);
    else
         return(-1);
}

/*
** filedate does a stat and returns the last modified date
*/
time_t
filedate(pathname)
 char *pathname;
{
    struct stat buf;
    int result;

    result = stat(pathname, &buf);

    if (result != 0)
         return(0);

    return(buf.st_ctime);
}

void
process_old_link(sockfd, filename)
 int sockfd;
 char *filename;
{
    FILE *DotFile;
    int            DoneFlags[5];
    static char    inputline[1024];

    static GopherStruct Gopherp;
    char *cp;

    if (DEBUG == TRUE)
         printf("Calling process_old_link with sockfd: %d, file: %s\n", sockfd, filename);

    if ((DotFile = fopen(filename, "r"))==NULL)
         return;

    DoneFlags[0] = DoneFlags[1] = DoneFlags[2]
         = DoneFlags[3] = DoneFlags[4] = 0;

    for (;;) {
         for (;;) {
              cp = fgets(inputline, 1024, DotFile);
              if (inputline[0] != '#' || cp == NULL)
                   break;
         }

         /*** Test for EOF ***/

         if (cp==NULL)
              break;

         /*** Test for the various field values. **/

         if (strncmp(inputline, "Type=", 5)==0) {
              GSsetType(&Gopherp, inputline[5]);
              DoneFlags[0] = 1;
         }

         if (strncmp(inputline, "Name=", 5)==0) {
              ZapCRLF(inputline);
              GSsetTitle(&Gopherp, inputline+5);
              DoneFlags[1] = 1;
         }

         if (strncmp(inputline, "Path=", 5)==0) {
              ZapCRLF(inputline);
              GSsetPath(&Gopherp, inputline+5);
              DoneFlags[2] = 1;
         }

         if (strncmp(inputline, "Host=", 5)==0) {
              ZapCRLF(inputline);
              GSsetHost(&Gopherp, inputline+5);
              DoneFlags[3] = 1;
         }

         if (strncmp(inputline, "Port=", 5)==0) {
              ZapCRLF(inputline);
              GSsetPort(&Gopherp, atoi(inputline+5));
              DoneFlags[4] = 1;
         }

         if (strncmp(inputline, "Numb=", 5)==0) {
              ZapCRLF(inputline);
              GSsetNum(&Gopherp, atoi(inputline+5));
         }

         /** Check to see if we've got all the parameters **/
         /** If we do, then send out the line ***/

         if ((DoneFlags[0] + DoneFlags[1] +
              DoneFlags[2] + DoneFlags[3] +
              DoneFlags[4]) == 5) {

              DoneFlags[0] = DoneFlags[1] = DoneFlags[2]
                   = DoneFlags[3] = DoneFlags[4] = 0;

              GDaddGS(&SortDir, &Gopherp);
         }
    }
    fclose(DotFile);
}

/*
* This function tries to find out what type of file a pathname is.
* It then fills in the VAR type variables ObjType & ServerPath with
* corresponding info.
*/
char
Getfiletypes(newpath, ObjType, ServerPath)
 char *newpath;
 char **ObjType;
 char **ServerPath;
{
    boolean dirresult;
    int Zefilefd;
    static char Zebuf[64];
    char *cp;
    static char Selstr[256];

    if (ServerPath != NULL)         /* Don't overwrite existing path if any */
         *ServerPath = Selstr;

    dirresult = isadir(newpath);

    if (dirresult == -1)             /** Symlink or Special **/
         return('3');

    if (dirresult == 1) {
         *ObjType = "1";
         *Selstr = '1';
         strcpy(Selstr +1, newpath);
         return;
    }
    else if (dirresult == 0) {       /** Some kind of data file.... */

         if ((Zefilefd = open(newpath, O_RDONLY)) < 0) {
              return('3');
         }

         bzero(Zebuf, 64);
         read(Zefilefd, Zebuf, 64);
         close(Zefilefd);

         /*** Check the first few bytes for sound data ***/

         cp = Zebuf;

         if (*cp++ == '.' && *cp++ == 's' &&
             *cp++ == 'n' && *cp++ == 'd') {
              *ObjType = "s";
              *Selstr = 's';
              strcpy(Selstr+1, newpath);

              return;
         }

         /*** Check and see if it's mailbox data ***/

         cp = Zebuf;

         if (*cp++ == 'F' && *cp++ == 'r' &&
             *cp++ == 'o' && *cp++ == 'm' &&
             *cp++ == ' ') {
              *ObjType = "1";
              *Selstr = 'm';
              strcpy(Selstr+1, newpath);

              return;
         }

         /*** Check for uuencoding data ***/

         cp = Zebuf;

         if (*cp++ == 'b' && *cp++ == 'e' && *cp++ == 'g' &&
             *cp++ == 'i' && *cp++ == 'n')  {
              *ObjType = "6";
              *Selstr = '6';
              strcpy(Selstr+1, newpath);

              return;
         }

         /*** The default is a generic text file ***/

         *ObjType = "0";
         *Selstr = '0';
         strcpy(Selstr + 1, newpath);
         return;
    }
    return('3');  /*** we shouldn't be able to get here..... ***/
}

/*
** This function lists out what is in a particular directory.
** it also outputs the contents of link files.
**
** Ack is this ugly.
*/
void
listdir(sockfd, pathname)
 int sockfd;
 char *pathname;
{
    struct FAB wild_fab;       /* Used in wildcard search */
    struct NAM wild_nam;       /* Used in conjunction with wild_fab */

    char template[] = "*.*;*"; /* default template */
    char fullname[256];        /* Input file spec given by user */
    char expanded[256];        /* filespec after logical expansion */
    char result[256];          /* result from search */
    char *pointer1, *pointer2, *pointer3, *pathp2, *sidep1;
    char *lbracketp, *rbracketp;
    int result2_len, side_len;
    register status;

    int            i;
    char           *cp, ch;

    char           sidename[256];
    char           filename[256];
    char           newpath[512];

    FILE           *SideFile;

    static char    portnum[16];
    GopherStruct   Gopherp;
    char           *Typep, *Pathp;

/* Initialize the wildcard FAB and NAM */
    wild_fab = cc$rms_fab;
    wild_nam = cc$rms_nam;

    wild_fab.fab$l_fna = fullname;
    wild_fab.fab$b_fac = FAB$M_GET;
    wild_fab.fab$l_fop = FAB$V_NAM;
    wild_fab.fab$l_nam = &wild_nam;
    wild_fab.fab$l_dna = template;
    wild_fab.fab$b_dns = strlen(template);

    wild_nam.nam$l_esa = expanded;
    wild_nam.nam$b_ess = 255;
    wild_nam.nam$l_rsa = result;
    wild_nam.nam$b_rss = 255;

    strcpy(fullname, pathname);
    wild_fab.fab$b_fns = strlen(fullname);
    if (((status = SYS$PARSE(&wild_fab)) &1) != 1) {
      fprintf(stderr, "Error %d on parse\n",status);
      fprintf(stderr, "pathname: %s\n",pathname);
      writestring(sockfd, "- Cannot access that directory\r\n.\r\n");
      return;
    }

    pointer1 = ( char * ) strchr( pathname, '[' );
    if ( (pointer1 != pathname) &&
         (strncmp(pathname, "gopher_root", 11) != 0) ) {
         writestring(sockfd, "- Cannot access that directory\r\n.\r\n");
         return;
    }

    if (chdir(pathname)<0) {
/*        fprintf(stderr, "error: %d %s\n",vmserrno,vms_errno_string()); */
         writestring(sockfd, "- Cannot access that directory\r\n.\r\n");
         return;
    }

    sprintf(portnum, "%d", GopherPort);

    for ( ; ; ) {
      if ((( status = SYS$SEARCH(&wild_fab)) &1) != 1) {
        if ( (status == RMS$_NMF) || (status == RMS$_FNF) )
          break;
        else {
          fprintf(stderr, "Error %d on search\n",status);
          writestring(sockfd, "- There were no files, or we had some other error on the search\r\n.\r\n");
          return;
        }
      }

         expanded[wild_nam.nam$b_esl] = 0;
         result[wild_nam.nam$b_rsl] = 0;
         pointer1 = ( char * ) strchr(result,']') + 1;
         pointer2 = ( char * ) strchr(result,';');
         result2_len = pointer2 - pointer1;
         pointer3 = ( char * ) malloc(result2_len + 1);
         strncpy(pointer3, "\0", result2_len + 1);
         strncpy(pointer3, pointer1, result2_len);

         strcpy(newpath, pathname);
         sidep1 = ( char * ) strchr(pathname, ']');
         side_len = sidep1 - pathname;
         strncpy(sidename, "\0", 256 );
         strncpy(sidename, pathname, side_len);
         strcat(sidename, ".cap]\0" );
         strcat(newpath, pointer3);
         strcpy(filename, pointer3);
         strcat(sidename, pointer3);
         free(pointer3);

         /** Only chew list out files that don't start with a dot **/
         /** or aren't named dev, usr, bin, etc, or core.         **/

         if ((filename[0] == '.') && isadir(filename)==0) {
              /*** This is a link file, let's process it ***/
              process_old_link(sockfd, filename);
         }


         if ((filename[0] != '.')
             && strcmp(filename, "bin") != 0 &&
                strcmp(filename, "dev") != 0 &&
                strcmp(filename, "usr") != 0 &&
                strcmp(filename, "core")!= 0 &&
                strcmp(filename, "etc") != 0)   {

              /** Check to see if there's a set-aside file with more info ***/
              /** But first initialize the Gopherstruct **/

              GSinit(&Gopherp);

              GSsetHost(&Gopherp, Zehostname);
              GSsetPort(&Gopherp, GopherPort);
              Typep = Pathp = NULL;


              if ((SideFile = fopen(sidename, "r"))!=0) {
                   if (DEBUG == TRUE)
                        printf("Side file name: %s\n", sidename);
                   Process_Side(SideFile, &Gopherp);
              }

              if (GSgetType(&Gopherp)=='\0' && *GSgetPath(&Gopherp) == '\0') {
                   Getfiletypes(newpath, &Typep, &Pathp);

                   if (*Typep =='3')
                        break;

                   GSsetType(&Gopherp, Typep[0]);

                   if ( *Typep == '1' ) {
                       pointer2 = ( char * ) strrchr(result,'.');
                       result2_len = pointer2 - pointer1;
/*                      gopher20 = 0;
                       gopher20 = strncmp(pathname,"gopher_root:[000000]",20);  */
                       lbracketp = ( char * ) strchr(pathname, '[');
                       if ((strncmp(pathname,"gopher_root",11) == 0 ) &&
                           (strncmp(lbracketp,"[000000]",8) == 0 ) )
                         pathp2 = ( char * ) strchr(pathname, '[');
                       else
                         pathp2 = ( char * ) strchr(pathname, ']');
                       strncpy(newpath, "\0", 511);
                       strncpy(newpath, "1", 1);
                       /*if (strncmp(pathname,"gopher_root:[000000]",20) == 0 ) */
                       if ((strncmp(pathname,"gopher_root",11) == 0 ) &&
                           (strncmp(lbracketp,"[000000]",8) == 0 ) )
                         strncat(newpath,pathname, (pathp2 - pathname) + 1);
                       else  {
                         strncat(newpath,pathname, (pathp2 - pathname) );
                         strcat(newpath,".");
                       }
                       strncat(newpath,pointer1,result2_len);
                       strcat(newpath,"]\0");
                       GSsetPath(&Gopherp, newpath);
                   }
                   else
                       GSsetPath(&Gopherp, Pathp);
              }

              if (GSgetTitle(&Gopherp) == NULL) {
                   /*** Check to see if we have a compressed file ***/

                   if (strcmp(filename + strlen(filename) -2, ".Z") ==0)
                        filename[strlen(filename) - 2] = '\0';

                   GSsetTitle(&Gopherp, filename);
              }
              else
                   GSsetTitle(&Gopherp, filename);

              /** Send out the complete line **/

              GDaddGS(&SortDir, &Gopherp);
         }
    }
    GDsort(&SortDir);
    GDtoNet(&SortDir, sockfd);

    writestring(sockfd, ".\r\n");
}


/*
* This processes a file containing any subset of
* Type, Name, Path, Port or Host, and returns pointers to the
* overriding data that it finds.
*
* The caller may choose to initialise the pointers - so we don't
* touch them unless we find an over-ride.
*/
Process_Side(sidefile, Gopherp)
 FILE *sidefile;
 GopherStruct *Gopherp;
{
    char inputline[MAXLINE];
    static char ItemType[3];
    static char ItemName[255];
    static char Path[255];
    static char Host[64];
    static char Port[10];
    char *cp;

    inputline[0] = '\0';

    for (;;) {
         for (;;) {
              cp = fgets(inputline, 1024, sidefile);
              if (inputline[0] != '#' || cp == NULL)
                   break;
         }

         /*** Test for EOF ***/
         if (cp==NULL)
              break;

         ZapCRLF(inputline);  /* should zap tabs as well! */

         /*** Test for the various field values. **/

         if (strncmp(inputline, "Type=", 5)==0) {
              GSsetType(Gopherp, inputline[5]);
         }
         else if (strncmp(inputline, "Host=", 5)==0) {
              GSsetHost(Gopherp, inputline+5);
         }

         else if (strncmp(inputline, "Port=", 5)==0) {
              GSsetPort(Gopherp, atoi(inputline+5));
         }

         else if (strncmp(inputline, "Path=", 5)==0) {
              GSsetPath(Gopherp, inputline+5);
         }

         else if (strncmp(inputline, "Numb=", 5)==0) {
              GSsetNum(Gopherp, atoi(inputline+5));
         }
    }
    fclose(sidefile);
}


/*
** This function opens the specified file, starts a zcat if needed,
** and barfs the file across the socket.
**
** It now also checks and sees if access is allowed
**
**
*/
void
printfile(sockfd, pathname, startbyte, endbyte)
 int sockfd;
 char *pathname;
 int startbyte, endbyte;
{
    FILE *ZeFile;
    char inputline[512];
    int i, rmtmpfile;
    char Zcatcommand[256];
    char *cp, *pointer1;

    chdir("gopher_root:[000000]");

    rmtmpfile = 0;
    pointer1 = (char * ) strchr( pathname, ':' );
    if ( (pointer1 != 0) &&
         (strncmp(pathname, "gopher_root", 11) != 0) ) {
         writestring(sockfd, "- Cannot access that file\r\n.\r\n");
         return;
    }

    /*** Check and see if the peer has permissions to read files ***/

    if (Can_Read(sockfd) == FALSE) {
         writestring(sockfd, BUMMERSTR);
         writestring(sockfd, "\r\nBummer.....\r\n.\r\n");
         close(sockfd);
         return;
    }

    if ( (ZeFile = fopen(pathname, "r")) == NULL) {
         /*
          * The specified file does not exist
          */
         writestring(sockfd, "- File does not exist!!!!!\r\n");
         writestring(sockfd, ".\r\n");
         return;
    }

    if (startbyte != 0)
         fseek(ZeFile, startbyte, 0);
    {
         FILE *pp;
         if (pp = specialfile(ZeFile, pathname)) {
              fclose(ZeFile);
              ZeFile = pp;
              rmtmpfile = 1;
         }
    }

    while (fgets(inputline, MAXLINE, ZeFile) != NULL) {

         ZapCRLF(inputline);
         if( inputline[0] != '$' ) {
           writestring(sockfd, inputline);
           writestring(sockfd, "\r\n");
         }

         if (endbyte >0) {
              if (ftell(ZeFile) >= endbyte)
                   break;
         }
    }
    Specialclose(ZeFile);
    if ( rmtmpfile == 1 )
      remove( pathname );
    writestring(sockfd, ".\r\n");
}

/*
* Warning! this part isn't working yet!
*/
#ifdef BUILTIN_SEARCH

Do_IndexTrans(sockfd, inputline)
 int sockfd;
 char *inputline;
{
    char *IndexDirectory;
    char *SearchString;
    char *cp;


    /** First siphon off the directory pathname **/
    IndexDirectory = inputline;

    cp = (char *) strchr(inputline, '\t');

    if (cp == NULL) {
         /** Give up it won't work..... **/
         writestring(sockfd, ".\r\n");
         return;
    }
    else
         *cp = '\0';


    /** And siphon off the search string **/

    SearchString = cp +1;

    /** Just in case, get rid of anything following a tab **/

    cp = (char *) strchr(SearchString, '\t');
    if (cp != NULL)
         *cp = '\0';

    /** Read in the proper hostdata file.... **/


    /** And call the appropriate query function **/

#ifdef NEXTSEARCH
    NeXTIndexQuery(sockfd, IndexDirectory, SearchString);
#endif

#ifdef WAISSEARCH
/*     WaisIndexQuery(sockfd, IndexDirectory, SearchString);*/
#endif

}

#endif /* BUILTIN_SEARCH */


#define BUFSIZE 1400  /* A pretty good value for ethernet */

void
echosound(sockfd, filename)
 int sockfd;
 char *filename;
{
    FILE *sndfile;
    unsigned char in[BUFSIZE];
    register int j;
    int gotbytes;

/*     if (strcmp(filename, "-") == 0) { */
         /*** Do some live digitization!! **/
/*        sndfile = popen("record -", "r");
    }
    else
         sndfile = fopen(filename, "r");
*/
    while(1) {
         gotbytes = fread(in, 1, BUFSIZE, sndfile);

         if (gotbytes == 0)
              break;       /*** end of file or error... ***/

         j = writen(sockfd, in, gotbytes);

         if (j == 0)
              break;       /*** yep another error condition ***/
    }
}