home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTGopher.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  18.4 KB  |  781 lines

  1. /*            GOPHER ACCESS                HTGopher.c
  2. **            =============
  3. **
  4. ** History:
  5. **    26 Sep 90    Adapted from other accesses (News, HTTP) TBL
  6. **    29 Nov 91    Downgraded to C, for portable implementation.
  7. */
  8.  
  9. /* Implements:
  10. */
  11. #include "HTGopher.h"
  12.  
  13.  
  14. #define GOPHER_PORT 70        /* See protocol spec */
  15. #define BIG 1024        /* Bug */
  16. #define LINE_LENGTH 256     /* Bug */
  17.  
  18. /*    Gopher entity types:
  19. */
  20. #define GOPHER_TEXT        '0'
  21. #define GOPHER_MENU        '1'
  22. #define GOPHER_CSO        '2'
  23. #define GOPHER_ERROR        '3'
  24. #define GOPHER_MACBINHEX    '4'
  25. #define GOPHER_PCBINHEX     '5'
  26. #define GOPHER_UUENCODED    '6'
  27. #define GOPHER_INDEX        '7'
  28. #define GOPHER_TELNET        '8'
  29. #define GOPHER_BINARY        '9'
  30. #define GOPHER_DUPLICATE    '+'
  31.  
  32. #define GOPHER_GIF        'g'
  33. #define GOPHER_IMAGE        'I'
  34. #define GOPHER_TN3270        'T'
  35.  
  36. #define GOPHER_HTML        'h'             /* HTML */
  37. #define GOPHER_WWW        'w'             /* W3 address */
  38. #define GOPHER_SOUND        's'
  39.  
  40. #define GOPHER_PLUS_IMAGE    ':'
  41. #define GOPHER_PLUS_MOVIE    ';'
  42. #define GOPHER_PLUS_SOUND    '<'
  43.  
  44. #include <ctype.h>
  45. #include "HTUtils.h"            /* Coding convention macros */
  46. #include "tcp.h"
  47.  
  48. #include "HTAlert.h"
  49. #include "HTParse.h"
  50. #include "HTFormat.h"
  51. #include "HTFile.h"
  52. #include "HTTCP.h"
  53.  
  54. /* #define TRACE 1 */
  55.  
  56. /*        Hypertext object building machinery
  57. */
  58. #include "HTML.h"
  59.  
  60. #define PUTC(c) (*targetClass.put_character)(target, c)
  61. #define PUTS(s) (*targetClass.put_string)(target, s)
  62. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  63. #define END(e) (*targetClass.end_element)(target, e)
  64. #define END_TARGET (*targetClass.end_document)(target)
  65. #define FREE_TARGET (*targetClass.free)(target)
  66. struct _HTStructured {
  67.     CONST HTStructuredClass *    isa;
  68.     /* ... */
  69. };
  70.  
  71. PRIVATE HTStructured *target;            /* the new hypertext */
  72. PRIVATE HTStructuredClass targetClass;        /* Its action routines */
  73.  
  74.  
  75. /*    Module-wide variables
  76. */
  77. PRIVATE int s;                    /* Socket for GopherHost */
  78.  
  79.  
  80. /*    Matrix of allowed characters in filenames
  81. **    -----------------------------------------
  82. */
  83.  
  84. PRIVATE BOOL acceptable[256];
  85. PRIVATE BOOL acceptable_inited = NO;
  86.  
  87. PRIVATE void init_acceptable NOARGS
  88. {
  89.     unsigned int i;
  90.     char * good =
  91.       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
  92.     for(i=0; i<256; i++) acceptable[i] = NO;
  93.     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
  94.     acceptable_inited = YES;
  95. }
  96.  
  97. PRIVATE CONST char hex[17] = "0123456789abcdef";
  98.  
  99. /*    Decode one hex character
  100. */
  101.  
  102. PRIVATE char from_hex ARGS1(char, c)
  103. {
  104.     return          (c>='0')&&(c<='9') ? c-'0'
  105.             : (c>='A')&&(c<='F') ? c-'A'+10
  106.             : (c>='a')&&(c<='f') ? c-'a'+10
  107.             :               0;
  108. }
  109.  
  110.  
  111.  
  112. /*    Paste in an Anchor
  113. **    ------------------
  114. **
  115. **    The title of the destination is set, as there is no way
  116. **    of knowing what the title is when we arrive.
  117. **
  118. ** On entry,
  119. **    HT    is in append mode.
  120. **    text    points to the text to be put into the file, 0 terminated.
  121. **    addr    points to the hypertext refernce address 0 terminated.
  122. */
  123. PRIVATE void write_anchor ARGS3(CONST char *,text, CONST char *,addr,
  124.                 char *, image_text)
  125. {
  126.     PUTS ("<A HREF=\"");
  127.     PUTS (addr);
  128.     PUTS ("\">");
  129.  
  130.     /* Throw in an inlined image, if one has been requested. */
  131.     if (image_text)
  132.       {
  133.     PUTS ("<IMG SRC=\"");
  134.     PUTS (image_text);
  135.     PUTS ("\"> ");
  136.       }
  137.  
  138.     PUTS(text);
  139.     PUTS("</A>");
  140. }
  141.  
  142.  
  143. /*    Parse a Gopher Menu document
  144. **    ============================
  145. **
  146. */
  147.  
  148. PRIVATE int parse_menu ARGS2 (
  149.     CONST char *,        arg,
  150.     HTParentAnchor *,    anAnchor)
  151. {
  152.   char gtype;
  153.   char ch;
  154.   char line[BIG];
  155.   char address[BIG];
  156.   char *name, *selector;        /* Gopher menu fields */
  157.   char *host;
  158.   char *port;
  159.   char *p = line;
  160.   extern int interrupted_in_htgetcharacter;
  161.   CONST char *title;
  162.  
  163. #define TAB        '\t'
  164. #define HEX_ESCAPE    '%'
  165.  
  166.   HTProgress ("Retrieving Gopher menu.");
  167.  
  168.   PUTS("<H1>Gopher Menu</H1>\n");
  169.  
  170.   START(HTML_DL);
  171.   while ((ch=HTGetCharacter ()) != (char)EOF)
  172.     {
  173.       if (interrupted_in_htgetcharacter)
  174.     {
  175.       if (TRACE)
  176.         fprintf (stderr, "parse_menu: picked up interrupt in htgc\n");
  177.       (*targetClass.handle_interrupt)(target);
  178.       return HT_INTERRUPTED;
  179.     }
  180.       if (ch != LF)
  181.     {
  182.       *p = ch;        /* Put character in line */
  183.       if (p< &line[BIG-1]) p++;
  184.     }
  185.       else
  186.     {
  187.       *p++ = 0;        /* Terminate line */
  188.       p = line;        /* Scan it to parse it */
  189.       port = 0;        /* Flag "not parsed" */
  190.       if (TRACE)
  191.         fprintf(stderr, "HTGopher: Menu item: %s\n", line);
  192.       gtype = *p++;
  193.  
  194.       /* Break on line with a dot by itself */
  195.       if ((gtype=='.') && ((*p=='\r') || (*p==0)))
  196.         break;
  197.  
  198.       if (gtype && *p)
  199.         {
  200.           name = p;
  201.           selector = strchr(name, TAB);
  202.           START(HTML_DD);
  203.           if (selector)
  204.         {
  205.           *selector++ = 0;    /* Terminate name */
  206.           host = strchr(selector, TAB);
  207.           if (host)
  208.             {
  209.               *host++ = 0;    /* Terminate selector */
  210.               port = strchr(host, TAB);
  211.               if (port)
  212.             {
  213.               char *junk;
  214.               port[0] = ':';        /* delimit host a la W3 */
  215.               junk = strchr(port, TAB);
  216.               if (junk)
  217.                 *junk++ = 0;    /* Chop port */
  218.               if ((port[1]=='0') && (!port[2]))
  219.                 port[0] = 0;    /* 0 means none */
  220.             } /* no port */
  221.             } /* host ok */
  222.         } /* selector ok */
  223.         } /* gtype and name ok */
  224.  
  225.       if (gtype == GOPHER_WWW)
  226.         {    /* Gopher pointer to W3 */
  227.           write_anchor(name, selector, "internal-gopher-text");
  228.         }
  229.       else if (port)
  230.         {        /* Other types need port */
  231.           if (gtype == GOPHER_TELNET)
  232.         {
  233.           if (*selector)
  234.             sprintf(address, "telnet://%s@%s/",
  235.                 selector, host);
  236.           else
  237.             sprintf(address, "telnet://%s/", host);
  238.         }
  239.           else if (gtype == GOPHER_TN3270)
  240.         {
  241.           if (*selector)
  242.             sprintf(address, "tn3270://%s@%s/",
  243.                 selector, host);
  244.           else
  245.             sprintf(address, "tn3270://%s/", host);
  246.         }
  247.           else
  248.         {            /* If parsed ok */
  249.           char *q;
  250.           unsigned char *p;
  251.           sprintf(address, "//%s/%c", host, gtype);
  252.           q = address+ strlen(address);
  253.           for(p=(unsigned char *)selector; *p; p++)
  254.             {    /* Encode selector string */
  255.               if (acceptable[*p]) *q++ = *p;
  256.               else
  257.             {
  258.               *q++ = HEX_ESCAPE;    /* Means hex coming */
  259.               *q++ = hex[(*p) >> 4];
  260.               *q++ = hex[(*p) & 15];
  261.             }
  262.             }
  263.           *q++ = 0;            /* terminate address */
  264.         }
  265.           /* Error response from Gopher doesn't deserve to
  266.          be a hyperlink. */
  267.           if (strcmp (address, "//error.host:1/0") != 0 &&
  268.           strcmp (address, "//error/0error") != 0 &&
  269.           strcmp (address, "//:/0") != 0 &&
  270.           gtype != GOPHER_ERROR)
  271.         {
  272.           switch (gtype)
  273.             {
  274.             case GOPHER_MENU:
  275.               write_anchor(name, address, "internal-gopher-menu");
  276.               break;
  277.             case GOPHER_TEXT:
  278.               write_anchor(name, address, "internal-gopher-text");
  279.               break;
  280.             case GOPHER_INDEX:
  281.             case GOPHER_CSO:
  282.               write_anchor(name, address, "internal-gopher-index");
  283.               break;
  284.             case GOPHER_IMAGE:
  285.             case GOPHER_GIF:
  286.             case GOPHER_PLUS_IMAGE:
  287.               write_anchor(name, address, "internal-gopher-image");
  288.               break;
  289.             case GOPHER_SOUND:
  290.             case GOPHER_PLUS_SOUND:
  291.               write_anchor(name, address, "internal-gopher-sound");
  292.               break;
  293.             case GOPHER_PLUS_MOVIE:
  294.               write_anchor(name, address, "internal-gopher-movie");
  295.               break;
  296.             case GOPHER_TELNET:
  297.             case GOPHER_TN3270:
  298.               write_anchor(name, address, "internal-gopher-telnet");
  299.               break;
  300.             case GOPHER_BINARY:
  301.             case GOPHER_MACBINHEX:
  302.             case GOPHER_PCBINHEX:
  303.             case GOPHER_UUENCODED:
  304.               write_anchor(name, address, "internal-gopher-binary");
  305.               break;
  306.             default:
  307.               write_anchor(name, address, "internal-gopher-unknown");
  308.               break;
  309.             }
  310.         }
  311.           else
  312.         {
  313.           /* Good error handling??? */
  314.           PUTS(line);
  315.         }
  316.         }
  317.       else
  318.         { /* parse error */
  319.           if (TRACE) fprintf(stderr,
  320.                  "HTGopher: Bad menu item.\n");
  321.           PUTS(line);
  322.         } /* parse error */
  323.       p = line;    /* Start again at beginning of line */
  324.     } /* if end of line */
  325.     } /* Loop over characters */
  326.   if (interrupted_in_htgetcharacter)
  327.     {
  328.       if (TRACE)
  329.     fprintf (stderr, "parse_menu: picked up interrupt in htgc\n");
  330.       (*targetClass.handle_interrupt)(target);
  331.       return HT_INTERRUPTED;
  332.     }
  333.  
  334.   END(HTML_DL);
  335.   END_TARGET;
  336.   FREE_TARGET;
  337.  
  338.   HTProgress ("Retrieved Gopher menu.");
  339.  
  340.   return 1;
  341. }
  342.  
  343. /*    Display a Gopher Index document
  344. **    -------------------------------
  345. */
  346.  
  347. PRIVATE void display_index ARGS2 (
  348.     CONST char *,    arg,
  349.     HTParentAnchor *,anAnchor)
  350. {
  351.   PUTS("<H1>Searchable Gopher Index</H1> <ISINDEX>");
  352.  
  353.   END_TARGET;
  354.   FREE_TARGET;
  355.   return;
  356. }
  357.  
  358.  
  359. /*    Display a Gopher CSO document
  360. **    -----------------------------
  361. */
  362.  
  363. PRIVATE void display_cso ARGS2 (
  364.     CONST char *,    arg,
  365.     HTParentAnchor *,anAnchor)
  366. {
  367.   PUTS("<H1>Searchable CSO Phonebook</H1> <ISINDEX>");
  368.  
  369.   END_TARGET;
  370.   FREE_TARGET;
  371.   return;
  372. }
  373.  
  374.  
  375. /*    Parse a Gopher CSO document
  376.  **    ============================
  377.  **
  378.  **   Accepts an open socket to a CSO server waiting to send us
  379.  **   data and puts it on the screen in a reasonable manner.
  380.  **
  381.  **   Perhaps this data can be automatically linked to some
  382.  **   other source as well???
  383.  **
  384.  **   Hacked into place by Lou Montulli@ukanaix.cc.ukans.edu
  385.  **
  386.  */
  387. PRIVATE int parse_cso ARGS2 (CONST char *,    arg,
  388.                  HTParentAnchor *,anAnchor)
  389. {
  390.   char ch;
  391.   char line[BIG];
  392.   char *p = line;
  393.   char *second_colon, last_char='\0';
  394.   extern int interrupted_in_htgetcharacter;
  395.  
  396.   HTProgress ("Retrieving CSO search results.");
  397.  
  398.   PUTS("<H1>CSO Search Results</H1>\n<PRE>");
  399.  
  400.   /* start grabbing chars from the network */
  401.   while ((ch=HTGetCharacter ()) != (char)EOF)
  402.     {
  403.       if (interrupted_in_htgetcharacter)
  404.     {
  405.       if (TRACE)
  406.         fprintf (stderr, "parse_cso: picked up interrupt in htgc\n");
  407.       (*targetClass.handle_interrupt)(target);
  408.       return HT_INTERRUPTED;
  409.     }
  410.       if (ch != '\n')
  411.     {
  412.       *p = ch;        /* Put character in line */
  413.       if (p< &line[BIG-1]) p++;
  414.     }
  415.       else
  416.     {
  417.       *p++ = 0;        /* Terminate line */
  418.       p = line;        /* Scan it to parse it */
  419.  
  420.     /* OK we now have a line in 'p' lets parse it and print it */
  421.  
  422.       /* Break on line that begins with a 2. It's the end of
  423.        * data.
  424.        */
  425.       if (*p == '2')
  426.         break;
  427.  
  428.       /*  lines beginning with 5 are errors,
  429.        *  print them and quit
  430.        */
  431.       if (*p == '5') {
  432.         START(HTML_H2);
  433.         PUTS(p+4);
  434.         END(HTML_H2);
  435.         break;
  436.       }
  437.  
  438.       if(*p == '-') {
  439.          /*  data lines look like  -200:#:
  440.           *  where # is the search result number and can be multiple
  441.           *  digits (infinate?)
  442.           *  find the second colon and check the digit to the
  443.           *  left of it to see if they are diferent
  444.           *  if they are then a different person is starting.
  445.           *  make this line an <h2>
  446.           */
  447.  
  448.         /* find the second_colon */
  449.          second_colon = strchr( strchr(p,':')+1, ':');
  450.  
  451.          if(second_colon != NULL) {  /* error check */
  452.  
  453.          if (*(second_colon-1) != last_char)   /* print seperator */
  454.            {
  455.              END(HTML_PRE);
  456.              START(HTML_H2);
  457.            }
  458.  
  459.          /* right now the record appears with the alias (first line)
  460.           * as the header and the rest as <pre> text
  461.           * It might look better with the name as the
  462.           * header and the rest as a <ul> with <li> tags
  463.           * I'm not sure whether the name field comes in any
  464.           * special order or if its even required in a record,
  465.           * so for now the first line is the header no matter
  466.           * what it is (it's almost always the alias)
  467.           * A <dl> with the first line as the <DT> and
  468.           * the rest as some form of <DD> might good also?
  469.           */
  470.  
  471.          /* print data */
  472.          PUTS(second_colon+1);
  473.          PUTS("\n");
  474.  
  475.          if (*(second_colon-1) != last_char)   /* end seperator */
  476.            {
  477.              END(HTML_H2);
  478.              START(HTML_PRE);
  479.            }
  480.  
  481.           /* save the char before the second colon
  482.            * for comparison on the next pass
  483.            */
  484.          last_char =  *(second_colon-1) ;
  485.  
  486.          } /* end if second_colon */
  487.       } /* end if *p == '-' */
  488.     } /* if end of line */
  489.  
  490.     } /* Loop over characters */
  491.   if (interrupted_in_htgetcharacter)
  492.     {
  493.       if (TRACE)
  494.     fprintf (stderr, "parse_cso: picked up interrupt in htgc\n");
  495.       (*targetClass.handle_interrupt)(target);
  496.       return HT_INTERRUPTED;
  497.     }
  498.  
  499.   /* end the text block */
  500.   PUTS("\n<PRE>");
  501.   END_TARGET;
  502.   FREE_TARGET;
  503.  
  504.   HTProgress ("Retrieved CSO search results.");
  505.  
  506.   return 1;  /* all done */
  507. } /* end of procedure */
  508.  
  509.  
  510.  
  511. /*        De-escape a selector into a command
  512. **        -----------------------------------
  513. **
  514. **    The % hex escapes are converted. Otheriwse, the string is copied.
  515. */
  516. PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
  517. {
  518.   char *p;
  519.  
  520.   if (!selector)
  521.     return;
  522.   if (!command)
  523.     return;
  524.  
  525.   p = strdup (selector);
  526.   HTUnEscape (p);
  527.  
  528.   strcpy (command, p);
  529.  
  530.   free (p);
  531.  
  532. #if 0
  533.   for (p = command; *p; p++)
  534.     if (*p == '+')
  535.       *p = ' ';
  536. #endif
  537.  
  538.   return;
  539. }
  540.  
  541.  
  542. /*        Load by name                    HTLoadGopher
  543. **        ============
  544. **
  545. **     Bug:    No decoding of strange data types as yet.
  546. **
  547. */
  548. PUBLIC int HTLoadGopher ARGS4(
  549.     char *,     arg,
  550.     HTParentAnchor *,    anAnchor,
  551.     HTFormat,        format_out,
  552.     HTStream*,        sink)
  553. {
  554.   char *command;            /* The whole command */
  555.   int status;                /* tcp return */
  556.   char gtype;                /* Gopher Node type */
  557.   char * selector;            /* Selector string */
  558.   int rv = 0;
  559.  
  560.   if (!acceptable_inited) init_acceptable();
  561.  
  562.   if (!arg)
  563.     return -3;        /* Bad if no name sepcified    */
  564.   if (!*arg)
  565.     return -2;        /* Bad if name had zero length    */
  566.  
  567.   if (TRACE) fprintf(stderr, "HTGopher: Looking for %s\n", arg);
  568.  
  569.   /* Get entity type, and selector string.
  570.    */
  571.   {
  572.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  573.     gtype = '1';                /* Default = menu */
  574.     selector = p1;
  575.     if ((*selector++=='/') && (*selector))
  576.       { /* Skip first slash */
  577.     gtype = *selector++;            /* Pick up gtype */
  578.       }
  579.     if (gtype == GOPHER_INDEX)
  580.       {
  581.     char * query;
  582.     query = strchr(selector, '?');  /* Look for search string */
  583.     if (!query || !query[1])
  584.       {        /* No search required */
  585.         target = HTML_new(anAnchor, format_out, sink);
  586.         targetClass = *target->isa;
  587.         display_index(arg, anAnchor);    /* Display "cover page" */
  588.         return HT_LOADED;            /* Local function only */
  589.       }
  590.     *query++ = 0;            /* Skip '?'     */
  591.     HTUnEscape (query);
  592.     command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
  593.  
  594.     de_escape(command, selector);
  595.  
  596.     strcat(command, "\t");
  597.     strcat(command, query);
  598.       }
  599.     else if (gtype == GOPHER_CSO)
  600.       {
  601.     char * query;
  602.     query = strchr(selector, '?');      /* Look for search string */
  603.     if (!query || !query[1])
  604.       {         /* No search required */
  605.         target = HTML_new(anAnchor, format_out, sink);
  606.         targetClass = *target->isa;
  607.         display_cso(arg, anAnchor);     /* Display "cover page" */
  608.         return HT_LOADED;                /* Local function only */
  609.       }
  610.     *query++ = 0;                /* Skip '?'     */
  611.     HTUnEscape (query);
  612.     command = malloc(strlen("query")+ 1 + strlen(query)+ 2 + 1);
  613.  
  614.     de_escape(command, selector);
  615.  
  616.     strcpy(command, "query ");
  617.     strcat(command, query);
  618.       }
  619.     else
  620.       {             /* Not index */
  621.     command = malloc(strlen(selector)+2+1);
  622.     de_escape(command, selector);
  623.       }
  624.     free(p1);
  625.   }
  626.  
  627.   /* Patch security hole. */
  628.   {
  629.     char *tmp;
  630.     for (tmp = command; *tmp; tmp++)
  631.       if (*tmp == CR || *tmp == LF)
  632.     *tmp = ' ';
  633. #if 0
  634.     if (TRACE)
  635.       fprintf (stderr, "Fixed security hole: '%s'\n", command);
  636. #endif
  637.     *tmp++ = CR;
  638.     *tmp++ = LF;
  639.     *tmp++ = 0;
  640. #if 0
  641.     if (TRACE)
  642.       fprintf (stderr, "Prepared command: '%s'\n", command);
  643. #endif
  644.   }
  645.  
  646.   status = HTDoConnect (arg, "Gopher", 70, &s);
  647.   if (status == HT_INTERRUPTED)
  648.     {
  649.       /* Interrupt cleanly. */
  650.       if (TRACE)
  651.     fprintf (stderr,
  652.          "Gopher: Interrupted on connect; recovering cleanly.\n");
  653.       HTProgress ("Connection interrupted.");
  654.       return HT_INTERRUPTED;
  655.     }
  656.   if (status<0)
  657.     {
  658.       if (TRACE)
  659.     fprintf(stderr,
  660.         "HTTPAccess: Unable to connect to remote host for `%s'.\n",
  661.         arg);
  662.       free(command);
  663.       return HT_NOT_LOADED;
  664.     }
  665.  
  666.   HTInitInput(s);        /* Set up input buffering */
  667.  
  668.   if (TRACE)
  669.     fprintf(stderr,
  670.         "HTGopher: Connected, writing command `%s' to socket %d\n",
  671.         command, s);
  672.  
  673.   status = NETWRITE(s, command, (int)strlen(command));
  674.   free(command);
  675.   if (status<0)
  676.     {
  677.       if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
  678.       NETCLOSE (s);
  679.       return HT_NOT_LOADED;
  680.     }
  681.  
  682.   /* Now read the data from the socket: */
  683.   switch (gtype)
  684.     {
  685.       int compressed;
  686.       HTAtom *enc;
  687.       extern int tweak_gopher_types;
  688.  
  689.     case GOPHER_MENU:
  690.     case GOPHER_INDEX:
  691.       target = HTML_new(anAnchor, format_out, sink);
  692.       targetClass = *target->isa;
  693.       rv = parse_menu(arg, anAnchor);
  694.       break;
  695.  
  696.     case GOPHER_CSO:
  697.       target = HTML_new(anAnchor, format_out, sink);
  698.       targetClass = *target->isa;
  699.       rv = parse_cso(arg, anAnchor);
  700.       break;
  701.  
  702.     case GOPHER_MACBINHEX:
  703.     case GOPHER_PCBINHEX:
  704.     case GOPHER_UUENCODED:
  705.     case GOPHER_BINARY:
  706.       if (!tweak_gopher_types)
  707.     rv = HTParseSocket(WWW_BINARY, format_out, anAnchor, s, sink, 0);
  708.       else
  709.     rv = HTParseSocket(HTFileFormat (arg, &enc, WWW_BINARY, &compressed),
  710.                format_out, anAnchor, s, sink, 0);
  711.       break;
  712.  
  713.     case GOPHER_GIF:
  714.     case GOPHER_IMAGE:
  715.     case GOPHER_PLUS_IMAGE:
  716.       if (!tweak_gopher_types)
  717.     rv = HTParseSocket(HTAtom_for ("image/gif"),
  718.                format_out, anAnchor, s, sink, 0);
  719.       else
  720.     rv = HTParseSocket(HTFileFormat (arg, &enc, HTAtom_for ("image/gif"),
  721.                      &compressed),
  722.                format_out, anAnchor, s, sink, 0);
  723.       break;
  724.  
  725.     case GOPHER_SOUND:
  726.     case GOPHER_PLUS_SOUND:
  727.       if (!tweak_gopher_types)
  728.     rv = HTParseSocket(HTAtom_for ("audio/basic"),
  729.                format_out, anAnchor, s, sink, 0);
  730.       else
  731.     rv = HTParseSocket(HTFileFormat (arg, &enc,
  732.                      HTAtom_for ("audio/basic"),
  733.                      &compressed),
  734.                format_out, anAnchor, s, sink, 0);
  735.       break;
  736.  
  737.     case GOPHER_PLUS_MOVIE:
  738.       /* Sigh..... */
  739.       if (!tweak_gopher_types)
  740.     rv = HTParseSocket(HTAtom_for ("video/mpeg"),
  741.                format_out, anAnchor, s, sink, 0);
  742.       else
  743.     rv = HTParseSocket(HTFileFormat (arg, &enc,
  744.                      HTAtom_for ("video/mpeg"),
  745.                      &compressed),
  746.                format_out, anAnchor, s, sink, 0);
  747.       break;
  748.  
  749.     case GOPHER_HTML:
  750.       if (!tweak_gopher_types)
  751.     rv = HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink, 0);
  752.       else
  753.     rv = HTParseSocket(HTFileFormat (arg, &enc, WWW_HTML, &compressed),
  754.                format_out, anAnchor, s, sink, 0);
  755.       break;
  756.  
  757.     case GOPHER_TEXT:
  758.     default:            /* @@ parse as plain text */
  759.       if (!tweak_gopher_types)
  760.     rv = HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink, 0);
  761.       else
  762.     rv = HTParseSocket
  763.       (HTFileFormat (arg, &enc, WWW_PLAINTEXT, &compressed),
  764.        format_out, anAnchor, s, sink, 0);
  765.       break;
  766.     } /* switch(gtype) */
  767.  
  768. /*  NETCLOSE(s);  Already closed... */
  769.   if (rv == HT_INTERRUPTED)
  770.     {
  771.       HTProgress ("Connection interrupted.");
  772.       return HT_INTERRUPTED;
  773.     }
  774.   else
  775.     {
  776.       return HT_LOADED;
  777.     }
  778. }
  779.  
  780. PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL };
  781.