SMOLNET PORTAL home about changes
/********************************************************************
 * wilkinson
 * 3.75VMS
 * 1995/09/25 11:20 
 * gopher_root1:[gopher.g2.vms2_13.object]GSgopherobj.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: GSgopherobj.c
 * Implement gopher directory functions.
 *********************************************************************
 * Revision History:
 * GSgopherobj.c,v
 * Revision 3.75VMS 1995/09/25 11:20    wilkinson
 * Consolodate VMS/Unix source code for server as well as client
 * - add create/modify date display flag (default is modify)
 * 
 * Revision 3.75  1995/02/22  05:30:25  lindner
 * reintroduce gsinit to gsnew
 *
 * Revision 3.74  1995/02/17  18:29:37  lindner
 * Abstract display support
 *
 * Revision 3.73  1995/02/16  22:32:44  lindner
 * HTML icon support
 *
 * Revision 3.72  1995/02/07  07:12:55  lindner
 * oops!
 *
 * Revision 3.71  1995/02/07  07:06:58  lindner
 * performance fixes
 *
 * Revision 3.70  1995/02/06  22:13:52  lindner
 * Performance fixes
 *
 * Revision 3.69  1995/02/02  17:14:46  lindner
 * Fix for memory leaks and accesses
 *
 * Revision 3.68  1995/02/01  22:08:02  lindner
 * Put back GSaddmerge
 *
 * Revision 3.67  1995/02/01  21:44:41  lindner
 * Remove GSmerge fcns, Add message/rfc822 and application/gopher to GSisText
 *
 * Revision 3.66  1994/12/15  17:30:49  lindner
 * Allow multi-line abstracts in link files, Fix for ftp URL generation
 *
 * Revision 3.65  1994/12/05  22:39:55  lindner
 * Fix Name and Path settings in GSfromURL
 *
 * Revision 3.64  1994/11/17  06:33:58  lindner
 * Fixes for VMS internationalization
 *
 * Revision 3.63  1994/11/13  06:30:48  lindner
 * Better GSfromURL
 *
 * Revision 3.62  1994/10/24  22:15:53  lindner
 * Add PDF type
 *
 * Revision 3.61  1994/10/18  21:37:03  lindner
 * Make sure buf is set
 *
 * Revision 3.60  1994/10/13  05:27:18  lindner
 * Compiler complaint fixes
 *
 * Revision 3.59  1994/09/29  19:26:45  lindner
 * Fix for debug messages and NULL values
 *
 * Revision 3.58  1994/08/19  16:13:09  lindner
 * mktime() etc, fixes from Alan Coopersmith
 *
 * Revision 3.57  1994/08/03  20:03:44  lindner
 * Fix for adding ask stuff
 *
 * Revision 3.56  1994/07/31  04:57:20  lindner
 * Fix for .names messup...
 *
 * Revision 3.55  1994/07/21  22:29:18  lindner
 * misc hacks
 *
 * Revision 3.54  1994/07/06  19:21:34  lindner
 * use strftime to get localized date and time
 *
 * Revision 3.53  1994/06/29  06:51:21  lindner
 * ifdef GINTERNATIONAL, use strftime to localize the date string
 * returned by GSgetModDate()
 *
 * in GSplusfromNet(), if there's anything in the ADMIN block other than
 * Admin, ModDate, or TTL entries, save it for the client's showinfo()
 * Server Information
 *
 * Move GSfromURL() return code definitions to header so other files can
 * call it
 *
 * Fix GStoNetURL() & HTML display of directories
 *
 * Round sizes in GSaddView
 *
 * Make GSfromURL() lie and say all http: is HTML so the client
 * passes it off to a http-speaking program anyway (assuming
 * all HTML viewers speak http, that is)  (Coopersmith)
 *
 * Revision 3.52  1994/06/29  05:45:56  lindner
 * Mods to pump tickets to the net
 *
 * Revision 3.51  1994/05/25  20:57:46  lindner
 * Remove unused var
 *
 * Revision 3.50  1994/05/06  02:29:46  lindner
 * Fix from Robert Beckett for binhex files
 *
 * Revision 3.49  1994/04/25  03:36:58  lindner
 * Modifications for Debug() and mismatched NULL arguments, added Debugmsg
 *
 * Revision 3.48  1994/04/25  02:26:30  lindner
 * Fix for url problems
 *
 * Revision 3.47  1994/04/25  02:16:31  lindner
 * Fix for |= mistype
 *
 * Revision 3.46  1994/04/22  06:41:35  lindner
 * More pacbell hacks for Domain=
 *
 * Revision 3.45  1994/04/22  03:26:15  lindner
 * pacbell hack
 *
 * Revision 3.44  1994/04/19  14:32:29  lindner
 * Change GD and GSfromLink routines to use FIO
 *
 * Revision 3.43  1994/04/13  19:16:17  lindner
 * Fix for ASK block bug
 *
 * Revision 3.42  1994/04/08  20:05:52  lindner
 * gcc -Wall fixes
 *
 * Revision 3.41  1994/04/07  17:27:09  lindner
 * Fix for pyramids
 *
 * Revision 3.40  1994/04/01  04:38:06  lindner
 * Fix for conditional macros
 *
 * Revision 3.39  1994/03/31  22:48:55  lindner
 * fix for Hgopher and ask requests
 *
 * Revision 3.38  1994/03/31  21:05:11  lindner
 * More debug code, and fix for Socket return values
 *
 * Revision 3.37  1994/03/17  04:37:41  lindner
 * Fix for spinning clients
 *
 * Revision 3.36  1994/03/08  15:56:17  lindner
 * gcc -Wall fixes
 *
 * Revision 3.35  1994/03/04  17:59:16  lindner
 * More URL fixes from A.C.
 *
 * Revision 3.34  1994/02/20  16:27:44  lindner
 * Remove dead code for GDtoNetHTML
 *
 * Revision 3.33  1994/01/25  06:51:21  lindner
 * Many additions for URL/HTML support, removed insidious dot at end of host..
 *
 * Revision 3.32  1994/01/10  03:28:12  lindner
 * Allow bangs in Domain= lines, to negate classes of hosts
 *
 * Revision 3.31  1994/01/06  06:10:53  lindner
 * fix for cvs entries
 *
 *
 * Revision 3.30  1994/01/06  05:43:46 lindner
 * Fix for type=1 and ask blocks.
 *
 * Revision 3.29  1993/12/27  16:25:39 lindner
 * Add fix for appending a period to domain names
 *
 * Revision 3.28 1993/11/29  01:07:23 lindner
 * In GSfromLink(), disable handling of "Domain_pat:" for VMS to keep the
 * compiler from complaining about the lack of re_comp() and re_exec().
 * "Domain_pat:" handling isn't needed in the client anyway.  (Wolfe)
 *
 * Add "AddInfo" argument to GStoLink().  This option decides whether to
 * add the Admin and ModDate fields to the basic information about the
 * gopher object.  Bookmarks should not include them, but requested
 * technical information ('=' and '^') should.  This change requires
 * changes to gopher/gopher.c and object/GDgopherdir.c; see below.
 * (Macrides)
 *
 * Revision 3.27  1993/11/05  07:24:46 lindner
 * Allow Type=1? etc.. in .link files
 *
 * Revision 3.26  1993/11/04  04:50:50 lindner
 * Add quotes around HREFs
 *
 * Revision 3.25  1993/11/04  02:10:47 lindner
 * Added Domain_pat= line to check for a regexp domains
 *
 * Revision 3.24  1993/11/03  15:35:40 lindner
 * Fix problems with bookmarks of gopher+ items
 *
 * Revision 3.23  1993/11/02  06:15:24 lindner
 * HTML additions
 *
 * Revision 3.22  1993/10/26  17:49:50 lindner
 * Fix for NULL view in GSisText()
 *
 * Revision 3.21  1993/10/22  20:02:33 lindner
 * Add Movie (;) and Info (i) type support
 *
 * Revision 3.20  1993/09/18  04:44:41 lindner
 * Additions to fix caching of Multiple view items
 *
 * Revision 3.19  1993/09/11  06:41:08 lindner
 * Fix for picky compilers
 *
 * Revision 3.18  1993/09/11  06:32:52  lindner
 * URL support
 *
 * Revision 3.17  1993/09/01  21:51:59  lindner
 * Remove GSnewSet, better initialization in GSnew()
 *
 * Revision 3.16  1993/08/19  20:24:11  lindner
 * Mitra's Debug patch
 *
 * Revision 3.15  1993/07/29  20:02:55  lindner
 * Removed dead variables
 *
 * Revision 3.14  1993/07/27  20:17:25  lindner
 * Fix improper bracketed debug output
 *
 * Revision 3.13  1993/07/27  05:30:23  lindner
 * Mondo Debug overhaul from Mitra
 *
 * Revision 3.12  1993/07/27  00:30:09  lindner
 * plus patch from Mitra
 *
 * Revision 3.11  1993/07/23  04:50:17  lindner
 * Mods to allow abstract/admin setting in .names files
 *
 * Revision 3.10  1993/07/21  03:31:09  lindner
 * Askdata can be stored locally, plus GSfromLink doesn't core dump with 
 * mangled items
 *
 * Revision 3.9  1993/07/14  20:37:11  lindner
 * Negative numbering patches
 *
 * Revision 3.8  1993/07/07  19:30:12  lindner
 * Split off socket based fcns to Sockets.c
 *
 * Revision 3.7  1993/07/06  20:22:40  lindner
 * Added listener and accept fcns
 *
 * Revision 3.6  1993/06/22  06:07:14  lindner
 * Added Domain= hacks..
 *
 * Revision 3.5  1993/04/15  17:44:52  lindner
 * Fixed link processing, mods from Mitra
 *
 * Revision 3.4  1993/03/26  19:50:46  lindner
 * Mitra fixes for better/clearer fromNet code
 *
 * Revision 3.3  1993/03/24  17:05:33  lindner
 * Additions for Localfile for each GopherObj
 *
 * Revision 3.2  1993/03/18  22:15:50  lindner
 * filtering, memory leaks fixed, GSmerge problems
 *
 *********************************************************************/


#ifdef VMS_SERVER
#define  GSGOPHEROBJ_C
#include "GopherD.h"
#endif

#include "GSgopherobj.h"

#if defined(mips) && defined(ultrix)   /*** Gross hack, yuck! ***/
#define _SIZE_T
#endif

#include "String.h"
#include "STRstring.h"
#include <stdio.h>
#include "compatible.h"
#include <errno.h>
#include "Malloc.h"
#include "Sockets.h"
#include "util.h"
#include "Debug.h"
#include "fileio.h"
#include <time.h>

#ifdef pyr
unsigned long errno;
#endif

/*
 * Make a new gopherobj...  Should reuse destroyed GopherObjs...
 */

GopherObj *
GSnew()
{
     GopherObj *temp;

     temp = (GopherObj *) malloc(sizeof(GopherObj));
     temp->Selstr    = STRnew();
     temp->Title     = STRnew();
     temp->Host      = STRnew();
     temp->Localfile = STRnew();
     temp->Localview = STRnew();
     temp->gplus     = NULL;
     temp->isask     = FALSE;
     temp->url       = NULL;

#ifdef VMS_SERVER
     temp->date_cr   = FALSE;
     temp->lookaside = FALSE;
     temp->Access = NULL;
     temp->Defaccess=ACC_FULL;
     temp->Head   = STRnew();
     temp->Foot   = STRnew();
     temp->RHead  = STRnew();
     temp->RFoot  = STRnew();
#endif

     GSinit(temp);

     return(temp);
}


/*
 * Initialize the gopherplus components of the object
 * (Only called for a gplus item)
 */

void
GSplusnew(gs)
  GopherObj *gs;
{
     if (gs->gplus == NULL) {
	  gs->gplus = (GplusObj *) malloc(sizeof(GplusObj));
     }
     
     gs->gplus->Admin    = STRnew();
     gs->gplus->ModDate  = STRnew();

     gs->gplus->Views    = VIAnew(10);
     gs->gplus->OtherBlocks = BLAnew(5);
     gs->gplus->Askdata  = NULL;
}


/*** Destroy gopher object ***/

void
GSdestroy(gs)
  GopherObj *gs;
{
     STRdestroy(gs->Selstr);
     STRdestroy(gs->Title);
     STRdestroy(gs->Host);
     if (GSgetLocalFile(gs) != NULL)
	  unlink(GSgetLocalFile(gs));
     STRdestroy(gs->Localfile);
     STRdestroy(gs->Localview);

     GSplusdestroy(gs);
     if (gs->url != NULL)
	  URLdestroy(gs->url);

#ifdef VMS_SERVER
     if (gs->Access != NULL)
	SiteArrDestroy(gs->Access);
     STRdestroy(gs->Head);
     STRdestroy(gs->Foot);
     STRdestroy(gs->RHead);
     STRdestroy(gs->RFoot);
#endif

     free(gs);
}
     

/*** Destroy Gopher+ attributes ***/

void
GSplusdestroy(gs)
  GopherObj *gs;
{
     if (gs->gplus != NULL) {
	  STRdestroy(gs->gplus->Admin);
	  STRdestroy(gs->gplus->ModDate);
	  VIAdestroy(gs->gplus->Views);
	  BLAdestroy(gs->gplus->OtherBlocks);

	  (void)GSsetAskdata(gs, NULL);

	  free(gs->gplus);
	  gs->gplus = NULL;
     }
}


/*
 * Clear out all the crud 
 */

void
GSinit(gs)
  GopherObj *gs;
{
     GSsetType(gs, '\0');
     
     STRinit(gs->Title);
     STRinit(gs->Selstr);
     STRinit(gs->Host);
     if (GSgetLocalFile(gs) != NULL)
	  unlink(GSgetLocalFile(gs));
     STRinit(gs->Localfile);
     STRinit(gs->Localview);

     gs->ttl = -1;
     gs->iPort = 0;
     GSsetNum(gs, 0);
     GSsetWeight(gs, 0);

     GSsetGplus(gs,FALSE);  /** Default is no gplus **/
     GSsetAsk(gs, FALSE);

     if (gs->url != NULL) 
	  URLdestroy(gs->url);
     gs->url = NULL;
     GSplusInit(gs);

#ifdef VMS_SERVER
     gs->date_cr = FALSE;
     gs->lookaside = FALSE;
     if (gs->Access != NULL)
	SiteArrDestroy(gs->Access);
     gs->Access = NULL;
     gs->Defaccess = ACC_FULL;
     STRinit(gs->Head);
     STRinit(gs->Foot);
     STRinit(gs->RHead);
     STRinit(gs->RFoot);
#endif

}


/*
 * Clear out gopher+ crud if it exists
 */

void
GSplusInit(gs)
  GopherObj *gs;
{
     if (gs->gplus != NULL) {
	  STRinit(gs->gplus->Admin);
	  STRinit(gs->gplus->ModDate);
	  VIAinit(gs->gplus->Views);
	  STAinit(gs->gplus->OtherBlocks);

     }
}


/* 
 * Set a URL for the gopherobject..
 */

void
GSsetURL(gs, url)
  GopherObj *gs;
  char      *url;
{
     if (gs->url == NULL)
	  gs->url = URLnew();
     
     URLset(gs->url, url);
}

int
GSgetNumBlocks(gs)
  GopherObj *gs;
{
     if (gs->gplus == NULL)
	  return(-1);

     return(BLAgetTop(gs->gplus->OtherBlocks));
}

Blockobj*
GSgetBlock(gs, bnum)
  GopherObj *gs;
  int bnum;
{
     if (gs->gplus == NULL)
	  return(NULL);

     return(BLAgetEntry(gs->gplus->OtherBlocks, bnum));
}

/*
 * Return the number of views if item is gopher+
 */

int
GSgetNumViews(gs)
  GopherObj *gs;
{
     if (gs->gplus == NULL)
	  return(0);
     
     return(VIAgetTop(gs->gplus->Views));
}

/*
 * Find out what a specific view is
 */

VIewobj *
GSgetView(gs, viewnum)
  GopherObj *gs;
  int        viewnum;
{
     if (gs->gplus == NULL)
	  return(NULL);
     
     return(VIAgetEntry(gs->gplus->Views, viewnum));
}


char *
GSgetURL(gs)
  GopherObj *gs;
{
     if (gs->url == NULL) {
	  gs->url = URLnew();
	  URLfromGS(gs->url, gs);
     }
     
     return(URLget(gs->url));
}


char *
GSgetURLhtml(gs)
  GopherObj *gs;
{
     char *cp = GSgetURL(gs);
     char *cp2;
     int   views;

     if ((cp != NULL) && (strncmp(cp, "gopher://&;, 9) == 0)) {
	  if ( GSgplusInited(gs) ) {
	       for (views=0; views< GSgetNumViews(gs); views++) {
		    if (!(strncasecmp(VIgetType(GSgetView(gs,views)),
				      "text/html", 9))) {

			 cp = strdup(cp);

			 /** find the type character **/
			 cp2 = strchr(cp+10, '/');
			 if (cp2 != NULL) {
			      *(++cp2) = 'h';
			      *(++cp2) = 'h';
     			 }
		     }
	       }
	  }
     }

     return cp;

}

char *
GStoNetURL(gs,url)
  GopherObj *gs;
  char *url;
{
     char *path, *ftphost, *ftppath;

     *url = '\0';
     path  = GSgetPath(gs);

     /** This is a server specific hack for now..  **/

     if ( (path != NULL) && (strncmp(path, "ftp:", 4) == 0) ) {
	  ftphost = path+4;
	  ftppath = strchr(ftphost, '@');

	  if (ftppath != NULL)
	  {
	       sprintf(url, "ftp://%.*s/", ftppath-ftphost, ftphost);
	       
	       ftppath++;

	       /*** The rest is the file/path ***/
	       if (*ftppath == '/')
		    ftppath++;

	       strcat(url, ftppath);
	       return(url);
	  }
     }
     strcpy(url,GSgetURLhtml(gs));
     return(url);
}


void
GSsetstringAsk(gs,ask)
  GopherObj *gs;
  char *ask;
{
     GSsetBlock(gs,"ASK",ask,TRUE);
     GSsetAsk(gs,TRUE);
}


Blockobj*
GSfindBlock(gs, blockname)
  GopherObj *gs;
  char *blockname;
{
     int x;
     
     x = BLAsearch(GSgetOtherBlocks(gs), blockname);

     Debug("GSfind num is %d\n", x);
     if (x < 0)
	  return(NULL);
     else
	  return(GSgetBlock(gs, x));
}

void
GSsetBlock(gs, blockname, text, appendIfPoss)
  GopherObj *gs;
  char *blockname;
  char *text;
  boolean appendIfPoss;
{
     Blockobj *bl = NULL;
     boolean newbl = FALSE;

     Debug("GSsetBlock:%s;",blockname);
     Debug("%s\r\n", text);

     if (appendIfPoss)
          bl = GSfindBlock(gs, blockname);

     if (bl == NULL) {
          newbl = TRUE;
          bl = BLnew();
     }

     BLsetName(bl, blockname);
     BLaddText(bl, text);
     
#ifdef DEBUGGING
     if (DEBUG)
	  BLtoNet(bl, fileno(stderr), TRUE);
#endif
     if (gs->gplus == NULL)
	  GSplusnew(gs);
     
     if (newbl) {
          BLApush(gs->gplus->OtherBlocks, bl);
          BLdestroy(bl);
     }

}

/*
 * Set the Askdata, destroy if necessary..
 */

char **
GSsetAskdata(gs, askdata)
  GopherObj *gs;
  char **askdata;
{
     if (!GSgplusInited(gs))
	  return(NULL);

     /** Destroy data if necessary **/
     if (gs->gplus->Askdata != NULL) {
	  int i=0;
	  char **Askmoo = gs->gplus->Askdata;
	  
	  while (Askmoo[i] != NULL) {
	       free(Askmoo[i++]);
	  }
	  free(Askmoo);
     }

     gs->gplus->Askdata = askdata;

     return(askdata);

}


/*
 * Set the administrator line as defined in Gopher+ protocol
 * Name <email>
 */


/* Converts the gopher+ moddate from <YYYYMMDDHHMMSS> to a struct tm ptr */
struct tm *
GSgetModDateTM(gs)
     GopherObj *gs ;
{
     static struct tm time;
     char *cp;
#ifndef NO_MKTIME
     time_t converted;
#endif

     if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) )
          return NULL;

     cp = strchr(STRget(gs->gplus->ModDate), '<');
     if (cp == NULL)
          return NULL;

#define ASCII_TO_INT(a)     (a - '0')
     /* Should really do some sanity checking on the input here */
     time.tm_year = ((ASCII_TO_INT(cp[1]) * 1000) + (ASCII_TO_INT(cp[2]) * 100)
                   + (ASCII_TO_INT(cp[3]) * 10) + ASCII_TO_INT(cp[4]) ) - 1900;
     time.tm_mon  = (ASCII_TO_INT(cp[5])  * 10) + ASCII_TO_INT(cp[6]) - 1;
     time.tm_mday = (ASCII_TO_INT(cp[7])  * 10) + ASCII_TO_INT(cp[8]);
     time.tm_hour = (ASCII_TO_INT(cp[9])  * 10) + ASCII_TO_INT(cp[10]);
     time.tm_min  = (ASCII_TO_INT(cp[11]) * 10) + ASCII_TO_INT(cp[12]);
     time.tm_sec  = (ASCII_TO_INT(cp[13]) * 10) + ASCII_TO_INT(cp[14]);
     time.tm_isdst = -1;      /* make the system figure it out */

#ifndef NO_MKTIME
     /* if mktime() is present, let it do sanity checking and day of
	  week setting for us */
     converted = mktime(&time);
     if (converted != -1)
	  return (localtime(&converted));
#endif
     return (&time);

}


#if defined(__VMS) || (defined(GINTERNATIONAL) && !defined(NO_STRFTIME))
/* In internationalized environments, convert the ModDate string to the
 *  native language when it's requested.
 * If i18n is not on, the macro from GSgopherobj.h is used.
 *  (The i18n definition's are turned on or off in Locale.h)
 */
char *
GSgetModDate(gs)
  GopherObj *gs;
{
     char	    dateBuf[256];
     char	    *datetime;
     struct tm	    *modTime;

     if ( (gs->gplus == NULL) || (STRget(gs->gplus->ModDate) == NULL) )
          return NULL;

     modTime = GSgetModDateTM(gs);

     if ( (modTime != NULL) &&
	  strftime(dateBuf, sizeof(dateBuf) - 16, "%c ", modTime) )
     {
          datetime = strchr(STRget(gs->gplus->ModDate), '<');
          if (datetime != NULL)
               strncat(dateBuf, datetime, 16);

          STRset(gs->gplus->ModDate, dateBuf);
     }

     return STRget(gs->gplus->ModDate);
}
#endif /* GINTERNATIONAL */



void
GSsetAdmin(gs, admin)
  GopherObj *gs;
  char *admin;
{
     if (gs->gplus == NULL)
	  GSplusnew(gs);

     STRset(gs->gplus->Admin, admin);
}     



void
GSsetModDate(gs, str)
  GopherObj *gs;
  char *str;
{
     if (gs->gplus == NULL) GSplusnew(gs);
     STRset(gs->gplus->ModDate, str);
}     



/** Add a view for a specific gopherobj **/

void
GSaddView(gs, view, language, estsize)
  GopherObj *gs;
  char *view;
  char *language;
  int estsize;
{
     char tmpstr[64];
     VIewobj *temp;

     if (estsize > 960)
	  sprintf(tmpstr, "%dk", (estsize + 512)/1024);
     else
      	  sprintf(tmpstr, ".%dk", ((estsize+64)*10)/1024);

     temp = VInew();

     VIsetType(temp, view);
     VIsetLang(temp, language);
     VIsetSize(temp, tmpstr);

     VIApush(gs->gplus->Views, temp);
     VIdestroy(temp);
}


void
GSaddBlock(gs, Blockname, file)
  GopherObj *gs;
  char *Blockname;
  char *file;
{
     Blockobj *bl;

     bl = BLnew();
     BLsetName(bl, Blockname);
     BLsetFile(bl, file);
     ;
     if (gs->gplus == NULL)
	  GSplusnew(gs);

     BLApush(gs->gplus->OtherBlocks, bl);

     BLdestroy(bl);
}



/*
 * Send a gopher reference into an fd
 */

void
GStoNet(gs, sockfd, fmt, ticket)
  GopherObj *gs;
  int       sockfd;
  GSformat  fmt;
  char      *ticket;
{
     char buf[1024];
     static char *nullword = "(NULL)";
     if (ticket == NULL)
	  ticket = "";

     buf[0] = GSgetType(gs);
     
     if (buf[0] == '\0')	/* For example a .names with no Type */
	buf[0] = '3'; 

     if (fmt == GSFORM_G0) {
	  sprintf(buf + 1, "%s\t%s%s\t%s\t%d",
		  GSgetTitle(gs) ? GSgetTitle(gs) : nullword,
		  ticket         ? ticket : nullword,
		  GSgetPath(gs)  ? GSgetPath(gs) : nullword,
		  GSgetHost(gs)  ? GSgetHost(gs) : nullword,
		  GSgetPort(gs));
	  
	  if (GSisAsk(gs))
	       strcat(buf, "\t?\r\n");
	  else if (GSisGplus(gs))
	       strcat(buf, "\t+\r\n");
	  else
	       strcat(buf, "\r\n");
	  
	  writestring(sockfd, buf);
	  Debug("GStoNet:%s", buf);
     } 
     else if (fmt == GSFORM_GPLUS) {
	  ;
     }
     else if (fmt == GSFORM_HTML) {
	  char url[256];

	  if (GSgetType(gs) == A_INFO) {
	       writestring(sockfd, GSgetTitle(gs));
	       return;
	  }
	  GStoNetURL(gs, url);
	  if (url[0] == '\0')
	       return;
	  sprintf(buf, "<A HREF=\"%s\">%s</A>\r\n", url, GSgetTitle(gs));
	  writestring(sockfd, buf);

	  if (GSgplusInited(gs)) {
	       int  j;
	       Blockobj *bl;

	       bl = GSfindBlock(gs, "ABSTRACT");
	       if (bl != NULL) {
		    writestring(sockfd, "<DD>");
		    BLtoNet(bl, sockfd, FALSE);
		    writestring(sockfd, "\r\n");
	       }
	  }

	  if (GSgetWeight(gs) != 0) {
	       sprintf(buf, "<DD>Score: %d\r\n", GSgetWeight(gs));
	       writestring(buf);
	  }
     }
	  
}


/*
 * Send a long gopher data descriptor
 *
 * filter is a character array of blocks to send
 */

void
GSplustoNet(gs, sockfd, filter, ticket)
  GopherObj *gs;
  int sockfd;
  char **filter;
  char *ticket;
{
     int     i;
     char    tmpstr[256];
     boolean sendviews, sendadmin, sendothers;
#ifdef VMS_SERVER
     char    *admin;
     char    *moddate;
#endif

     if (filter == NULL)
	  sendviews = sendadmin = sendothers = TRUE;
     else {
	  sendviews = sendadmin = sendothers = FALSE;
	  for (i=0; filter[i] != NULL ;i++) {
	       if (strcasecmp(filter[i], "VIEWS")==0)
		    sendviews = TRUE;
	       else if (strcasecmp(filter[i], "ADMIN")==0)
		    sendadmin = TRUE;
	       else 
		    sendothers = TRUE;
	  }
     }    
     
     
     /** Send out the old style INFO stuff **/
     writestring(sockfd, "+INFO: ");
     GStoNet(gs,sockfd, GSFORM_G0, ticket);


     /** Only write out "interesting" URLs **/

     if (GSgetURL(gs) != NULL) {
	  if (strncmp(GSgetURL(gs), "gopher:", 7) != 0) {
	       writestring(sockfd, "+URL:\r\n ");
	       writestring(sockfd, GSgetURL(gs));
	       writestring(sockfd, "\r\n");
	  }
     }


     if (GSgplusInited(gs)) {
	  /*** Should special case for local filename.... ***/
	  
#ifndef VMS_SERVER
	  if (GSgetAdmin(gs) != NULL && GSgetModDate(gs) != NULL && sendadmin){
	       writestring(sockfd, "+ADMIN:\r\n Admin: ");
	       writestring(sockfd, GSgetAdmin(gs));
	       writestring(sockfd, "\r\n Mod-Date: ");
	       writestring(sockfd, GSgetModDate(gs));
	       if (GSgetTTL(gs) > -1) {
		    writestring(sockfd, "\r\n TTL: ");
		    sprintf(tmpstr, "%d", GSgetTTL(gs));
		    writestring(sockfd, tmpstr);
	       }
	       writestring(sockfd, "\r\n");
	  }
#else
	  if (sendadmin) {
	    admin=GSgetAdmin(gs);
	    moddate=GSgetModDate(gs);
	    if (admin || moddate || (GSgetTTL(gs) > -1)) {
		  writestring(sockfd, "+ADMIN:\r\n");
		  if (admin) {
		       writestring(sockfd, " Admin: ");
		       writestring(sockfd, admin);
		       writestring(sockfd, "\r\n");
		  }
		  if (moddate){
		       writestring(sockfd, " Mod-Date: ");
		       writestring(sockfd, moddate);
		       writestring(sockfd, "\r\n");
		  }
		  if (GSgetTTL(gs) > -1){
		       writestring(sockfd, " TTL: ");
		       sprintf(tmpstr, "%d", GSgetTTL(gs));
		       writestring(sockfd, tmpstr);
		       writestring(sockfd, "\r\n");
		  }
	      }
	  }
#endif
	  if (GSgetNumViews(gs) > 0 && sendviews) {
	       writestring(sockfd, "+VIEWS:\r\n");
	       for (i=0 ; i< GSgetNumViews(gs); i++) {
		    VItoLine(GSgetView(gs, i), tmpstr);
		    writestring(sockfd, tmpstr);
		    writestring(sockfd, "\r\n");
	       }
	  }
	  
	  if (sendothers) {

	       for (i=0; i< GSgetNumBlocks(gs); i++)
		    BLtoNet(GSgetBlock(gs, i),sockfd, TRUE);
	  }
     }
}


/*** GSplusfromnet() assumes that the leading +INFO text has been read from
     the file descriptor.

     It returns 1  if there's more data (and another INFO block)
                0  if there's EOF
		SOFTERROR caller can keep reading
		HARDERROR caller should stop reading
 ***/

int
GSplusfromNet(gs, fd)
  GopherObj *gs;
  int fd;
{
     int result,readok,i;
     boolean nextinfo = FALSE;
     char plusfield;
     Blockobj *bl;
     char inputline[512];
     
     if (gs->gplus == NULL)
	  GSplusnew(gs);

     bl = BLnew();

     /** get the gopher-data descriptor **/
     readok = GSfromNet(gs, fd);

     if (readok == HARDERROR)
	  return(HARDERROR);

     /** If readok is softerror we still need to look for blocks to throw
         away any data before next item **/

     /** Now start looking for blocks.  Process blocks if we know how.  **/
     /** State: _GotGREF_ **/

     if ((result = readrecvbuf(fd, &plusfield, 1))<0)
	  return(HARDERROR);

     while (!nextinfo) {
	  if (result == 0) { 	       /*** We're done ***/
	       BLdestroy(bl);

	       if (readok == SOFTERROR)
		    return(SOFTERROR);
	       else
		    return(FOUNDEOF);
	  }
	  switch (plusfield) {

	  case '.':
	       readline(fd, inputline, sizeof(inputline));
	       result = FOUNDEOF;
	       break;

	  case '+':
	       /*** State _NewBlock_ ***/

	       if (readtoken(fd,inputline, sizeof(inputline), ':') <= 0) 
		    return(HARDERROR);

	       if (strcasecmp(inputline, "INFO")==0) {
		    /** Get rid of the space. **/
		    readrecvbuf(fd, &plusfield,1);
		    BLdestroy(bl);
		    if (readok == SOFTERROR)
			 return(SOFTERROR);
		    else
			 return(MORECOMING);
	       }

	       /** Specialized fromNets here **/

	       BLinit(bl);
	       if ((result=BLfromNet(bl, fd, inputline)) <0) {
		    /** Bad read **/
		    BLdestroy(bl);
		    return(HARDERROR);
	       }
	       else if (result == FOUNDEOF)   /** EOF, block read ok **/
		    nextinfo = TRUE;
	       else 
		    nextinfo = FALSE;


	       /*** See what the block is. Transform it if necessary ***/
	       if (strcasecmp(BLgetName(bl), "VIEWS")==0) {
		    VIAfromBL(gs->gplus->Views, bl);
	       } else if (strcasecmp(BLgetName(bl), "ADMIN")==0) {
		    int	 saveAdminBlock = 0;

		    for (i=0; i<BLgetNumLines(bl); i++) {
			 char *cp;
			 
			 cp = BLgetLine(bl, i);
			 
			 if (strncasecmp(cp, "Admin: ",7)==0)
			      GSsetAdmin(gs, cp+7);
			 else if (strncasecmp(cp, "Mod-Date: ", 10)==0)
			      GSsetModDate(gs, cp+10);
			 else if (strncasecmp(cp, "TTL: ", 5) == 0)
			      GSsetTTL(gs, atoi(cp+5));
			 else
			      saveAdminBlock = 1;
		    }
		    if (saveAdminBlock)
			 BLApush(gs->gplus->OtherBlocks, bl);
	       
	       } else if (strcasecmp(BLgetName(bl), "URL")==0) {
		    char *cp;

		    cp = BLgetLine(bl, 0);
		    if (cp != NULL)
			 GSsetURL(gs, cp);
	       }
	       else
		    BLApush(gs->gplus->OtherBlocks, bl);

	       break;  /** '+' **/

	  default: /*** Hmmm plusfield wasn't a plus or '.' **/
	       return(HARDERROR);

	  } /** switch plusfield **/

     }   /** While **/

     BLdestroy(bl);

     return(FOUNDEOF);
}



/* GSfromNet - no comments on the original, so this is my (Mitra's) guess
   GSfromNet reads a gopher style line, it is called from:
   GDfromNet - in which case the gopher line is the whole item or;
   GSplusfromNet - in which case it is the part after the +INFO.
   It should return after reading the whole line, however in gopher+1.2b2
   this is not always the case, especially if it sees a Type=3
   returns:
       1      blank line (I think?)
       0      ok
      -1 HARDERROR    readfield etc error - give up
      -2 SOFTERROR    unrecognized or unhandleable type - skip on to next one
*/




extern int readfield();
extern int readline();

int
GSfromNet(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     char foo[1024], *cp;
     
     if (readtoken(sockfd, foo, 1024, '\t')<= 0) {
	  /* EOF or error */
	  return(HARDERROR);
     }

     GSsetType(gs, foo[0]);

     /** Get the kind of file from the first character **/
     /** Filter out files that we can't deal with **/

     switch (GSgetType(gs)) {
       case A_PDF:
       case A_FILE:
       case A_DIRECTORY:
       case A_MACHEX:
       case A_PCBIN:
       case A_CSO:
       case A_INDEX:
       case A_TELNET:
       case A_SOUND:
       case A_UNIXBIN:
       case A_GIF:
       case A_HTML:
       case A_TN3270:
       case A_MIME:
       case A_IMAGE:
       case A_INFO:
       case A_MOVIE:
       case A_APP:
#ifdef VMS_SERVER
       case A_UUENCODE:
#endif
	  break;
     case A_ERROR:
	  GSsetPath(gs, "");
	  GSsetHost(gs, "");
	  GSsetGplus(gs, FALSE);
	  ZapCRLF(foo+1);
	  cp = foo + strlen(foo+1);
	  while (*cp == '.' && (*(cp-1) == '\n' || *(cp-1) == '\r')) {
	       *cp = '\0';
	       ZapCRLF(foo+1);
	  }
	  break;
     case A_EOI:
	  if (foo[1] == '\r' && foo[2] == '\n')
	       return(1);
     default:
	  /** Can't handle this type **/
	  readline(sockfd, foo, 1024);/** Cleanup **/
	  return(SOFTERROR);
     }

     /** Suck off the User Displayable name **/
     cp = foo+1;
     while (*cp == '\n' || *cp == '\r')
	    cp++;
     GSsetTitle(gs, cp);
     
     /** Suck off the Pathname **/
     if (readtoken(sockfd, foo, 1024, '\t') <= 0)
	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);
     GSsetPath(gs, foo);

     /** Suck off the hostname **/
     if (readtoken(sockfd, foo, 1024, '\t') <= 0)
	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);

     GSsetHost(gs, foo);

     if (readline(sockfd, foo, 1024)<=0)
	  return(GSgetType(gs)==A_ERROR?0:HARDERROR);

     GSsetPort(gs, 0);

     /** Get the port number **/
     if ((cp = strchr(foo, '\t')) != NULL) {
	  *cp = '\0';
	  switch (*(cp+1)) {
	  case '?':
	       GSsetAsk(gs, TRUE);
	  case '+':
	       GSsetGplus(gs, TRUE);
	       break;
	  }
     }

     GSsetPort(gs, atoi(foo));
     DebugGSplusPrint(gs,"GSfromNet end:");

     return(0);
}


/** Copy a GopherObj ***/

void
GScpy(dest, orig)
  GopherObj *dest, *orig;
{
     dest->sFileType = orig->sFileType;
     dest->iPort     = orig->iPort;
     dest->Itemnum   = orig->Itemnum;

#ifdef VMS_SERVER
     dest->date_cr   = orig->date_cr;
     dest->lookaside = orig->lookaside;
     if (dest->Access != NULL)
	SiteArrDestroy(dest->Access);
     dest->Access = NULL;
     if (orig->Access != NULL) {
	Site *temp;
	int i;
	dest->Access = SiteArrayNew();
	for (i=0; i< DAgetTop(orig->Access); i++) {
	  temp = SiteArrgetEntry(orig->Access, i);
	  SiteArrayAdd(dest->Access,temp->domain,temp->Level);
	}
     }
     dest->Defaccess = orig->Defaccess;
     GSsetHeader(dest, GSgetHeader(orig));
     GSsetFooter(dest, GSgetFooter(orig));
     GSsetRHeader(dest, GSgetRHeader(orig));
     GSsetRFooter(dest, GSgetRFooter(orig));
#endif

     GSsetTitle(dest, GSgetTitle(orig));
     GSsetPath(dest, GSgetPath(orig));
     GSsetHost(dest, GSgetHost(orig));
     
     GSsetGplus(dest, GSisGplus(orig));
     GSsetAsk(dest, GSisAsk(orig));
     GSsetURL(dest, GSgetURL(orig));
     GSsetTTL(dest, GSgetTTL(orig));

     STRinit(dest->Localfile);
     STRinit(dest->Localview);
     GSpluscpy(dest, orig);
     
}

void
GSpluscpy(dest, orig)
  GopherObj *dest, *orig;
{
     if (GSgplusInited(orig)) {
	  if (!GSgplusInited(dest))
	       GSplusnew(dest);
	  GSsetAdmin(dest, GSgetAdmin(orig));
	  GSsetModDate(dest, GSgetModDate(orig));
	  VIAcpy(dest->gplus->Views, orig->gplus->Views);
	  BLAcpy(dest->gplus->OtherBlocks, orig->gplus->OtherBlocks);
	  (void)GSsetAskdata(dest, GSgetAskdata(orig));
     }
}

/** GSmerge combines 2 gopher objects overwriting fields defined in overlay **/
/* Called by GDaddGSmerge to merge gs into directory, that is only called 
   by function to do this from a link */
void
GSmerge(gs, overlay)
  GopherObj *gs, *overlay;
{
     char *tempstr;
     
     DebugGSplusPrint(gs,"GSmerge: Original");
     DebugGSplusPrint(overlay,"GSmerge: Overlay");
     
     if (GSgetHost(overlay) != NULL) {
	  
	  if (GSgetType(overlay) != '\0') {
	       /* Just setting Type wont work, since they interelated with Path
	        * so set first char of path as well */
	       GSsetType(gs, GSgetType(overlay));
	       tempstr = GSgetPath(gs);
	       
	       if (*tempstr != A_DIRECTORY) {
		    tempstr[0] = GSgetType(overlay);
		    GSsetPath(gs, tempstr);
	       }
	       if (GSisAsk(overlay) && GSgetType(overlay) == A_DIRECTORY)  
		    GSaddView(gs, "application/gopher+-menu", "En_US", 0); 
	       /* lang==GDCgetLang(Config), 0 == dummy size here */
	       /* need this patch for proper protocol & Hgopher, 
		  which ignores gopher0 type in favor of +VIEW */
	  }

	  if (GSgetTitle(overlay) != NULL)
	       GSsetTitle(gs, GSgetTitle(overlay));
	  /* Don't set path - that is the key to the merge, and in the overlay
	     most probably has the first char set to ' '  ????*/
	  if (GSgetHost(overlay) != NULL)
	       GSsetHost(gs, GSgetHost(overlay));
	  if (GSgetPort(overlay) != 0)
	       GSsetPort(gs, GSgetPort(overlay));
	  if (GSgetNum(overlay) != -1)
	       GSsetNum(gs, GSgetNum(overlay));
	  if (GSgetWeight(overlay) != 0)
	       GSsetWeight(gs, GSgetWeight(overlay));
	  if (GSgetAdmin(overlay) != NULL)
               GSsetAdmin(gs, GSgetAdmin(overlay));

#ifdef VMS_SERVER
          if (GSisCreateDate(overlay))
		GSsetCreateDate(gs);
	  if (overlay->Access != NULL) {
	    Site *temp;
	    int i;
	    if (gs->Access != NULL) {
		SiteArrDestroy(gs->Access);
		gs->Access = NULL;
	    }
	    gs->Access = SiteArrayNew();
	    for (i=0; i< DAgetTop(overlay->Access); i++) {
		temp = SiteArrgetEntry(overlay->Access, i);
		SiteArrayAdd(gs->Access,temp->domain,temp->Level);
	    }
	  }
	  if (GSgetHeader(overlay) != NULL)
	       GSsetHeader(gs, GSgetHeader(overlay));
	  if (GSgetFooter(overlay) != NULL)
	       GSsetFooter(gs, GSgetFooter(overlay));
	  if (GSgetRHeader(overlay) != NULL)
	       GSsetRHeader(gs, GSgetRHeader(overlay));
	  if (GSgetRFooter(overlay) != NULL)
	       GSsetRFooter(gs, GSgetRFooter(overlay));
#endif

          if (overlay->gplus != NULL)
          {
	       if (!GSgplusInited(gs))
                    GSplusnew(gs);
               BLAcpy(gs->gplus->OtherBlocks,
		      overlay->gplus->OtherBlocks);
	  }
     }
     DebugGSplusPrint(gs,"GSmerge: Result");
}


/** Compare two GopherObjs ***/

int
GScmp(gs1, gs2)
  GopherObj *gs1, *gs2;
{
     if (GSgetTitle(gs1) == NULL)
	  return(1);
     if (GSgetTitle(gs2) == NULL)
	  return(-1);

     return(strcasecmp(GSgetTitle(gs1), GSgetTitle(gs2)));
}


/*********** The following functions implement the gopher/gopher+
  protocol, mostly
  GSconnect(), then GStransmit(), GSsendHeader() GSrecvHeader();
************/



/* GSconnect performs a connection to socket 'service' on host
 * 'host'.  Host can be a hostname or ip-address.  If 'host' is null, the
 * local host is assumed.   The parameter full_hostname will, on return,
 * contain the expanded hostname (if possible).  Note that full_hostname is a
 * pointer to a char *, and is allocated by connect_to_gopher()
 *
 * returns Errors : ErrSocket* defined in Sockets.h or socket
 *
 */

int
GSconnect(gs)
  GopherObj *gs;
{
     int sockfd; 

     Debug("GSconnect: Host=%s",GSgetHost(gs));
     Debug("Port=%d\r\n",GSgetPort(gs));
     
     sockfd = SOCKconnect(GSgetHost(gs), GSgetPort(gs));
     
     return(sockfd);
}


/*
 * GStransmit sends the request from the client to the server
 *
 * All parameters are optional except for gs and sockfd.
 * 
 * the rest pertain to gopher+ transmission.
 */

void 
GStransmit(gs, sockfd, search, command, view)
  GopherObj *gs;
  int sockfd;
  char *search;
  char *command;
  char *view;
{
     char *cp;
     char **ask = GSgetAskdata(gs);
     int i;

     writestring(sockfd, GSgetPath(gs));
     if (search != NULL) {
	  writestring(sockfd, "\t");
	  writestring(sockfd, search);
     }

     /** Only send if gplus **/
     if  (!GSisGplus(gs)) {
	  writestring(sockfd, "\r\n");
	  return;
     }

     if (command != NULL) {
	  writestring(sockfd, "\t");
	  writestring(sockfd, command);
     }	  

     if (view != NULL) {
	  writestring(sockfd, view);
     }
     if (ask == NULL) 
	  writestring(sockfd, "\r\n");
     else {
	  writestring(sockfd, "\t1\r\n");

	  GSsendHeader(sockfd, -1);
	  
	  for (i=0; ;i++) {
	       cp = ask[i];

	       if (cp == NULL)
		    break;

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



/*
 * GSsendHeader generates an appropriate header on sockfd
 *
 */
void
GSsendHeader(sockfd, size)
  int sockfd;
  int size;
{
     char sizestr[64];

     sprintf(sizestr, "+%d\r\n", size);
     writestring(sockfd, sizestr);
}


/** GSsendErrorHeader sends an error message header/message to the client **/
void
GSsendErrorHeader(gs,sockfd,errortype,errormsg)
  GopherObj *gs;
  int       sockfd;
  int       errortype;
  char      *errormsg;
{
     char tmpstr[512];

     sprintf(tmpstr, "-%d %s\r\n", errortype, errormsg);
     writestring(sockfd, tmpstr);
}



/*
 * GSrecvHeader will retrieve a gopher+ header, if it exists
 *
 * It returns the expected number of bytes that are headed our
 * way, if it can.
 * 
 * Otherwise it returns -1 to indicate to read until \r\n.\r\n
 * or -2 to indicate to read to EOF
 *
 * If it encounters an error, it returns 0 and sets errno to the
 * gopher error class.
 */

int
GSrecvHeader(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     char headerline[256];

     Debugmsg("GSrecvHeader\n");
     if (GSisGplus(gs)) {
	  if (readline(sockfd, headerline, sizeof(headerline))<=0)
		  return(0);
	  ZapCRLF(headerline);
	  if (*headerline == '+') {
	       if (*(headerline+1) == '-')
		    return(- (atoi(headerline+2)));
	       else
		    return(atoi(headerline+1));
	  } 
	  else if (*headerline == '-') {
	       /*** Oh no! an error! ***/
	       errno = atoi(headerline+1);
	       return(0);
	  }
     }
     /*** Guess if we're running old style gopher ***/
     else {
	  switch (GSgetType(gs)) {
	  case A_SOUND:
	  case A_IMAGE:
	  case A_GIF:
	  case A_UNIXBIN:
	  case A_PCBIN:
	       return(-2);
	       break;
	  default:
	       return(-1);
	  }
     }
     return(-1); /** Should never get here **/
}


/*
 * This routine will load up the item information from a gopher item
 * if the item hasn't transferred it already...
 *
 * The savename param, if TRUE will keep the current name and type
 */

void
GSgetginfo(gs, savename)
  GopherObj *gs;
  boolean   savename;
{
     int    sockfd, bytes;
     char   inputline[256];
     String *tempname = NULL;
     char   temptype;

     if (!GSisGplus(gs))
	  return;

     /** Try not to overwrite stuff unnecessarily.. **/

     if (GSgplusInited(gs)) {
	  if (GSgetAdmin(gs)!=NULL)
	       return;
	  if (GSgetAskdata(gs) != NULL)
	       return;
     }

     if (savename) {
	  tempname = STRnew();
	  STRset(tempname, GSgetTitle(gs));
	  temptype = GSgetType(gs);
     }

     GSplusnew(gs);

     /** Send out the request **/
     if ((sockfd = GSconnect(gs)) <0) {
	  /*check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));*/
	  return;
     }
     
     GStransmit(gs, sockfd, NULL, "!", NULL, NULL);
     bytes = GSrecvHeader(gs,sockfd);

     if (bytes == 0)
	  return;

     /***  Read off the first info block ***/
     readtoken(sockfd, inputline, sizeof(inputline), ' ');

     GSplusfromNet(gs, sockfd);
     
     if (savename) {
	  GSsetTitle(gs, STRget(tempname));
	  GSsetType(gs, temptype);
	  STRdestroy(tempname);
     }

}


/*
 * GSfromLink takes an FIO structure and starts reading from it.
 *
 * It reads until it finds a line it recognizes, then
 *
 * It keeps going until it finds
 *   eof, a non-recognized line, as long as there is a valid Path= line
 *
 * returns -1 on an error, 0 for EOF, 1 for success
 */

int
GSfromLink(gs, fio, host, port, directory, peer)
  GopherObj *gs;
  FileIO    *fio;
  char      *host;
  int       port;
  char      *directory;
  char      *peer;
{
     int doneflags = 0;
     char buf[1024];
     int bytesread;

     boolean DomainDefault = TRUE; /** Default for using domain stuff yes/no */
     boolean BadDomain = FALSE;	   /** For use with the Domain= line **/
     boolean DidDomain = FALSE;    /** Needed to make Domain= lines
				      into logical or's **/
     buf[0] = '\0';


     Debugmsg("GSfromLink...\n");
     while ((bytesread = FIOreadlinezap(fio, buf, sizeof(buf)))>0) {
	  if (buf[0] == '#') {
	       if (doneflags & G_PATH)
		    break;   /* comment */
	       else
		    continue;
	  }

	  ZapCRLF(buf);

	  if (strncasecmp(buf, GS_TYPE, strlen(GS_TYPE))==0) {
	       GSsetType(gs, buf[strlen(GS_TYPE)]);

	       if (buf[strlen(GS_TYPE)+1] == '+')
		    GSsetGplus(gs, TRUE);
	       if (buf[strlen(GS_TYPE)+1] == '?')
		    GSsetAsk(gs, TRUE);
	       doneflags |= G_TYPE;
	  }

	  else if (strncasecmp(buf, GS_NAME, strlen(GS_NAME))==0) {
	       GSsetTitle(gs, buf+strlen(GS_NAME));
	       doneflags |= G_NAME;
	  }

	  else if (strncasecmp(buf, GS_PATH, strlen(GS_PATH))==0) {
#ifndef VMS_SERVER
	       if (strncmp(buf+strlen(GS_PATH), "~/",2) == 0 ||
		   strncmp(buf+strlen(GS_PATH), "./",2) == 0) {
		    char tmpstr[256];

		    *tmpstr = '.';
		    strcpy(tmpstr+1, directory);
		    if (directory[strlen(directory)-1] == '/')
			 strcat(tmpstr, buf+strlen(GS_PATH)+2);
		    else
			 strcat(tmpstr, buf+strlen(GS_PATH)+1);
		    GSsetPath(gs, tmpstr);
	       } else
		    GSsetPath(gs, buf+strlen(GS_PATH));
#else
	       char *bp = buf+strlen(GS_PATH);
	       String *acp = STRnew();

	       while (*(bp + strlen(bp)-1) == GDCcontinue && bytesread >0) {
		    /* A continuation line */
		    *(bp + strlen(bp)-1) = '\0';
		    STRcat(acp,bp);
		    bytesread = FIOreadlinezap(fio, bp=buf, sizeof(buf));
	       }
	       STRcat(acp, bp);
	       GSsetPath(gs, STRget(acp));
	       STRdestroy(acp);
#endif
	       doneflags |= G_PATH;
	  }
 	  
	  else if (strncasecmp(buf, GS_HOST, strlen(GS_HOST))==0) {
	       if (buf[strlen(GS_HOST)] == '+' 
			&& buf[strlen(GS_HOST)+1] == '\0')
		    GSsetHost(gs, host);
	       else
		    GSsetHost(gs, buf+strlen(GS_HOST));

	       doneflags |= G_HOST;
	  }

	  else if (strncasecmp(buf, GS_PORT, strlen(GS_PORT))==0) {
	       if (buf[strlen(GS_PORT)] == '+' 
			&& buf[strlen(GS_PORT)+1] == '\0')
		    GSsetPort(gs, port);
	       else
		    GSsetPort(gs, atoi(buf+strlen(GS_PORT)));

	       doneflags |= G_PORT;
	  }

	  else if (strncasecmp(buf, GS_NUMB, strlen(GS_NUMB))==0)
	       GSsetNum(gs, atoi(buf+strlen(GS_NUMB)));

	  else if (strncasecmp(buf, GS_ABSTRACT, strlen(GS_ABSTRACT))==0) {
	       char *acp;

	       acp = buf+strlen(GS_ABSTRACT);

	       while (*(buf + strlen(buf)-1) == GDCcontinue && bytesread >0) {
		    /* A continuation line */
		    *(buf + strlen(buf)-1) = '\0';
		    GSsetAbstract(gs, acp);
		    bytesread = FIOreadlinezap(fio, buf, sizeof(buf));
		    acp = buf;
	       }
	       GSsetAbstract(gs, acp);
	  }
	  else if (strncasecmp(buf, GS_ADMIN, strlen(GS_ADMIN)) == 0)
	       GSsetAdmin(gs, buf +strlen(GS_ADMIN));
	  else if (strncasecmp(buf, GS_URL, strlen(GS_URL)) == 0)
	       doneflags |= GSfromURL(gs, buf + strlen(GS_URL), host, port);
#ifndef VMS_SERVER
	  else if (strncmp(buf, "Domaindef=", 10)==0 && peer != NULL) {
	       DomainDefault = (strcasecmp(buf+10, "no")!=0);
	       Debug("Default Domain is %s\n", buf+10);
	  }
	  else if (strncmp(buf, "Domain=", 7) ==0 && peer != NULL) {
	       /** Check to see if the peer matches the domain **/
	       int     peerlen,domainlen;
	       boolean TestResult = !DomainDefault;
	       char    *host = buf+7;

	       if (*host == '!') {
		    TestResult = TRUE;
		    host++;
	       }

	       peerlen = strlen(peer);
	       domainlen = strlen(host);

	       if (DidDomain == TRUE && BadDomain == FALSE)
		    break;

	       if (domainlen > peerlen) {
		    BadDomain = !TestResult;
	       } else if (strncasecmp(buf+7, peer + peerlen - domainlen, domainlen)== 0) {
		    /** Domains match, do it! **/
		    BadDomain = TestResult;
	       } else
		    BadDomain = !TestResult;
	       
	       DidDomain = TRUE;
	  }
#endif
#ifndef __VMS
	  else if (strncmp(buf, "Domain_pat=", 11) ==0 && peer != NULL) {
	       char    *host = buf+11;
	       boolean TestResult = !DomainDefault;

	       if (DidDomain == TRUE && BadDomain == FALSE)
		    break;
	       
	       if (*host == '!') {
		    host++;
		    TestResult = TRUE;
	       }

	       /** Check for domain using regexps **/
	       if (re_comp(host) != NULL)
		    break;
	       if (re_exec(peer) == 1)
		    BadDomain = TestResult;
	       else
		    BadDomain = !TestResult;

	       DidDomain = TRUE;
	  }
#endif
	  else if (strncasecmp(buf, GS_TTL, strlen(GS_TTL)) == 0) {
	       GSsetTTL(gs, atoi(buf+strlen(GS_TTL)));
	  }
#ifdef VMS_SERVER
	  else
	  if (strncasecmp(buf, GS_CREATE, strlen(GS_CREATE))==0)
	       GSsetCreateDate(gs);
	  else
	  if (strncasecmp(buf, GS_MODIFY, strlen(GS_MODIFY))==0)
	       GSsetModifyDate(gs);
	  else
	  if (strncasecmp(buf, GS_HDDN, strlen(GS_HDDN))==0)
	       GSsetNum(gs, -99);
	  else
	  if (strncasecmp(buf, GS_ACCS, strlen(GS_ACCS))==0) {
	    if (GSgetAccess(gs) == NULL)
		GSsetAccess(gs, SiteArrayNew());
	    GSsetAccessSite(gs, buf+strlen(GS_ACCS));
	    GSsetDefAcc(gs,SiteDefAccess(gs->Access));
	    if (GSgetDefAcc(gs) == ACC_UNKNOWN)
		GSsetDefAcc(gs,ACC_FULL);
	  }
	  else
	  if (strncasecmp(buf, GS_HEAD, strlen(GS_HEAD))==0)
	       GSsetHeader(gs, buf+strlen(GS_HEAD));
	  else
	  if (strncasecmp(buf, GS_FOOT, strlen(GS_FOOT))==0)
	       GSsetFooter(gs, buf+strlen(GS_FOOT));
	  else
	  if (strncasecmp(buf, GS_RHEAD, strlen(GS_RHEAD))==0)
	       GSsetRHeader(gs, buf+strlen(GS_RHEAD));
	  else
	  if (strncasecmp(buf, GS_RFOOT, strlen(GS_RFOOT))==0)
	       GSsetRFooter(gs, buf+strlen(GS_RFOOT));
#endif
	  else
	       break;  /*** Unknown name/item ***/
     }

     Debugmsg("Done with this link item\n");

     if (BadDomain)
	  return(SOFTERROR);

     if (bytesread == 0) {
	  if (doneflags & G_PATH)
	       return(FOUNDEOF);  /** Found the eof, plus there's a g item **/
	  else 
	       return(HARDERROR); /** Mangled item, plus eof, stop the game **/
     }

     if (doneflags & G_PATH)
	  return(MORECOMING);  /** Found item, more coming. **/
     else
	  return(SOFTERROR);   /** Mangled item, more coming.. **/
}


 
/*
 * Fill in a GopherObj, given a URL
 */

 
int
GSfromURL(gs, urltxt, host, port, doneflags)
  GopherObj *gs;
  char	    *urltxt;
  char      *host;
  int        port;
  int        doneflags;
{
     char tempbuf[256];
     Url  *url;
     UrlServiceType serviceType;

     url = URLnew();
     URLset(url, urltxt);

     Debug("GSfromURL: %s\r\n",urltxt);
     GSsetURL(gs, urltxt);
     serviceType = URLgetService(url);
     
     switch (serviceType) {
     case http:
	  if (! (doneflags & G_TYPE)) {
	       /* It may not be HTML, but we lie and say it is so the  */
	       /* client passes it off to a http-speaking program */
	       GSsetType(gs, A_HTML);
	       doneflags |= G_TYPE;
	  }

	  if (! (doneflags & G_PATH)) {
	       sprintf(tempbuf, "GET /%s", URLgetPath(url));
	       GSsetPath(gs, tempbuf);
	  } 
	  doneflags |= G_PATH;

	  break;

     case ftp:
	  if (! (doneflags & G_HOST)) {
	       GSsetHost(gs, host);
	       doneflags |= G_HOST;
	  }
	  if (! (doneflags & G_PORT)) {
	       GSsetPort(gs, port);
	       doneflags |= G_PORT;
	  }
	  break;

     case telnet:
	  if (! (doneflags & G_TYPE)) {
	       GSsetType(gs, A_TELNET);
	       doneflags |= G_TYPE;
	  }

     case tn3270:
	  if (! (doneflags & G_TYPE)) {
	       GSsetType(gs, A_TN3270);
	       doneflags |= G_TYPE;
	  }
	  break;

     case gopher:

	  if (! (doneflags & G_TYPE)) {
	       GSsetType(gs, URLgetGophType(url));
	       doneflags |= G_TYPE;
	  }

	  break;

     default:
	  /* A type we can't deal with... */
	  return(doneflags);
     }

     if (! (doneflags & G_NAME)) {
	  GSsetTitle(gs, URLget(url));
	  doneflags |= G_NAME;
     }

     /* Use login & password if needed & present */
     switch (serviceType) {
     case telnet:
     case tn3270:
	  if (URLgetUser(url) != NULL)
	       GSsetPath(gs, URLgetUser(url));
	  else
	       GSsetPath(gs, "");
	  doneflags |= G_PATH;
	  break;
     }

     if (serviceType == ftp) {
	  if (!(doneflags & G_PATH)) {
	       if (URLgetPath(url) != NULL && *URLgetPath(url) != '\0')
		    sprintf(tempbuf, "ftp:%s@/%s", URLgetHost(url),
			    URLgetPath(url));
	       else
		    sprintf(tempbuf, "ftp:%s@/", URLgetHost(url));

	       GSsetPath(gs, tempbuf);
	       doneflags |= G_PATH;
	  }
	  if (! (doneflags & G_TYPE)) {
	       if ((*(URLgetPath(url)) == '\0') ||
	            *(URLgetPath(url) + strlen(URLgetPath(url))-1) == '/')
		    GSsetType(gs, A_DIRECTORY);
	       else
		    GSsetType(gs, A_FILE);

	       doneflags |= G_TYPE;
	  }
     } else {
	  if (! (doneflags & G_HOST)) {
	       GSsetHost(gs, URLgetHost(url));
	       doneflags |= G_HOST;
	  }
	  if (! (doneflags & G_PORT)) {
	       GSsetPort(gs, URLgetPort(url));
	       doneflags |= G_PORT;
	  }
     }
	  

     if (! (doneflags & G_PATH) || GSgetPath(gs) == NULL) {
          char *cp;
	  GSsetPath(gs, (cp=URLgetPath(url)) ? cp : "");
	  doneflags |= G_PATH;
     }

     return doneflags;
}
  



void
GStoLink(gs, fd, AddInfo)
  GopherObj *gs;
  int fd;
  BOOLEAN AddInfo;
{
     char gtype[2];
     char portnum[16];
     
     gtype[0] = GSgetType(gs);
     gtype[1] = '\0';

     writestring(fd, "#");
     writestring(fd, "\nType=");
     writestring(fd, gtype);
     if (GSisGplus(gs))
	  writestring(fd, "+");
     writestring(fd, "\nName=");
     writestring(fd, GSgetTitle(gs));
     writestring(fd, "\nPath=");
     writestring(fd, GSgetPath(gs));
     writestring(fd, "\nHost=");
     writestring(fd, GSgetHost(gs));
     writestring(fd, "\nPort=");
     sprintf(portnum, "%d", GSgetPort(gs));
     writestring(fd, portnum);
     writestring(fd, "\n");
     if (GSisGplus(gs) && GSgplusInited(gs) && AddInfo) {
	  writestring(fd, "Admin=");
	  writestring(fd, GSgetAdmin(gs));
	  writestring(fd, "\nModDate=");
	  writestring(fd, GSgetModDate(gs));
	  writestring(fd, "\n");
     }
}


boolean
GSisText(gs, view)
  GopherObj *gs;
  char *view;
{
     if (view == NULL) {
	  switch (GSgetType(gs)) {
	  case A_DIRECTORY:
	  case A_FILE:
	  case A_MIME:
	  case A_CSO:
	  case A_MACHEX:
	  case A_HTML:
	       return(TRUE);

	  default:
	       return(FALSE);
	  }
     }
     else {
	  char viewstowage[64], *cp;

	  strcpy(viewstowage, view);
	  if ((cp=strchr(viewstowage, ' '))!=NULL) {
	       *cp = '\0';
	       view = viewstowage;
	  }

	  if (strncasecmp(view, "Text",4) == 0 ||
	      strncasecmp(view, "message/rfc822", 14)==0 ||
	      strncasecmp(view, "application/postscript", 21)==0 ||
	      strncasecmp(view, "application/mac-binhex40", 24)==0 ||
	      strncasecmp(view, "application/rtf", 15) == 0 ||
	      strncasecmp(view, "application/gopher", 18) == 0)



	       return(TRUE);
	  else
	       return(FALSE);
     }
}

#ifdef DEBUGGING
void
GSplusPrint(gs,head)
  GopherObj *gs;
  char *head;
{
     int i;
     int oldDebug = DEBUG;
     DEBUG=FALSE;
     fprintf(stderr,"%s: Type=%c,Title=%s,Path=%s,Host=%s,Port=%d,Num=%d,Weight=%d,Plus=%d,Ask=%d\r\n",
	     head,
	     GSgetType(gs),
	     GSgetTitle(gs),
	     GSgetPath(gs),
	     GSgetHost(gs),
	     GSgetPort(gs),
	     GSgetNum(gs),
	     GSgetWeight(gs),
	     GSisGplus(gs),
	     GSisAsk(gs)
	     );

#ifdef VMS_SERVER
     fprintf(stderr,"%*c%s,Lookaside=%d,Access=%d,Head=%s,Foot=%s,RHead=%s,RFoot=%s\r\n",
	     strlen(head)+2,' ',
	     (gs->date_cr)?"Display=Create":"Display=Modify",
	     (gs->lookaside),
	     (gs->Access)?1:0,
	     GSgetHeader(gs),
	     GSgetFooter(gs),
	     GSgetRHeader(gs),
	     GSgetRFooter(gs)
	     );
#endif
     
     if (GSgplusInited(gs))
	  for (i=0; i< GSgetNumBlocks(gs); i++) {
	       BLtoNet(GSgetBlock(gs, i), fileno(stderr), TRUE);
	  }
     fprintf(stderr,"===============\r\n");
     DEBUG = oldDebug;
}
#endif

#ifdef VMS_SERVER
/*
 * This tests to see if the current Gopher Object has an access specification 
 * matches the current client's address, which allows the specified access.
 */
AccessResult
GScanAccess(int sockfd, GopherObj *gs, int access)
{
     AccessResult	test;

     if (GSgetAccess(gs) == NULL)
	return(SITE_OK);

     Debug("GScanAccess: Testing %s/", CurrentPeerName);
     Debug("%s ", CurrentPeerIP);
     Debug("for %s\n", (access==ACC_SEARCH)?"searching":
			(access==ACC_READ)?"reading":
			(access==ACC_BROWSE)?"browsing":
			(access==ACC_FTP)?"ftping":
			(access==ACC_EXEC)?"script execution":
			"some unexpected access");
     test = SiteAccess(GSgetAccess(gs), CurrentPeerName, CurrentPeerIP, 
					access, 0);
     if (test != SITE_OK)
	if ((GSgetDefAcc(gs) & access) == access)
	    return(SITE_OK);
     return(test);
}


/*
 * Fill in a GopherObj, given an HREF= link from a WWW anchor..
 * So far only works with http
 */

void
GSfromHREF(gs, href)
  GopherObj *gs;
  char *href;
{
     char *cp;
     char *host;
     char path[1024];
     
     Debug("GSfromHREF %s\r\n",href)

     if (strncasecmp(href, "http://";, 7)==0) {
	  host = href +7;
	  cp  = strchr(href+7, '/');
	  if (cp == NULL)
	       return;
	  
	  *cp = '\0';
	  strcpy(path, "GET ");
	  strcat(path, cp+1);
	  GSsetPath(gs, path);

	  GSsetType(gs, A_HTML);

	  GSsetPath(gs, path);
	  
	  cp = strchr(host, ':');
	  if (cp==NULL) 
	       GSsetPort(gs, 80);  /** default WWW port **/
	  else {
	       GSsetPort(gs, atoi(cp+1));
	       *cp = '\0';
	  }

	  GSsetHost(gs, host);
     }
}

void
GStoNetHTML(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     static char buf[1024];
     static char pathbuf[1024];

     buf[0] = '\0';
     pathbuf[0] = '\0';

     /** Convert Path so that spaces are %20 **/
     Tohexstr(GSgetPath(gs), pathbuf);

     sprintf(buf, "<A HREF=http://%s:%d/%s>%s</A>",
	     GSgetHost(gs),
	     GSgetPort(gs),
	     pathbuf,
	     GSgetTitle(gs));

     writestring(sockfd, buf);

     Debug("HTML: %s\n", buf);
     
     if (GSgetWeight(gs) != 0) {
	  sprintf(buf, "Score: %d\r\n", GSgetWeight(gs));
	  writestring(sockfd, buf);
     }
     else
	  writestring(sockfd, "\r\n");

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