SMOLNET PORTAL home about changes
/********************************************************************
 * wilkinson
 * 3.36VMS-2
 * 1996/01/28 14:00   
 * gopher_root1:[gopher.g2.vms2_13.gopherd]gopherdconf.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: gopherdconf.c
 * Routines to parse the gopherd.conf file.
 *********************************************************************
 * Revision History:
 * gopherdconf.c,v
 * Revision 3.36VMS-2  1996/01/28 14:00  wilkinson
 * Allow OPCOM message console selection configuration to have spaces
 *
 * Revision 3.36VMS-1  1996/01/24 11:00  wilkinson
 * Made OPCOM message console selection configurable
 *
 * Revision 3.36VMS 1995/09/25 14:31    wilkinson
 * Consolodate VMS/Unix source code for server as well as client
 *
 * Revision 3.36  1995/02/16  22:32:39  lindner
 * HTML icon support
 *
 * Revision 3.35  1995/02/13  19:07:06  lindner
 * Add MaxConnection limit for total server
 *
 * Revision 3.34  1995/02/11  06:21:26  lindner
 * remove pid junk
 *
 * Revision 3.33  1995/02/06  22:14:50  lindner
 * remove unused variables
 *
 * Revision 3.32  1995/02/02  17:13:54  lindner
 * Fix memory leaks
 *
 * Revision 3.31  1994/12/15  17:33:19  lindner
 * Add BlockScript: keyword support
 *
 * Revision 3.30  1994/12/08  05:23:00  lindner
 * Fix wording problem in integrity check
 *
 * Revision 3.29  1994/10/10  18:39:36  lindner
 * Change auxconf: to use regular expressions
 *
 * Revision 3.28  1994/09/29  19:59:16  lindner
 * Force people to update their gopherd.conf files
 *
 * Revision 3.27  1994/08/18  22:28:17  lindner
 * Abstract for top level and malloc casts
 *
 * Revision 3.26  1994/08/03  03:33:25  lindner
 * Include files in SERVERDIR
 *
 * Revision 3.25  1994/07/22  22:56:18  lindner
 * More NO_AUTH stuff..
 *
 * Revision 3.24  1994/07/22  22:26:25  lindner
 * NO_AUTHENTICATION mods
 *
 * Revision 3.23  1994/07/22  16:37:07  lindner
 * Add INCLUDE directive for gopherd.conf files
 *
 * Revision 3.22  1994/07/21  17:23:05  lindner
 * Add File Separator code
 *
 * Revision 3.21  1994/06/29  05:30:08  lindner
 * Add code to handle lists of authscripts and authitems
 *
 * Revision 3.20  1994/03/31  22:46:26  lindner
 * Allow whitespace in the gopherd.conf file before tokens
 *
 * Revision 3.19  1994/03/17  21:18:39  lindner
 * Massive reworking of access limits
 *
 * Revision 3.18  1994/03/17  04:30:17  lindner
 * VMS fixes gopherd.h
 *
 * Revision 3.17  1994/03/08  15:56:01  lindner
 * gcc -Wall fixes
 *
 * Revision 3.16  1994/01/21  03:59:11  lindner
 * Add support for timezone variable and fix return type
 *
 * Revision 3.15  1993/12/09  20:48:16  lindner
 * Debug fixes
 *
 * Revision 3.14  1993/10/27  20:36:59  lindner
 * Plug memory leak
 *
 * Revision 3.13  1993/10/20  03:18:17  lindner
 * Code for ignore_patt:
 *
 * Revision 3.12  1993/10/11  04:39:49  lindner
 * Allow quotes in dirname for auxconf
 *
 * Revision 3.11  1993/10/07  05:19:54  lindner
 * Update for gateway items and GDCevalDir
 *
 * Revision 3.10  1993/10/04  06:48:57  lindner
 * mods to support auxconf files
 *
 * Revision 3.9  1993/09/30  17:05:11  lindner
 * start of subconf
 *
 * Revision 3.8  1993/09/21  04:16:50  lindner
 * Move cache settings into gopherd.conf
 *
 * Revision 3.7  1993/08/23  18:46:17  lindner
 * Crude addition of a veronica top-level block
 *
 * Revision 3.6  1993/08/20  18:03:07  lindner
 * Mods to allow gopherd.conf files control ftp gateway access
 *
 * Revision 3.5  1993/07/27  05:27:49  lindner
 * Mondo Debug overhaul from Mitra
 *
 * Revision 3.4  1993/07/23  03:19:20  lindner
 * Mods for using decoder:'s
 *
 * Revision 3.3  1993/04/15  17:09:22  lindner
 * none
 *
 * Revision 3.2  1993/03/24  20:25:14  lindner
 * Addition for secureusers file
 *
 * Revision 3.1.1.1  1993/02/11  18:02:51  lindner
 * Gopher+1.2beta release
 *
 * Revision 1.3  1993/02/09  22:30:56  lindner
 * Additions for multi-languages
 *
 * Revision 1.2  1993/01/30  23:57:44  lindner
 * Gopher+ stuff
 *
 * Revision 1.1  1992/12/10  23:13:27  lindner
 * gopher 1.1 release
 *
 *
 *********************************************************************/

#ifdef VMS_SERVER
#define GSGOPHEROBJ_C
#endif
#include "gopherdconf.h"
#ifdef VMS_SERVER
#undef GSGOPHEROBJ_C
#include <ctype.h>
#include "fileio.h"
#include <opcdef.h>
#endif
#include "Malloc.h"
#include "String.h"
#include <stdio.h>
#include "util.h"
#include "Debug.h"
#include "conf.h"

#ifdef VMS_SERVER
	/*  To avoid compliation errors when #including "gopherd.h" or
	    "globals.h" to get these external variables, we'll just 
	    define them as external here...
	*/
extern int vms_OPC$M_num;
extern int vms_OPC$M_NM[];
extern char *vms_OPC$M_TXT[];
#endif

/*********************************************/

GDCobj *
GDCnew()
{
     GDCobj *gdc;

     gdc = (GDCobj *) malloc(sizeof(GDCobj));

     gdc->Extensions   = EXAnew();

#ifndef NO_AUTHENTICATION
     gdc->Sites        = SiteArrayNew();
     gdc->Securityon   = FALSE;

     gdc->Authroutines = AUTHAnew(5);
     gdc->Authitems    = AUTHITEMSnew(5);
     gdc->Serverpw     = STRnew();
#endif

     gdc->RunFromInetd = FALSE;
     gdc->Caching      = TRUE;
     gdc->Cachetime    = 180;  /** Three minutes ***/

     gdc->Logfile      = STRnew();
     gdc->Errorfile    = STRnew();
#ifndef VMS_SERVER
     gdc->Data_Dir     = STRnew();
#else
     gdc->DataDir      = STRnew();
#endif
     gdc->Hostname     = STRnew();
     gdc->Port         = 0;
     gdc->chroot       = TRUE;
     gdc->Defaccess    = ACC_FULL;
     gdc->BummerMsg    = STRnew();
     
     gdc->Admin        = STRnew();
     gdc->AdminEmail   = STRnew();
     gdc->Abstract     = STRnew();

     gdc->Site         = STRnew();
     gdc->Org          = STRnew();
     gdc->Geog         = STRnew();
     gdc->Loc          = STRnew();
     gdc->Lang         = STRnew();
     gdc->TZ           = 0;
     gdc->VeronicaIndex = TRUE;

     gdc->Tixfile      = STRnew();

     STRset(gdc->Logfile, "");
     STRset(gdc->Errorfile, "");
     STRset(gdc->BummerMsg, "");
     STRset(gdc->Hostname, "");

     gdc->other_dirs = STAnew(2);
     gdc->other_gdcs = STAnew(2);

     gdc->FileSeparators = STAnew(5);

     gdc->BlkScriptBlocks = STAnew(5);
     gdc->BlkScripts      = STAnew(5);

     gdc->MaxConnections  = -1;

#ifdef VMS_SERVER
		/* Override Unix defaults w/ VMS ones, or provide 
		   initialization when it's omitted under Unix	    */
     gdc->Port	       = GOPHER_PORT;
     gdc->chroot       = FALSE;
     gdc->TZ           = -1;
     gdc->TimeZoneText = STRnew();
     gdc->MaxLoad      = 0.0;

     /* New VMS configuration options */
     gdc->IsGplus      = FALSE;
     gdc->IgnoreAll    = FALSE;
     gdc->SortDir      = TRUE;
     gdc->FTPPort      = -1;
     gdc->EXECPort     = -1;
     gdc->SRCHPort     = -1;
     gdc->OVERPort     = -1;
     gdc->OVERSize     = -1;
     gdc->SortShell    = TRUE;
     gdc->SortGREP     = TRUE;
     gdc->SortCMD1     = TRUE;
     gdc->ReadTimeout  = READTIMEOUT;
     gdc->OPCOM	       = OPC$M_NM_CENTRL | OPC$M_NM_NTWORK;
     gdc->Hidden_Prefix= STRnew();
     gdc->Link_Prefix  = STRnew();
     gdc->LookAside    = STRnew();
     gdc->DName	       = STRnew();
     gdc->DHead	       = STRnew();
     gdc->DFoot	       = STRnew();
     gdc->Scratch_Dir  = STRnew();
     gdc->Support_Dir  = STRnew();
     gdc->Spawn_Init   = STRnew();
     gdc->RestartLnm   = STRnew();
     gdc->ConfigFile   = STRnew();
     gdc->LogFileTag   = STRnew();
     gdc->rollover     = ROLLOVER_NEVER;

     /* Initialize strings for VMS */
     STRset(gdc->DataDir, DATA_DIRECTORY);
     STRset(gdc->Admin, "");
     STRset(gdc->AdminEmail, "");
     STRset(gdc->Site, "");
     STRset(gdc->Org, "");
     STRset(gdc->Geog, "");
     STRset(gdc->Lang, "");
     STRset(gdc->Loc, "");
     STRset(gdc->TimeZoneText,"");
     STRset(gdc->Hidden_Prefix, "_");
     STRset(gdc->Link_Prefix, ".");
     STRset(gdc->LookAside, ".cap");
     STRset(gdc->DName, "");
     STRset(gdc->DHead, "");
     STRset(gdc->DFoot, "");
     STRset(gdc->Scratch_Dir,"sys$scratch:");
     STRset(gdc->Support_Dir,"sys$disk:");
     STRset(gdc->Spawn_Init, LOGINCOM);
     STRset(gdc->RestartLnm, RESTART);
     STRset(gdc->ConfigFile, CONF_FILE);
     STRset(gdc->LogFileTag, "");
#endif
     return(gdc);
}

void
GDCdestroy(gdc)
  GDCobj *gdc;
{
#ifdef VMS_SERVER	    /* Oops... did this get misplaced for unix? */
     EXAdestroy(gdc->Extensions);
#endif
#ifndef NO_AUTHENTICATION
#ifndef VMS_SERVER	    /* Oops... did this get misplaced for unix? */
     EXAdestroy(gdc->Extensions);
#endif
     SiteArrDestroy(gdc->Sites);
     AUTHAdestroy(gdc->Authroutines);
     AUTHITEMSdestroy(gdc->Authitems);
     STRdestroy(gdc->Serverpw);
#endif

     STRdestroy(gdc->Logfile);
     STRdestroy(gdc->BummerMsg);
     STRdestroy(gdc->Hostname);
     STRdestroy(gdc->Lang);
     STRdestroy(gdc->Tixfile);
     STRdestroy(gdc->Abstract);

     /** etc..**/

     STAdestroy(gdc->other_dirs);
     STAdestroy(gdc->other_gdcs);
     STAdestroy(gdc->FileSeparators);

     STAdestroy(gdc->BlkScriptBlocks);
     STAdestroy(gdc->BlkScripts);

#ifdef VMS_SERVER
     STRdestroy(gdc->Hidden_Prefix);
     STRdestroy(gdc->Link_Prefix);
     STRdestroy(gdc->LookAside);
     STRdestroy(gdc->DName);
     STRdestroy(gdc->DHead);
     STRdestroy(gdc->DFoot);
     STRdestroy(gdc->Scratch_Dir);
     STRdestroy(gdc->Support_Dir);
     STRdestroy(gdc->Spawn_Init);
     STRdestroy(gdc->RestartLnm);     
     STRdestroy(gdc->ConfigFile);     
     STRdestroy(gdc->LogFileTag);
			/* Don't forget these; they're strings, too */
     STRdestroy(gdc->Errorfile);
     STRdestroy(gdc->DataDir);
     STRdestroy(gdc->Admin);
     STRdestroy(gdc->AdminEmail);
     STRdestroy(gdc->Site);
     STRdestroy(gdc->Org);
     STRdestroy(gdc->Loc);
     STRdestroy(gdc->Geog);
     STRdestroy(gdc->TimeZoneText);
#endif
     free(gdc);
}

#define MAXGDCFLEVEL 10
static FILE *GDCfiles[MAXGDCFLEVEL]; /* can 'include' up to ten levels deep */
static int  GDCfilenum = -1;

static void
GDCfilePush(f)
  FILE *f;
{
     if ((++GDCfilenum) < MAXGDCFLEVEL)
	  GDCfiles[GDCfilenum] = f;
     else {
#ifndef VMS_SERVER
	  fprintf(stderr, "Nesting level too deep for include directive\n");
	  exit(-1);
#else
	  VMS$fprintf(stderr, "Nesting level too deep for include directive\n");
	  gopherd_exit(-1);
#endif
     }
}

static
char *
GDCfileGetline(inputline, size)
  char *inputline;
  int  size;
{
     char* farg;

     while (NULL == (farg = fgets(inputline, size, GDCfiles[GDCfilenum]))) {
	  if (GDCfilenum == 0) {
	       fclose(GDCfiles[GDCfilenum]);
	       return(farg);
	  }

	  /** Try popping up to the previous file.. **/
	  fclose(GDCfiles[GDCfilenum]);
	  GDCfilenum--;
     }
     
     return(farg);
}


/*
 * Parse Gopherd.conf tokens..
 */

static
boolean
GDCtokens(gdc, token, rest)
  GDCobj *gdc;
  char *token;
  char *rest;
{
     boolean success = TRUE;

#ifdef VMS_SERVER
     Debug("GDC: %s: ", token);
     Debug("%s\n", rest);

     if ((strcasecmp(token, "ACCESS") != 0) &&
	    (strcasecmp(token, "ABSTRACT") != 0) &&
		    /* Add here any other selections which we don't
			want to allow a "!" to terminate the line as
			traditional OpenVMS DCL-style commentary   */
	    (strcasecmp(token, "ADMINEMAIL") != 0))
     {
	char *cp;
	
	if ((cp = strchr(rest, '!')) != NULL) {
	    *cp = '\0';		/*  Allow VMS-style "!" commentary as well  */
	    for(--cp;isspace(*cp)||!isprint(*cp);cp--)
		*cp = '\0';    /*  Strip trailing non-text		    */

	}
    }
#endif


     if (strcasecmp(token, "ADMIN")==0)
	  GDCsetAdmin(gdc, rest);
#ifndef NO_AUTHENTICATION
     else if (strcasecmp(token, "ACCESS") == 0) {
	  Accesslevel moo;

	  success = SiteProcessLine(gdc->Sites, rest, gdc->Defaccess);
	  moo = SiteDefAccess(gdc->Sites);

	  if (moo != ACC_UNKNOWN)
	       gdc->Defaccess = moo;

	  gdc->Securityon = TRUE;
     }
     else if (strcasecmp(token, "AUTHSCRIPT")==0) {
	  success = AUTHAprocessLine(gdc->Authroutines, rest);
     }
     else if (strcasecmp(token, "AUTHITEM")==0) {
	  success = AUTHITEMSprocessLine(gdc->Authitems, rest);
     }
     else if (strcasecmp(token, "SERVERPW")==0) {
	  GDCsetPW(gdc, rest);
     } 
#endif
     else if (strcasecmp(token, "ADMINEMAIL")==0)
	  GDCsetAdminEmail(gdc, rest);
     else if (strcasecmp(token, "HOSTALIAS")==0)
	  GDCsetHostname(gdc, rest);
     else if (strcasecmp(token, "SITE")==0)
	  GDCsetSite(gdc, rest);
     else if (strcasecmp(token, "ORG")==0)
	  GDCsetOrg(gdc, rest);
     else if (strcasecmp(token, "LOC")==0)
	  GDCsetLoc(gdc, rest);
     else if (strcasecmp(token, "LOGFILE")==0)
	  GDCsetLogfile(gdc, rest);
     else if (strcasecmp(token, "ERRORFILE")==0)
	  GDCsetErrorfile(gdc, rest);
     else if (strcasecmp(token, "GEOG")==0)
	  GDCsetGeog(gdc, rest);
     else if (strcasecmp(token, "BUMMERMSG")==0)
	  GDCsetBummerMsg(gdc, rest);
     else if (strcasecmp(token, "LANGUAGE")==0) {
	  rest = skip_whitespace(rest);
	  GDCsetLang(gdc, rest);
     }
#ifndef VMS_SERVER		/*  Do TimeZones differently for OpenVMS */
     else if (strcasecmp(token, "TZ") == 0)
	  GDCsetTZ(gdc,atoi(rest));
#endif
     else if (strcasecmp(token, "VIEWEXT")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_VIEW, rest, 
				   GDCgetLang(gdc));
     }
     else if (strcasecmp(token, "BLOCKEXT")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_BLOCK, rest, NULL);
     }
     else if (strcasecmp(token, "BLOCKREFEXT")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_BLOCKREF, rest, NULL);
     }
     else if (strcasecmp(token, "IGNORE")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_IGNORE, rest, NULL);
     }
     else if (strcasecmp(token, "IGNORE_PATT")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_IGNOREPAT, rest, NULL);
     }
     else if (strcasecmp(token, "DECODER")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_DECODER, rest, NULL);
     } else if (strcasecmp(token, "ABSTRACT") == 0) {
	  GDCsetAbstract(gdc, rest);
     }
     else if (strcasecmp(token, "VERONICAINDEX")==0) {
	  if (strcasecmp(rest, "no")==0)
	       GDCsetShouldIndex(gdc, FALSE);
	  else
	       GDCsetShouldIndex(gdc, TRUE);
     }
     else if (strcasecmp(token, "CACHETIME")==0) {
	  GDCsetCachetime(gdc, atoi(rest));
     }
     else if (strcasecmp(token, "FILESEP")==0) {
	  GDCaddFileSep(gdc, rest);
     } 
     else if (strcasecmp(token, "INCLUDE") == 0) {
	  /** Check to see if we can open the file.. **/
	  /** Then push it onto the stack.. **/
	  FILE *gdcfile;

#ifndef VMS_SERVER
	  if (*rest != '/') {
	       char tmpstr[256];

	       sprintf(tmpstr, "%s/%s", GDCDIR, rest);
	       strcpy(rest, tmpstr);
	  }
#endif
	  if ((gdcfile = fopen(rest, "r")) == (FILE *) NULL) {
#ifndef VMS_SERVER
	       fprintf(stderr,"Cannot open file '%s'\n", rest);
	       exit(-1);
#else
	       VMS$fprintf(stderr,"Cannot open file '%s'\n", rest);
	       gopherd_exit(-1);
#endif
	  }
	  GDCfilePush(gdcfile);
	  
     }
     else if (strcasecmp(token, "BLOCKSCRIPT") == 0) {
	  char *cp;

	  /** Block name is first argument, script to run is second **/

	  cp = strchr(rest, ' ');
	  if (cp != NULL) {
	       *cp = '\0';
	       cp++;
	       
	       GDCpushBlockScript(gdc, rest, cp);

	  } else {
	       success = FALSE;
	  }
     }
#ifndef VMS_SERVER
     else if (strcasecmp(token, "AUXCONF")==0) {
	  /** Directory is first arg, conf file is second **/
	  char *cp;

	  if (*rest == '\"') {
	       /*** " Dir is in quotes.. ***/
	       rest++;
	       cp = strchr(rest, '\"'); /* " Fix for hilit19 */
	       if (cp != NULL) {
		    *cp = '\0';
		    cp ++;
	       }
	  } else {
	       cp = strchr(rest, ' ');
	  }

	  if (cp == NULL)
	       success = FALSE;
	  else {
	       *cp = '\0';
	       GDCpushOtherGDC(gdc, rest, cp+1);
	  }
     }
#endif
     else if (strcasecmp(token, "MAXCONNECTIONS")==0) {
	  int numconns = atoi(rest);

	  if (numconns > 1)
	       GDCsetMaxconns(gdc, atoi(rest));
     }
#ifdef VMS_SERVER
	    /*	VMS configuration options follow			*/
	    /*	Although these first few options probably ought to be
		    configurable for Unix as well, oughtn't they?	*/
     else if (strcasecmp(token, "EXT")==0) {
	  success = EXAprocessLine(gdc->Extensions, EXT_MISC, rest, NULL);
     }
     else if (strcasecmp(token, "DataDirectory")==0)
	  GDCsetDatadir(gdc, rest);

     else if (strcasecmp(token, "Port")==0)
	  GDCsetPort(gdc, atoi(rest));

     else if (strcasecmp(token, "INETD")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetInetdActive(gdc,TRUE);
	    else
		GDCsetInetdActive(gdc, FALSE);
     }
     else if (strcasecmp(token, "MaxLoad")==0)
	  GDCsetMaxLoad(gdc, atof(rest));

     else
     if ((strcasecmp(token, "TimeZone")==0) || (strcasecmp(token, "TZ")==0)) {
	  if ( (strncmp(rest, "-", 1)==0) && isdigit(*(rest+1)) )
	    GDCsetTZ(gdc, -atoi(rest+1));
	  else if ( (strncmp(rest, "+", 1)==0) && isdigit(*(rest+1)) )
		GDCsetTZ(gdc, atoi(rest+1));
	  else if (isdigit(*rest))
		GDCsetTZ(gdc, atoi(rest));
	    else {  /* Mnemonic Timezone */
		GDCsetTZ(gdc,-1);
		GDCsetTimeZone(gdc,rest);		    
		return(success);
	    }
	  if (abs(GDCgetTZ(gdc))<100)
	    GDCsetTZ(gdc,GDCgetTZ(gdc)*100);
     }

	    /*	Now these remaining options are actually VMS specific	*/
     else if (strcasecmp(token, "IsGplus")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetIsGplus(gdc,TRUE);
	    else
		GDCsetIsGplus(gdc, FALSE);
     }
     else if (strcasecmp(token, "IgnoreAll")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetIgnoreAll(gdc,TRUE);
	    else
		GDCsetIgnoreAll(gdc, FALSE);
     }
     else if (strcasecmp(token, "SortDir")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetSortDir(gdc,TRUE);
	    else
		GDCsetSortDir(gdc, FALSE);
     }
     else if (strcasecmp(token, "FTPPort")==0) {
	    if (strcasecmp(rest, "None")==0)
		GDCsetFTPPort(gdc,0);
	    else
		GDCsetFTPPort(gdc, atoi(rest));
     }
     else if (strcasecmp(token, "EXECPort")==0) {
	    if (strcasecmp(rest, "None")==0)
		GDCsetEXECPort(gdc,0);
	    else
		GDCsetEXECPort(gdc, atoi(rest));
     }
     else if (strcasecmp(token, "SRCHPort")==0) {
	    if (strcasecmp(rest, "None")==0)
		GDCsetSRCHPort(gdc,0);
	    else
		GDCsetSRCHPort(gdc, atoi(rest));
     }
     
     else if (strcasecmp(token, "OVERPort")==0) {
	    int	 threshold;
	    char *cp = strchr(rest,',');

	    GDCsetOVERSize(gdc,-1); 
	    if (strcasecmp(rest, "None")==0)
		GDCsetOVERPort(gdc,0);
	    else {
		GDCsetOVERPort(gdc, atoi(rest));
		if (cp) {
		    while (*(++cp)==' ');
		    GDCsetOVERSize(gdc, (threshold=atoi(cp))?threshold:-1);
		}
	    }
     }
     else if (strcasecmp(token, "SortShell")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetSortShell(gdc,TRUE);
	    else
		GDCsetSortShell(gdc, FALSE);
     }
     else if (strcasecmp(token, "SortGREP")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetSortGREP(gdc,TRUE);
	    else
		GDCsetSortGREP(gdc, FALSE);
     }
     else if (strcasecmp(token, "SortCMD1")==0) {
	    if (strcasecmp(rest, "True")==0)
		GDCsetSortCMD1(gdc,TRUE);
	    else
		GDCsetSortCMD1(gdc, FALSE);
     }
     else
     if (strcasecmp(token, "ReadTimeout")==0)
	    GDCsetReadTimeout(gdc, atoi(rest));

     else if (strcasecmp(token, "Console")==0) {
	    int	 opc;
	    int ox;
	    char *cp = strchr(rest,'+');

	    GDCsetOPCOM(gdc, opc = 0);
	    while (rest) {
		if (cp)
		    *cp = '\0';
		while (*rest==' ')
		    rest++;
		ox = strlen(rest) - 1;
		while (*(rest+ox)==' ')
		    *(rest+ox--) = '\0';
		for (ox=0; ox<vms_OPC$M_num; ox++) {
		    if (strcasecmp(rest, vms_OPC$M_TXT[ox])==0)
			GDCsetOPCOM(gdc, opc |= vms_OPC$M_NM[ox]);
		}
		if (cp) {
		    rest = cp+1;
		    cp = strchr(rest,'+');
		}
		else
		    rest = NULL;
	    }
	    if (opc==0)
		GDCsetOPCOM(gdc, opc = (OPC$M_NM_CENTRL | OPC$M_NM_NTWORK));
     }

     else if (strcasecmp(token, "Hidden")==0)
	  GDCsetHiddenPrefix(gdc, rest);

     else if (strcasecmp(token, "Link")==0)
	  GDCsetLinkPrefix(gdc, rest);

     else if (strcasecmp(token, "LookAside")==0)
	  GDCsetLookAside(gdc, rest);

     else if (strcasecmp(token, "DName")==0)
	  GDCsetDName(gdc, rest);

     else if (strcasecmp(token, "DHead")==0)
	  GDCsetDHead(gdc, rest);

     else if (strcasecmp(token, "DFoot")==0)
	  GDCsetDFoot(gdc, rest);

     else if (strcasecmp(token, "ScratchDir")==0)
	  GDCsetScratchDir(gdc, rest);

     else if (strcasecmp(token, "SupportDir")==0)
	  GDCsetSupportDir(gdc, rest);

     else if (strcasecmp(token, "SpawnInit")==0)
          GDCsetSpawnInit(gdc, rest);

     else if (strcasecmp(token, "Restart")==0)
	  GDCsetRestart(gdc, rest);

     else if (strcasecmp(token, "LogTag")==0)
	    GDCsetLogTag(gdc, rest);

     else if (strcasecmp(token, "Rollover")==0) {
	    if (strcasecmp(rest, "Daily")==0)
		GDCsetRollover(gdc,ROLLOVER_DAILY);
	    if (strcasecmp(rest, "Monthly")==0)
		GDCsetRollover(gdc,ROLLOVER_MONTHLY);
	    if (strcasecmp(rest, "Hourly")==0)
		GDCsetRollover(gdc,ROLLOVER_HOURLY);
	    if (strcasecmp(rest, "Annually")==0)
		GDCsetRollover(gdc,ROLLOVER_ANNUALLY);
	    if (strcasecmp(rest, "Weekly")==0)
		GDCsetRollover(gdc,ROLLOVER_WEEKLY);
	    else
		GDCsetRollover(gdc, ROLLOVER_NEVER);
     }
     else if (strcasecmp(token, "CACHE")==0) {
	    if (strncasecmp(rest, "TRUE", 4)==0)
		GDCsetCaching(gdc,TRUE);
	    else
		GDCsetCaching(gdc, FALSE);
	  }
     else if (strcasecmp(token, "DO_CHROOT")==0) {
	    if (strncasecmp(rest, "TRUE", 4)==0)
		GDCsetchroot(gdc,TRUE);
	    else
		GDCsetchroot(gdc, FALSE);
	  }
#endif
     else
	  success = FALSE;
     
     return(success);
}


void
GDCfromFile(gdc, filename)
  GDCobj *gdc;
  char *filename;
{
     FILE *gdcfile;
     char inputline[512];
     char *cp, *linestart, *token, *restofline;
     boolean success;
#ifdef VMS_SERVER
     int bad_cfg=0;
#endif


     if ((gdcfile = fopen(filename, "r")) == (FILE *) NULL) {
#ifndef VMS_SERVER
	  printf("Cannot open file '%s'\n", filename);
	  exit(-1);
#else
	  VMS$fprintf(stderr,"Cannot open file '%s'\n", filename);
	  bad_cfg++;
#endif
     }

     GDCfilePush(gdcfile);

#ifdef VMS_SERVER
    if (!GDCfilenum)	    /*	Only record the topmost config file */
	GDCsetConfig(gdc, fgetname(gdcfile, inputline));
#endif

     while (GDCfileGetline(inputline, sizeof(inputline))) {
	  int inputlen;
	  ZapCRLF(inputline);

	  inputlen = strlen(inputline);	  

	  while ((inputlen > 1) && inputline[inputlen-1] == GDCcontinue) {
	       inputline[inputlen-1] = '\n';
	       if (!GDCfileGetline(inputline + inputlen, 
			      sizeof(inputline)-inputlen))
		    break;
	       ZapCRLF(inputline+inputlen);
	       inputlen += strlen(inputline+inputlen);
	  }

	  /** Eat whitespace **/
 	  linestart= inputline;
	  for ( ; *linestart != '\0' && *linestart <= ' '; linestart++) ;
 
 	  if (*linestart == '#' || *linestart == '\0') /** Ignore comments **/
	       continue;

	  cp = strchr(linestart, ':');
	  if (cp == NULL) {
#ifndef VMS_SERVER
	       fprintf(stderr, "Bad line '%s'\n", inputline);
	       fprintf(stderr, "Line needs a colon\n");
	       exit(-1);
#else
	       VMS$fprintf(stderr, "Bad line '%s'\nLine needs a colon\n", 
						    inputline);
	       bad_cfg++;
	       continue;
#endif
	  }
	  *cp = '\0';
	  token      = linestart;
	  restofline = cp+1;
	  while (*restofline == ' ' || *restofline == '\t')
	       restofline++;
	  
	  success = GDCtokens(gdc, token, restofline);
	  if (!success) {
#ifndef VMS_SERVER
	       fprintf(stderr, "Bad line '%s'\n", inputline);
	       if (strcasecmp(inputline, "ext")==0)
		    fprintf(stderr, "please upgrade your gopherd.conf file\n");
	       exit(-1);
#else
	       VMS$fprintf(stderr, "Bad line '%s'\n", inputline);
	       *cp = ':';
	       bad_cfg++;
	       continue;
#endif
	  }
     }
#ifdef VMS_SERVER
     fclose(gdcfile);
     if (bad_cfg) {
	VMS$fprintf(stderr, "\n\t%d configuration errors detected\n",bad_cfg);
	VMS$fprintf(stderr, "\tplease upgrade your configuration file\n");
				/* exit(-1) from detached server would just */
	if (gdc->RunFromInetd)	/*  loop; so ignore the errors now, have    */
	    gopherd_exit(-1);	/*  the user fix them & restart		    */

	VMS$fprintf(stderr,
		    "\tand restart this detached server with the new cfg\n");
     }
#endif
}


void
GDCpushOtherGDC(gdc, dir, conffile)
  GDCobj *gdc;
  char *dir;
  char *conffile;
{
     String *temp;

     temp = STRnew();
     STRset(temp, dir);
     STApush(gdc->other_dirs, temp);

     STRset(temp, conffile);
     STApush(gdc->other_gdcs, temp);

     STRdestroy(temp);
}


void
GDCpushBlockScript(gdc, blockname, scriptname)
  GDCobj *gdc;
  char   *blockname;
  char   *scriptname;
{
     String *temp;

     temp = STRnew();
     STRset(temp, blockname);
     STApush(gdc->BlkScriptBlocks, temp);

     STRset(temp, scriptname);
     STApush(gdc->BlkScripts, temp);

     STRdestroy(temp);
}
/*
 * Search for otherdir gdcs
 */

char *
GDCfindOtherGDC(gdc, dir)
  GDCobj *gdc;
  char *dir;
{
     int  i;

     for (i=0; i<STAgetTop(gdc->other_dirs); i++)
	  
	  if (strcmp(STAgetText(gdc->other_dirs, i), dir))
	       return(STAgetText(gdc->other_gdcs, i));
     
     return(NULL);
}


boolean
GDCevalDir(gdc, dir)
  GDCobj *gdc;
  char   *dir;
{
     int  i, gdcdirlen;
     char *gdcdir, *conf;
     int dirlen;
     if (dir == NULL)
	  return(FALSE);

     dirlen = strlen(dir);

     for (i=0; i<STAgetTop(gdc->other_dirs); i++) {
	  gdcdir = STAgetText(gdc->other_dirs, i);
	  gdcdirlen = strlen(gdcdir);

/*	  if (gdcdirlen <= dirlen && strncmp(dir, gdcdir, gdcdirlen)==0) {*/
	  if (re_comp(gdcdir))
	       return(TRUE);
	  if (re_exec(dir)) {
	       conf = STAgetText(gdc->other_gdcs, i);
	       GDCfromFile(gdc, conf);
	       Debug("Loading up aux gopherd.conf file %s", conf);
	       Debug("For item %s\n", gdcdir);
	  }
     }
     return(TRUE);
}

#ifndef NO_AUTHENTICATION
AccessResult
GDCCanSearch(gdc, hostname, ipnum, sessions)
  GDCobj *gdc;
  char   *hostname, *ipnum;
  int     sessions;
{
     AccessResult test;

     Debug("Testing %s/", hostname);
     Debug("%s for searching\n", ipnum);

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);

     test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_SEARCH, sessions);

     return(test);
}



AccessResult
GDCCanRead(gdc, hostname, ipnum, sessions)
  GDCobj *gdc;
  char   *hostname, *ipnum;
  int     sessions;
{
     AccessResult test;

     Debug("Testing %s/", hostname);
     Debug("%s for reading\n", ipnum);

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);

     test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_READ, sessions);

     return(test);
}


AccessResult
GDCCanBrowse(gdc, hostname, ipnum, sessions)
  GDCobj *gdc;
  char   *hostname, *ipnum;
  int    sessions;
{
     AccessResult test;

     Debug("Testing %s/", hostname);
     Debug("%s for browsing\n", ipnum);

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);


     test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_BROWSE, sessions);

     return(test);

}


AccessResult
GDCCanFTP(gdc, hostname, ipnum, sessions)
  GDCobj *gdc;
  char   *hostname, *ipnum;
  int     sessions;
{
     AccessResult test;

     Debug("Testing %s/", hostname);
     Debug("%s for ftping\n", ipnum);

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);

     test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_FTP, sessions);

     return(test);

}

#ifdef VMS_SERVER

AccessResult
GDCCanEXEC(gdc, hostname, ipnum, sessions)
  GDCobj *gdc;
  char   *hostname, *ipnum;
  int     sessions;
{
     AccessResult test;

     Debug("Testing %s/", hostname);
     Debug("%s for exec\n", ipnum);

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);

     test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_EXEC, sessions);

     return(test);

}

AccessResult
GDCcanAccess(gdc, hostname, ipnum, sessions, access)
  GDCobj *gdc;
  char *hostname, *ipnum;
  int sessions, access;
{

     if (gdc->Securityon == FALSE)
	  return(SITE_OK);

	 switch(access) {
     case ACC_READ:   return(GDCCanRead(gdc, hostname, ipnum, sessions));
     case ACC_BROWSE: return(GDCCanBrowse(gdc, hostname, ipnum, sessions));
     case ACC_SEARCH: return(GDCCanSearch(gdc, hostname, ipnum, sessions));
     case ACC_FTP:    return(GDCCanFTP(gdc, hostname, ipnum, sessions));
     case ACC_EXEC:   return(GDCCanEXEC(gdc, hostname, ipnum, sessions));
     default:         return(SITE_OK);
	 }
}
#endif

char *
GDCauthType(gdc, item)
  GDCobj *gdc;
  char         *item;
{
     if (item == NULL)
	  return(NULL);

     return(AUTHITEMSfindType(gdc->Authitems, item));
}

AUTHresult
GDCvalidate(gdc, type, username, password, hostname, hostip)
  GDCobj *gdc;
  char *type;
  char *username, *password, *hostname, *hostip;
{
     if (type == NULL)
	  return(AUTHRES_OK);	/* If no auth method then it's okay. */

     return(AUTHAvalidate(gdc->Authroutines,type, username, password,
			  hostname, hostip));
}

/*
 * Find the ask block for an authenticated item..
 */

char **
GDCauthAsk(gdc, path)
  GDCobj *gdc;
  char   *path;
{
     AUTH *auth;

     Debug("Looking for %s\n", path);

     auth = AUTHITEMSfindAUTH(gdc->Authitems, gdc->Authroutines, path);

     if (auth == NULL) {
	  Debug("Couldn't find %s\n", path);
	  return(NULL);
     }
     return(AUTHgetAskFcn(auth)(path, "", ""));
}

#endif

/** Is this file ignored? **/
boolean
GDCignore(gdc, fname)
  GDCobj *gdc;
  char *fname;
{
     Extobj *ext;

     ext = EXnew();

     if (EXAsearch(gdc->Extensions, ext, fname, EXT_IGNORE)) {
	  EXdestroy(ext);
	  return(TRUE);
     } else {	  
	  EXdestroy(ext);
	  return(FALSE);
     }
}


boolean 
GDCViewExtension(gdc, fext, extin)
  GDCobj *gdc;
  char *fext;
  Extobj **extin;
{
     Extobj *ext;
     
     ext = EXnew();

     if (EXAsearch(gdc->Extensions, ext, fext, EXT_VIEW)) {
	  *extin = ext;
	  return(TRUE);
     }
     else {
	  EXdestroy(ext);
	  return(FALSE);
     }

}

boolean 
GDCBlockExtension(gdc, fext, ext)
  GDCobj *gdc;
  char *fext;
  Extobj *ext;
{

     if (EXAsearch(gdc->Extensions, ext, fext, EXT_BLOCK)) {
	  return(TRUE);
     }
     else
	  return(FALSE);
}



void
GDCaddFileSep(gdc, expr)
  GDCobj *gdc;
  char   *expr;
{
     String *temp;

     temp = STRnew();
     if (temp == NULL)
	  return;
     
     STRset(temp, expr);
     STApush(gdc->FileSeparators, temp);
     STRdestroy(temp);
}

static boolean
GDCisBlank(string)
  char *string;
{
     if (string == NULL || 
	 strncmp(string, "blank", 5) == 0)
	  return TRUE;
     else
	  return(FALSE);
}

void
GDCintegrityCheck(gdc)
  GDCobj *gdc;
{
#ifdef VMS_SERVER
#ifdef fprintf
#define hold_fprintf fprintf
#undef fprintf
#endif
#define fprintf	VMS$fprintf
#endif
     if (GDCisBlank(GDCgetSite(gdc)))
	  fprintf(stderr, "** Please set the Site: line in your configuration file!\n");

     if (GDCisBlank(GDCgetOrg(gdc)))
	  fprintf(stderr, "** Please set the Org: line in your configuration file!\n");

     if (GDCisBlank(GDCgetLoc(gdc)))
	  fprintf(stderr, "** Please set the Loc: line in your configuration file!\n");

     if (GDCisBlank(GDCgetAbstract(gdc)))
	  fprintf(stderr, "** Please set the Abstract: line in your configuration file!\n");

     if (GDCisBlank(GDCgetAdmin(gdc)))
	  fprintf(stderr, "** Please set the Admin: line in your configuration file!\n");

     if (GDCisBlank(GDCgetAdminEmail(gdc)))
	  fprintf(stderr, "** Please set the AdminEmail: line in configuration file!\n");
#ifdef VMS_SERVER
#ifdef hold_fprintf
#define fprintf hold_fprintf
#undef hold_fprintf
#endif
#endif

}


#ifdef VMS_SERVER
	    /** VMS version supports logfile rollover, so we can't just pick
		up a string and return it like the UNIX macro does -- we need
		real code which builds the current timestamped value and
		returns it. **/
char
*GDCgetLogfile(gdc)
    GDCobj *gdc;
{
    static
	char    rollover[256];
    char    insert[11];
    time_t  Now;
    char    *mx;
    int	    i;
    char    cdate[26];
    extern
      char  log_alq[10];
    extern
      char  log_deq[10];
    static
	char *DaysofWeek="SunMonTueWedThuFriSat";
    static
	char *Months = "JanFebMarAprMayJunJulAugSepOctNovDec";
#define	DOW	(cdate+0)
#define MMM     (cdate+4)
#define DD      (cdate+8)
#define HH	(cdate+11)
#define YYYY    (cdate+20)

    if (!gdc)
	return(NULL);
    if (!(mx=STRget(gdc->Logfile)))
	return(NULL);
    if (!strlen(mx))
	return(NULL);
    time(&Now);
    strcpy(cdate,(char *)ctime(&Now));
    cdate[3] = cdate[7]= cdate[10]= cdate[13] = cdate[24]= '\0';
    if (GDCgetRollover(gdc)==ROLLOVER_WEEKLY) {
	mx=strstr(DaysofWeek,DOW);
	if (i = ((mx - DaysofWeek)/3)) {
	    Now -= (i * (24 * 60 * 60));
	    strcpy(cdate,(char *)ctime(&Now));
	    cdate[3] = cdate[7]= cdate[10]= cdate[13] = cdate[24]= '\0';
	}
    }
    if (cdate[8]==' ')
	cdate[8] = '0';
    mx=strstr(Months,MMM);
    i = mx - Months;
    sprintf(insert,"%s%02d%s%s",YYYY,(i/3)+1,DD,HH);
    switch(GDCgetRollover(gdc))
    {
    case ROLLOVER_ANNUALLY:	insert[4] = '\0';   i = ALQ_ANNUAL; break;
    case ROLLOVER_MONTHLY:	insert[6] = '\0';   i = ALQ_MONTH;  break;
    case ROLLOVER_WEEKLY:	insert[8] = '\0';   i = ALQ_WEEK;   break;
    case ROLLOVER_DAILY:	insert[8] = '\0';   i = ALQ_DAY;    break;
    case ROLLOVER_HOURLY:	insert[10]= '\0';   i = ALQ_HOUR;   break;
    case ROLLOVER_NEVER:    
    default:			insert[0] = '\0';   i = ALQ_NEVER;
    }
    sprintf(log_alq,"alq=%d",i);
    sprintf(log_deq,"deq=%d",i);
    strcpy(rollover,STRget(gdc->Logfile));
    strcat(rollover,insert);
    return(rollover);
}

void
GDCfromLink(gdc, fio, sep)
  GDCobj *gdc;
  FileIO *fio;
  char sep;
{
     char inputline[512];
     char *cp, *linestart, *token, *restofline;
     int inputlen;
     boolean success;
     int position;

     position = FIOtell(fio);
     while (inputlen = FIOreadlinezap(fio, inputline, sizeof(inputline))) {
	  while ((inputlen > 1) && inputline[inputlen-1] == GDCcontinue) {
	       inputline[inputlen-1] = '\n';
	       if (inputlen += FIOreadlinezap(fio, inputline + inputlen, 
					  sizeof(inputline)-inputlen))
		    break;
	  }
	  ZapCRLF(inputline);
	  inputlen = strlen(inputline);

	  /** Eat whitespace **/
 	  linestart= inputline;
	  for ( ; *linestart != '\0' && *linestart <= ' '; linestart++) ;
 
 	  if (*linestart == '#' || *linestart == '\0') /** Ignore comments **/
	       continue;

	  if (!(cp = strchr(linestart, sep))) {
	      FIOseek(fio, position);	/*  Back up to beginning of this line */
	      return;
	  }
	  *cp = '\0';
	  token      = linestart;
	  restofline = cp+1;
	  while (*restofline == ' ' || *restofline == '\t')
	       restofline++;
	  
	  success = GDCtokens(gdc, token, restofline);
	  if (!success) {
	      FIOseek(fio, position);	/*  Back up to beginning of this line */
	      return;
	  }
	  position = FIOtell(fio);
     }
}

GDCobj *
GDCcpy(orig)
  GDCobj *orig;
{
     GDCobj *dest = GDCnew();

     EXAcpy(dest->Extensions, orig->Extensions);

#ifndef NO_AUTHENTICATION
     SiteArrayCopy(dest->Sites, orig->Sites);
     dest->Securityon   = orig->Securityon;

     AUTHAcpy(dest->Authroutines, orig->Authroutines);
     AUTHITEMScpy(dest->Authitems, orig->Authitems);
     STRcpy(dest->Serverpw, orig->Serverpw);
#endif

     dest->RunFromInetd = orig->RunFromInetd;
     dest->Caching      = orig->Caching;
     dest->Cachetime    = orig->Cachetime;                 

     STRcpy(dest->Logfile, orig->Logfile);
     STRcpy(dest->Errorfile, orig->Errorfile);
     STRcpy(dest->DataDir, orig->DataDir);
     STRcpy(dest->Hostname, orig->Hostname);
     dest->Port         = orig->Port;
     dest->chroot       = orig->chroot;
     dest->Defaccess    = orig->Defaccess;
     STRcpy(dest->BummerMsg, orig->BummerMsg);
     
     STRcpy(dest->Admin, orig->Admin);
     STRcpy(dest->AdminEmail, orig->AdminEmail);
     STRcpy(dest->Abstract, orig->Abstract);

     STRcpy(dest->Site, orig->Site);
     STRcpy(dest->Org, orig->Org);
     STRcpy(dest->Geog, orig->Geog);
     STRcpy(dest->Loc, orig->Loc);
     STRcpy(dest->Lang, orig->Lang);
     dest->TZ           = orig->TZ;
     dest->VeronicaIndex = orig->VeronicaIndex;

     STRcpy(dest->Tixfile, orig->Tixfile);

     STAcpy(dest->other_dirs, orig->other_dirs);
     STAcpy(dest->other_gdcs, orig->other_gdcs);

     STAcpy(dest->FileSeparators, orig->FileSeparators);

     STAcpy(dest->BlkScriptBlocks, orig->BlkScriptBlocks);
     STAcpy(dest->BlkScripts, orig->BlkScripts);

     dest->MaxConnections  = orig->MaxConnections;

     STRcpy(dest->TimeZoneText, orig->TimeZoneText);
     dest->MaxLoad      = orig->MaxLoad;

     /* New VMS configuration options */
     dest->IsGplus      = orig->IsGplus;
     dest->IgnoreAll    = orig->IgnoreAll;
     dest->SortDir      = orig->SortDir;
     dest->FTPPort      = orig->FTPPort;
     dest->EXECPort     = orig->EXECPort;
     dest->SRCHPort     = orig->SRCHPort;
     dest->OVERPort     = orig->OVERPort;
     dest->OVERSize     = orig->OVERSize;
     dest->SortShell    = orig->SortShell;
     dest->SortGREP     = orig->SortGREP;
     dest->SortCMD1     = orig->SortCMD1;
     dest->ReadTimeout  = orig->ReadTimeout;
     dest->OPCOM        = orig->OPCOM;
     STRcpy(dest->Hidden_Prefix, orig->Hidden_Prefix);
     STRcpy(dest->Link_Prefix, orig->Link_Prefix);
     STRcpy(dest->LookAside, orig->LookAside);
     STRcpy(dest->DName, orig->DName);
     STRcpy(dest->DHead, orig->DHead);
     STRcpy(dest->DFoot, orig->DFoot);
     STRcpy(dest->Scratch_Dir, orig->Scratch_Dir);
     STRcpy(dest->Support_Dir, orig->Support_Dir);
     STRcpy(dest->Spawn_Init, orig->Spawn_Init);
     STRcpy(dest->RestartLnm, orig->RestartLnm);
     STRcpy(dest->ConfigFile, orig->ConfigFile);
     STRcpy(dest->LogFileTag, orig->LogFileTag);
     dest->rollover     = orig->rollover;

     return(dest);
}  
#endif
.
Response: text/plain
Original URLgopher://bitreich.org/0/gopher2007/2007-gopher-mirror/gop...
Content-Typetext/plain; charset=utf-8