home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / gopher / Unix / gopher+1.2b4 / object / GSgopherobj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-15  |  26.6 KB  |  1,283 lines

  1. /********************************************************************
  2.  * lindner
  3.  * 3.5
  4.  * 1993/04/15 17:44:52
  5.  * /home/mudhoney/GopherSrc/CVS/gopher+/object/GSgopherobj.c,v
  6.  * Exp
  7.  *
  8.  * Paul Lindner, University of Minnesota CIS.
  9.  *
  10.  * Copyright 1991, 1992 by the Regents of the University of Minnesota
  11.  * see the file "Copyright" in the distribution for conditions of use.
  12.  *********************************************************************
  13.  * MODULE: GSgopherobj.c
  14.  * Implement gopher directory functions.
  15.  *********************************************************************
  16.  * Revision History:
  17.  * GSgopherobj.c,v
  18.  * Revision 3.5  1993/04/15  17:44:52  lindner
  19.  * Fixed link processing, mods from Mitra
  20.  *
  21.  * Revision 3.4  1993/03/26  19:50:46  lindner
  22.  * Mitra fixes for better/clearer fromNet code
  23.  *
  24.  * Revision 3.3  1993/03/24  17:05:33  lindner
  25.  * Additions for Localfile for each GopherObj
  26.  *
  27.  * Revision 3.2  1993/03/18  22:15:50  lindner
  28.  * filtering, memory leaks fixed, GSmerge problems
  29.  *
  30.  * Revision 3.1.1.1  1993/02/11  18:03:06  lindner
  31.  * Gopher+1.2beta release
  32.  *
  33.  * Revision 2.4  1993/02/09  22:47:16  lindner
  34.  * New GSisText() More fixes for gopher+
  35.  *
  36.  * Revision 2.3  1993/01/31  00:22:51  lindner
  37.  * Added GSplusfromNet(), updated gplus for VIewobjs and BLobjs
  38.  * Better GSplustoNet(), New fcn GSmerge()
  39.  * GSfromLink parses differently
  40.  * GStoLink adds a couple more entries.
  41.  *
  42.  * Revision 2.2  1993/01/14  21:23:38  lindner
  43.  * Merged in 1.1 mods to mainline.
  44.  *
  45.  * Revision 2.1  1993/01/14  21:19:43  lindner
  46.  * Addition of Gopher+ structures and routines (first cut)
  47.  *
  48.  * Revision 1.1.1.3  1993/01/11  19:54:06  lindner
  49.  * Fixed syntax in UCX #defines. (DuH!)
  50.  *
  51.  * Revision 1.1.1.2  1993/01/08  19:00:00  lindner
  52.  * Mods for UCX
  53.  *
  54.  * Revision 1.1.1.1  1992/12/31  05:01:31  lindner
  55.  * Chnages for VMS.
  56.  *
  57.  * Revision 1.1  1992/12/10  23:27:52  lindner
  58.  * gopher 1.1 release
  59.  *
  60.  *
  61.  *********************************************************************/
  62.  
  63. #include "GSgopherobj.h"
  64.  
  65. #if defined(mips) && defined(ultrix)   /*** Gross hack, yuck! ***/
  66. #define _SIZE_T
  67. #endif
  68.  
  69. #include "String.h"
  70. #include "compatible.h"
  71. #include "STRstring.h"
  72. #include <stdio.h>
  73. #include "compatible.h"
  74. #include <errno.h>
  75. extern int DEBUG;
  76.  
  77. /*
  78.  * These are needed for GSconnect().  Ick.
  79.  */
  80. #ifdef VMS
  81. #include <socket.h>
  82. #include <in.h>
  83. #include <file.h>
  84. #include <inet.h>
  85. #include <netdb.h>
  86.  
  87. #else
  88. #include <sys/types.h>
  89. #include <sys/socket.h>
  90. #include <netinet/in.h>
  91. #include <sys/file.h>
  92. #ifndef hpux
  93. #include <arpa/inet.h>
  94. #endif
  95. #include <netdb.h>
  96. #endif  /* not VMS */
  97.  
  98. #include "Malloc.h"
  99.  
  100.  
  101. /*
  102.  * Make a new gopherobj, set the fields accordingly...
  103.  */
  104.  
  105. GopherObj *
  106. GSnewSet(objtype, Title, Selstr, Host, Port)
  107.   char objtype;
  108.   char *Title;
  109.   char *Selstr;
  110.   char *Host;
  111.   int Port;
  112. {
  113.      GopherObj *temp;
  114.  
  115.      temp = (GopherObj *) malloc(sizeof(GopherObj));
  116.      temp->Selstr   = STRnewSet(Selstr);
  117.      temp->Title    = STRnewSet(Title);
  118.      temp->Host     = STRnewSet(Host);
  119.      temp->iPort    = Port;
  120.      GSsetNum(temp, -1);
  121.      GSsetWeight(temp, 0);
  122.  
  123.      return(temp);
  124. }
  125.  
  126.  
  127. /*
  128.  * Make a new gopherobj...  Should reuse destroyed GopherObjs...
  129.  */
  130.  
  131. GopherObj *
  132. GSnew()
  133. {
  134.      GopherObj *temp;
  135.  
  136.      temp = (GopherObj *) malloc(sizeof(GopherObj));
  137.      temp->Selstr    = STRnew();
  138.      temp->Title     = STRnew();
  139.      temp->Host      = STRnew();
  140.      temp->Localfile = STRnew();
  141.      temp->gplus     = NULL;
  142.      GSinit(temp);
  143.      return(temp);
  144. }
  145.  
  146. void
  147. GSplusnew(gs)
  148.   GopherObj *gs;
  149. {
  150.      if (gs->gplus == NULL) {
  151.       gs->gplus = (GplusObj *) malloc(sizeof(GplusObj));
  152.      }
  153.      
  154.      gs->gplus->abstract = STRnew();
  155.      gs->gplus->Admin    = STRnew();
  156.      gs->gplus->ModDate  = STRnew();
  157.  
  158.      gs->gplus->Views    = VIAnew(10);
  159.      gs->gplus->OtherBlocks = BLAnew(5);
  160. }
  161.  
  162.  
  163. /*** Destroy gopher object ***/
  164.  
  165. void
  166. GSdestroy(gs)
  167.   GopherObj *gs;
  168. {
  169.      STRdestroy(gs->Selstr);
  170.      STRdestroy(gs->Title);
  171.      STRdestroy(gs->Host);
  172.      if (GSgetLocalFile(gs) != NULL)
  173.       unlink(GSgetLocalFile(gs));
  174.      STRdestroy(gs->Localfile);
  175.  
  176.      GSplusdestroy(gs);
  177.      free(gs);
  178. }
  179.      
  180.  
  181. /*** Destroy Gopher+ attributes ***/
  182.  
  183. void
  184. GSplusdestroy(gs)
  185.   GopherObj *gs;
  186. {
  187.      if (gs->gplus != NULL) {
  188.       STRdestroy(gs->gplus->abstract);
  189.       STRdestroy(gs->gplus->Admin);
  190.       STRdestroy(gs->gplus->ModDate);
  191.       VIAdestroy(gs->gplus->Views);
  192.       BLAdestroy(gs->gplus->OtherBlocks);
  193.  
  194.       free(gs->gplus);
  195.      }
  196. }
  197.  
  198.  
  199. /*
  200.  * Clear out all the crud 
  201.  */
  202.  
  203. void
  204. GSinit(gs)
  205.   GopherObj *gs;
  206. {
  207.      GSsetType(gs, '\0');
  208.      
  209.      STRinit(gs->Title);
  210.      STRinit(gs->Selstr);
  211.      STRinit(gs->Host);
  212.      STRinit(gs->Localfile);
  213.  
  214.      gs->iPort = 0;
  215.      GSsetNum(gs, -1);
  216.      GSsetWeight(gs, 0);
  217.  
  218.      GSsetGplus(gs,FALSE);  /** Default is no gplus **/
  219.      GSsetAsk(gs, FALSE);
  220.  
  221.      GSplusInit(gs);
  222. }
  223.  
  224.  
  225. /*
  226.  * Clear out gopher+ crud if it exists
  227.  */
  228.  
  229. void
  230. GSplusInit(gs)
  231.   GopherObj *gs;
  232. {
  233.      if (gs->gplus != NULL) {
  234.       STRinit(gs->gplus->abstract);
  235.       STRinit(gs->gplus->Admin);
  236.       STRinit(gs->gplus->ModDate);
  237.       VIAinit(gs->gplus->Views);
  238.       STAinit(gs->gplus->OtherBlocks);
  239.  
  240.      }
  241. }
  242.  
  243. /**************************************************/
  244. void
  245. GSsetAbstract(gs, abstract)
  246.   GopherObj *gs;
  247.   char *abstract;
  248. {
  249.      char tmpstr[256];
  250.      Blockobj *bl;
  251.  
  252.      bl = BLnew();
  253.  
  254.      BLsetName(bl, "ABSTRACT");
  255.      BLaddText(bl, abstract);
  256.      
  257.      if (gs->gplus == NULL)
  258.       GSplusnew(gs);
  259.      
  260.      BLApush(gs->gplus->OtherBlocks, bl);
  261.      BLdestroy(bl);
  262. }
  263.  
  264. void
  265. GSsetAdmin(gs, admin)
  266.   GopherObj *gs;
  267.   char *admin;
  268. {
  269.      if (gs->gplus == NULL)
  270.       GSplusnew(gs);
  271.  
  272.      STRset(gs->gplus->Admin, admin);
  273. }     
  274.  
  275. void
  276. GSsetModDate(gs, str)
  277.   GopherObj *gs;
  278.   char *str;
  279. {
  280.      if (gs->gplus == NULL) GSplusnew(gs);
  281.      STRset(gs->gplus->ModDate, str);
  282. }     
  283.  
  284.  
  285.  
  286. /** Add a view for a specific gopherobj **/
  287.  
  288. void
  289. GSaddView(gs, view, language, estsize)
  290.   GopherObj *gs;
  291.   char *view;
  292.   char *language;
  293.   int estsize;
  294. {
  295.      char tmpstr[64];
  296.      VIewobj *temp;
  297.  
  298.      if (estsize > 1024)
  299.       sprintf(tmpstr, "%dk", estsize/1024);
  300.      else
  301.             sprintf(tmpstr, ".%dk", (estsize*10)/1024);
  302.  
  303.      temp = VInew();
  304.  
  305.      VIsetType(temp, view);
  306.      VIsetLang(temp, language);
  307.      VIsetSize(temp, tmpstr);
  308.  
  309.      VIApush(gs->gplus->Views, temp);
  310.      VIdestroy(temp);
  311. }
  312.  
  313.  
  314. void
  315. GSaddBlock(gs, Blockname, file)
  316.   GopherObj *gs;
  317.   char *Blockname;
  318.   char *file;
  319. {
  320.      Blockobj *bl;
  321.  
  322.      bl = BLnew();
  323.      BLsetName(bl, Blockname);
  324.      BLsetFile(bl, file);
  325.      ;
  326.      if (gs->gplus == NULL)
  327.       GSplusnew(gs);
  328.  
  329.      BLApush(gs->gplus->OtherBlocks, bl);
  330. }
  331.  
  332.  
  333.  
  334. /*
  335.  * Send a gopher reference into an fd
  336.  */
  337.  
  338. void
  339. GStoNet(gs, sockfd)
  340.   GopherObj *gs;
  341.   int sockfd;
  342. {
  343.      static char buf[1024];
  344.  
  345.      buf[0] = GSgetType(gs);
  346.  
  347.      if (buf[0] == '\0')    /* For example a .names with no Type */
  348.     buf[0] = '3'; 
  349.  
  350.      sprintf(buf + 1, "%s\t%s\t%s\t%d",
  351.          GSgetTitle(gs),
  352.          GSgetPath(gs),
  353.          GSgetHost(gs),
  354.          GSgetPort(gs));
  355.  
  356.      if (GSisAsk(gs))
  357.       strcat(buf, "\t?\r\n");
  358.      else if (GSisGplus(gs))
  359.       strcat(buf, "\t+\r\n");
  360.      else
  361.       strcat(buf, "\r\n");
  362.      
  363.      writestring(sockfd, buf);
  364.      
  365.      if (DEBUG)
  366.       fprintf(stderr, buf);
  367.  
  368. }
  369.  
  370.  
  371. /*
  372.  * Send a long gopher data descriptor
  373.  *
  374.  * filter is a character array of blocks to send
  375.  */
  376.  
  377. void
  378. GSplustoNet(gs, sockfd, filter)
  379.   GopherObj *gs;
  380.   int sockfd;
  381.   char **filter;
  382. {
  383.      int     i;
  384.      char    tmpstr[256];
  385.      boolean sendviews, sendadmin, sendothers;
  386.  
  387.      if (filter == NULL)
  388.       sendviews = sendadmin = sendothers = TRUE;
  389.      else {
  390.       sendviews = sendadmin = sendothers = FALSE;
  391.       for (i=0; filter[i] != NULL ;i++) {
  392.            if (strcasecmp(filter[i], "VIEWS")==0)
  393.             sendviews = TRUE;
  394.            else if (strcasecmp(filter[i], "ADMIN")==0)
  395.             sendadmin = TRUE;
  396.            else 
  397.             sendothers = TRUE;
  398.       }
  399.      }    
  400.      
  401.      
  402.      /** Send out the old style INFO stuff **/
  403.      writestring(sockfd, "+INFO: ");
  404.      GStoNet(gs,sockfd);
  405.  
  406.      if (GSgplusInited(gs)) {
  407.       /*** Should special case for local filename.... ***/
  408.  
  409.       if (GSgetAdmin(gs) != NULL && GSgetModDate(gs) != NULL && sendadmin){
  410.            writestring(sockfd, "+ADMIN:\r\n Admin: ");
  411.            writestring(sockfd, GSgetAdmin(gs));
  412.            writestring(sockfd, "\r\n Mod-Date: ");
  413.            writestring(sockfd, GSgetModDate(gs));
  414.            writestring(sockfd, "\r\n");
  415.       }
  416.       if (GSgetNumViews(gs) > 0 && sendviews) {
  417.            writestring(sockfd, "+VIEWS:\r\n");
  418.            for (i=0 ; i< GSgetNumViews(gs); i++) {
  419.             VItoLine(GSgetView(gs, i), tmpstr);
  420.             writestring(sockfd, tmpstr);
  421.             writestring(sockfd, "\r\n");
  422.            }
  423.       }
  424.       if (sendothers)
  425.            for (i=0; i< GSgetNumBlocks(gs); i++)
  426.             BLtoNet(GSgetBlock(gs, i),sockfd);
  427.      }
  428. }
  429.  
  430.  
  431. /*** GSplusfromnet() assumes that the leading +INFO text has been read from
  432.      the file descriptor.
  433.  
  434.      It returns 1  if there's more data (and another INFO block)
  435.                 0  if there's EOF
  436.         SOFTERROR caller can keep reading
  437.         HARDERROR caller should stop reading
  438.  ***/
  439.  
  440. int
  441. GSplusfromNet(gs, fd)
  442.   GopherObj *gs;
  443.   int fd;
  444. {
  445.      int result,readok,i;
  446.      boolean nextinfo = FALSE;
  447.      char plusfield;
  448.      Blockobj *bl;
  449.      char inputline[512];
  450.      
  451.      if (gs->gplus == NULL)
  452.       GSplusnew(gs);
  453.  
  454.      bl = BLnew();
  455.  
  456.      /** get the gopher-data descriptor **/
  457.      readok = GSfromNet(gs, fd);
  458.  
  459.      if (readok == HARDERROR)
  460.       return(HARDERROR);
  461.  
  462.      /** If readok is softerror we still need to look for blocks to throw
  463.          away any data before next item **/
  464.  
  465.      /** Now start looking for blocks.  Process blocks if we know how.  **/
  466.      /** State: _GotGREF_ **/
  467.  
  468.      if ((result = readrecvbuf(fd, &plusfield, 1))<0)
  469.       return(HARDERROR);
  470.  
  471.      while (!nextinfo) {
  472.       if (result == 0) {            /*** We're done ***/
  473.            BLdestroy(bl);
  474.  
  475.            if (readok == SOFTERROR)
  476.             return(SOFTERROR);
  477.            else
  478.             return(FOUNDEOF);
  479.       }
  480.       switch (plusfield) {
  481.  
  482.       case '.':
  483.            readline(fd, inputline, sizeof(inputline));
  484.            result = FOUNDEOF;
  485.            break;
  486.  
  487.       case '+':
  488.            /*** State _NewBlock_ ***/
  489.  
  490.            readtotoken(fd,inputline, sizeof(inputline), ':');
  491.  
  492.            if (strcasecmp(inputline, "INFO")==0) {
  493.             /** Get rid of the space. **/
  494.             readrecvbuf(fd, &plusfield,1);
  495.             BLdestroy(bl);
  496.             if (readok == SOFTERROR)
  497.              return(SOFTERROR);
  498.             else
  499.              return(MORECOMING);
  500.            }
  501.  
  502.            /** Specialized fromNets here **/
  503.  
  504.            BLinit(bl);
  505.            if ((result=BLfromNet(bl, fd, inputline)) <0) {
  506.             /** Bad read **/
  507.             BLdestroy(bl);
  508.             return(HARDERROR);
  509.            }
  510.            else if (result == FOUNDEOF)   /** EOF, block read ok **/
  511.             nextinfo = TRUE;
  512.            else 
  513.             nextinfo = FALSE;
  514.  
  515.  
  516.            /*** See what the block is. Transform it if necessary ***/
  517.            if (strcasecmp(BLgetName(bl), "VIEWS")==0) {
  518.             VIAfromBL(gs->gplus->Views, bl);
  519.            } else if (strcasecmp(BLgetName(bl), "ADMIN")==0) {
  520.             for (i=0; i<BLgetNumLines(bl); i++) {
  521.              char *cp;
  522.              
  523.              cp = BLgetLine(bl, i);
  524.              
  525.              if (strncasecmp(cp, "Admin: ",7)==0)
  526.                   GSsetAdmin(gs, cp+7);
  527.              if (strncasecmp(cp, "Mod-Date: ", 10)==0)
  528.                   GSsetModDate(gs, cp+10);
  529.             }
  530.            } else
  531.             BLApush(gs->gplus->OtherBlocks, bl);
  532.  
  533.            break;  /** '+' **/
  534.  
  535.       default: /*** Hmmm plusfield wasn't a plus or '.' **/
  536.            return(HARDERROR);
  537.  
  538.       } /** switch plusfield **/
  539.  
  540.      }   /** While **/
  541.  
  542.      BLdestroy(bl);
  543.  
  544.      return(FOUNDEOF);
  545. }
  546.  
  547.  
  548. void
  549. GStoNetHTML(gs, sockfd)
  550.   GopherObj *gs;
  551.   int sockfd;
  552. {
  553.      static char buf[1024];
  554.      static char pathbuf[1024];
  555.  
  556.      buf[0] = '\0';
  557.      pathbuf[0] = '\0';
  558.  
  559.      /** Convert Path so that spaces are %20 **/
  560.      Tohexstr(GSgetPath(gs), pathbuf);
  561.  
  562.      sprintf(buf, "<A HREF=http://%s:%d/%s>%s</A>",
  563.          GSgetHost(gs),
  564.          GSgetPort(gs),
  565.          pathbuf,
  566.          GSgetTitle(gs));
  567.  
  568.      writestring(sockfd, buf);
  569.  
  570.      if (DEBUG)
  571.       fprintf(stderr, "HTML: %s", buf);
  572.      
  573.      if (GSgetWeight(gs) != 0) {
  574.       sprintf(buf, "Score: %d\r\n", GSgetWeight(gs));
  575.       writestring(sockfd, buf);
  576.      }
  577.      else
  578.       writestring(sockfd, "\r\n");
  579.  
  580. }
  581.  
  582.  
  583. /*
  584.  * Fill in a GopherObj, given an HREF= link from a WWW anchor..
  585.  * So far only works with http
  586.  */
  587.  
  588. void
  589. GSfromHREF(gs, href)
  590.   GopherObj *gs;
  591.   char *href;
  592. {
  593.      char *cp;
  594.      char *host;
  595.      char path[1024];
  596.      
  597.      if (strncasecmp(href, "http://", 7)==0) {
  598.       host = href +7;
  599.       cp  = strchr(href+7, '/');
  600.       if (cp == NULL)
  601.            return;
  602.       
  603.       *cp = '\0';
  604.       strcpy(path, "GET ");
  605.       strcat(path, cp+1);
  606.       GSsetPath(gs, path);
  607.  
  608.       GSsetType(gs, 'h');
  609.  
  610.       GSsetPath(gs, path);
  611.       
  612.       cp = strchr(host, ':');
  613.       if (cp==NULL) 
  614.            GSsetPort(gs, 80);  /** default WWW port **/
  615.       else {
  616.            GSsetPort(gs, atoi(cp+1));
  617.            *cp = '\0';
  618.       }
  619.  
  620.       GSsetHost(gs, host);
  621.      }
  622. }
  623.       
  624.  
  625.  
  626. /* GSfromNet - no comments on the original, so this is my (Mitra's) guess
  627.    GSfromNet reads a gopher style line, it is called from:
  628.    GDfromNet - in which case the gopher line is the whole item or;
  629.    GSplusfromNet - in which case it is the part after the +INFO.
  630.    It should return after reading the whole line, however in gopher+1.2b2
  631.    this is not always the case, especially if it sees a Type=3
  632.    returns:
  633.        1      blank line (I think?)
  634.        0      ok
  635.       -1 HARDERROR    readfield etc error - give up
  636.       -2 SOFTERROR    unrecognized or unhandleable type - skip on to next one
  637. */
  638.  
  639.  
  640.  
  641.  
  642. extern int readfield();
  643. extern int readline();
  644.  
  645. int
  646. GSfromNet(gs, sockfd)
  647.   GopherObj *gs;
  648.   int sockfd;
  649. {
  650.      char foo[1024], *cp;
  651.      
  652.      if (readfield(sockfd, foo, 1024)<= 0) {
  653.       /* EOF or error */
  654.       return(HARDERROR);
  655.      }
  656.  
  657.      GSsetType(gs, foo[0]);
  658.  
  659.      /** Get the kind of file from the first character **/
  660.      /** Filter out files that we can't deal with **/
  661.  
  662.      switch (GSgetType(gs)) {
  663.        case A_FILE:
  664.        case A_DIRECTORY:
  665.        case A_MACHEX:
  666.        case A_PCBIN:
  667.        case A_CSO:
  668.        case A_INDEX:
  669.        case A_TELNET:
  670.        case A_SOUND:
  671.        case A_UNIXBIN:
  672.        case A_GIF:
  673.        case A_HTML:
  674.        case A_TN3270:
  675.        case A_MIME:
  676.        case A_IMAGE:
  677.       break;
  678.        case A_EOI:
  679.       if (foo[1] == '\r' && foo[2] == '\n')
  680.            return(1);
  681.      default:
  682.       /** Can't handle this type **/
  683.       readline(sockfd, foo, 1024);/** Cleanup **/
  684.       return(SOFTERROR);
  685.      }
  686.  
  687.      /** Suck off the User Displayable name **/
  688.      GSsetTitle(gs, foo+1);
  689.      
  690.      /** Suck off the Pathname **/
  691.      if (readfield(sockfd, foo, 1024) == 0)
  692.       return(HARDERROR);
  693.      GSsetPath(gs, foo);
  694.  
  695.      /** Suck off the hostname **/
  696.      if (readfield(sockfd, foo, 1024) == 0)
  697.       return(HARDERROR);
  698.      GSsetHost(gs, foo);
  699.  
  700.      if (readline(sockfd, foo, 1024)==0)
  701.       return(HARDERROR); 
  702.  
  703.      GSsetPort(gs, 0);
  704.  
  705.      /** Get the port number **/
  706.      if ((cp = strchr(foo, '\t')) != NULL) {
  707.       *cp = '\0';
  708.       switch (*(cp+1)) {
  709.       case '?':
  710.            GSsetAsk(gs, TRUE);
  711.       case '+':
  712.            GSsetGplus(gs, TRUE);
  713.            break;
  714.       }
  715.      }
  716.  
  717.      GSsetPort(gs, atoi(foo));
  718.      if (DEBUG) GSplusPrint(gs,"GSfromNet end:");
  719.  
  720.      return(0);
  721. }
  722.  
  723.  
  724. /** Copy a GopherObj ***/
  725.  
  726. void
  727. GScpy(dest, orig)
  728.   GopherObj *dest, *orig;
  729. {
  730.      dest->sFileType = orig->sFileType;
  731.      dest->iPort     = orig->iPort;
  732.      dest->Itemnum   = orig->Itemnum;
  733.  
  734.      GSsetTitle(dest, GSgetTitle(orig));
  735.      GSsetPath(dest, GSgetPath(orig));
  736.      GSsetHost(dest, GSgetHost(orig));
  737.      
  738.      GSsetGplus(dest, GSisGplus(orig));
  739.      GSsetAsk(dest, GSisAsk(orig));
  740.  
  741.      GSsetLocalFile(dest, GSgetLocalFile(orig));
  742.      GSpluscpy(dest, orig);
  743.      
  744. }
  745.  
  746. void
  747. GSpluscpy(dest, orig)
  748.   GopherObj *dest, *orig;
  749. {
  750.      if (GSgplusInited(orig)) {
  751.       if (!GSgplusInited(dest))
  752.            GSplusnew(dest);
  753.       GSsetAdmin(dest, GSgetAdmin(orig));
  754.       GSsetModDate(dest, GSgetModDate(orig));
  755.       VIAcpy(dest->gplus->Views, orig->gplus->Views);
  756.       BLAcpy(dest->gplus->OtherBlocks, orig->gplus->OtherBlocks);
  757.       
  758.      }
  759. }
  760.  
  761. /** GSmerge combines 2 gopher objects overwriting fields defined in overlay **/
  762. /* Called by GDaddGSmerge to merge gs into directory, that is only called 
  763.    by function to do this from a link */
  764. void
  765. GSmerge(gs, overlay)
  766.   GopherObj *gs, *overlay;
  767. {
  768.      char *tempstr;
  769.  
  770.      if (DEBUG) {
  771.     GSplusPrint(gs,"GSmerge: Original");
  772.     GSplusPrint(overlay,"GSmerge: Overlay");
  773.      }
  774.      if (GSgetHost(overlay) == NULL) {
  775.  
  776.       if (GSgetType(overlay) != '\0') {
  777.            /* Just setting Type wont work, since they interelated with Path
  778.             * so set first char of path as well */
  779.         GSsetType(gs, GSgetType(overlay));
  780.         tempstr = GSgetPath(gs);
  781.         tempstr[0] = GSgetType(overlay);
  782.         GSsetPath(gs, tempstr);
  783.       }
  784.       if (GSgetTitle(overlay) != NULL)
  785.            GSsetTitle(gs, GSgetTitle(overlay));
  786.       /* Dont set path - that is the key to the merge, and in the overlay
  787.          most probably has the first char set to ' '  ????*/
  788.       if (GSgetHost(overlay) != NULL)
  789.            GSsetHost(gs, GSgetHost(overlay));
  790.       if (GSgetPort(overlay) != 0)
  791.            GSsetPort(gs, GSgetPort(overlay));
  792.       if (GSgetNum(overlay) != -1)
  793.            GSsetNum(gs, GSgetNum(overlay));
  794.       if (GSgetWeight(overlay) != 0)
  795.            GSsetWeight(gs, GSgetWeight(overlay));
  796.      }
  797.      if (DEBUG) GSplusPrint(gs,"GSmerge: Result");
  798. }
  799.  
  800.  
  801. /** Compare two GopherObjs ***/
  802.  
  803. int
  804. GScmp(gs1, gs2)
  805.   GopherObj *gs1, *gs2;
  806. {
  807.      if (GSgetTitle(gs1) == NULL)
  808.       return(1);
  809.      if (GSgetTitle(gs2) == NULL)
  810.       return(-1);
  811.  
  812.      return(strcmp(GSgetTitle(gs1), GSgetTitle(gs2)));
  813. }
  814.  
  815.  
  816. /*********** The following functions implement the gopher/gopher+
  817.   protocol, mostly
  818.   GSconnect(), then GStransmit(), GSsendHeader() GSrecvHeader();
  819. ************/
  820.  
  821.  
  822.  
  823. /* GSconnect performs a connection to socket 'service' on host
  824.  * 'host'.  Host can be a hostname or ip-address.  If 'host' is null, the
  825.  * local host is assumed.   The parameter full_hostname will, on return,
  826.  * contain the expanded hostname (if possible).  Note that full_hostname is a
  827.  * pointer to a char *, and is allocated by connect_to_gopher()
  828.  *
  829.  * Errors:
  830.  *
  831.  * -1 get service failed
  832.  * -2 get host failed
  833.  * -3 socket call failed
  834.  * -4 connect call failed
  835.  */
  836.  
  837. int
  838. GSconnect(gs)
  839.   GopherObj *gs;
  840. {
  841.      struct sockaddr_in Server;
  842.      struct hostent *HostPtr;
  843.      int iSock = 0;
  844.      int ERRinet = -1;
  845.  
  846. #ifdef _CRAY
  847.      ERRinet = 0xFFFFFFFF;  /* -1 doesn't sign extend on 64 bit machines */
  848. #endif
  849.  
  850.      /*** Find the hostname address ***/
  851.      
  852.      if ((Server.sin_addr.s_addr = inet_addr(GSgetHost(gs))) == ERRinet) {
  853.       if (HostPtr = gethostbyname(GSgetHost(gs))) {
  854.            bzero((char *) &Server, sizeof(Server));
  855.            bcopy(HostPtr->h_addr, (char *) &Server.sin_addr, HostPtr->h_length);
  856.            Server.sin_family = HostPtr->h_addrtype;
  857.       } else
  858.            return (-2);
  859.      } else
  860.       Server.sin_family = AF_INET;
  861.  
  862.      Server.sin_port = (unsigned short) htons(GSgetPort(gs));
  863.  
  864.      /*** Open the socket ***/
  865.  
  866.      if ((iSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  867.       return (-3);
  868.  
  869. #ifndef UCX
  870.      setsockopt(iSock, SOL_SOCKET, ~SO_LINGER, 0, 0);
  871. #endif
  872.  
  873.      setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, 0, 0);
  874.      setsockopt(iSock, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
  875.  
  876.      /*** Connect ***/
  877.  
  878.      if (connect(iSock, (struct sockaddr *) &Server, sizeof(Server)) < 0) {
  879.       closenet(iSock);
  880.       return (-4);
  881.      }
  882.      return(iSock);
  883. }
  884.  
  885. /*
  886.  * GStransmit sends the request from the client to the server
  887.  *
  888.  * All parameters are optional except for gs and sockfd.
  889.  * 
  890.  * the rest pertain to gopher+ transmission.
  891.  */
  892.  
  893. void 
  894. GStransmit(gs, sockfd, search, command, view, ask)
  895.   GopherObj *gs;
  896.   int sockfd;
  897.   char *search;
  898.   char *command;
  899.   char *view;
  900.   char **ask;
  901. {
  902.      char *cp;
  903.      int i;
  904.  
  905.      writestring(sockfd, GSgetPath(gs));
  906.      if (search != NULL) {
  907.       writestring(sockfd, "\t");
  908.       writestring(sockfd, search);
  909.      }
  910.  
  911.      /** Only send if gplus **/
  912.      if  (!GSisGplus(gs)) {
  913.       writestring(sockfd, "\r\n");
  914.       return;
  915.      }
  916.  
  917.      if (command != NULL) {
  918.       writestring(sockfd, "\t");
  919.       writestring(sockfd, command);
  920.      }      
  921.  
  922.      if (view != NULL) {
  923.       writestring(sockfd, view);
  924.      }
  925.      if (ask == NULL) 
  926.       writestring(sockfd, "\r\n");
  927.      else {
  928.       writestring(sockfd, "\t1\r\n");
  929.  
  930.       GSsendHeader(sockfd, -1);
  931.       
  932.       for (i=0; ;i++) {
  933.            cp = ask[i];
  934.  
  935.            if (cp == NULL)
  936.             break;
  937.  
  938.            writestring(sockfd, cp);
  939.            writestring(sockfd, "\r\n");
  940.       }
  941.       writestring(sockfd, ".\r\n");
  942.      }
  943. }
  944.  
  945.  
  946.  
  947. /*
  948.  * GSsendHeader generates an appropriate header on sockfd
  949.  *
  950.  */
  951. void
  952. GSsendHeader(sockfd, size)
  953.   int sockfd;
  954.   int size;
  955. {
  956.      char sizestr[64];
  957.  
  958.      sprintf(sizestr, "+%d\r\n", size);
  959.      writestring(sockfd, sizestr);
  960. }
  961.  
  962.  
  963. /** GSsendErrorHeader sends an error message header/message to the client **/
  964. void
  965. GSsendErrorHeader(gs,sockfd,errortype,errormsg)
  966.   GopherObj *gs;
  967.   int       sockfd;
  968.   int       errortype;
  969.   char      *errormsg;
  970. {
  971.      char tmpstr[512];
  972.  
  973.      sprintf(tmpstr, "-%d %s\r\n", errortype, errormsg);
  974.      writestring(sockfd, tmpstr);
  975. }
  976.  
  977.  
  978.  
  979. /*
  980.  * GSrecvHeader will retrieve a gopher+ header, if it exists
  981.  *
  982.  * It returns the expected number of bytes that are headed our
  983.  * way, if it can.
  984.  * 
  985.  * Otherwise it returns -1 to indicate to read until \r\n.\r\n
  986.  * or -2 to indicate to read to EOF
  987.  *
  988.  * If it encounters an error, it returns 0 and sets errno to the
  989.  * gopher error class.
  990.  */
  991.  
  992. int
  993. GSrecvHeader(gs, sockfd)
  994.   GopherObj *gs;
  995.   int sockfd;
  996. {
  997.      char headerline[128];
  998.      char *cp;
  999.  
  1000.      if (DEBUG) fprintf(stderr,"GSrecvHeader");
  1001.      if (GSisGplus(gs)) {
  1002.       readline(sockfd, headerline, sizeof(headerline));
  1003.       ZapCRLF(headerline);
  1004.       if (*headerline == '+') {
  1005.            if (*(headerline+1) == '-')
  1006.             return(- (atoi(headerline+2)));
  1007.            else
  1008.             return(atoi(headerline+1));
  1009.       } 
  1010.       else if (*headerline == '-') {
  1011.            /*** Oh no! an error! ***/
  1012.            errno = atoi(headerline+1);
  1013.            return(0);
  1014.       }
  1015.      }
  1016.      /*** Guess if we're running old style gopher ***/
  1017.      else {
  1018.       switch (GSgetType(gs)) {
  1019.       case A_SOUND:
  1020.       case A_IMAGE:
  1021.       case A_GIF:
  1022.       case A_UNIXBIN:
  1023.       case A_PCBIN:
  1024.            return(-2);
  1025.            break;
  1026.       default:
  1027.            return(-1);
  1028.       }
  1029.      }
  1030. }
  1031.  
  1032.  
  1033. /*
  1034.  * This routine will load up the item information from a gopher item
  1035.  * if the item hasn't transferred it already...
  1036.  */
  1037.  
  1038. void
  1039. GSgetginfo(gs)
  1040.   GopherObj *gs;
  1041. {
  1042.      int sockfd, bytes;
  1043.      char inputline[256];
  1044.  
  1045.      if (!GSisGplus(gs))
  1046.       return;
  1047.  
  1048.      if (GSgplusInited(gs))
  1049.       return;
  1050.  
  1051.  
  1052.      GSplusnew(gs);
  1053.  
  1054.      /** Send out the request **/
  1055.      if ((sockfd = GSconnect(gs)) <0) {
  1056.       /*check_sock(sockfd, GSgetHost(gs), GSgetPort(gs));*/
  1057.       return;
  1058.      }
  1059.      
  1060.      GStransmit(gs, sockfd, NULL, "!", NULL, NULL);
  1061.      bytes = GSrecvHeader(gs,sockfd);
  1062.  
  1063.      /***  Read off the first info block ***/
  1064.      readtotoken(sockfd, inputline, sizeof(inputline), ' ');
  1065.  
  1066.      GSplusfromNet(gs, sockfd);
  1067.      
  1068. }
  1069.  
  1070.  
  1071. /*
  1072.  * GSfromLink takes an open file descriptor and starts reading from it.
  1073.  * It starts reading.
  1074.  *
  1075.  * It reads until it finds a line it recognizes, then
  1076.  *
  1077.  * It keeps going until it finds
  1078.  *   eof, a non-recognized line, as long as there is a valid Path= line
  1079.  *
  1080.  * returns -1 on an error, 0 for EOF, 1 for success
  1081.  */
  1082.  
  1083. #define    G_PATH    (1<<0)
  1084. #define    G_TYPE  (1<<1)
  1085. #define    G_NAME    (1<<2)
  1086. #define    G_PORT    (1<<3)
  1087. #define    G_HOST    (1<<4)
  1088. #define    G_ALL (G_PATH | G_TYPE | G_NAME | G_PORT | G_HOST)
  1089.  
  1090. int
  1091. GSfromLink(gs, fd, host, port, directory)
  1092.   GopherObj *gs;
  1093.   int       fd;
  1094.   char      *host;
  1095.   int       port;
  1096.   char      *directory;
  1097. {
  1098.      int doneflags = 0;
  1099.      char buf[1024];
  1100.      int bytesread;
  1101.  
  1102.      if (DEBUG) fprintf(stderr,"GSfromLink...");
  1103.      while ((bytesread = readline(fd, buf, 1024))>0) {
  1104.  
  1105.       if (DEBUG)
  1106.            printf(buf);
  1107.  
  1108.       if (buf[0] == '#') {
  1109.            if (doneflags && G_PATH)
  1110.             break;   /* comment */
  1111.            else
  1112.             continue;
  1113.       }
  1114.  
  1115.       ZapCRLF(buf);
  1116.  
  1117.  
  1118.       if (strncmp(buf, "Type=", 5)==0) {
  1119.            GSsetType(gs, buf[5]);
  1120.            if (buf[6] == '+')
  1121.             GSsetGplus(gs, TRUE);
  1122.            doneflags |= G_TYPE;
  1123.       }
  1124.  
  1125.       else if (strncmp(buf, "Name=", 5)==0) {
  1126.            GSsetTitle(gs, buf+5);
  1127.            doneflags |= G_NAME;
  1128.       }
  1129.  
  1130.       else if (strncmp(buf, "Path=", 5)==0) {
  1131.            if (strncmp(buf+5, "~/",2) == 0 ||
  1132.            strncmp(buf+5, "./",2) == 0) {
  1133.             char tmpstr[256];
  1134.             
  1135.             *tmpstr = '.';
  1136.             strcpy(tmpstr+1, directory);
  1137.             if (strcmp(directory, "/")==0)
  1138.              strcat(tmpstr, buf+7);
  1139.             else
  1140.              strcat(tmpstr, buf+6);
  1141.             GSsetPath(gs, tmpstr);
  1142.            } else
  1143.             GSsetPath(gs, buf+5);
  1144.            doneflags |= G_PATH;
  1145.       }
  1146.        
  1147.       else if (strncmp(buf, "Host=", 5)==0) {
  1148.            if (buf[5] == '+' && buf[6] == '\0')
  1149.             GSsetHost(gs, host);
  1150.            else
  1151.             GSsetHost(gs, buf+5);
  1152.  
  1153.            doneflags |= G_HOST;
  1154.       }
  1155.  
  1156.       else if (strncmp(buf, "Port=", 5)==0) {
  1157.            if (buf[5] == '+' && buf[6] == '\0')
  1158.             GSsetPort(gs, port);
  1159.            else
  1160.             GSsetPort(gs, atoi(buf+5));
  1161.  
  1162.            doneflags |= G_PORT;
  1163.       }
  1164.  
  1165.       else if (strncmp(buf, "Numb=", 5)==0)
  1166.            GSsetNum(gs, atoi(buf+5));
  1167.  
  1168.       else if (strncmp(buf, "Abstract=", 9)==0)
  1169.            GSsetAbstract(gs, buf+9);
  1170.       else if (strncmp(buf, "Admin=", 6) == 0)
  1171.            GSsetAdmin(gs, buf +6);
  1172.       else
  1173.            break;  /*** Unknown name/item ***/
  1174.      }
  1175.  
  1176.      if (DEBUG)
  1177.       printf("Done with this link item\n");
  1178.  
  1179.      if (bytesread == 0) {
  1180.       if (doneflags && G_PATH)
  1181.            return(FOUNDEOF);  /** Found the eof, plus there's a g item **/
  1182.       else 
  1183.            return(HARDERROR); /** Mangled item, plus eof, stop the game **/
  1184.      }
  1185.  
  1186.      if (doneflags && G_PATH)
  1187.       return(MORECOMING);
  1188.      else
  1189.       return(SOFTERROR);
  1190. }
  1191.  
  1192.  
  1193.  
  1194. void
  1195. GStoLink(gs, fd)
  1196.   GopherObj *gs;
  1197.   int fd;
  1198. {
  1199.      char gtype[2];
  1200.      char portnum[16];
  1201.      
  1202.      gtype[0] = GSgetType(gs);
  1203.      gtype[1] = '\0';
  1204.  
  1205.      writestring(fd, "#");
  1206.      writestring(fd, "\nType=");
  1207.      writestring(fd, gtype);
  1208.      if (GSisGplus(gs))
  1209.       writestring(fd, "+");
  1210.      writestring(fd, "\nName=");
  1211.      writestring(fd, GSgetTitle(gs));
  1212.      writestring(fd, "\nPath=");
  1213.      writestring(fd, GSgetPath(gs));
  1214.      writestring(fd, "\nHost=");
  1215.      writestring(fd, GSgetHost(gs));
  1216.      writestring(fd, "\nPort=");
  1217.      sprintf(portnum, "%d", GSgetPort(gs));
  1218.      writestring(fd, portnum);
  1219.      writestring(fd, "\n");
  1220.      if (GSisGplus(gs) && GSgplusInited(gs)) {
  1221.       writestring(fd, "Admin=");
  1222.       writestring(fd, GSgetAdmin(gs));
  1223.       writestring(fd, "\nModDate=");
  1224.       writestring(fd, GSgetModDate(gs));
  1225.       writestring(fd, "\n");
  1226.      }
  1227. }
  1228.  
  1229.  
  1230. boolean
  1231. GSisText(gs, view)
  1232.   GopherObj *gs;
  1233.   char *view;
  1234. {
  1235.      char viewstowage[64], *cp;
  1236.  
  1237.      strcpy(viewstowage, view);
  1238.      if ((cp=strchr(viewstowage, ' '))!=NULL) {
  1239.       *cp = '\0';
  1240.       view = viewstowage;
  1241.      }
  1242.  
  1243.      if (view == NULL) {
  1244.       switch (GSgetType(gs)) {
  1245.       case A_FILE:
  1246.       case A_MACHEX:
  1247.            return(TRUE);
  1248.  
  1249.       default:
  1250.            return(FALSE);
  1251.       }
  1252.      }
  1253.      else {
  1254.       if (strcasecmp(view, "Text") == 0 ||
  1255.           strncasecmp(view, "application/postscript", 21)==0)
  1256.            return(TRUE);
  1257.       else
  1258.            return(FALSE);
  1259.      }
  1260. }
  1261.  
  1262. void
  1263. GSplusPrint(gs,head)
  1264.   GopherObj *gs;
  1265.   char *head;
  1266. {
  1267.     fprintf(stderr,"%s: Type=%c,Title=%s,Path=%s,Host=%s,Port=%d,Num=%d,Weight=%d,Plus=%d,Ask=%d\r\n",
  1268.         head,
  1269.         GSgetType(gs),
  1270.         GSgetTitle(gs),
  1271.         GSgetPath(gs),
  1272.         GSgetHost(gs),
  1273.         GSgetPort(gs),
  1274.         GSgetNum(gs),
  1275.         GSgetWeight(gs),
  1276.         GSisGplus(gs),
  1277.         GSisAsk(gs)
  1278.     );
  1279. }
  1280.  
  1281.      
  1282.  
  1283.