about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTGopher.c
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2012-02-20 01:32:18 -0500
committerThomas E. Dickey <dickey@invisible-island.net>2012-02-20 01:32:18 -0500
commitbb5fd6e44e480f571bcb713788cc50eea44095e5 (patch)
treedc3b9975b9bf9e18ce454348ab31ae232a372107 /WWW/Library/Implementation/HTGopher.c
parent3e8c172cd64e8a34029b60208c0d3016d3609505 (diff)
downloadlynx-snapshots-bb5fd6e44e480f571bcb713788cc50eea44095e5.tar.gz
snapshot of project "lynx", label v2-8-8dev_10b
Diffstat (limited to 'WWW/Library/Implementation/HTGopher.c')
-rw-r--r--WWW/Library/Implementation/HTGopher.c1973
1 files changed, 0 insertions, 1973 deletions
diff --git a/WWW/Library/Implementation/HTGopher.c b/WWW/Library/Implementation/HTGopher.c
deleted file mode 100644
index 885943e6..00000000
--- a/WWW/Library/Implementation/HTGopher.c
+++ /dev/null
@@ -1,1973 +0,0 @@
-/*
- * $LynxId: HTGopher.c,v 1.55 2011/06/11 13:06:08 tom Exp $
- *
- *			GOPHER ACCESS				HTGopher.c
- *			=============
- *
- *  History:
- *	26 Sep 90	Adapted from other accesses (News, HTTP) TBL
- *	29 Nov 91	Downgraded to C, for portable implementation.
- *	10 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Added a
- *			  form-based CSO/PH gateway.  Can be invoked via a
- *			  "cso://host[:port]/" or "gopher://host:105/2"
- *			  URL.	If a gopher URL is used with a query token
- *			  ('?'), the old ISINDEX procedure will be used
- *			  instead of the form-based gateway.
- *	15 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Pass
- *			  port 79, gtype 0 gopher URLs to the finger
- *			  gateway.
- */
-
-#define HTSTREAM_INTERNAL 1
-
-#include <HTUtils.h>		/* Coding convention macros */
-
-#ifndef DISABLE_GOPHER
-#include <HTAlert.h>
-#include <HTParse.h>
-#include <HTTCP.h>
-#include <HTFinger.h>
-
-/*
- *  Implements.
- */
-#include <HTGopher.h>
-
-#define GOPHER_PORT 70		/* See protocol spec */
-#define CSO_PORT 105		/* See protocol spec */
-#define BIG 1024		/* Bug */
-#define LINE_LENGTH 256		/* Bug */
-
-/*
- *  Gopher entity types.
- */
-#define GOPHER_TEXT		'0'
-#define GOPHER_MENU		'1'
-#define GOPHER_CSO		'2'
-#define GOPHER_ERROR		'3'
-#define GOPHER_MACBINHEX	'4'
-#define GOPHER_PCBINARY		'5'
-#define GOPHER_UUENCODED	'6'
-#define GOPHER_INDEX		'7'
-#define GOPHER_TELNET		'8'
-#define GOPHER_BINARY		'9'
-#define GOPHER_GIF		'g'
-#define GOPHER_HTML		'h'	/* HTML */
-#define GOPHER_CHTML		'H'	/* HTML */
-#define GOPHER_SOUND		's'
-#define GOPHER_WWW		'w'	/* W3 address */
-#define GOPHER_IMAGE		'I'
-#define GOPHER_TN3270		'T'
-#define GOPHER_INFO		'i'
-#define GOPHER_DUPLICATE	'+'
-#define GOPHER_PLUS_IMAGE	':'	/* Addition from Gopher Plus */
-#define GOPHER_PLUS_MOVIE	';'
-#define GOPHER_PLUS_SOUND	'<'
-#define GOPHER_PLUS_PDF		'P'
-
-#include <HTFormat.h>
-
-/*
- *  Hypertext object building machinery.
- */
-#include <HTML.h>
-
-#include <LYStrings.h>
-#include <LYUtils.h>
-#include <LYLeaks.h>
-
-#define PUTC(c) (*targetClass.put_character)(target, c)
-#define PUTS(s) (*targetClass.put_string)(target, s)
-#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
-#define END(e) (*targetClass.end_element)(target, e, 0)
-#define FREE_TARGET (*targetClass._free)(target)
-
-#define NEXT_CHAR HTGetCharacter()
-
-/*
- *  Module-wide variables.
- */
-static int s;			/* Socket for gopher or CSO host */
-
-struct _HTStructured {
-    const HTStructuredClass *isa;	/* For gopher streams */
-    /* ... */
-};
-
-static HTStructured *target;	/* the new gopher hypertext */
-static HTStructuredClass targetClass;	/* Its action routines */
-
-struct _HTStream {
-    HTStreamClass *isa;		/* For form-based CSO  gateway - FM */
-};
-
-typedef struct _CSOfield_info {	/* For form-based CSO gateway - FM */
-    struct _CSOfield_info *next;
-    char *name;
-    char *attributes;
-    char *description;
-    int id;
-    int lookup;
-    int indexed;
-    int url;
-    int max_size;
-    int defreturn;
-    int explicit_return;
-    int reserved;
-    int gpublic;
-    char name_buf[16];		/* Avoid malloc if we can */
-    char desc_buf[32];		/* Avoid malloc if we can */
-    char attr_buf[80];		/* Avoid malloc if we can */
-} CSOfield_info;
-
-static CSOfield_info *CSOfields = NULL;		/* For form-based CSO gateway - FM */
-
-typedef struct _CSOformgen_context {	/* For form-based CSO gateway - FM */
-    const char *host;
-    const char *seek;
-    CSOfield_info *fld;
-    int port;
-    int cur_line;
-    int cur_off;
-    int rep_line;
-    int rep_off;
-    int public_override;
-    int field_select;
-} CSOformgen_context;
-
-/*	Matrix of allowed characters in filenames
- *	=========================================
- */
-static BOOL acceptable[256];
-static BOOL acceptable_inited = NO;
-
-static void init_acceptable(void)
-{
-    unsigned int i;
-    const char *good =
-    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
-
-    for (i = 0; i < 256; i++)
-	acceptable[i] = NO;
-    for (; *good; good++)
-	acceptable[(unsigned int) *good] = YES;
-    acceptable_inited = YES;
-}
-
-/*	Decode one hex character
- *	========================
- */
-static const char hex[17] = "0123456789abcdef";
-
-static char from_hex(int c)
-{
-    return (char) ((c >= '0') && (c <= '9') ? c - '0'
-		   : (c >= 'A') && (c <= 'F') ? c - 'A' + 10
-		   : (c >= 'a') && (c <= 'f') ? c - 'a' + 10
-		   : 0);
-}
-
-/*	Paste in an Anchor
- *	==================
- *
- *	The title of the destination is set, as there is no way
- *	of knowing what the title is when we arrive.
- *
- * On entry,
- *	HT	is in append mode.
- *	text	points to the text to be put into the file, 0 terminated.
- *	addr	points to the hypertext refernce address 0 terminated.
- */
-BOOLEAN HT_Is_Gopher_URL = FALSE;
-
-static void write_anchor(const char *text, const char *addr)
-{
-    BOOL present[HTML_A_ATTRIBUTES];
-    const char *value[HTML_A_ATTRIBUTES];
-
-    int i;
-
-    for (i = 0; i < HTML_A_ATTRIBUTES; i++)
-	present[i] = 0;
-    present[HTML_A_HREF] = YES;
-    ((const char **) value)[HTML_A_HREF] = addr;
-    present[HTML_A_TITLE] = YES;
-    ((const char **) value)[HTML_A_TITLE] = text;
-
-    CTRACE((tfp, "HTGopher: adding URL: %s\n", addr));
-
-    HT_Is_Gopher_URL = TRUE;	/* tell HTML.c that this is a Gopher URL */
-    (*targetClass.start_element) (target, HTML_A, present,
-				  (const char **) value, -1, 0);
-
-    PUTS(text);
-    END(HTML_A);
-}
-
-/*	Parse a Gopher Menu document
- *	============================
- */
-static void parse_menu(const char *arg GCC_UNUSED,
-		       HTParentAnchor *anAnchor)
-{
-    char gtype;
-    int ich;
-    char line[BIG];
-    char *name = NULL, *selector = NULL;	/* Gopher menu fields */
-    char *host = NULL;
-    char *port;
-    char *p = line;
-    const char *title;
-    int bytes = 0;
-    int BytesReported = 0;
-    char buffer[128];
-
-#define TAB		'\t'
-#define HEX_ESCAPE	'%'
-
-    START(HTML_HTML);
-    PUTC('\n');
-    START(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_TITLE);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else
-	PUTS(GOPHER_MENU_TITLE);
-    END(HTML_TITLE);
-    PUTC('\n');
-    END(HTML_HEAD);
-    PUTC('\n');
-
-    START(HTML_BODY);
-    PUTC('\n');
-    START(HTML_H1);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else
-	PUTS(GOPHER_MENU_TITLE);
-    END(HTML_H1);
-    PUTC('\n');
-    START(HTML_PRE);
-    PUTC('\n');			/* newline after HTML_PRE forces split-line */
-    while ((ich = NEXT_CHAR) != EOF) {
-
-	if (interrupted_in_htgetcharacter) {
-	    CTRACE((tfp,
-		    "HTGopher: Interrupted in HTGetCharacter, apparently.\n"));
-	    goto end_html;
-	}
-
-	if ((char) ich != LF) {
-	    *p = (char) ich;	/* Put character in line */
-	    if (p < &line[BIG - 1])
-		p++;
-
-	} else {
-	    *p++ = '\0';	/* Terminate line */
-	    bytes += (int) (p - line);	/* add size */
-	    p = line;		/* Scan it to parse it */
-	    port = 0;		/* Flag "not parsed" */
-	    CTRACE((tfp, "HTGopher: Menu item: %s\n", line));
-	    gtype = *p++;
-
-	    if (bytes > BytesReported + 1024) {
-		sprintf(buffer, TRANSFERRED_X_BYTES, bytes);
-		HTProgress(buffer);
-		BytesReported = bytes;
-	    }
-
-	    /* Break on line with a dot by itself */
-	    if ((gtype == '.') && ((*p == '\r') || (*p == 0)))
-		break;
-
-	    if (gtype && *p) {
-		name = p;
-		selector = strchr(name, TAB);
-		if (selector) {
-		    *selector++ = '\0';		/* Terminate name */
-		    /*
-		     * Gopher+ Type=0+ objects can be binary, and will
-		     * have 9 or 5 beginning their selector.  Make sure
-		     * we don't trash the terminal by treating them as
-		     * text. - FM
-		     */
-		    if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY ||
-						 *selector == GOPHER_PCBINARY))
-			gtype = *selector;
-		    host = strchr(selector, TAB);
-		    if (host) {
-			*host++ = '\0';		/* Terminate selector */
-			port = strchr(host, TAB);
-			if (port) {
-			    char *junk;
-
-			    port[0] = ':';	/* delimit host a la W3 */
-			    junk = strchr(port, TAB);
-			    if (junk)
-				*junk = '\0';	/* Chop port */
-			    if ((port[1] == '0') && (!port[2]))
-				port[0] = '\0';		/* 0 means none */
-			}	/* no port */
-		    }		/* host ok */
-		}		/* selector ok */
-	    }
-	    /* gtype and name ok */
-	    /* Nameless files are a separator line */
-	    if (name != NULL && gtype == GOPHER_TEXT) {
-		int i = (int) strlen(name) - 1;
-
-		while (name[i] == ' ' && i >= 0)
-		    name[i--] = '\0';
-		if (i < 0)
-		    gtype = GOPHER_INFO;
-	    }
-
-	    if (gtype == GOPHER_WWW) {	/* Gopher pointer to W3 */
-		PUTS("(HTML) ");
-		write_anchor(name, selector);
-
-	    } else if (gtype == GOPHER_INFO) {
-		/* Information or separator line */
-		PUTS("       ");
-		PUTS(name);
-
-	    } else if (port) {	/* Other types need port */
-		char *address = 0;
-		const char *format = *selector ? "%s//%s@%s/" : "%s//%s/";
-
-		if (gtype == GOPHER_TELNET) {
-		    PUTS(" (TEL) ");
-		    HTSprintf0(&address, format, STR_TELNET_URL, selector, host);
-		} else if (gtype == GOPHER_TN3270) {
-		    PUTS("(3270) ");
-		    HTSprintf0(&address, format, STR_TN3270_URL, selector, host);
-		} else {	/* If parsed ok */
-		    char *r;
-
-		    switch (gtype) {
-		    case GOPHER_TEXT:
-			PUTS("(FILE) ");
-			break;
-		    case GOPHER_MENU:
-			PUTS(" (DIR) ");
-			break;
-		    case GOPHER_CSO:
-			PUTS(" (CSO) ");
-			break;
-		    case GOPHER_PCBINARY:
-			PUTS(" (BIN) ");
-			break;
-		    case GOPHER_UUENCODED:
-			PUTS(" (UUE) ");
-			break;
-		    case GOPHER_INDEX:
-			PUTS("  (?)  ");
-			break;
-		    case GOPHER_BINARY:
-			PUTS(" (BIN) ");
-			break;
-		    case GOPHER_GIF:
-		    case GOPHER_IMAGE:
-		    case GOPHER_PLUS_IMAGE:
-			PUTS(" (IMG) ");
-			break;
-		    case GOPHER_SOUND:
-		    case GOPHER_PLUS_SOUND:
-			PUTS(" (SND) ");
-			break;
-		    case GOPHER_MACBINHEX:
-			PUTS(" (HQX) ");
-			break;
-		    case GOPHER_HTML:
-		    case GOPHER_CHTML:
-			PUTS("(HTML) ");
-			break;
-		    case 'm':
-			PUTS("(MIME) ");
-			break;
-		    case GOPHER_PLUS_MOVIE:
-			PUTS(" (MOV) ");
-			break;
-		    case GOPHER_PLUS_PDF:
-			PUTS(" (PDF) ");
-			break;
-		    default:
-			PUTS("(UNKN) ");
-			break;
-		    }
-
-		    HTSprintf0(&address, "//%s/%c", host, gtype);
-
-		    for (r = selector; *r; r++) {	/* Encode selector string */
-			if (acceptable[UCH(*r)]) {
-			    HTSprintf(&address, "%c", *r);
-			} else {
-			    HTSprintf(&address, "%c%c%c",
-				      HEX_ESCAPE,	/* Means hex coming */
-				      hex[(TOASCII(*r)) >> 4],
-				      hex[(TOASCII(*r)) & 15]);
-			}
-		    }
-		}
-		/* Error response from Gopher doesn't deserve to
-		   be a hyperlink. */
-		if (strcmp(address, "gopher://error.host:1/0"))
-		    write_anchor(name, address);
-		else
-		    PUTS(name);
-		FREE(address);
-	    } else {		/* parse error */
-		CTRACE((tfp, "HTGopher: Bad menu item.\n"));
-		PUTS(line);
-
-	    }			/* parse error */
-
-	    PUTC('\n');
-	    p = line;		/* Start again at beginning of line */
-
-	}			/* if end of line */
-
-    }				/* Loop over characters */
-
-  end_html:
-    END(HTML_PRE);
-    PUTC('\n');
-    END(HTML_BODY);
-    PUTC('\n');
-    END(HTML_HTML);
-    PUTC('\n');
-    FREE_TARGET;
-
-    return;
-}
-
-/*	Parse a Gopher CSO document from an ISINDEX query.
- *	==================================================
- *
- *   Accepts an open socket to a CSO server waiting to send us
- *   data and puts it on the screen in a reasonable manner.
- *
- *   Perhaps this data can be automatically linked to some
- *   other source as well???
- *
- *  Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
- *  on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
- *  secret@dxcern.cern.ch .
- */
-static void parse_cso(const char *arg,
-		      HTParentAnchor *anAnchor)
-{
-    int ich;
-    char line[BIG];
-    char *p = line;
-    char *first_colon, *second_colon, last_char = '\0';
-    const char *title;
-
-    START(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_TITLE);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else
-	PUTS(GOPHER_CSO_SEARCH_RESULTS);
-    END(HTML_TITLE);
-    PUTC('\n');
-    END(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_H1);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else {
-	PUTS(arg);
-	PUTS(GOPHER_SEARCH_RESULTS);
-    }
-    END(HTML_H1);
-    PUTC('\n');
-    START(HTML_PRE);
-
-    /*
-     * Start grabbing chars from the network.
-     */
-    while ((ich = NEXT_CHAR) != EOF) {
-	if ((char) ich != LF) {
-	    *p = (char) ich;	/* Put character in line */
-	    if (p < &line[BIG - 1])
-		p++;
-	} else {
-	    *p = '\0';		/* Terminate line */
-	    p = line;		/* Scan it to parse it */
-	    /*
-	     * OK we now have a line in 'p'.  Lets parse it and print it.
-	     */
-
-	    /*
-	     * Break on line that begins with a 2.  It's the end of data.
-	     */
-	    if (*p == '2')
-		break;
-
-	    /*
-	     * Lines beginning with 5 are errors.  Print them and quit.
-	     */
-	    if (*p == '5') {
-		START(HTML_H2);
-		PUTS(p + 4);
-		END(HTML_H2);
-		break;
-	    }
-
-	    if (*p == '-') {
-		/*
-		 * Data lines look like -200:#:
-		 * where # is the search result number and can be multiple
-		 * digits (infinite?).
-		 * Find the second colon and check the digit to the left of it
-		 * to see if they are diferent.  If they are then a different
-		 * person is starting.  Make this line an <h2>.
-		 */
-
-		/*
-		 * Find the second_colon.
-		 */
-		second_colon = NULL;
-		first_colon = strchr(p, ':');
-		if (first_colon != NULL) {
-		    second_colon = strchr(first_colon + 1, ':');
-		}
-
-		if (second_colon != NULL) {	/* error check */
-
-		    if (*(second_colon - 1) != last_char)
-			/* print seperator */
-		    {
-			END(HTML_PRE);
-			START(HTML_H2);
-		    }
-
-		    /*
-		     * Right now the record appears with the alias (first line)
-		     * as the header and the rest as <pre> text.
-		     *
-		     * It might look better with the name as the header and the
-		     * rest as a <ul> with <li> tags.  I'm not sure whether the
-		     * name field comes in any special order or if its even
-		     * required in a record, so for now the first line is the
-		     * header no matter what it is (it's almost always the
-		     * alias).
-		     *
-		     * A <dl> with the first line as the <DT> and the rest as
-		     * some form of <DD> might good also?
-		     */
-
-		    /*
-		     * Print data.
-		     */
-		    PUTS(second_colon + 1);
-		    PUTC('\n');
-
-		    if (*(second_colon - 1) != last_char)
-			/* end seperator */
-		    {
-			END(HTML_H2);
-			START(HTML_PRE);
-		    }
-
-		    /*
-		     * Save the char before the second colon for comparison on
-		     * the next pass.
-		     */
-		    last_char = *(second_colon - 1);
-
-		}		/* end if second_colon */
-	    }			/* end if *p == '-' */
-	}			/* if end of line */
-
-    }				/* Loop over characters */
-
-    /* end the text block */
-    PUTC('\n');
-    END(HTML_PRE);
-    PUTC('\n');
-    FREE_TARGET;
-
-    return;			/* all done */
-}				/* end of procedure */
-
-/*	Display a Gopher CSO ISINDEX cover page.
- *	========================================
- */
-static void display_cso(const char *arg,
-			HTParentAnchor *anAnchor)
-{
-    const char *title;
-
-    START(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_TITLE);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else
-	PUTS(GOPHER_CSO_INDEX);
-    END(HTML_TITLE);
-    PUTC('\n');
-    START(HTML_ISINDEX);
-    PUTC('\n');
-    END(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_H1);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else {
-	PUTS(arg);
-	PUTS(INDEX_SEGMENT);
-    }
-    END(HTML_H1);
-    PUTS(GOPHER_CSO_INDEX_SUBTITLE);
-    START(HTML_P);
-    PUTS(GOPHER_CSO_SOLICIT_KEYWORDS);
-    START(HTML_P);
-    PUTS(SEGMENT_KEYWORDS_WILL);
-    PUTS(SEGMENT_PERSONS_DB_NAME);
-
-    if (!HTAnchor_title(anAnchor))
-	HTAnchor_setTitle(anAnchor, arg);
-
-    FREE_TARGET;
-    return;
-}
-
-/*	Display a Gopher Index document.
- *	================================
- */
-static void display_index(const char *arg,
-			  HTParentAnchor *anAnchor)
-{
-    const char *title;
-
-    START(HTML_HEAD);
-    PUTC('\n');
-    PUTC('\n');
-    START(HTML_TITLE);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else
-	PUTS(GOPHER_INDEX_TITLE);
-    END(HTML_TITLE);
-    PUTC('\n');
-    START(HTML_ISINDEX);
-    PUTC('\n');
-    END(HTML_HEAD);
-    PUTC('\n');
-    START(HTML_H1);
-    if ((title = HTAnchor_title(anAnchor)))
-	PUTS(title);
-    else {
-	PUTS(arg);
-	PUTS(INDEX_SEGMENT);
-    }
-    END(HTML_H1);
-    PUTS(GOPHER_INDEX_SUBTITLE);
-    START(HTML_P);
-    PUTS(GOPHER_SOLICIT_KEYWORDS);
-
-    if (!HTAnchor_title(anAnchor))
-	HTAnchor_setTitle(anAnchor, arg);
-
-    FREE_TARGET;
-    return;
-}
-
-/*	De-escape a selector into a command.
- *	====================================
- *
- *	The % hex escapes are converted. Otheriwse, the string is copied.
- */
-static void de_escape(char *command, const char *selector)
-{
-    const char *p = selector;
-    char *q;
-
-    if (command == NULL)
-	outofmem(__FILE__, "HTLoadGopher");
-
-    assert(command != NULL);
-
-    q = command;
-    while (*p) {		/* Decode hex */
-	if (*p == HEX_ESCAPE) {
-	    char c;
-	    unsigned int b;
-
-	    p++;
-	    c = *p++;
-	    b = UCH(from_hex(c));
-	    c = *p++;
-	    if (!c)
-		break;		/* Odd number of chars! */
-	    *q++ = (char) FROMASCII((b << 4) + UCH(from_hex(c)));
-	} else {
-	    *q++ = *p++;	/* Record */
-	}
-    }
-    *q = '\0';			/* Terminate command */
-}
-
-/*	Free the CSOfields structures. - FM
- *	===================================
- */
-static void free_CSOfields(void)
-{
-    CSOfield_info *cur = CSOfields;
-    CSOfield_info *prev;
-
-    while (cur) {
-	if (cur->name != cur->name_buf)
-	    FREE(cur->name);
-	if (cur->attributes != cur->attr_buf)
-	    FREE(cur->attributes);
-	if (cur->description != cur->desc_buf)
-	    FREE(cur->description);
-	prev = cur;
-	cur = cur->next;
-	FREE(prev);
-    }
-
-    return;
-}
-
-/*	Interpret CSO/PH form template keys. - FM
- *	=========================================
- */
-static void interpret_cso_key(const char *key,
-			      char *buf,
-			      int *length,
-			      CSOformgen_context * ctx,
-			      HTStream *Target)
-{
-    CSOfield_info *fld;
-
-    if ((fld = ctx->fld) != 0) {
-	/*
-	 * Most substitutions only recognized inside of loops.
-	 */
-	int error = 0;
-
-	if (0 == StrNCmp(key, "$(FID)", 6)) {
-	    sprintf(buf, "%d", fld->id);
-	} else if (0 == StrNCmp(key, "$(FDESC)", 8)) {
-	    sprintf(buf, "%.2046s", fld->description);
-	} else if (0 == StrNCmp(key, "$(FDEF)", 7)) {
-	    strcpy(buf, fld->defreturn ? " checked" : "");
-	} else if (0 == StrNCmp(key, "$(FNDX)", 7)) {
-	    strcpy(buf, fld->indexed ? "*" : "");
-	} else if (0 == StrNCmp(key, "$(FSIZE)", 8)) {
-	    sprintf(buf, " size=%d maxlength=%d",
-		    fld->max_size > 55 ? 55 : fld->max_size,
-		    fld->max_size);
-	} else if (0 == StrNCmp(key, "$(FSIZE2)", 9)) {
-	    sprintf(buf, " maxlength=%d", fld->max_size);
-	} else {
-	    error = 1;
-	}
-	if (!error) {
-	    *length = (int) strlen(buf);
-	    return;
-	}
-    }
-    buf[0] = '\0';
-    if (0 == StrNCmp(key, "$(NEXTFLD)", 10)) {
-	if (!ctx->fld)
-	    fld = CSOfields;
-	else
-	    fld = ctx->fld->next;
-	switch (ctx->field_select) {
-	case 0:
-	    /*
-	     * 'Query' fields, public and lookup attributes.
-	     */
-	    for (; fld; fld = fld->next)
-		if (fld->gpublic && (fld->lookup == 1))
-		    break;
-	    break;
-	case 1:
-	    /*
-	     * 'Query' fields, accept lookup attribute.
-	     */
-	    for (; fld; fld = fld->next)
-		if (fld->lookup == 1)
-		    break;
-	    break;
-	case 2:
-	    /*
-	     * 'Return' fields, public only.
-	     */
-	    for (; fld; fld = fld->next)
-		if (fld->gpublic)
-		    break;
-	    break;
-	case 3:
-	    /*
-	     * All fields.
-	     */
-	    break;
-	}
-	if (fld) {
-	    ctx->cur_line = ctx->rep_line;
-	    ctx->cur_off = ctx->rep_off;
-	}
-	ctx->fld = fld;
-
-    } else if ((0 == StrNCmp(key, "$(QFIELDS)", 10)) ||
-	       (0 == StrNCmp(key, "$(RFIELDS)", 10))) {
-	/*
-	 * Begin iteration sequence.
-	 */
-	ctx->rep_line = ctx->cur_line;
-	ctx->rep_off = ctx->cur_off;
-	ctx->fld = (CSOfield_info *) 0;
-	ctx->seek = "$(NEXTFLD)";
-	ctx->field_select = (key[2] == 'Q') ? 0 : 2;
-	if (ctx->public_override)
-	    ctx->field_select++;
-
-    } else if (0 == StrNCmp(key, "$(NAMEFLD)", 10)) {
-	/*
-	 * Special, locate name field.  Flag lookup so QFIELDS will skip it.
-	 */
-	for (fld = CSOfields; fld; fld = fld->next)
-	    if (strcmp(fld->name, "name") == 0 ||
-		strcmp(fld->name, "Name") == 0) {
-		if (fld->lookup)
-		    fld->lookup = 2;
-		break;
-	    }
-	ctx->fld = fld;
-    } else if (0 == StrNCmp(key, "$(HOST)", 7)) {
-	strcpy(buf, ctx->host);
-    } else if (0 == StrNCmp(key, "$(PORT)", 7)) {
-	sprintf(buf, "%d", ctx->port);
-    } else {
-	/*
-	 * No match, dump key to buffer so client sees it for debugging.
-	 */
-	size_t out = 0;
-
-	while (*key && (*key != ')')) {
-	    buf[out++] = (*key++);
-	    if (out > sizeof(buf) - 2) {
-		buf[out] = '\0';
-		(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		out = 0;
-	    }
-	}
-	buf[out++] = ')';
-	buf[out] = '\0';
-    }
-    *length = (int) strlen(buf);
-    return;
-}
-
-/*	Parse the elements in a CSO/PH fields structure. - FM
- *	=====================================================
- */
-static int parse_cso_field_info(CSOfield_info *blk)
-{
-    char *info, *max_spec;
-
-    /*
-     * Initialize all fields to default values.
-     */
-    blk->indexed = blk->lookup = blk->reserved = blk->max_size = blk->url = 0;
-    blk->defreturn = blk->explicit_return = blk->gpublic = 0;
-
-    /*
-     * Search for keywords in info string and set values.  Attributes are
-     * converted to all lower-case for comparison.
-     */
-    info = blk->attributes;
-    LYLowerCase(info);
-    if (strstr(info, "indexed "))
-	blk->indexed = 1;
-    if (strstr(info, "default "))
-	blk->defreturn = 1;
-    if (strstr(info, "public "))
-	blk->gpublic = 1;
-    if (strstr(info, "lookup "))
-	blk->lookup = 1;
-    if (strstr(info, "url ")) {
-	blk->url = 1;
-	blk->defreturn = 1;
-    }
-    max_spec = strstr(info, "max ");
-    if (max_spec) {
-	sscanf(&max_spec[4], "%d", &blk->max_size);
-    } else {
-	blk->max_size = 32;
-    }
-
-    return 0;
-}
-
-/*	Parse a reply from a CSO/PH fields request. - FM
- *	================================================
- */
-static int parse_cso_fields(char *buf,
-			    size_t size)
-{
-    int ich;
-    char *p = buf;
-    int i, code = 0, prev_code;
-    size_t alen;
-    char *indx, *name;
-    CSOfield_info *last, *newf;
-
-    last = CSOfields = (CSOfield_info *) 0;
-    prev_code = -2555;
-    buf[0] = '\0';
-
-    /*
-     * Start grabbing chars from the network.
-     */
-    while ((ich = NEXT_CHAR) != EOF) {
-	if (interrupted_in_htgetcharacter) {
-	    CTRACE((tfp,
-		    "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"));
-	    free_CSOfields();
-	    buf[0] = '\0';
-	    return HT_INTERRUPTED;
-	}
-
-	if ((char) ich != LF) {
-	    *p = (char) ich;	/* Put character in buffer */
-	    if (p < &buf[size - 1]) {
-		p++;
-	    }
-	} else {
-	    *p = '\0';		/* Terminate line */
-	    p = buf;		/* Scan it to parse it */
-
-	    /* OK we now have a line in 'p' lets parse it.
-	     */
-
-	    /*
-	     * Break on line that begins with a 2.  It's the end of data.
-	     */
-	    if (*p == '2')
-		break;
-
-	    /*
-	     * Lines beginning with 5 are errors.  Print them and quit.
-	     */
-	    if (*p == '5') {
-		strcpy(buf, p);
-		return 5;
-	    }
-
-	    if (*p == '-') {
-		/*
-		 * Data lines look like -200:#:
-		 * where # is the search result number and can be multiple
-		 * digits (infinite?).
-		 */
-
-		/*
-		 * Check status, ignore any non-success.
-		 */
-		if (p[1] != '2')
-		    continue;
-
-		/*
-		 * Parse fields within returned line into status, ndx, name,
-		 * data.
-		 */
-		indx = NULL;
-		name = NULL;
-		for (i = 0; p[i]; i++) {
-		    if (p[i] == ':') {
-			p[i] = '\0';
-			if (!indx) {
-			    indx = (char *) &p[i + 1];
-			    code = atoi(indx);
-			} else if (!name) {
-			    name = (char *) &p[i + 1];
-			} else {
-			    i++;
-			    break;
-			}
-		    }
-		}
-		/*
-		 * Add data to field structure.
-		 */
-		if (name) {
-		    if (code == prev_code) {
-			/*
-			 * Remaining data are description.  Save in current
-			 * info block.
-			 */
-			if (last != NULL) {
-			    alen = strlen((char *) &p[i]) + 1;
-			    if (alen > sizeof(last->desc_buf)) {
-				if (last->description != last->desc_buf)
-				    FREE(last->description);
-				if (!(last->description = (char *) malloc(alen))) {
-				    outofmem(__FILE__, "HTLoadCSO");
-				}
-			    }
-			    strcpy(last->description, (char *) &p[i]);
-			}
-		    } else {
-			/*
-			 * Initialize new block, append to end of list to
-			 * preserve order.
-			 */
-			newf = typecalloc(CSOfield_info);
-
-			if (!newf) {
-			    outofmem(__FILE__, "HTLoadCSO");
-			}
-
-			assert(newf != NULL);
-
-			if (last)
-			    last->next = newf;
-			else
-			    CSOfields = newf;
-			last = newf;
-
-			newf->next = (CSOfield_info *) 0;
-			newf->name = newf->name_buf;
-			alen = strlen(name) + 1;
-			if (alen > sizeof(newf->name_buf)) {
-			    if (!(newf->name = (char *) malloc(alen))) {
-				outofmem(__FILE__, "HTLoadCSO");
-			    }
-			}
-			strcpy(newf->name, name);
-
-			newf->attributes = newf->attr_buf;
-			alen = strlen((char *) &p[i]) + 2;
-			if (alen > sizeof(newf->attr_buf)) {
-			    if (!(newf->attributes = (char *) malloc(alen))) {
-				outofmem(__FILE__, "HTLoadCSO");
-			    }
-			}
-			strcpy(newf->attributes, (char *) &p[i]);
-			strcpy((char *) &newf->attributes[alen - 2], " ");
-			newf->description = newf->desc_buf;
-			newf->desc_buf[0] = '\0';
-			newf->id = atoi(indx);
-			/*
-			 * Scan for keywords.
-			 */
-			parse_cso_field_info(newf);
-		    }
-		    prev_code = code;
-		} else
-		    break;
-	    }			/* end if *p == '-' */
-	}			/* if end of line */
-
-    }				/* Loop over characters */
-
-    /* end the text block */
-
-    if (buf[0] == '\0') {
-	return -1;		/* no response */
-    }
-    buf[0] = '\0';
-    return 0;			/* all done */
-}				/* end of procedure */
-
-/*	Generate a form for submitting CSO/PH searches. - FM
- *	====================================================
- */
-static int generate_cso_form(char *host,
-			     int port,
-			     char *buf,
-			     HTStream *Target)
-{
-    int i, j, length;
-    size_t out;
-    int full_flag = 1;
-    const char *key;
-    const char *line;
-    CSOformgen_context ctx;
-    static const char *ctemplate[] =
-    {
-	"<HTML>\n<HEAD>\n<TITLE>CSO/PH Query Form for $(HOST)</TITLE>\n</HEAD>\n<BODY>",
-	"<H2><I>CSO/PH Query Form</I> for <EM>$(HOST)</EM></H2>",
-	"To search the database for a name, fill in one or more of the fields",
-	"in the form below and activate the 'Submit query' button.  At least",
-	"one of the entered fields must be flagged as indexed.",
-	"<HR><FORM method=\"POST\" action=\"cso://$(HOST)/\">",
-	"[ <input type=\"submit\" value=\"Submit query\"> | ",
-	"<input type=\"reset\" value=\"Clear fields\"> ]",
-	"<P><DL>",
-	"   <DT>Search parameters (* indicates indexed field):",
-	"   <DD>",
-	"$(NAMEFLD)    <DL COMPACT>\n    <DT><I>$(FDESC)</I>$(FNDX)",
-	"    <DD>Last: <input name=\"q_$(FID)\" type=\"text\" size=49$(FSIZE2)>",
-	"    <DD>First: <input name=\"q_$(FID)\" type=\"text\" size=48$(FSIZE2)>",
-	"$(QFIELDS)    <DT><I>$(FDESC)</I>$(FNDX)",
-	"    <DD><input name=\"q_$(FID)\" type=\"text\" $(FSIZE)>\n$(NEXTFLD)",
-	"    </DL>",
-	"   </DL>\n<P><DL>",
-	"   <DT>Output format:",
-	"   <DD>Returned data option: <select name=\"return\">",
-	"    <option>default<option selected>all<option>selected</select><BR>",
-	"$(RFIELDS)    <input type=\"checkbox\" name=\"r_$(FID)\"$(FDEF)> $(FDESC)<BR>",
-	"$(NEXTFLD)    ",
-	"   </DL></FORM><HR>\n</BODY>\n</HTML>",
-	(char *) 0
-    };
-
-    memset(&ctx, 0, sizeof(ctx));
-    ctx.host = host;
-    ctx.seek = (char *) 0;
-    ctx.port = port;
-    ctx.fld = (CSOfield_info *) 0;
-    ctx.public_override = full_flag;
-    /*
-     * Parse the strings in the template array to produce HTML document to send
-     * to client.  First line is skipped for 'full' lists.
-     */
-    out = 0;
-    buf[out] = '\0';
-    for (i = full_flag ? /***1***/ 0 : 0;
-	 ctemplate[i];
-	 i++) {
-	/*
-	 * Search the current string for substitution, flagged by $(
-	 */
-	for (line = ctemplate[i], j = 0; line[j]; j++) {
-	    if ((line[j] == '$') && (line[j + 1] == '(')) {
-		/*
-		 * Command detected, flush output buffer and find closing ')'
-		 * that delimits the command.
-		 */
-		buf[out] = '\0';
-		if (out > 0)
-		    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		for (key = &line[j]; line[j + 1] && (line[j] != ')'); j++) {
-		    ;
-		}
-		/*
-		 * Save context, interpet command and restore updated context.
-		 */
-		ctx.cur_line = i;
-		ctx.cur_off = j;
-		interpret_cso_key(key, buf, &length, &ctx, Target);
-		i = ctx.cur_line;
-		j = ctx.cur_off;
-		line = ctemplate[i];
-		out = (size_t) length;
-
-		if (ctx.seek) {
-		    /*
-		     * Command wants us to skip (forward) to indicated token. 
-		     * Start at current position.
-		     */
-		    size_t slen = strlen(ctx.seek);
-
-		    for (; ctemplate[i]; i++) {
-			for (line = ctemplate[i]; line[j]; j++) {
-			    if (line[j] == '$')
-				if (0 == StrNCmp(ctx.seek, &line[j], slen)) {
-				    if (j == 0)
-					j = (int) strlen(ctemplate[--i]) - 1;
-				    else
-					--j;
-				    line = ctemplate[i];
-				    ctx.seek = (char *) 0;
-				    break;
-				}
-			}
-			if (!ctx.seek)
-			    break;
-			j = 0;
-		    }
-		    if (ctx.seek) {
-			char *temp = 0;
-
-			HTSprintf0(&temp, GOPHER_CSO_SEEK_FAILED, ctx.seek);
-			(*Target->isa->put_block) (Target, temp, (int) strlen(temp));
-			FREE(temp);
-		    }
-		}
-	    } else {
-		/*
-		 * Non-command text, add to output buffer.
-		 */
-		buf[out++] = line[j];
-		if (out > (sizeof(buf) - 3)) {
-		    buf[out] = '\0';
-		    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    out = 0;
-		}
-	    }
-	}
-	buf[out++] = '\n';
-	buf[out] = '\0';
-    }
-    if (out > 0)
-	(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-
-    return 0;
-}
-
-/*	Generate a results report for CSO/PH form-based searches. - FM
- *	==============================================================
- */
-static int generate_cso_report(HTStream *Target)
-{
-    int ich;
-    char line[BIG];
-    char *buf = 0;
-    char *p = line, *href = NULL;
-    int len, i, prev_ndx, ndx;
-    char *rcode, *ndx_str, *fname, *fvalue, *l;
-    CSOfield_info *fld;
-    BOOL stop = FALSE;
-
-    /*
-     * Read lines until non-negative status.
-     */
-    prev_ndx = -100;
-    /*
-     * Start grabbing chars from the network.
-     */
-    while (!stop && (ich = NEXT_CHAR) != EOF) {
-	if (interrupted_in_htgetcharacter) {
-	    CTRACE((tfp,
-		    "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n"));
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	    goto end_CSOreport;
-	}
-
-	if ((char) ich != LF) {
-	    *p = (char) ich;	/* Put character in line */
-	    if (p < &line[BIG - 1]) {
-		p++;
-	    }
-	} else {
-	    *p = '\0';		/* Terminate line */
-	    /*
-	     * OK we now have a line.  Load it as 'p' and parse it.
-	     */
-	    p = line;
-	    if (p[0] != '-' && p[0] != '1') {
-		stop = TRUE;
-	    }
-	    rcode = (p[0] == '-') ? &p[1] : p;
-	    ndx_str = fname = NULL;
-	    len = (int) strlen(p);
-	    for (i = 0; i < len; i++) {
-		if (p[i] == ':') {
-		    p[i] = '\0';
-		    if (!ndx_str) {
-			fname = ndx_str = &p[i + 1];
-		    } else {
-			fname = &p[i + 1];
-			break;
-		    }
-		}
-	    }
-	    if (ndx_str) {
-		ndx = atoi(ndx_str);
-		if (prev_ndx != ndx) {
-		    if (prev_ndx != -100) {
-			HTSprintf0(&buf, "</DL></DL>\n");
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    }
-		    if (ndx == 0) {
-			HTSprintf0(&buf,
-				   "<HR><DL><DT>Information/status<DD><DL><DT>\n");
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    } else {
-			HTSprintf0(&buf,
-				   "<HR><DL><DT>Entry %d:<DD><DL COMPACT><DT>\n", ndx);
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    }
-		    prev_ndx = ndx;
-		}
-	    } else {
-		HTSprintf0(&buf, "<DD>%s\n", rcode);
-		(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		continue;
-	    }
-	    if ((*rcode >= '2') && (*rcode <= '5') && (fname != ndx_str)) {
-		while (*fname == ' ') {
-		    fname++;	/* trim leading spaces */
-		}
-		for (fvalue = fname; *fvalue; fvalue++) {
-		    if (*fvalue == ':') {
-			*fvalue++ = '\0';
-			i = (int) strlen(fname) - 1;
-			while (i >= 0 && fname[i] == ' ') {
-			    fname[i--] = '\0';	/* trim trailing */
-			}
-			break;
-		    }
-		}
-		if (fvalue) {
-		    while (*fvalue == ' ') {
-			fvalue++;	/* trim leading spaces */
-		    }
-		}
-		if (*fname) {
-		    for (fld = CSOfields; fld; fld = fld->next) {
-			if (!strcmp(fld->name, fname)) {
-			    if (fld->description) {
-				fname = fld->description;
-			    }
-			    break;
-			}
-		    }
-		    if (fld && fld->url) {
-			HTSprintf0(&buf,
-				   "<DT><I>%s</I><DD><A HREF=\"%s\">%s</A>\n",
-				   fname, fvalue, fvalue);
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    } else {
-			HTSprintf0(&buf, "<DT><I>%s</I><DD>", fname);
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-			buf[0] = '\0';
-			l = fvalue;
-			while (*l) {
-			    if (*l == '<') {
-				StrAllocCat(buf, "&lt;");
-				l++;
-			    } else if (*l == '>') {
-				StrAllocCat(buf, "&gt;");
-				l++;
-			    } else if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
-				       StrNCmp(l, "snews://", 8) &&
-				       StrNCmp(l, "nntp://", 7) &&
-				       StrNCmp(l, "snewspost:", 10) &&
-				       StrNCmp(l, "snewsreply:", 11) &&
-				       StrNCmp(l, "newspost:", 9) &&
-				       StrNCmp(l, "newsreply:", 10) &&
-				       StrNCmp(l, "ftp://", 6) &&
-				       StrNCmp(l, "file:/", 6) &&
-				       StrNCmp(l, "finger://", 9) &&
-				       StrNCmp(l, "http://", 7) &&
-				       StrNCmp(l, "https://", 8) &&
-				       StrNCmp(l, "wais://", 7) &&
-				       StrNCmp(l, STR_MAILTO_URL,
-					       LEN_MAILTO_URL) &&
-				       StrNCmp(l, "cso://", 6) &&
-				       StrNCmp(l, "gopher://", 9)) {
-				HTSprintf(&buf, "%c", *l++);
-			    } else {
-				StrAllocCat(buf, "<a href=\"");
-				StrAllocCopy(href, l);
-				StrAllocCat(buf, strtok(href, " \r\n\t,>)\""));
-				StrAllocCat(buf, "\">");
-				while (*l && !strchr(" \r\n\t,>)\"", *l)) {
-				    HTSprintf(&buf, "%c", *l++);
-				}
-				StrAllocCat(buf, "</a>");
-				FREE(href);
-			    }
-			}
-			StrAllocCat(buf, "\n");
-			(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    }
-		} else {
-		    HTSprintf0(&buf, "<DD>");
-		    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		    buf[0] = '\0';
-		    l = fvalue;
-		    while (*l) {
-			if (*l == '<') {
-			    StrAllocCat(buf, "&lt;");
-			    l++;
-			} else if (*l == '>') {
-			    StrAllocCat(buf, "&gt;");
-			    l++;
-			} else if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
-				   StrNCmp(l, "snews://", 8) &&
-				   StrNCmp(l, "nntp://", 7) &&
-				   StrNCmp(l, "snewspost:", 10) &&
-				   StrNCmp(l, "snewsreply:", 11) &&
-				   StrNCmp(l, "newspost:", 9) &&
-				   StrNCmp(l, "newsreply:", 10) &&
-				   StrNCmp(l, "ftp://", 6) &&
-				   StrNCmp(l, "file:/", 6) &&
-				   StrNCmp(l, "finger://", 9) &&
-				   StrNCmp(l, "http://", 7) &&
-				   StrNCmp(l, "https://", 8) &&
-				   StrNCmp(l, "wais://", 7) &&
-				   StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
-				   StrNCmp(l, "cso://", 6) &&
-				   StrNCmp(l, "gopher://", 9)) {
-			    HTSprintf(&buf, "%c", *l++);
-			} else {
-			    StrAllocCat(buf, "<a href=\"");
-			    StrAllocCopy(href, l);
-			    StrAllocCat(buf, strtok(href, " \r\n\t,>)\""));
-			    StrAllocCat(buf, "\">");
-			    while (*l && !strchr(" \r\n\t,>)\"", *l)) {
-				HTSprintf(&buf, "%c", *l++);
-			    }
-			    StrAllocCat(buf, "</a>");
-			    FREE(href);
-			}
-		    }
-		    StrAllocCat(buf, "\n");
-		    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-		}
-	    } else {
-		HTSprintf0(&buf, "<DD>%s\n", fname ? fname : rcode);
-		(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-	    }
-	}
-    }
-  end_CSOreport:
-    if (prev_ndx != -100) {
-	HTSprintf0(&buf, "</DL></DL>\n");
-	(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-    }
-    FREE(buf);
-    return 0;
-}
-
-/*	CSO/PH form-based search gateway - FM			HTLoadCSO
- *	=====================================
- */
-static int HTLoadCSO(const char *arg,
-		     HTParentAnchor *anAnchor,
-		     HTFormat format_out,
-		     HTStream *sink)
-{
-    static const char end_form[] = "</BODY>\n</HTML>\n";
-    char *host, *cp, *data;
-    int port = CSO_PORT;
-    int status;			/* tcp return */
-    bstring *command = NULL;
-    bstring *content = NULL;
-    int len, i, j, start, finish, flen, ndx;
-    int return_type, has_indexed;
-    CSOfield_info *fld;
-    char buf[2048];
-    HTFormat format_in = WWW_HTML;
-    HTStream *Target = NULL;
-
-    if (!acceptable_inited)
-	init_acceptable();
-
-    if (!arg)
-	return -3;		/* Bad if no name sepcified     */
-    if (!*arg)
-	return -2;		/* Bad if name had zero length  */
-    CTRACE((tfp, "HTLoadCSO: Looking for %s\n", arg));
-
-    /*
-     * Set up a socket to the server for the data.
-     */
-    status = HTDoConnect(arg, "cso", CSO_PORT, &s);
-    if (status == HT_INTERRUPTED) {
-	/*
-	 * Interrupt cleanly.
-	 */
-	CTRACE((tfp,
-		"HTLoadCSO: Interrupted on connect; recovering cleanly.\n"));
-	_HTProgress(CONNECTION_INTERRUPTED);
-	return HT_NOT_LOADED;
-    }
-    if (status < 0) {
-	CTRACE((tfp, "HTLoadCSO: Unable to connect to remote host for `%s'.\n",
-		arg));
-	return HTInetStatus("connect");
-    }
-
-    HTInitInput(s);		/* Set up input buffering */
-
-    HTBprintf(&command, "fields%c%c", CR, LF);
-    if (TRACE) {
-	CTRACE((tfp, "HTLoadCSO: Connected, writing command `"));
-	trace_bstring(command);
-	CTRACE((tfp, "' to socket %d\n", s));
-    }
-    _HTProgress(GOPHER_SENDING_CSO_REQUEST);
-    status = (int) NETWRITE(s, BStrData(command), BStrLen(command));
-    BStrFree(command);
-    if (status < 0) {
-	CTRACE((tfp, "HTLoadCSO: Unable to send command.\n"));
-	return HTInetStatus("send");
-    }
-    _HTProgress(GOPHER_SENT_CSO_REQUEST);
-
-    /*
-     * Now read the data from the socket.
-     */
-    status = parse_cso_fields(buf, sizeof(buf));
-    if (status) {
-	NETCLOSE(s);
-	if (status == HT_INTERRUPTED) {
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	} else if (buf[0] != '\0') {
-	    HTAlert(buf);
-	} else {
-	    HTAlert(FAILED_NO_RESPONSE);
-	}
-	return HT_NOT_LOADED;
-    }
-    Target = HTStreamStack(format_in,
-			   format_out,
-			   sink, anAnchor);
-    if (!Target || Target == NULL) {
-	char *temp = 0;
-
-	HTSprintf0(&temp, CANNOT_CONVERT_I_TO_O,
-		   HTAtom_name(format_in), HTAtom_name(format_out));
-	HTAlert(temp);
-	FREE(temp);
-	NETCLOSE(s);
-	return HT_NOT_LOADED;
-    }
-    host = HTParse(arg, "", PARSE_HOST);
-    if ((cp = HTParsePort(host, &port)) != NULL) {
-	if (port == CSO_PORT) {
-	    *cp = '\0';
-	}
-    }
-    anAnchor->safe = TRUE;
-    if (isBEmpty(anAnchor->post_data)) {
-	generate_cso_form(host, port, buf, Target);
-	(*Target->isa->_free) (Target);
-	FREE(host);
-	NETCLOSE(s);
-	free_CSOfields();
-	return HT_LOADED;
-    }
-
-    HTBprintf(&command,
-	      "<HTML>\n<HEAD>\n<TITLE>CSO/PH Results on %s</TITLE>\n</HEAD>\n<BODY>\n",
-	      host);
-    (*Target->isa->put_block) (Target, BStrData(command), BStrLen(command));
-    BStrFree(command);
-    FREE(host);
-
-    BStrCopy(content, anAnchor->post_data);
-    assert(content != NULL);
-
-    if (BStrData(content)[BStrLen(content) - 1] != '&')
-	BStrCat0(content, "&");
-
-    data = BStrData(content);
-    len = BStrLen(content);
-    for (i = 0; i < len; i++) {
-	if (data[i] == '+') {
-	    data[i] = ' ';
-	}
-    }
-
-    data = BStrData(content);
-    HTUnEscape(data);		/* FIXME: could it have embedded null? */
-    len = BStrLen(content);
-
-    return_type = 0;
-    has_indexed = 0;
-    start = 0;
-    for (i = 0; i < len; i++) {
-	if (!data[i] || data[i] == '&') {
-	    /*
-	     * Value parsed.  Unescape characters and look for first '=' to
-	     * delimit field name from value.
-	     */
-	    flen = i - start;
-	    finish = start + flen;
-	    data[finish] = '\0';
-	    for (j = start; j < finish; j++) {
-		if (data[j] == '=') {
-		    /*
-		     * data[start..j-1] is field name,
-		     * [j+1..finish-1] is value.
-		     */
-		    if ((data[start + 1] == '_') &&
-			((data[start] == 'r') || (data[start] == 'q'))) {
-			/*
-			 * Decode fields number and lookup field info.
-			 */
-			sscanf(&data[start + 2], "%d=", &ndx);
-			for (fld = CSOfields; fld; fld = fld->next) {
-			    if (ndx == fld->id) {
-				if ((j + 1) >= finish)
-				    break;	/* ignore nulls */
-				if (data[start] == 'q') {
-				    /*
-				     * Append field to query line.
-				     */
-				    if (fld->lookup) {
-					if (fld->indexed)
-					    has_indexed = 1;
-					if (isBEmpty(command)) {
-					    BStrCopy0(command, "query ");
-					} else {
-					    BStrCat0(command, " ");
-					}
-					HTBprintf(&command, "%s=\"%s\"",
-						  fld->name, &data[j + 1]);
-				    } else {
-					strcpy(buf,
-					       "Warning: non-lookup field ignored<BR>\n");
-					(*Target->isa->put_block) (Target,
-								   buf,
-								   (int)
-								   strlen(buf));
-				    }
-				} else if (data[start] == 'r') {
-				    fld->explicit_return = 1;
-				}
-				break;
-			    }
-			}
-		    } else if (!StrNCmp(&data[start], "return=", 7)) {
-			if (!strcmp(&data[start + 7], "all")) {
-			    return_type = 1;
-			} else if (!strcmp(&data[start + 7], "selected")) {
-			    return_type = 2;
-			}
-		    }
-		}
-	    }
-	    start = i + 1;
-	}
-    }
-    BStrFree(content);
-    if (isBEmpty(command) || !has_indexed) {
-	NETCLOSE(s);
-	strcpy(buf,
-	       "<EM>Error:</EM> At least one indexed field value must be specified!\n");
-	(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-	strcpy(buf, "</BODY>\n</HTML>\n");
-	(*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-	(*Target->isa->_free) (Target);
-	free_CSOfields();
-	return HT_LOADED;
-    }
-    /*
-     * Append return fields.
-     */
-    if (return_type == 1) {
-	BStrCat0(command, " return all");
-    } else if (return_type == 2) {
-	BStrCat0(command, " return");
-	for (fld = CSOfields; fld; fld = fld->next) {
-	    if (fld->explicit_return) {
-		HTBprintf(&command, " %s", fld->name);
-	    }
-	}
-    }
-    HTBprintf(&command, "%c%c", CR, LF);
-    strcpy(buf, "<H2>\n<EM>CSO/PH command:</EM> ");
-    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-    (*Target->isa->put_block) (Target, BStrData(command), BStrLen(command));
-    strcpy(buf, "</H2>\n");
-    (*Target->isa->put_block) (Target, buf, (int) strlen(buf));
-    if (TRACE) {
-	CTRACE((tfp, "HTLoadCSO: Writing command `"));
-	trace_bstring(command);
-	CTRACE((tfp, "' to socket %d\n", s));
-    }
-    status = (int) NETWRITE(s, BStrData(command), BStrLen(command));
-    BStrFree(command);
-    if (status < 0) {
-	CTRACE((tfp, "HTLoadCSO: Unable to send command.\n"));
-	free_CSOfields();
-	return HTInetStatus("send");
-    }
-    generate_cso_report(Target);
-    NETCLOSE(s);
-    (*Target->isa->put_block) (Target, end_form, (int) sizeof(end_form) - 1);
-    (*Target->isa->_free) (Target);
-    FREE(host);
-    free_CSOfields();
-    return HT_LOADED;
-}
-
-/*	Load by name.						HTLoadGopher
- *	=============
- *
- *  Bug:  No decoding of strange data types as yet.
- *
- */
-static int HTLoadGopher(const char *arg,
-			HTParentAnchor *anAnchor,
-			HTFormat format_out,
-			HTStream *sink)
-{
-    char *command;		/* The whole command */
-    int status;			/* tcp return */
-    char gtype;			/* Gopher Node type */
-    char *selector;		/* Selector string */
-
-    if (!acceptable_inited)
-	init_acceptable();
-
-    if (!arg)
-	return -3;		/* Bad if no name sepcified     */
-    if (!*arg)
-	return -2;		/* Bad if name had zero length  */
-    CTRACE((tfp, "HTGopher: Looking for %s\n", arg));
-
-    /*
-     * If it's a port 105 GOPHER_CSO gtype with no ISINDEX token ('?'), use the
-     * form-based CSO gateway (otherwise, return an ISINDEX cover page or do
-     * the ISINDEX search).  - FM
-     */
-    {
-	size_t len;
-
-	if ((len = strlen(arg)) > 5) {
-	    if (0 == strcmp((const char *) &arg[len - 6], ":105/2")) {
-		/* Use CSO gateway. */
-		CTRACE((tfp, "HTGopher: Passing to CSO/PH gateway.\n"));
-		return HTLoadCSO(arg, anAnchor, format_out, sink);
-	    }
-	}
-    }
-
-    /*
-     * If it's a port 79/0[/...] URL, use the finger gateway.  - FM
-     */
-    if (strstr(arg, ":79/0") != NULL) {
-#ifndef DISABLE_FINGER
-	CTRACE((tfp, "HTGopher: Passing to finger gateway.\n"));
-	return HTLoadFinger(arg, anAnchor, format_out, sink);
-#else /* finger is disabled */
-	HTAlert(COULD_NOT_ACCESS_DOCUMENT);
-	return HT_NOT_LOADED;
-#endif /* DISABLE_FINGER */
-    }
-
-    /*
-     * Get entity type, and selector string.
-     */
-    {
-	char *p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
-
-	gtype = '1';		/* Default = menu */
-	selector = p1;
-	if ((*selector++ == '/') && (*selector)) {	/* Skip first slash */
-	    gtype = *selector++;	/* Pick up gtype */
-	}
-	if (gtype == GOPHER_INDEX) {
-	    char *query;
-
-	    /*
-	     * Search is allowed.
-	     */
-	    HTAnchor_setIndex(anAnchor, anAnchor->address);
-	    query = strchr(selector, '?');	/* Look for search string */
-	    if (!query || !query[1]) {	/* No search required */
-		target = HTML_new(anAnchor, format_out, sink);
-		targetClass = *target->isa;
-		display_index(arg, anAnchor);	/* Display "cover page" */
-		return HT_LOADED;	/* Local function only */
-	    }
-	    *query++ = '\0';	/* Skip '?'     */
-	    command =
-		(char *) malloc(strlen(selector) + 1 + strlen(query) + 2 + 1);
-	    if (command == NULL)
-		outofmem(__FILE__, "HTLoadGopher");
-
-	    assert(command != NULL);
-
-	    de_escape(command, selector);	/* Bug fix TBL 921208 */
-
-	    strcat(command, "\t");
-
-	    {			/* Remove plus signs 921006 */
-		char *p;
-
-		for (p = query; *p; p++) {
-		    if (*p == '+')
-			*p = ' ';
-		}
-	    }
-
-	    de_escape(&command[strlen(command)], query);	/* bug fix LJM 940415 */
-	} else if (gtype == GOPHER_CSO) {
-	    char *query;
-
-	    /*
-	     * Search is allowed.
-	     */
-	    query = strchr(selector, '?');	/* Look for search string */
-	    if (!query || !query[1]) {	/* No search required */
-		target = HTML_new(anAnchor, format_out, sink);
-		targetClass = *target->isa;
-		display_cso(arg, anAnchor);	/* Display "cover page" */
-		return HT_LOADED;	/* Local function only */
-	    }
-	    HTAnchor_setIndex(anAnchor, anAnchor->address);
-	    *query++ = '\0';	/* Skip '?'     */
-	    command = (char *) malloc(strlen("query") + 1 +
-				      strlen(query) + 2 + 1);
-	    if (command == NULL)
-		outofmem(__FILE__, "HTLoadGopher");
-
-	    assert(command != NULL);
-
-	    de_escape(command, selector);	/* Bug fix TBL 921208 */
-
-	    strcpy(command, "query ");
-
-	    {			/* Remove plus signs 921006 */
-		char *p;
-
-		for (p = query; *p; p++) {
-		    if (*p == '+')
-			*p = ' ';
-		}
-	    }
-	    de_escape(&command[strlen(command)], query);	/* bug fix LJM 940415 */
-
-	} else {		/* Not index */
-	    command = (char *) malloc(strlen(selector) + 2 + 1);
-	    if (command == NULL)
-		outofmem(__FILE__, "HTLoadGopher");
-
-	    assert(command != NULL);
-
-	    de_escape(command, selector);
-	}
-	FREE(p1);
-    }
-
-    {
-	char *p = command + strlen(command);
-
-	*p++ = CR;		/* Macros to be correct on Mac */
-	*p++ = LF;
-	*p = '\0';
-    }
-
-    /*
-     * Set up a socket to the server for the data.
-     */
-    status = HTDoConnect(arg, "gopher", GOPHER_PORT, &s);
-    if (status == HT_INTERRUPTED) {
-	/*
-	 * Interrupt cleanly.
-	 */
-	CTRACE((tfp, "HTGopher: Interrupted on connect; recovering cleanly.\n"));
-	_HTProgress(CONNECTION_INTERRUPTED);
-	FREE(command);
-	return HT_NOT_LOADED;
-    }
-    if (status < 0) {
-	CTRACE((tfp, "HTGopher: Unable to connect to remote host for `%s'.\n",
-		arg));
-	FREE(command);
-	return HTInetStatus("connect");
-    }
-
-    HTInitInput(s);		/* Set up input buffering */
-
-    CTRACE((tfp, "HTGopher: Connected, writing command `%s' to socket %d\n",
-	    command, s));
-
-#ifdef NOT_ASCII
-    {
-	char *p;
-
-	for (p = command; *p; p++) {
-	    *p = TOASCII(*p);
-	}
-    }
-#endif
-
-    _HTProgress(GOPHER_SENDING_REQUEST);
-
-    status = (int) NETWRITE(s, command, (int) strlen(command));
-    FREE(command);
-    if (status < 0) {
-	CTRACE((tfp, "HTGopher: Unable to send command.\n"));
-	return HTInetStatus("send");
-    }
-
-    _HTProgress(GOPHER_SENT_REQUEST);
-
-    /*
-     * Now read the data from the socket.
-     */
-    switch (gtype) {
-
-    case GOPHER_TEXT:
-	HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
-	break;
-
-    case GOPHER_HTML:
-    case GOPHER_CHTML:
-	HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
-	break;
-
-    case GOPHER_GIF:
-    case GOPHER_IMAGE:
-    case GOPHER_PLUS_IMAGE:
-	HTParseSocket(HTAtom_for("image/gif"),
-		      format_out, anAnchor, s, sink);
-	break;
-
-    case GOPHER_MENU:
-    case GOPHER_INDEX:
-	target = HTML_new(anAnchor, format_out, sink);
-	targetClass = *target->isa;
-	parse_menu(arg, anAnchor);
-	break;
-
-    case GOPHER_CSO:
-	target = HTML_new(anAnchor, format_out, sink);
-	targetClass = *target->isa;
-	parse_cso(arg, anAnchor);
-	break;
-
-    case GOPHER_SOUND:
-    case GOPHER_PLUS_SOUND:
-	HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink);
-	break;
-
-    case GOPHER_PLUS_MOVIE:
-	HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink);
-	break;
-
-    case GOPHER_PLUS_PDF:
-	HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor,
-		      s, sink);
-	break;
-
-    case GOPHER_MACBINHEX:
-    case GOPHER_PCBINARY:
-    case GOPHER_UUENCODED:
-    case GOPHER_BINARY:
-    default:
-	/*
-	 * Specifying WWW_UNKNOWN forces dump to local disk.
-	 */
-	HTParseSocket(WWW_UNKNOWN, format_out, anAnchor, s, sink);
-	break;
-
-    }				/* switch(gtype) */
-
-    NETCLOSE(s);
-    return HT_LOADED;
-}
-
-#ifdef GLOBALDEF_IS_MACRO
-#define _HTGOPHER_C_1_INIT { "gopher", HTLoadGopher, NULL }
-GLOBALDEF(HTProtocol, HTGopher, _HTGOPHER_C_1_INIT);
-#define _HTCSO_C_1_INIT { "cso", HTLoadCSO, NULL }
-GLOBALDEF(HTProtocol, HTCSO, _HTCSO_C_1_INIT);
-#else
-GLOBALDEF HTProtocol HTGopher =
-{"gopher", HTLoadGopher, NULL};
-GLOBALDEF HTProtocol HTCSO =
-{"cso", HTLoadCSO, NULL};
-#endif /* GLOBALDEF_IS_MACRO */
-
-#endif /* not DISABLE_GOPHER */