/********************************************************************
* wilkinson
* 3.27VMS
* 1996/01/24  11:00
* gopher_root1:[gopher.g2.vms2_13.gopherd]index.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: index.c
* Routines to deal with various types of indexes.
*********************************************************************
* Revision History:
* index.c,v
* Revision 3.27VMS-1 1996/01/24 11:00  wilkinson
* Made OPCOM message console selection configurable
*
* Revision 3.27VMS 1995/09/25 14:45    wilkinson
* Consolodate VMS/Unix source code for server as well as client
*
* Revision 3.27  1995/02/16  22:32:40  lindner
* HTML icon support
*
* Revision 3.26  1994/11/29  05:07:10  lindner
* Allow script paths with spaces, better error messages
*
* Revision 3.25  1994/10/19  03:32:17  lindner
* NO_INDEXING hack
*
* Revision 3.24  1994/10/13  05:17:49  lindner
* Compiler complaint fixes
*
* Revision 3.23  1994/10/02  02:58:08  lindner
* Fix for gopher+ index accesses
*
* Revision 3.22  1994/07/21  17:22:27  lindner
* /dev/null code from Brian
*
* Revision 3.21  1994/06/29  05:25:43  lindner
* Add Gticket
*
* Revision 3.20  1994/05/14  04:18:14  lindner
* Fix weird prob...
*
* Revision 3.19  1994/04/27  19:22:20  lindner
* compatible.h added
*
* Revision 3.18  1994/04/08  21:08:07  lindner
* Fix a bug in the Shell Search code
*
* Revision 3.17  1994/03/08  15:56:04  lindner
* gcc -Wall fixes
*
* Revision 3.16  1994/01/21  03:55:38  lindner
* Remove old dead HTML code, update function declarations
*
* Revision 3.15  1993/11/02  06:01:43  lindner
* HTML mods
*
* Revision 3.14  1993/09/30  16:57:05  lindner
* Fix for WAIS and $ requests
*
* Revision 3.13  1993/09/21  02:47:36  lindner
* Fix index problem closing null file handle
*
* Revision 3.12  1993/09/11  04:40:44  lindner
* Don't fork for localhost mindex databases
*
* Revision 3.11  1993/08/23  18:34:28  lindner
* Add fixfile() call
*
* Revision 3.10  1993/08/06  14:30:44  lindner
* Fixes for better security logging
*
* Revision 3.9  1993/08/05  20:44:02  lindner
* Use Gpopen instead of popen, remove extra filtering code
*
* Revision 3.8  1993/08/04  22:12:43  lindner
* Mods to use Gpopen
*
* Revision 3.7  1993/07/27  05:27:50  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.6  1993/07/26  15:31:13  lindner
* mods for application/gopher-menu
*
* Revision 3.5  1993/07/20  23:56:44  lindner
* LOGGopher mods
*
* Revision 3.4  1993/04/15  21:41:38  lindner
* Added $ to list of naughty characters (just in case)
*
* Revision 3.3  1993/04/09  15:54:29  lindner
* Fixes for indexes with gopher+
*
* Revision 3.2  1993/03/26  19:47:06  lindner
* First crack at gopherplussing Indexing
*
* Revision 3.1.1.1  1993/02/11  18:02:52  lindner
* Gopher+1.2beta release
*
* Revision 1.3  1993/01/30  23:57:44  lindner
* Removed html code, moved parsing of the inputline to gopherd.c
*
* Revision 1.2  1992/12/14  21:36:05  lindner
* Fixed problem in ShellIndexQuery, cp wasn't being incremented.
* Also added special character elimination from GrepIndexQuery
*
* Revision 1.1  1992/12/10  23:13:27  lindner
* gopher 1.1 release
*
*
*********************************************************************/


#include "compatible.h"
#include "gopherd.h"
#include "command.h"

#include "fileio.h"
#include "Debug.h"
#ifdef VMS_SERVER
#undef stat                /** Stupid openers thing **/
#undef chdir               /** Use the real runtime routine under VMS **/
#include "serverutil.h"
#include <opcdef.h>
#endif
#include <stdio.h>

#ifndef NO_INDEXING

#define WAISTYPE 1
#define NEXTTYPE 2
#define SHLLTYPE 3
#define GREPTYPE 4
#ifdef VMS_SERVER
#define CMD1TYPE 5

#include <syidef.h>
void SearchVersion(int, GopherObj *, GopherDirObj *);
void Kaboom(int, GopherObj *, GopherDirObj *);
void CreateGO4link(GopherObj *, GopherDirObj *, char *);
void CreateFTPlink(GopherObj *, GopherDirObj *, char *);
void CreateEXElink(GopherObj *, GopherDirObj *, char *, char, char *);
void GDpostprocVMS(GopherDirObj *, GDCobj *, int, boolean);
#ifdef system
#undef system
#endif
#define system(a) VMS$system(a)

boolean     Do_Sort;
boolean     Do_DateNsize;
boolean     Do_Gplus;
boolean     tried_support;
#endif


void
Do_IndexTrans(sockfd, IndexDirectory, cmd, SendEOF)
 int sockfd;
 char *IndexDirectory;
 CMDobj *cmd;
 boolean SendEOF;
{
    char *cp = NULL;
    char *dbName = NULL;
#ifndef VMS_SERVER
    char INDEXHost[256], INDEXPath[256];  /** Hard coded limits, ugh! **/
#else
    String *INDEXHost;                    /** No hard coded limits this way **/
    String *INDEXPath;
    int  status;
    char *inputline = CMDgetSelstr(cmd);
#endif
    int  INDEXPort=0;
    int  Index_type=0;
    char *SearchString = CMDgetSearch(cmd);
    boolean isgplus;
    char *view = CMDgetView(cmd);
    char tmp_IndexDirectory[256];

    Debug("Index Dir is %s\n", IndexDirectory);

#ifdef VMS_SERVER
 {
    /** First siphon off the sorting & DateNsize tokens, if any **/
    char *cp;

    Do_Sort = -1;          /* Do Sort: Not TRUE, Not FALSE */
    Do_DateNsize = -1;     /* Same for Do DateNsize        */
    Do_Gplus = FALSE;      /* G+ is *not* the default      */
    cp = inputline;

    while (*cp==':')
    {
       if (strncasecmp(cp,":sort:",strlen(":sort:"))==0) {
           inputline = (cp += strlen(":sort")) + 1;
           Do_Sort = TRUE;
       }
       else
       if (strncasecmp(cp,":nosort:",strlen(":nosort:"))==0) {
           inputline = (cp += strlen(":nosort")) + 1;
           Do_Sort = FALSE;
       }
       else
       if (strncasecmp(cp,":G+:",strlen(":G+:"))==0) {
           inputline = (cp += strlen(":G+")) + 1;
           Do_Gplus = TRUE;
       }
       else
       if (strncasecmp(cp,":noG+:",strlen(":noG+:"))==0) {
           inputline = (cp += strlen(":noG+")) + 1;
           Do_Gplus = FALSE;
       }
       else
       if (strncasecmp(cp,":G0:",strlen(":G0:"))==0) {
           inputline = (cp += strlen(":G0")) + 1;
           Do_Gplus = FALSE;
       }
       else
       if (strncasecmp(cp,":D&Sz:",strlen(":D&Sz:"))==0) {
           inputline = (cp += strlen(":D&Sz")) + 1;
           Do_DateNsize = TRUE;
       }
       else
       if (strncasecmp(cp,":noD&Sz:",strlen(":noD&Sz:"))==0) {
           inputline = (cp += strlen(":noD&Sz")) + 1;
           Do_DateNsize = FALSE;
       }
    }
 }
#endif

    Index_type = Find_index_type(IndexDirectory);

    isgplus = CMDisGplus(cmd);

    Debug("Index type is %d\n", Index_type);

    if (Index_type < 0) {
         /**** Error condition, unknown index type... ****/
#ifdef VMS_SERVER
         SetAbrtFile(IndexErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
         Abortoutput(sockfd, "Unknown index type");
         return;
    }

    if (Index_type == WAISTYPE) {
         /*** The selector string has both the directory and the dbname... ***/
         cp = strrchr(IndexDirectory, '/');

         if (cp == NULL)
              dbName = "index";
         else {
              dbName= cp+1;
              *cp='\0';
         }
    }
#ifndef VMS_SERVER
    else if (Index_type == SHLLTYPE) {
         /*** The selector string has both the directory and the dbname... ***/
         cp = strrchr(IndexDirectory, '/');
         if (cp == NULL) {
                 dbName = IndexDirectory;
                 IndexDirectory = tmp_IndexDirectory;
                 strcpy (tmp_IndexDirectory, "/");
         } else {
              dbName= cp+1;
              *cp='\0';
         }
    }

    if (Read_hostdata(IndexDirectory, INDEXHost, &INDEXPort, INDEXPath, dbName) <0) {
         LOGGopher(sockfd, "Malformed hostdata file");
         writestring(sockfd, "0Error on server, malformed hostdata\t\t\t1\r\n.\r\n");
         return;
    }

    if (Index_type == SHLLTYPE) {
         if (IndexDirectory == tmp_IndexDirectory)
              IndexDirectory = dbName;
         else
              *(dbName-1) = '/';
    }

    /* Doctor up the indexdirectory path if we're not running chroot()
     * we use fixfile to keep things secure....
     */

    if (!dochroot)
         IndexDirectory = fixfile(IndexDirectory);
#else
   else
       if (Index_type == SHLLTYPE)
           if (tried_support)
               uchdir(GDCgetSupportDir(Config));
   INDEXHost = STRnew();
   STRset(INDEXHost, GDCgetHostname(Config));
   INDEXPath = STRnew();
   STRset(INDEXPath, IndexDirectory);
   INDEXPort = GDCgetPort(Config);
#endif

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

    switch (Index_type) {

    case NEXTTYPE:

         NeXTIndexQuery(sockfd, SearchString, IndexDirectory, NULL,
                        INDEXHost, INDEXPort, INDEXPath, isgplus, view);
         break;

    case WAISTYPE:
         WaisIndexQuery(sockfd, IndexDirectory, SearchString, dbName,
                        INDEXHost, INDEXPort, INDEXPath, isgplus, view);
         break;

    case GREPTYPE:
#ifndef VMS_SERVER
         GrepIndexQuery(sockfd, IndexDirectory, SearchString,
                        INDEXHost, INDEXPort, INDEXPath);
#else
         if (Do_DateNsize == -1)
           Do_DateNsize = TRUE;
         if (Do_Sort == -1)
           Do_Sort = GDCgetSortGREP(Config);
         if (Do_Gplus == -1)
           Do_Gplus = GDCgetIsGplus(Config);
         GrepIndexQuery(sockfd, IndexDirectory, SearchString,
                        STRget(INDEXHost), INDEXPort, STRget(INDEXPath));
#endif
         break;

    case SHLLTYPE:
#ifndef VMS_SERVER
         ShellIndexQuery(sockfd, IndexDirectory, SearchString, isgplus);
#else
         if (Do_DateNsize == -1)
           Do_DateNsize = TRUE;
         if (Do_Sort == -1)
           Do_Sort = GDCgetSortShell(Config);
         if (Do_Gplus == -1)
           Do_Gplus = GDCgetIsGplus(Config);
         ShellIndexQuery(sockfd, IndexDirectory, SearchString, isgplus,
                        STRget(INDEXHost), INDEXPort, STRget(INDEXPath));
#endif
         break;
#ifdef VMS_SERVER
    case CMD1TYPE:
         if (Do_DateNsize == -1)
           Do_DateNsize = TRUE;
         if (Do_Sort == -1)
           Do_Sort = GDCgetSortCMD1(Config);
         if (Do_Gplus == -1)
           Do_Gplus = GDCgetIsGplus(Config);
         CMD1IndexQuery(sockfd, IndexDirectory, SearchString,
                        STRget(INDEXHost), INDEXPort, STRget(INDEXPath));
         break;
#endif
    }
#ifdef VMS_SERVER
    STRdestroy(INDEXHost);
    STRdestroy(INDEXPath);
#endif

    /** Finish the transmission attempt **/
    if (SendEOF)
         writestring(sockfd, ".\r\n");

    /** Log it here so we get the query in the logfile **/

    if (dbName)
         LOGGopher(sockfd,"search %s/%s for %s", IndexDirectory,
                   dbName, SearchString);
    else

         LOGGopher(sockfd, "search %s for %s", IndexDirectory, SearchString);
}



/*
* Try to figure out what each type of object is
*
* index types are
*   Error       == -1
*   WAIS        == 1
*   NeXT        == 2
*   ShellScript == 3
*   Grep        == 4
*/

int
Find_index_type(gopherpath)
 char *gopherpath;
{
    char Teststr[512];
    FILE *Testfile;
#ifdef VMS_SERVER
    char VMSdirectory[256];
    char *cp;


    tried_support = FALSE;
    /*** Check for the VMS CMD1TYPE designator ($) ***/
    if (gopherpath[0] == '$') {
       cp = skip_whitespace(gopherpath+1);
       strcpy(Teststr, cp);
       if ((cp=strchr(Teststr,' '))!=NULL)
           *cp = '\0';
       /*** Check for a valid CMD1TYPE sub-type ***/
       if ((strcasecmp(Teststr,"SEARCH")==0)
           || (strcasecmp(Teststr,"SEARCH&")==0)
           || (strcasecmp(Teststr,"SEARCH|")==0)
           || (strcasecmp(Teststr,"SEARCH~&")==0)
           || (strcasecmp(Teststr,"SEARCH~|")==0)
           || (strcasecmp(Teststr,"SINCE")==0)
           || (strcasecmp(Teststr,"BEFORE")==0)
           || (strcasecmp(Teststr,"VERSION")==0)
           || (strcasecmp(Teststr,"KABOOM")==0)
           || (strcasecmp(Teststr,"GO4LINK")==0)
           || (strcasecmp(Teststr,"FTPLINK")==0)
           || (strcasecmp(Teststr,"EXELINK")==0)) {
           return(CMD1TYPE);
       }
    }

    {
       char *cp1, path[256];

       strcpy(Teststr, gopherpath);
       /* find end of first selector token */
       if ((cp=strchr(Teststr,' '))!=NULL)
       *cp = '\0';
       if (Teststr[0] == '/') {
        /* Convert to VMS pathspec before testing */
        strcpy(path, (cp1=VMS$WWW_to_VMS(Teststr, A_FILE)) ? cp1 : Teststr);
        strcpy(Teststr, path);
       }
       else
           strcpy(path, Teststr);  /*  In case we need it later... */
   Try_Support:
       Testfile = fopen_VMSopt(Teststr, "r");
       if (Testfile != NULL) {
         /** Shell script? **/
       if (strcasecmp(Teststr+strlen(Teststr)-strlen(".SHELL"),".SHELL")==0)
         if (getc(Testfile) == '$')
              if (getc(Testfile) == '!') {
                   fclose(Testfile);
                   return(SHLLTYPE);
              }
       }
       else
           if (!tried_support) {
               tried_support = TRUE;
               strcpy(Teststr, GDCgetSupportDir(Config));
               strcat(Teststr, path);  /* Told ya we'd need it...  */
               goto Try_Support;
       }
    }

    strcpy(Teststr, gopherpath);
    if (Teststr[0] == '/') {
        /* Convert to VMS pathspec */
        char *cp1, path[256];
        strcpy(path, (cp1=VMS$WWW_to_VMS(Teststr, A_FILE)) ? cp1 : Teststr);
        strcpy(Teststr, path);
    }
    if ((cp=strchr(Teststr,'[')) && strchr(cp,']'))
         return(GREPTYPE);

#else
    strcpy(Teststr, gopherpath);
    strcat(Teststr, "/.index/index.ixif");

    Testfile = rfopen(Teststr, "r");
    if (Testfile != NULL) {
         /*** Next Index ***/
         fclose(Testfile);
         return(NEXTTYPE);
    }


    strcpy(Teststr, gopherpath);
    strcat(Teststr, ".inv");

    Testfile = rfopen(Teststr, "r");
    if (Testfile != NULL) {
         /*** WAIS Index ***/
         fclose(Testfile);
         return(WAISTYPE);
    }


    strcpy(Teststr, gopherpath);
    if (isadir(Teststr) == 1) {
         return(GREPTYPE);
    }

    Testfile = rfopen(Teststr, "r");
    if (Testfile != NULL) {
         /** Shell script? **/
         if (getc(Testfile) == '#')
              if (getc(Testfile) == '!') {
                   fclose(Testfile);
                   return(SHLLTYPE);
              }
    }
#endif

    return(-1);
}

#ifndef VMS_SERVER
/*
* Read in the data from a hostdata file...
*
* Try "<dbname>.hostdata" first, fall back to "hostdata" otherwise
*/

int
Read_hostdata(IndexDirectory, INDEXHost, INDEXPort, INDEXPath, dbName)
 char *IndexDirectory;
 char *INDEXHost, *INDEXPath;
 int  *INDEXPort;
 char *dbName;
{
    FILE *Hostfile;
    char hostdataName[256];

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

    rchdir(IndexDirectory);  /** Change into the index directory **/

    sprintf(hostdataName, "%s.hostdata", dbName);  /* try idx.hostdata */
    if ((Hostfile = ufopen(hostdataName, "r")) == NULL)
         Hostfile = ufopen("hostdata", "r");

    if (Hostfile == NULL) {
         /*** Use the current host/port as the default ***/
         strcpy(INDEXHost, Zehostname);
         *INDEXPort = GopherPort;
         strcpy(INDEXPath, Data_Dir);
    }
    else {
         char tempbuf[255];

         if (fgets(INDEXHost, 64, Hostfile) == NULL)
              return(-1);

         ZapCRLF(INDEXHost);

         if (fgets(tempbuf, 255, Hostfile) == NULL)
              return(-1);

         if ((*INDEXPort=atoi(tempbuf))==0)
              return(-1);

         if (fgets(INDEXPath, 256, Hostfile) == NULL)
              return(-1);

         ZapCRLF(INDEXPath);
         fclose(Hostfile);
    }

    return(0);
}
#endif

/*
* This is a searching function that runs grep across files
* in a single directory...
*/

void
GrepIndexQuery(sockfd, Indexdir, Searchstr, INDEXHost, INDEXPort, INDEXPath)
 int sockfd;
 char *Indexdir;
 char *Searchstr;
 char *INDEXHost;
 int INDEXPort;
 char *INDEXPath;
{
    FILE *moocow;
    char command[512];
    char inputline[512];
    char *cp;
    GopherObj *gs;
    GopherDirObj *gd;
#ifdef VMS_SERVER
    char path[256];
    char *c2, *c3;

    if (Indexdir[0] == '/') {
        /* Convert to VMS pathspec */
        strcpy(path, (c2=VMS$WWW_to_VMS(Indexdir, A_FILE)) ? c2 : Indexdir);
        strcpy(Indexdir, path);
    }

    c3 = c2 = (char *) malloc(sizeof(char)*strlen(Indexdir)+1);
    strcpy(c3,Indexdir);

    while (strlen(c2)) {
       cp = strpbrk(c2,",");
       if (cp)
           *cp = '\0';
       if (!VMS$Validate_Filespec(c2)) {
           LOGGopher(sockfd, "GREP Illegal syntax for %s", Indexdir);
           SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
           Abortoutput(sockfd, "Eh? Confusing Request");
           free(c3);
           return;
       }
       c2 += strlen(c2) + ((cp==NULL) ? 0 : 1);
    }
    free(c3);
#endif

    gs = GSnew();
    gd = GDnew(32);

#ifndef VMS_SERVER
    cp = Searchstr;
    while (*cp != '\0') {
         if (*cp == ';' ||*cp == '"' || *cp == '`' || *cp == '$')
              *cp = '.';
         cp++;
    }

    sprintf(command, "egrep \"%s\" \"%s\"/*", Searchstr, Indexdir);
#else
    GSsetDefaults(gs);     /* Oughtn't Unix do this to?    */
    strcpy(vms_pipe_file, cp=tempnam(GDCgetScratchDir(Config),NULL));
    free(cp);
    /*** If not Inetd/MULTINET_SERVER and no EGREP, use SEARCH ***/
    if (!RunFromInetd && getenv("EGREP") == NULL) {
         while(cp=strpbrk(Searchstr," \t\r\n")) {
            if (*(cp-1)==',')
                strcpy(cp,cp+1);
            else
                *cp = ',';
         }
         sprintf(command,"$ search/nohead/output=%s/window=0 %s %s",
                       vms_pipe_file, Indexdir, Searchstr);
    }
    /*** Use EGREP ***/
    else {
         cp = Searchstr;
         while (*cp != '\0') {
              if (*cp == '"' || *cp == '`')
                   *cp = '.';
              cp++;
         }
         if (Indexdir[strlen(Indexdir)-1] == ']')
              sprintf(command,"$ egrep -il \">%s\" -e \"%s\" \"%s*.*;\"",
                              vms_pipe_file, Searchstr, Indexdir);
         else
              sprintf(command,"$ egrep -il \">%s\" -e \"%s\" \"%s\"",
                              vms_pipe_file, Searchstr, Indexdir);
    }
    if (strlen(Searchstr)==0) {
       writestring(sockfd, ".\r\n");
       LOGGopher(sockfd, "Null Search String");
       GSdestroy(gs);
       GDdestroy(gd);
       return;
    }
#endif
    Debug("Grep command is %s\n", command);

    moocow = Gpopen(sockfd, command, "r");

    if (moocow == NULL) {
         LOGGopher(sockfd, "Couldn't open grep command");
#ifdef VMS_SERVER
         GSdestroy(gs);        /*  Oughtn't Unix do this too?  */
         GDdestroy(gd);
#endif
         return;
    }

    while (fgets(inputline, 512, moocow)) {
         ZapCRLF(inputline);
         GSsetType(gs, A_FILE);

#ifndef VMS_SERVER
         cp = strstr(inputline, INDEXPath) + strlen(INDEXPath);
         GSsetTitle(gs, cp);

         cp = strchr(inputline, ':');
         *cp='\0';
         cp =strstr(inputline, INDEXPath) + strlen(INDEXPath);
#else
         if (isadir(inputline)==1)
               GSsetType(gs,A_DIRECTORY);
         /* force names lowercase */
         str_tolower(cp=inputline);
         /* create the path and filename */
         strcpy(path, cp);
         c2 = strchr(path,']') + 1;
         *c2 = '\0';
         c2 = strchr(inputline,';');
         *c2 = '\0';
         c2 = strchr(inputline,']')+1;
         if (GDCignore(Config,c2))
               continue;
         if (chdir(path)<0)
               continue;
         c2 = (char *)malloc(2+strlen(inputline));
         *c2 = GSgetType(gs);
         strcpy(c2+1,inputline);
         strcpy(inputline, c2);
         free(c2);
         cp = inputline;
#endif
         GSsetPath(gs, cp);
         GSsetHost(gs, INDEXHost);
         GSsetPort(gs, INDEXPort);

         GDaddGS(gd, gs);
    }
#ifdef VMS_SERVER
    if (GDgetNumitems(gd)) {
         GDpostprocVMS(gd,Config,GopherPort,Do_Gplus);
         if (Do_DateNsize) GDaddDateNsize(gd);
         if (Do_Sort) GDsort(gd);
#endif
    if (UsingHTML)
         GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, NULL);
    else {
         GDtoNet(gd, sockfd, GSFORM_G0, Gticket, NULL);
    }
#ifdef VMS_SERVER
}
    GSdestroy(gs);
    GDdestroy(gd);
#endif
    pclose(moocow);
}



/*
* This starts up a shell script that's defined to be an index gateway
*
* The shell script should write out standard gopher directory protocol.
*/

#ifndef VMS_SERVER
void
ShellIndexQuery(sockfd, Script, Searchstring, isgplus)
#else
void
ShellIndexQuery(sockfd, Script, Searchstring, isgplus,
                               INDEXHost, INDEXPort, INDEXPath)
 char *INDEXHost;
 int INDEXPort;
 char *INDEXPath;
#endif
 int sockfd;
 char *Script;
 char *Searchstring;
 boolean isgplus;
{
    GopherDirObj *gd;
    char          Command[512];
#ifndef VMS_SERVER
    FileIO       *Searchf;
#else
    FILE         *Searchf;
#endif
    int           result;

#ifdef VMS_SERVER
    char         *cp;
    char         *cfg;
    int          status;

    if((cp=strchr(Script,' '))!=NULL) {
        char *cp1, path[256];
        *cp = '\0';
        if(Script[0] == '/')
            /* Convert to first pathspec to VMS syntax */
            sprintf(path, "%s ",
                          (cp1=VMS$WWW_to_VMS(Script, A_FILE)) ? cp1 : Script);
        else
            sprintf(path, "%s ", Script);
        if(*(cp+1) == '/')
            /* Convert to second pathspec to VMS syntax */
            strcat(path, (cp1=VMS$WWW_to_VMS(cp+1, A_FILE)) ? cp1 : cp+1);
        else
            strcat(path, cp+1);
        *cp = ' ';
        strcpy(Script, path);

        strcpy(vms_pipe_file, Script);
        if ((cp=strchr(vms_pipe_file,' '))!=NULL)
           *cp = '\0';
        if (!VMS$Validate_Filespec(vms_pipe_file)) {
           LOGGopher(sockfd, "SHELL Illegal syntax for %s", Script);
           SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
           Abortoutput(sockfd, "Eh? Confusing Request","syntax");
           return;
        }
        /*** Clean up the arguments, remove  ; and " and `**/
        cp = Searchstring;
        while (*cp != '\0') {
             if (*cp == ';' ||*cp == '"' || *cp == '`')
                  *cp = '.';
             cp++;
        }
    }
#endif

    gd = GDnew(32);

#ifndef VMS_SERVER
    sprintf(Command, "\"%s\" %s", Script, Searchstring);

    Searchf = FIOopenCmdline(Command, "r");
#else
    strcpy(vms_pipe_file, cp=tempnam(GDCgetScratchDir(Config),NULL));
    free(cp);
    cp = GDCgetDatadir(Config);
    cfg = (char *) malloc(sizeof(char)*strlen(cp+1));
    strcpy(cfg, cp);
    if ((cp=strchr(cfg,':'))!=NULL)
       *cp='\0';
    sprintf(Command, "$ @%s %s \"%s\" \"%s\" %d %s", Script, vms_pipe_file,
                                   Searchstring, INDEXHost, INDEXPort,
                                       cfg);
    LOGGopher(sockfd, "Command: %s", Command);
    free(cfg);

    Searchf = Gpopen(sockfd, Command, "r");
#endif

    if (Searchf == NULL) {
#ifdef VMS_SERVER
         SetAbrtFile(NothingThere, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
#endif
         Abortoutput(sockfd,"Cannot execute shell script");
#ifdef VMS_SERVER
         GDdestroy(gd);        /* Oughtn't Unix do this, too?  */
         return;
#endif
    }

#ifndef VMS_SERVER
    GDfromNet(gd, FIOgetfd(Searchf), NULL);
#else
    GDfromNet(gd, fileno(Searchf), NULL);

    if (GDgetNumitems(gd)) {
         GDpostprocVMS(gd,Config,GopherPort,Do_Gplus);
         if (Do_DateNsize) GDaddDateNsize(gd);
         if (Do_Sort) GDsort(gd);
#endif

    if (UsingHTML)
         GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, NULL);
    else {
         if (isgplus)
              GDplustoNet(gd, sockfd, NULL, Gticket);
         else
              GDtoNet(gd, sockfd, GSFORM_G0, Gticket, NULL);
    }
#ifdef VMS_SERVER
    }
    result = pclose(Searchf);
#else

    result = FIOclose(Searchf);
#endif

    GDdestroy(gd);
}


#ifndef WAISSEARCH

void
WaisIndexQuery(sockfd, index_directory, SearchWords, new_db_name, INDEXHost, INDEXPort, INDEXPath, isgplus, view)
 int sockfd;
 char *index_directory;
 char *SearchWords;
 char *new_db_name;
 char *INDEXHost;
 int  INDEXPort;
 char *INDEXPath;
 boolean isgplus;
 char *view;
{
#ifdef VMS_SERVER
    LOGGopher(sockfd, "WAIS index %s, srch '%s', Db %s", index_directory,
                               SearchWords, new_db_name);
    SetAbrtFile(IndexErr, NULL, KeepAbrtGS, NULL);
    Abortoutput(sockfd, "Sorry, no WAIS indexing supported");
#else
    Abortoutput(sockfd, "This Server doesn't have WAIS compiled in it...");
#endif
    return;
}
#endif

#ifndef NEXTSEARCH
void
NeXTIndexQuery(sockfd, SearchWords, ZIndexDirectory, DatabaseNm, INDEXHost, INDEXPort, INDEXPath, isgplus, view)
 int sockfd;
 char *SearchWords;
 char *ZIndexDirectory;
 char *DatabaseNm;  /*** Not used by the next indexer... ***/
 char *INDEXHost;
 int INDEXPort;
 char *INDEXPath;
 boolean isgplus;
 char *view;
{
#ifdef VMS_SERVER
    LOGGopher(sockfd, "NeXT index %s, srch '%s'", ZIndexDirectory,
                               SearchWords);
    SetAbrtFile(IndexErr, NULL, KeepAbrtGS, NULL);
    Abortoutput(sockfd, "Sorry, no NeXT indexing supported");
#else
    Abortoutput(sockfd, "This isn't a NeXT... is it?");
#endif
    return;
}
#endif

#else /* NO_INDEXING */
void
Do_IndexTrans(sockfd, IndexDirectory, cmd, SendEOF)
 int sockfd;
 char *IndexDirectory;
 CMDobj *cmd;
{
#ifdef VMS_SERVER
    SetAbrtFile(IndexErr, NULL, KeepAbrtGS, NULL);
#endif
    Abortoutput(sockfd, "Sorry, no indexing in this server");
}
#endif /* NO_INDEXING */

#if !defined(NO_INDEXING) && defined(VMS_SERVER)

/*
* This starts up a 1-line command that's defined to be an "index gateway"
* Currently this is only implemented for VMS
*
* Currently "SEARCH", "SINCE", "BEFORE", "VERSION", "GO4LINK", "FTPLINK"
* and "EXELINK" are supported.  "KABOOM" is a debugging "feature."
*
* "SEARCH" performs a case-insensitive search for terms, without any
* interpretation of printable characters, with leading and trailing spaces
* trimmed, and with embedded spaces treated as the Boolean OR.  The search
* corresponds to the expectations of the client's Boldit() routine.  SEARCH
* may be suffixed by &, |, ~& or ~| to select the VMS /MATCH= operand, and
* override the default, implicit /MATCH=OR.
*
* "SINCE" and "BEFORE" return lists of files modified since or before
* the supplied term, a date specification.
*
* "VERSION" displays version information about the server.  It's not really
* a "search" even though it acts like one.
*
* "GO4LINK" uses a pseudo-SearchString (the Internet address of a Gopher
* server) to create a link tuple which the client can use to connect to
* a server there.
*
* "FTPLINK" uses a pseudo-SearchString (the Internet address of an FTP
* archive) to create a link tuple which the client can pass to the server's
* FTP gateway.
*
* "EXELINK" uses a pseudo-SearchString (the exec script arguments the user
* wishes to pass) to create a link tuple which the client can pass to the
* server for execution of a script.  The type of output to be expected by
* the client and the script to be executed are specified with the execlink
* keyword.  E.g.,  "Path=7$exelink Xscriptname"  where "X" is the Type=
* entry for the link tuple to be generated, and "scriptname" is the script
* file name to be used in the Path=exec:<args>:scriptname tuple entry.  The
* <args>, if any, are supplied by the user as the terms of the pseudo-search.
*
* "KABOOM" causes an access violation for debugging purposes.  It will only
*  work when the local host alias and client requestor match the values
*  specified in the environment variables (symbols or logicals) named
*  GOPHERD_KABOOM_HOST and GOPHERD_KABOOM_CLIENT, respectively.
*
* The command must write output in the same format as the GrepIndexQuery()
* output is formatted (that is, the result must be a menu of one entry or
* more).
*
*/

void
CMD1IndexQuery(int sockfd, char *CMDcmd, char *Searchstr, char *INDEXHost,
                   int INDEXPort, char *INDEXPath)
{
    FILE *moocow;
    char command[512];
    char inputline[512];
    char *filename;
    char path[256];
    char *cp;
    GopherObj *gs;
    GopherDirObj *gd;
    int  status;
    char *c2, *c3;
    char *beg, *end;
    char VMSSearchstr[256];
    char *match;
    char exectype;
    int i;

    if (filename=strchr((c2 = skip_whitespace(CMDcmd+1)),' ')) {
       filename++;
       if (*c2=='e' || *c2=='E') {
           exectype = *filename++;
           if (*filename==' ')
               filename=skip_whitespace(filename);
       }
    }
    else switch (*c2) {    /*  Some of these CMD1 searches don't need files */
       case 'k':
       case 'K':   goto files_valid;

       case 'g':
       case 'G':   goto files_valid;

       case 'f':
       case 'F':   goto files_valid;

       case 'v':
       case 'V':   goto files_valid;

       default:    goto illegal;
    }

    if (filename[0] == '/') {
        /* Convert to VMS pathspec */
        char *cp1, path[256];
        strcpy(path, (cp1=VMS$WWW_to_VMS(filename, A_FILE)) ? cp1 : filename);
        strcpy(filename, path);
    }

    c3 = c2 = (char *) malloc(sizeof(char)*strlen(filename)+1);
    strcpy(c3,filename);
    while (strlen(c2)) {
       cp = strpbrk(c2,",");
       if (cp)
           *cp = '\0';
       if (!VMS$Validate_Filespec(c2)) {
    illegal:
           LOGGopher(sockfd, "CMD1 Illegal syntax for %s", CMDcmd);
           SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
           Abortoutput(sockfd, "Eh? Confusing Request");
           free(c3);
           return;
       }
       c2 += strlen(c2) + ((cp==NULL) ? 0 : 1);
    }
    free(c3);

files_valid:
    /*** Establish the output file for the VMS command ***/
    strcpy(vms_pipe_file, cp=tempnam(GDCgetScratchDir(Config),NULL));
    free(cp);

    /*** Get the CMD1TYPE sub-type ***/
    cp = skip_whitespace(CMDcmd+1);

    if ((strncasecmp(cp, "SEARCH ", 7)==0)
       || (strncasecmp(cp, "SEARCH& ", 8)==0)
       || (strncasecmp(cp, "SEARCH| ", 8)==0)
       || (strncasecmp(cp, "SEARCH~& ", 9)==0)
       || (strncasecmp(cp, "SEARCH~| ", 9)==0)) {
    /*** Determine match qualification, if any ***/
       match = NULL;
       if (strncmp(cp+6,"& ",2)==0)
           match = "/MATCH=AND";
       else
       if (strncmp(cp+6,"| ",2)==0)
           match = "/MATCH=OR";
       else
       if (strncmp(cp+6,"~& ",3)==0)
           match = "/MATCH=NAND";
       else
       if (strncmp(cp+6,"~| ",3)==0)
           match = "/MATCH=NOR";
    /*** Convert Searchstr for the VMS SEARCH command ***/
       beg = Searchstr;
       end = beg + strlen(Searchstr);
       i = 0;
       /*** Trim any leading spaces and prefix a double-quote ***/
       while (*(beg) == ' ' && beg < end) beg++;
       VMSSearchstr[i++] = '"';
       while (beg < end) {
             /*** Reiterate any embedded double-quotes ***/
             if (*beg == '"') {
                VMSSearchstr[i++] = '"';
                VMSSearchstr[i++] = *(beg++);
             }
             /*** Replace any embedded spaces with a "," triplet ***/
             /*** or omit trailing spaces                        ***/
             else if (*beg == ' ') {
                while (*beg == ' ' && beg < end) beg++;
                if (beg < end) {
                   VMSSearchstr[i++] = '"';
                   VMSSearchstr[i++] = ',';
                   VMSSearchstr[i++] = '"';
                }
             }
             /*** Otherwise, use the character as entered ***/
             else VMSSearchstr[i++] = *(beg++);
       }
       /*** Append a double-quote and terminate ***/
       VMSSearchstr[i++] = '"';
       VMSSearchstr[i] = '\0';
    }
    else
    if (strncasecmp(cp, "VERSION", 7)==0 ||
        strncasecmp(cp, "KABOOM", 6)==0 ||
        strncasecmp(cp, "GO4LINK", 7)==0 ||
        strncasecmp(cp, "FTPLINK", 7)==0 ||
        strncasecmp(cp, "EXELINK", 7)==0)
    {
       /*** No further validation required ***/
    }
    else {
    /*** "SINCE" or "BEFORE" -- Validate the entry as a date ***/
       if (!Validate_Date(Searchstr)) {
          LOGGopher(sockfd, "Illegal search term \"%s\" for %s", Searchstr,
                            CMDcmd);
          SetAbrtFile(SyntaxErr, NULL /* AbortGS ??? */, KeepAbrtGS, NULL);
          Abortoutput(sockfd, "Eh? Unusable Search Term(s)");
          return;
       }
    }

    gs = GSnew();
    GSsetDefaults(gs);
    gd = GDnew(32);

    /*** Format the command string ***/
    if (strncasecmp(cp, "SEARCH", 6)==0) {
        sprintf(command,"$search%s/nohead/output=%s/window=0 %s %s",
           match==NULL?"":match, vms_pipe_file, filename, VMSSearchstr);
        /*** Log the string actually used in the search ***/
        LOGGopher(sockfd, "VMSSearchstr: %s", VMSSearchstr);
    }
    else if (strncasecmp(cp, "SINCE ", 6)==0)
        sprintf(command, "$dir/nohead/notrail/output=%s/since=%s %s",
                         vms_pipe_file, Searchstr, filename);
    else if (strncasecmp(cp, "BEFORE ", 7)==0)
        sprintf(command, "$dir/nohead/notrail/output=%s/before=%s %s",
                         vms_pipe_file, Searchstr, filename);
    else if (strncasecmp(cp, "VERSION", 7)==0) {
       sprintf(command, "determine version level");
       vms_pipe_file[0] = '\0';  /* Won't need output file */
    }
    else if (strncasecmp(cp, "KABOOM", 6)==0) {
       sprintf(command, "generate accvio exception");
       vms_pipe_file[0] = '\0';  /* Won't need output file */
    }
    else if (strncasecmp(cp, "GO4LINK", 7)==0) {
       sprintf(command, "create a Gopher Server tuple");
       vms_pipe_file[0] = '\0';  /* Won't need output file */
    }
    else if (strncasecmp(cp, "FTPLINK", 7)==0) {
       sprintf(command, "create an FTP gateway tuple");
       vms_pipe_file[0] = '\0';  /* Won't need output file */
    }
    else if (strncasecmp(cp, "EXELINK", 7)==0) {
       sprintf(command, "create an Exec script tuple");
       vms_pipe_file[0] = '\0';  /* Won't need output file */
    }
    Debug("CMD is %s\n", command);

    if (strlen(vms_pipe_file)==0) {
    /* One of our non-DCL commands...VERSION, GO4LINK, FTPLINK, EXELINK... */
       moocow = NULL;
       GSsetHost(gs, INDEXHost);
       GSsetPort(gs, INDEXPort);
       switch(*cp) {
       case 'k':
       case 'K':   Kaboom(sockfd, gs, gd);
                   break;
       case 'g':
       case 'G':   CreateGO4link(gs, gd, Searchstr);
                   break;
       case 'f':
       case 'F':   CreateFTPlink(gs, gd, Searchstr);
                   break;
       case 'e':
       case 'E':   CreateEXElink(gs, gd, Searchstr, exectype, filename);
                   break;
       case 'v':
       case 'V':   SearchVersion(sockfd, gs,gd);
                   break;
       default:    LOGGopher(sockfd, "Unknown non-DCL search %s",cp);
       }
       Do_Sort = Do_DateNsize = FALSE;
    }
    else {     /*  Read results much like we do for GREP indexes    */
            moocow = Gpopen(sockfd, command, "r");
            if (moocow == NULL) {
                 LOGGopher(sockfd,"Couldn't call/open CMD - %s",
                                                           STRerror(errno));
                 GSdestroy(gs);
                 GDdestroy(gd);
                 return;
            }

            cp = inputline;
            while (fgets(cp+1, sizeof(inputline)-1, moocow)) {
                 GSsetType(gs, A_FILE);
                 if (isadir(cp+1)==1)
                       GSsetType(gs,A_DIRECTORY);
                 inputline[0] = GSgetType(gs);
                 ZapCRLF(inputline);
                 /* force names lowercase */
                 str_tolower(cp+1);
                 /* create the path and filename */
                 strcpy(path, cp+1);
                 c2 = strchr(path,']') + 1;
                 *c2 = '\0';
                 c2 = strchr(inputline,';');
                 *c2 = '\0';
                 c2 = strchr(inputline,']')+1;
                 if (GDCignore(Config,c2))
                       continue;
                 if (chdir(path)<0)
                       continue;
                 GSsetPath(gs, inputline);
                 GSsetHost(gs, INDEXHost);
                 GSsetPort(gs, INDEXPort);

                 GDaddGS(gd, gs);
            }
            pclose(moocow);
       }
       if (GDgetNumitems(gd)) {
             if (Do_Sort) GDsort(gd);
             GDpostprocVMS(gd,Config,GopherPort,Do_Gplus);
             if (Do_DateNsize) GDaddDateNsize(gd);
             if (UsingHTML)
               GDtoNet(gd, sockfd, GSFORM_HTML, Gticket, NULL);
              else {
                     GDtoNet(gd, sockfd, GSFORM_G0, Gticket, NULL);
              }
       }
       GSdestroy(gs);
       GDdestroy(gd);
}

void
SearchVersion(int sockfd, GopherObj *gs, GopherDirObj *gd)
{
   FILE *fp;
   static long i;
   static char path[256];
   char *x, *cp;
   char image[512];
   time_t howlong  = time(NULL) - ServerStarted + 1;
   int connperhour = (Connections * 3600) / howlong;
   char *started;
   struct stat stats_buf;
   struct dsc$descriptor_s
        dsc$tstamp = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
                               /* Structures IHDdef & IHIdef extrapolated from
                                   $IHDDEF and $IHIDEF, respectively, as
                                   found in SYS$LIBRARY:LIB.MLB, VMS v5.2  */
#ifndef __ALPHA
   struct IHDdef
       {
           short   IHD$W_SIZE;
           short   IHD$W_ACTIVOFF;
           short   IHD$W_SYMDBGOFF;
           short   IHD$W_IMGIDOFF;
           short   IHD$W_PATCHOFF;
           short   IHD$W_VERSION_ARRAY_OFF;
       /*  There's more, but we don't need it... */
       } *ihd$;

   struct IHIdef
       {
           char    IHI$T_IMGNAM[40];
           char    IHI$T_IMGID[16];
           char    IHI$Q_LINKTIME[8];
           char    IHI$T_LINKID[16];
       } *ihi$;
#else
   struct IHIdef
       {
           char    IHI$Q_LINKTIME[8];
           char    IHI$T_IMGNAM[40];
           char    IHI$T_IMGID[16];
           char    IHI$T_LINKID[16];
       } *ihi$;
#endif
   struct itmlst {
 unsigned short int length;
 unsigned short int code;
 char *bufadr;
 int *retlen;
   }   itmlist[3] = {  { 8, SYI$_VERSION, &path[100], (int *) &i},
                       { 31, SYI$_HW_NAME, &path[120], (int *) &i},
                       { 0, 0, 0, 0 } };

#define VCFG(a,b)\
{ sprintf(path,(a),b);GSsetTitle(gs,path);GDaddGS(gd,gs); }

#define VCFG2(a,b,c)\
{ sprintf(path,(a),b,c);GSsetTitle(gs,path);GDaddGS(gd,gs); }

#define IFAVAIL(a) (a?(strlen((a))==0?"N/A":(a)):"N/A")

#define IFAVAILQ(a) (a?(strlen((a))==0?"":"\""):""), \
                   (a?(strlen((a))==0?"N/A":(a)):"N/A"), \
                   (a?(strlen((a))==0?"":"\""):"")

   bzero(image, sizeof(image));
   bzero(path, sizeof(path));
   GSsetGplus(gs,TRUE);
   GSsetNum(gs, 0);
   GSsetType(gs, path[0] = A_FILE);
   strcat(path,pname);
   if ((cp = strrchr(path, ';')) != 0)
        *cp = '\0';
   GSsetPath(gs, path);
   if ((fp = ufopen(pname, "r")) == NULL)
       GSsetType(gs, A_ERROR);
   else {                      /*  Read in 1st block of Image Header */
       if (fread(image, sizeof(char), sizeof(image), fp) == 0)
           GSsetType(gs, A_ERROR);
       else {                  /* Pick up offset to Image Name & Ident */
#ifndef __ALPHA
           ihd$ = (struct IHDdef *)image;
           i = ihd$->IHD$W_IMGIDOFF;
           ihi$ = (struct IHIdef *)(image+i);
#else
           ihi$ = (struct IHIdef *)(image+192);
#endif
           strcpy(path,"%fn [%ts, %sz]");
           GSsetTitle(gs,path);
           ustat(pname, &stats_buf);
           GSaddDateNsize(gs, stats_buf);
           sprintf(path, "Image:         %s", GSgetTitle(gs));
           GSsetTitle(gs,path);
           GSsetType(gs, A_UNIXBIN);
           sprintf(path,"%c%s", A_UNIXBIN, fgetname(fp,path+1));
           GSsetPath(gs,path);
           GDaddGS(gd, gs);
           GSsetType(gs, A_INFO);
           GSsetPath(gs,"");
           if (DEBUG) {
               GSsetTitle(gs,"DEBUG Mode:    Debugging");
               GDaddGS(gd, gs);
           }
           strcpy(path,"Image Name:    \"");
           strncat(path,ihi$->IHI$T_IMGNAM+1,ihi$->IHI$T_IMGNAM[0]);
           strcat(path, "\"");
           GSsetTitle(gs,path);    GDaddGS(gd, gs);
           strcpy(path,"Image File ID: \"");
           strncat(path,ihi$->IHI$T_IMGID+1,ihi$->IHI$T_IMGID[0]);
           strcat(path, "\"");
           GSsetTitle(gs,path);    GDaddGS(gd, gs);
           bzero(path,sizeof(path)-100);
           strcpy(path,"Link date/time: ");
           dsc$tstamp.dsc$a_pointer = path+strlen(path);
           dsc$tstamp.dsc$w_length = sizeof(path)-strlen(path);
           LIB$FORMAT_DATE_TIME(&dsc$tstamp,&(ihi$->IHI$Q_LINKTIME),0,&i,0);
           *(dsc$tstamp.dsc$a_pointer + i) = '\0';
           if ((*dsc$tstamp.dsc$a_pointer) == ' ')
               memmove(dsc$tstamp.dsc$a_pointer,dsc$tstamp.dsc$a_pointer+1,
                                                               i-1);
           GSsetTitle(gs,path);    GDaddGS(gd, gs);
           strcpy(path,"Linker ID:     \"");
           strncat(path,ihi$->IHI$T_LINKID+1,ihi$->IHI$T_LINKID[0]);
           strcat(path, "\"");
           GSsetTitle(gs,path);    GDaddGS(gd, gs);
       }
       fclose(fp);
   }
   if (SS$_NORMAL!=SYS$GETSYIW(0,0,0,itmlist,0,0,0))
       GSsetType(gs, A_ERROR);
   else {
       path[120+i] = '\0';
       for (i=107; path[i]==' '; path[i--] = '\0');
       str_tolower(&path[100]);
       sprintf(path, "Host System:    %s, VMS %s",&path[120],&path[100]);
       GSsetTitle(gs,path);
       GDaddGS(gd, gs);
   }


   if (GSgetType(gs)==A_ERROR)
       LOGGopher(sockfd,"Could not report version information");
   else
       LOGGopher(sockfd,"Reported version");

#ifdef MULTINET
#define TCPIP_AGENT   "TGV Multinet"
#endif
#ifdef UCX
#define TCPIP_AGENT   "UCX"
#endif
#ifdef WOLLONGONG
#define TCPIP_AGENT   "Wollongong"
#endif
#ifdef TCPIP_AGENT
   sprintf(path, "TCP/IP agent:   %s", TCPIP_AGENT);
   GSsetTitle(gs,path);
   GDaddGS(gd, gs);
#endif

   sprintf(path, "Revision Level: %s-%s", GOPHERD_VERSION, PATCH_LEVEL);
   GSsetTitle(gs, path);
   GDaddGS(gd, gs);

   if (Connections) {
       started = ctime(&ServerStarted);
       *(started+24) = '\0';
       VCFG("Server Started: %s", started);
       VCFG("Connections:    %d", Connections);
       VCFG("Connects/Hr:    %d", connperhour);
       VCFG("Concurrent:     %d", ActiveSessions);
   }

   GSsetType(gs, A_DIRECTORY);
   sprintf(path,"%c%s", A_DIRECTORY, GDCgetDatadir(Config));
   GSsetPath(gs,path);
   VCFG("DataDirectory:  %s", IFAVAIL(GDCgetDatadir(Config)));
   GSsetType(gs, A_FILE);
   sprintf(path,"%c%s", A_FILE, GDCgetConfig(Config));
   if (cp=strchr(path,';'))
       *cp = '\0';
   GSsetPath(gs,path);
   strcpy (image, path+1);
   VCFG("ConfigFile:     %s", IFAVAIL(image));
   VCFG("HostAlias:      %s", IFAVAIL(GDCgetHostname(Config)));
   VCFG("Port:           %d", GDCgetPort(Config));
   VCFG("Site:           %s", IFAVAIL(GDCgetSite(Config)));
   VCFG("Abstract:       %s", IFAVAIL(GDCgetAbstract(Config)));
   VCFG("Admin:          %s", IFAVAIL(GDCgetAdmin(Config)));
   VCFG("AdminEmail:     %s", IFAVAIL(GDCgetAdminEmail(Config)));
   VCFG("Org:            %s", IFAVAIL(GDCgetOrg(Config)));
   VCFG("Loc:            %s", IFAVAIL(GDCgetLoc(Config)));
   VCFG("Lang:           %s", IFAVAIL(GDCgetLang(Config)));
   VCFG("Geog:           %s", IFAVAIL(GDCgetGeog(Config)));
   if (GDCgetTZ(Config)==-1)
       VCFG("TimeZone:       %s", GDCgetTimeZone(Config))
   else
       VCFG("TimeZone:       %d", GDCgetTZ(Config));
   VCFG("ReadTimeout:    %d", GDCgetReadTimeout(Config));
   if (GDCgetOPCOM(Config)==0)
       VCFG("Console:        %s", "None")
   else {
       int opc = GDCgetOPCOM(Config);
       int ox;
       String *cbuf = STRnew();
       for (ox=0; ox<vms_OPC$M_num; ox++) {
           if (opc & vms_OPC$M_NM[ox]) {
               if (STRlen(cbuf))
                   STRcat(cbuf,"+");
               STRcat(cbuf, vms_OPC$M_TXT[ox]);
           }
       }
       VCFG("Console:        %s", STRget(cbuf));
       STRdestroy(cbuf);
   }
   VCFG("InetD:          %s", GDCgetInetdActive(Config)==TRUE?"TRUE":"FALSE");
   VCFG("IgnoreAll:      %s", GDCgetIgnoreAll(Config)==TRUE?"TRUE":"FALSE");
   VCFG("IsGPlus:        %s", GDCgetIsGplus(Config)==TRUE?"TRUE":"FALSE");
   VCFG("SortDir:        %s", GDCgetSortDir(Config)==TRUE?"TRUE":"FALSE");
   if (!GDCgetFTPPort(Config))
       VCFG("FTPPort:        %s", "NONE")
   else
       VCFG("FTPPort:        %d", GDCgetFTPPort(Config));
   if (!GDCgetEXECPort(Config))
       VCFG("EXECPort:       %s", "NONE")
   else
       VCFG("EXECPort:       %d", GDCgetEXECPort(Config));
   if (!GDCgetSRCHPort(Config))
       VCFG("SRCHPort:       %s", "NONE")
   else
       VCFG("SRCHPort:       %d", GDCgetSRCHPort(Config));
   if (!GDCgetOVERPort(Config) || (GDCgetOVERSize(Config)==-1))
       VCFG("OVERPort:       %s", "NONE")
   else
       VCFG2("OVERPort:       %d, %d", GDCgetOVERPort(Config),
                                           GDCgetOVERSize(Config));
   VCFG("SortGREP:       %s", GDCgetSortGREP(Config)==TRUE?"TRUE":"FALSE");
   VCFG("SortCMD1:       %s", GDCgetSortCMD1(Config)==TRUE?"TRUE":"FALSE");
   VCFG("SortShell:      %s", GDCgetSortShell(Config)==TRUE?"TRUE":"FALSE");
   VCFG("Veronica:       %s", GDCgetShouldIndex(Config)==TRUE?"TRUE":"FALSE");
   VCFG("Cache:          %s", GDCgetCaching(Config)==TRUE?"TRUE":"FALSE");
   if (GDCgetCaching(Config))
       VCFG("CacheTime:          %d", GDCgetCachetime(Config));
   VCFG("MaxLoad:        %g", GDCgetMaxLoad(Config));
   VCFG("MaxConnections: %d", GDCgetMaxconns(Config));
   VCFG("Do_chroot:      %s", GDCgetchroot(Config)==TRUE?"TRUE":"FALSE");
   VCFG("BummerMsg:      %s%s%s", IFAVAILQ(GDCgetBummerMsg(Config)));
   VCFG("DName:          %s%s%s", IFAVAILQ(GDCgetDName(Config)));
   VCFG("DHead:          %s%s%s", IFAVAILQ(GDCgetDHead(Config)));
   VCFG("DFoot:          %s%s%s", IFAVAILQ(GDCgetDFoot(Config)));
   VCFG("Hidden:         %s%s%s", IFAVAILQ(GDCgetHiddenPrefix(Config)));
   VCFG("Link:           %s%s%s", IFAVAILQ(GDCgetLinkPrefix(Config)));
   VCFG("Lookaside:      %s%s%s", IFAVAILQ(GDCgetLookAside(Config)));
   VCFG("Restart:        %s", IFAVAIL(GDCgetRestart(Config)));
   GSsetType(gs,A_FILE);
   if (Config->Securityon) {
       int i;
       Site *temp;
       VCFG("Security:       %s", "ON");
       VCFG2("Access:         %s: %s", "default",
                           AccessLevelText(Config->Defaccess,0));
       for (i=0; i< DAgetTop(Config->Sites); i++) {
           temp = SiteArrgetEntry(Config->Sites,i);
           VCFG2("Access:         %s: %s", STRget(temp->domain),
                           AccessLevelText(temp->Level,temp->maxsessions));
       }

       if (Config->Authroutines || Config->Authitems || Config->Serverpw
               || Config->Tixfile) {
           /*  Need to provide for an Authorizations routine dump  */
       }
   }
   if (Config->Extensions) {
       /* Need to provide for an extensions dump yet */
   }
   if (Config->other_dirs) {
       /* Need to provide for an other_dirs dump yet */
   }
   if (Config->other_gdcs) {
       /* Need to provide for an other_gdcs dump yet */
   }
   if (Config->FileSeparators) {
       /* Need to provide for a FileSeparators dump yet */
   }
   if (Config->BlkScriptBlocks) {
       /* Need to provide for a BlkScriptBlocks dump yet */
   }
   if (Config->BlkScripts) {
       /* Need to provide for a BlkScripts dump yet */
   }
   if (strcasecmp(GDCgetLogfile(Config),"syslog")==0) {
       path[0] = A_FILE;       /*  Kind of tough to display the syslog */
       path[1] = '\0';
   }
   else
       sprintf(path,"%c%s", A_FILE, GDCgetLogfile(Config));
   GSsetType(gs,A_FILE);
   if (strlen(path)>1) {
       GSsetPath(gs,path);
   }
   else
       GSsetPath(gs,BummerMsg);
   VCFG("LogFile:        %s", IFAVAIL(GDCgetLogfile(Config)));
   x =  "Rollover:       %s";
   switch(GDCgetRollover(Config)) {
   case ROLLOVER_DAILY:    VCFG(x,"DAILY");        break;
   case ROLLOVER_HOURLY:   VCFG(x,"HOURLY");       break;
   case ROLLOVER_MONTHLY:  VCFG(x,"MONTHLY");      break;
   case ROLLOVER_ANNUALLY: VCFG(x,"ANNUALLY");     break;
   case ROLLOVER_WEEKLY:   VCFG(x,"WEEKLY");       break;
   case ROLLOVER_NEVER:
   default:                VCFG(x,"NEVER");
   }
   VCFG("LogTag:         %s", IFAVAIL(GDCgetLogTag(Config)));
   sprintf(path,"%c%s", A_FILE, GDCgetErrorfile(Config));
   if (strlen(path)>1)
       GSsetPath(gs,path);
   else
       GSsetPath(gs,NoSuchFile);
   VCFG("ErrorFile:      %s", IFAVAIL(GDCgetErrorfile(Config)));
   sprintf(path,"%c%s", A_DIRECTORY, GDCgetScratchDir(Config));
   if (strlen(path)>1) {
       GSsetPath(gs,path);
       GSsetType(gs,A_DIRECTORY);
   }
   else {
       GSsetPath(gs,BummerMsg);
       GSsetType(gs,A_FILE);
   }
   VCFG("ScratchDir:     %s", IFAVAIL(GDCgetScratchDir(Config)));
   GSsetType(gs, A_FILE);
   sprintf(path,"%c%s", A_FILE, GDCgetSpawnInit(Config));
   if (strlen(path)>1)
       GSsetPath(gs,path);
   else
       GSsetPath(gs,NoSuchFile);
   VCFG("SpawnInit:      %s", IFAVAIL(GDCgetSpawnInit(Config)));
   sprintf(path,"%c%s", A_DIRECTORY, GDCgetSupportDir(Config));
   if (strlen(path)>1) {
       GSsetPath(gs,path);
       GSsetType(gs,A_DIRECTORY);
   }
   else {
       GSsetPath(gs,BummerMsg);
       GSsetType(gs,A_FILE);
   }
   VCFG("SupportDir:     %s", IFAVAIL(GDCgetSupportDir(Config)));
}

/**
*      Kaboom() causes the server to ACCVIO if, and only if, the environment
*          variables (that is, symbols or logicals) named GOPHERD_KABOOM_HOST
*          and GOPHERD_KABOOM_CLIENT match (case insensitive) the host name
*          this server is using and the client peer *name* making the pseudo-
*          search request.  Otherwise reports that the request is denied.
**/
void
Kaboom(int sockfd, GopherObj *gs, GopherDirObj *gd)
{
   char BoomBoom[256];
   char *cp;
   void (*Die)() = NULL;

   GSsetDefaults(gs);
   GSsetType(gs, A_INFO);
   GSsetPath(gs, "");
   sprintf(BoomBoom, "KaBOOM denied on %s for %s",
                           GDCgetHostname(Config), CurrentPeerName);
   GSsetTitle(gs, BoomBoom);
   GDaddGS(gd, gs);
   if (cp = getenv("GOPHERD_KABOOM_HOST")) {
       if (strcasecmp(cp, GDCgetHostname(Config)))
           return;
       if (cp = getenv("GOPHERD_KABOOM_CLIENT"))
           if (strcasecmp(cp, CurrentPeerName))
               return;
   }
   if (cp) {
       LOGGopher(sockfd,"KaBOOM authorized!  BOOM!");
       Die();
   }
}

void
CreateGO4link(GopherObj *gs, GopherDirObj *gd, char *GO4Site)
{
   char GO4buf[256], *cp;

   GSsetType(gs, A_DIRECTORY);
   if (cp=strchr(GO4Site,' ')) {
       *cp = '\0';
       GSsetPort(gs,atoi(++cp));
   }
   else
       GSsetPort(gs,70);   /* Hard coded since that's the Gopher standard,
                               and we have to take a guess here */
   GSsetHost(gs, GO4Site);
   GSsetPath(gs, "");
   bzero(GO4buf, sizeof(GO4buf));
   sprintf(GO4buf, "Link to Gopher host %s on port %d",
                       GO4Site, GSgetPort(gs));
   GSsetTitle(gs, GO4buf);
   GDaddGS(gd, gs);
}

void
CreateFTPlink(GopherObj *gs, GopherDirObj *gd, char *FTPSite)
{
   char ftpbuf[256], *cp;

   bzero(ftpbuf, sizeof(ftpbuf));
   sprintf(ftpbuf, "%cftp:%s@/", A_DIRECTORY, FTPSite);
   GSsetType(gs, ftpbuf[0]);
   GSsetPath(gs, ftpbuf+1);
   cp = ftpbuf + strlen(ftpbuf);
   strcat(ftpbuf, "Link to aFTP host ");
   strcat(ftpbuf, FTPSite);
   GSsetTitle(gs, cp);
   GDaddGS(gd, gs);
}

void
CreateEXElink(GopherObj *gs, GopherDirObj *gd, char *EXECargs, char EXECtype,
                                       char *EXECfile)
{
   char exebuf[256], *cp;

   bzero(exebuf, sizeof(exebuf));
   sprintf(exebuf, "%cexec:%s:%s", EXECtype, EXECargs, EXECfile);
   GSsetType(gs, exebuf[0]);
   GSsetPath(gs, exebuf+1);
   cp = exebuf + strlen(exebuf);
   strcat(exebuf, "Execute script with arguments ");
   strcat(exebuf, EXECargs);
   GSsetTitle(gs, cp);
   GDaddGS(gd, gs);
}

/*
*  *date locates a date specification.  Make sure that's what it is, and
*  return TRUE or FALSE.
*/
boolean
Validate_Date(char *date)
{
   int status;
   char quadword[8];
   $DESCRIPTOR(dsc$date,"");
   long flags = 0x7f;
   if (date==NULL)
       return(FALSE);
   dsc$date.dsc$a_pointer = date;
   dsc$date.dsc$w_length = strlen(date);
   status = LIB$CONVERT_DATE_STRING(&dsc$date, quadword,0,&flags,0,0);
   if ((status &1)!=1)
       return(FALSE);
   return(TRUE);
}
#endif