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