about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTFTP.c
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation/HTFTP.c')
-rw-r--r--WWW/Library/Implementation/HTFTP.c4224
1 files changed, 0 insertions, 4224 deletions
diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c
deleted file mode 100644
index 480eacfd..00000000
--- a/WWW/Library/Implementation/HTFTP.c
+++ /dev/null
@@ -1,4224 +0,0 @@
-/*
- * $LynxId: HTFTP.c,v 1.101 2012/02/09 12:34:48 tom Exp $
- *
- *			File Transfer Protocol (FTP) Client
- *			for a WorldWideWeb browser
- *			===================================
- *
- *	A cache of control connections is kept.
- *
- * Note: Port allocation
- *
- *	It is essential that the port is allocated by the system, rather
- *	than chosen in rotation by us (POLL_PORTS), or the following
- *	problem occurs.
- *
- *	It seems that an attempt by the server to connect to a port which has
- *	been used recently by a listen on the same socket, or by another
- *	socket this or another process causes a hangup of (almost exactly)
- *	one minute.  Therefore, we have to use a rotating port number.
- *	The problem remains that if the application is run twice in quick
- *	succession, it will hang for what remains of a minute.
- *
- * Authors
- *	TBL	Tim Berners-lee <timbl@info.cern.ch>
- *	DD	Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
- *	LM	Lou Montulli <montulli@ukanaix.cc.ukans.edu>
- *	FM	Foteos Macrides <macrides@sci.wfeb.edu>
- * History:
- *	 2 May 91	Written TBL, as a part of the WorldWideWeb project.
- *	15 Jan 92	Bug fix: close() was used for NETCLOSE for control soc
- *	10 Feb 92	Retry if cached connection times out or breaks
- *	 8 Dec 92	Bug fix 921208 TBL after DD
- *	17 Dec 92	Anon FTP password now just WWWuser@ suggested by DD
- *			fails on princeton.edu!
- *	27 Dec 93 (FM)	Fixed up so FTP now works with VMS hosts.  Path
- *			must be Unix-style and cannot include the device
- *			or top directory.
- *	?? ??? ?? (LM)	Added code to prompt and send passwords for non
- *			anonymous FTP
- *	25 Mar 94 (LM)	Added code to recognize different ftp server types
- *			and code to parse dates and sizes on most hosts.
- *	27 Mar 93 (FM)	Added code for getting dates and sizes on VMS hosts.
- *
- * Notes:
- *			Portions Copyright 1994 Trustees of Dartmouth College
- *			Code for recognizing different FTP servers and
- *			parsing "ls -l" output taken from Macintosh Fetch
- *			program with permission from Jim Matthews,
- *			Dartmouth Software Development Team.
- */
-
-/*
- * BUGS:	@@@	Limit connection cache size!
- * 		Error reporting to user.
- * 		400 & 500 errors are ack'ed by user with windows.
- * 		Use configuration file for user names
- * 
- *		Note for portability this version does not use select() and
- *		so does not watch the control and data channels at the
- *		same time.
- */
-
-#include <HTUtils.h>
-
-#include <HTAlert.h>
-
-#include <HTFTP.h>		/* Implemented here */
-#include <HTTCP.h>
-#include <HTTP.h>
-#include <HTFont.h>
-
-#define REPEAT_PORT		/* Give the port number for each file */
-#define REPEAT_LISTEN		/* Close each listen socket and open a new one */
-
-/* define POLL_PORTS		 If allocation does not work, poll ourselves.*/
-#define LISTEN_BACKLOG 2	/* Number of pending connect requests (TCP) */
-
-#define FIRST_TCP_PORT	1024	/* Region to try for a listening port */
-#define LAST_TCP_PORT	5999
-
-#define LINE_LENGTH 256
-
-#include <HTParse.h>
-#include <HTAnchor.h>
-#include <HTFile.h>		/* For HTFileFormat() */
-#include <HTBTree.h>
-#include <HTChunk.h>
-#ifndef IPPORT_FTP
-#define IPPORT_FTP	21
-#endif /* !IPORT_FTP */
-
-#include <LYUtils.h>
-#include <LYGlobalDefs.h>
-#include <LYStrings.h>
-#include <LYLeaks.h>
-
-typedef struct _connection {
-    struct _connection *next;	/* Link on list         */
-    unsigned long addr;		/* IP address           */
-    int socket;			/* Socket number for communication */
-    BOOL binary;		/* Binary mode? */
-} connection;
-
-/*		Hypertext object building machinery
-*/
-#include <HTML.h>
-
-/*
- * socklen_t is the standard, but there are many pre-standard variants.
- * This ifdef works around a few of those cases.
- *
- * Information was obtained from header files on these platforms:
- *	AIX 4.3.2, 5.1
- *	HPUX 10.20, 11.00, 11.11
- *	IRIX64 6.5
- *	Tru64 4.0G, 4.0D, 5.1
- */
-#if defined(SYS_IRIX64)
-	/* IRIX64 6.5 socket.h may use socklen_t if SGI_SOURCE is not defined */
-#  if _NO_XOPEN4 && _NO_XOPEN5
-#    define LY_SOCKLEN socklen_t
-#  elif _ABIAPI
-#    define LY_SOCKLEN int
-#  elif _XOPEN5
-#    if (_MIPS_SIM != _ABIO32)
-#      define LY_SOCKLEN socklen_t
-#    else
-#      define LY_SOCKLEN int
-#    endif
-#  else
-#    define LY_SOCKLEN size_t
-#  endif
-#elif defined(SYS_HPUX)
-#  if defined(_XOPEN_SOURCE_EXTENDED) && defined(SO_PROTOTYPE)
-#    define LY_SOCKLEN socklen_t
-#  else	/* HPUX 10.20, etc. */
-#    define LY_SOCKLEN int
-#  endif
-#elif defined(SYS_TRU64)
-#  if defined(_POSIX_PII_SOCKET)
-#    define LY_SOCKLEN socklen_t
-#  elif defined(_XOPEN_SOURCE_EXTENDED)
-#    define LY_SOCKLEN size_t
-#  else
-#    define LY_SOCKLEN int
-#  endif
-#else
-#  define LY_SOCKLEN socklen_t
-#endif
-
-#define PUTC(c)      (*target->isa->put_character) (target, c)
-#define PUTS(s)      (*target->isa->put_string)    (target, s)
-#define START(e)     (*target->isa->start_element) (target, e, 0, 0, -1, 0)
-#define END(e)       (*target->isa->end_element)   (target, e, 0)
-#define FREE_TARGET  (*target->isa->_free)         (target)
-#define ABORT_TARGET (*target->isa->_free)         (target)
-
-struct _HTStructured {
-    const HTStructuredClass *isa;
-    /* ... */
-};
-
-/*	Global Variables
- *	---------------------
- */
-int HTfileSortMethod = FILE_BY_NAME;
-
-#ifndef DISABLE_FTP		/*This disables everything to end-of-file */
-static char ThisYear[8];
-static char LastYear[8];
-static int TheDate;
-static BOOLEAN HaveYears = FALSE;
-
-/*	Module-Wide Variables
- *	---------------------
- */
-static connection *connections = NULL;	/* Linked list of connections */
-static char response_text[LINE_LENGTH + 1];	/* Last response from ftp host */
-static connection *control = NULL;	/* Current connection */
-static int data_soc = -1;	/* Socket for data transfer =invalid */
-static char *user_entered_password = NULL;
-static char *last_username_and_host = NULL;
-
-/*
- * Some ftp servers are known to have a broken implementation of RETR.  If
- * asked to retrieve a directory, they get confused and fail subsequent
- * commands such as CWD and LIST.
- */
-static int Broken_RETR = FALSE;
-
-/*
- * Some ftp servers are known to have a broken implementation of EPSV.  The
- * server will hang for a long time when we attempt to connect after issuing
- * this command.
- */
-#ifdef INET6
-static int Broken_EPSV = FALSE;
-#endif
-
-typedef enum {
-    GENERIC_SERVER
-    ,MACHTEN_SERVER
-    ,UNIX_SERVER
-    ,VMS_SERVER
-    ,CMS_SERVER
-    ,DCTS_SERVER
-    ,TCPC_SERVER
-    ,PETER_LEWIS_SERVER
-    ,NCSA_SERVER
-    ,WINDOWS_NT_SERVER
-    ,WINDOWS_2K_SERVER
-    ,MS_WINDOWS_SERVER
-    ,MSDOS_SERVER
-    ,APPLESHARE_SERVER
-    ,NETPRESENZ_SERVER
-    ,DLS_SERVER
-} eServerType;
-
-static eServerType server_type = GENERIC_SERVER;	/* the type of ftp host */
-static int unsure_type = FALSE;	/* sure about the type? */
-static BOOLEAN use_list = FALSE;	/* use the LIST command? */
-
-static int interrupted_in_next_data_char = FALSE;
-
-#ifdef POLL_PORTS
-static PortNumber port_number = FIRST_TCP_PORT;
-#endif /* POLL_PORTS */
-
-static BOOL have_socket = FALSE;	/* true if master_socket is valid */
-static unsigned master_socket;	/* Listening socket = invalid */
-
-static char port_command[255];	/* Command for setting the port */
-static fd_set open_sockets;	/* Mask of active channels */
-static unsigned num_sockets;	/* Number of sockets to scan */
-static PortNumber passive_port;	/* Port server specified for data */
-
-#define NEXT_CHAR HTGetCharacter()	/* Use function in HTFormat.c */
-
-#define DATA_BUFFER_SIZE 2048
-static char data_buffer[DATA_BUFFER_SIZE];	/* Input data buffer */
-static char *data_read_pointer;
-static char *data_write_pointer;
-
-#define NEXT_DATA_CHAR next_data_char()
-static int close_connection(connection * con);
-
-#ifdef LY_FIND_LEAKS
-/*
- *  This function frees module globals. - FM
- */
-static void free_FTPGlobals(void)
-{
-    FREE(user_entered_password);
-    FREE(last_username_and_host);
-    if (control) {
-	if (control->socket != -1)
-	    close_connection(control);
-	FREE(control);
-    }
-}
-#endif /* LY_FIND_LEAKS */
-
-/* PUBLIC						HTVMS_name()
- *		CONVERTS WWW name into a VMS name
- * ON ENTRY:
- *	nn		Node Name (optional)
- *	fn		WWW file name
- *
- * ON EXIT:
- *	returns		vms file specification
- *
- * Bug: Returns pointer to static -- non-reentrant
- */
-char *HTVMS_name(const char *nn,
-		 const char *fn)
-{
-    /* We try converting the filename into Files-11 syntax.  That is, we assume
-     * first that the file is, like us, on a VMS node.  We try remote (or
-     * local) DECnet access.  Files-11, VMS, VAX and DECnet are trademarks of
-     * Digital Equipment Corporation.  The node is assumed to be local if the
-     * hostname WITHOUT DOMAIN matches the local one.  @@@
-     */
-    static char *vmsname;
-    char *filename = (char *) malloc(strlen(fn) + 1);
-    char *nodename = (char *) malloc(strlen(nn) + 2 + 1);	/* Copies to hack */
-    char *second;		/* 2nd slash */
-    char *last;			/* last slash */
-
-    const char *hostname = HTHostName();
-
-    if (!filename || !nodename)
-	outofmem(__FILE__, "HTVMSname");
-
-    assert(filename != NULL);
-    assert(nodename != NULL);
-
-    strcpy(filename, fn);
-    strcpy(nodename, "");	/* On same node?  Yes if node names match */
-    if (StrNCmp(nn, "localhost", 9)) {
-	const char *p;
-	const char *q;
-
-	for (p = hostname, q = nn;
-	     *p && *p != '.' && *q && *q != '.'; p++, q++) {
-	    if (TOUPPER(*p) != TOUPPER(*q)) {
-		char *r;
-
-		strcpy(nodename, nn);
-		r = strchr(nodename, '.');	/* Mismatch */
-		if (r)
-		    *r = '\0';	/* Chop domain */
-		strcat(nodename, "::");		/* Try decnet anyway */
-		break;
-	    }
-	}
-    }
-
-    second = strchr(filename + 1, '/');		/* 2nd slash */
-    last = strrchr(filename, '/');	/* last slash */
-
-    if (!second) {		/* Only one slash */
-	HTSprintf0(&vmsname, "%s%s", nodename, filename + 1);
-    } else if (second == last) {	/* Exactly two slashes */
-	*second = '\0';		/* Split filename from disk */
-	HTSprintf0(&vmsname, "%s%s:%s", nodename, filename + 1, second + 1);
-	*second = '/';		/* restore */
-    } else {			/* More than two slashes */
-	char *p;
-
-	*second = '\0';		/* Split disk from directories */
-	*last = '\0';		/* Split dir from filename */
-	HTSprintf0(&vmsname, "%s%s:[%s]%s",
-		   nodename, filename + 1, second + 1, last + 1);
-	*second = *last = '/';	/* restore filename */
-	for (p = strchr(vmsname, '['); *p != ']'; p++)
-	    if (*p == '/')
-		*p = '.';	/* Convert dir sep.  to dots */
-    }
-    FREE(nodename);
-    FREE(filename);
-    return vmsname;
-}
-
-/*	Procedure: Read a character from the data connection
- *	----------------------------------------------------
- */
-static int next_data_char(void)
-{
-    int status;
-
-    if (data_read_pointer >= data_write_pointer) {
-	status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
-	if (status == HT_INTERRUPTED)
-	    interrupted_in_next_data_char = 1;
-	if (status <= 0)
-	    return EOF;
-	data_write_pointer = data_buffer + status;
-	data_read_pointer = data_buffer;
-    }
-#ifdef NOT_ASCII
-    {
-	char c = *data_read_pointer++;
-
-	return FROMASCII(c);
-    }
-#else
-    return UCH(*data_read_pointer++);
-#endif /* NOT_ASCII */
-}
-
-/*	Close an individual connection
- *
- */
-static int close_connection(connection * con)
-{
-    connection *scan;
-    int status;
-
-    CTRACE((tfp, "HTFTP: Closing control socket %d\n", con->socket));
-    status = NETCLOSE(con->socket);
-    if (TRACE && status != 0) {
-#ifdef UNIX
-	CTRACE((tfp, "HTFTP:close_connection: %s", LYStrerror(errno)));
-#else
-	if (con->socket != INVSOC)
-	    HTInetStatus("HTFTP:close_connection");
-#endif
-    }
-    con->socket = -1;
-    if (connections == con) {
-	connections = con->next;
-	return status;
-    }
-    for (scan = connections; scan; scan = scan->next) {
-	if (scan->next == con) {
-	    scan->next = con->next;	/* Unlink */
-	    if (control == con)
-		control = (connection *) 0;
-	    return status;
-	}			/*if */
-    }				/* for */
-    return -1;			/* very strange -- was not on list. */
-}
-
-static char *help_message_buffer = NULL;	/* global :( */
-
-static void init_help_message_cache(void)
-{
-    FREE(help_message_buffer);
-}
-
-static void help_message_cache_add(char *string)
-{
-    if (help_message_buffer)
-	StrAllocCat(help_message_buffer, string);
-    else
-	StrAllocCopy(help_message_buffer, string);
-
-    CTRACE((tfp, "Adding message to help cache: %s\n", string));
-}
-
-static char *help_message_cache_non_empty(void)
-{
-    return (help_message_buffer);
-}
-
-static char *help_message_cache_contents(void)
-{
-    return (help_message_buffer);
-}
-
-/*	Send One Command
- *	----------------
- *
- *	This function checks whether we have a control connection, and sends
- *	one command if given.
- *
- * On entry,
- *	control	points to the connection which is established.
- *	cmd	points to a command, or is zero to just get the response.
- *
- *	The command should already be terminated with the CRLF pair.
- *
- * On exit,
- *	returns:  1 for success,
- *		  or negative for communication failure (in which case
- *		  the control connection will be closed).
- */
-static int write_cmd(const char *cmd)
-{
-    int status;
-
-    if (!control) {
-	CTRACE((tfp, "HTFTP: No control connection set up!!\n"));
-	return HT_NO_CONNECTION;
-    }
-
-    if (cmd) {
-	CTRACE((tfp, "  Tx: %s", cmd));
-#ifdef NOT_ASCII
-	{
-	    char *p;
-
-	    for (p = cmd; *p; p++) {
-		*p = TOASCII(*p);
-	    }
-	}
-#endif /* NOT_ASCII */
-	status = (int) NETWRITE(control->socket, cmd, (unsigned) strlen(cmd));
-	if (status < 0) {
-	    CTRACE((tfp,
-		    "HTFTP: Error %d sending command: closing socket %d\n",
-		    status, control->socket));
-	    close_connection(control);
-	    return status;
-	}
-    }
-    return 1;
-}
-
-/*
- * For each string in the list, check if it is found in the response text.
- * If so, return TRUE.
- */
-static BOOL find_response(HTList *list)
-{
-    BOOL result = FALSE;
-    HTList *p = list;
-    char *value;
-
-    while ((value = (char *) HTList_nextObject(p)) != NULL) {
-	if (LYstrstr(response_text, value)) {
-	    result = TRUE;
-	    break;
-	}
-    }
-    return result;
-}
-
-/*	Execute Command and get Response
- *	--------------------------------
- *
- *	See the state machine illustrated in RFC959, p57. This implements
- *	one command/reply sequence.  It also interprets lines which are to
- *	be continued, which are marked with a "-" immediately after the
- *	status code.
- *
- *	Continuation then goes on until a line with a matching reply code
- *	an a space after it.
- *
- * On entry,
- *	control	points to the connection which is established.
- *	cmd	points to a command, or is zero to just get the response.
- *
- *	The command must already be terminated with the CRLF pair.
- *
- * On exit,
- *	returns:  The first digit of the reply type,
- *		  or negative for communication failure.
- */
-static int response(const char *cmd)
-{
-    int result;			/* Three-digit decimal code */
-    int continuation_response = -1;
-    int status;
-
-    if ((status = write_cmd(cmd)) < 0)
-	return status;
-
-    do {
-	char *p = response_text;
-
-	for (;;) {
-	    int ich = NEXT_CHAR;
-
-	    if (((*p++ = (char) ich) == LF)
-		|| (p == &response_text[LINE_LENGTH])) {
-
-		char continuation;
-
-		if (interrupted_in_htgetcharacter) {
-		    CTRACE((tfp,
-			    "HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
-		    NETCLOSE(control->socket);
-		    control->socket = -1;
-		    return HT_INTERRUPTED;
-		}
-
-		*p = '\0';	/* Terminate the string */
-		CTRACE((tfp, "    Rx: %s", response_text));
-
-		/* Check for login or help messages */
-		if (!StrNCmp(response_text, "230-", 4) ||
-		    !StrNCmp(response_text, "250-", 4) ||
-		    !StrNCmp(response_text, "220-", 4))
-		    help_message_cache_add(response_text + 4);
-
-		sscanf(response_text, "%d%c", &result, &continuation);
-		if (continuation_response == -1) {
-		    if (continuation == '-')	/* start continuation */
-			continuation_response = result;
-		} else {	/* continuing */
-		    if (continuation_response == result &&
-			continuation == ' ')
-			continuation_response = -1;	/* ended */
-		}
-		if (result == 220 && find_response(broken_ftp_retr)) {
-		    Broken_RETR = TRUE;
-		    CTRACE((tfp, "This server is broken (RETR)\n"));
-		}
-#ifdef INET6
-		if (result == 220 && find_response(broken_ftp_epsv)) {
-		    Broken_EPSV = TRUE;
-		    CTRACE((tfp, "This server is broken (EPSV)\n"));
-		}
-#endif
-		break;
-	    }
-	    /* if end of line */
-	    if (interrupted_in_htgetcharacter) {
-		CTRACE((tfp,
-			"HTFTP: Interrupted in HTGetCharacter, apparently.\n"));
-		NETCLOSE(control->socket);
-		control->socket = -1;
-		return HT_INTERRUPTED;
-	    }
-
-	    if (ich == EOF) {
-		CTRACE((tfp, "Error on rx: closing socket %d\n",
-			control->socket));
-		strcpy(response_text, "000 *** TCP read error on response\n");
-		close_connection(control);
-		return -1;	/* End of file on response */
-	    }
-	}			/* Loop over characters */
-
-    } while (continuation_response != -1);
-
-    if (result == 421) {
-	CTRACE((tfp, "HTFTP: They close so we close socket %d\n",
-		control->socket));
-	close_connection(control);
-	return -1;
-    }
-    if ((result == 255 && server_type == CMS_SERVER) &&
-	(0 == strncasecomp(cmd, "CWD", 3) ||
-	 0 == strcasecomp(cmd, "CDUP"))) {
-	/*
-	 * Alas, CMS returns 255 on failure to CWD to parent of root.  - PG
-	 */
-	result = 555;
-    }
-    return result / 100;
-}
-
-static int send_cmd_1(const char *verb)
-{
-    char command[80];
-
-    sprintf(command, "%.*s%c%c", (int) sizeof(command) - 4, verb, CR, LF);
-    return response(command);
-}
-
-static int send_cmd_2(const char *verb, const char *param)
-{
-    char *command = 0;
-    int status;
-
-    HTSprintf0(&command, "%s %s%c%c", verb, param, CR, LF);
-    status = response(command);
-    FREE(command);
-
-    return status;
-}
-
-#define send_cwd(path) send_cmd_2("CWD", path)
-
-/*
- * This function should try to set the macintosh server into binary mode.  Some
- * servers need an additional letter after the MACB command.
- */
-static int set_mac_binary(eServerType ServerType)
-{
-    /* try to set mac binary mode */
-    if (ServerType == APPLESHARE_SERVER ||
-	ServerType == NETPRESENZ_SERVER) {
-	/*
-	 * Presumably E means "Enable".  - KW
-	 */
-	return (2 == response("MACB E\r\n"));
-    } else {
-	return (2 == response("MACB\r\n"));
-    }
-}
-
-/* This function gets the current working directory to help
- * determine what kind of host it is
- */
-
-static void get_ftp_pwd(eServerType *ServerType, BOOLEAN *UseList)
-{
-    char *cp;
-
-    /* get the working directory (to see what it looks like) */
-    int status = response("PWD\r\n");
-
-    if (status < 0) {
-	return;
-    } else {
-	cp = strchr(response_text + 5, '"');
-	if (cp)
-	    *cp = '\0';
-	if (*ServerType == TCPC_SERVER) {
-	    *ServerType = ((response_text[5] == '/') ?
-			   NCSA_SERVER : TCPC_SERVER);
-	    CTRACE((tfp, "HTFTP: Treating as %s server.\n",
-		    ((*ServerType == NCSA_SERVER) ?
-		     "NCSA" : "TCPC")));
-	} else if (response_text[5] == '/') {
-	    /* path names beginning with / imply Unix,
-	     * right?
-	     */
-	    if (set_mac_binary(*ServerType)) {
-		*ServerType = NCSA_SERVER;
-		CTRACE((tfp, "HTFTP: Treating as NCSA server.\n"));
-	    } else {
-		*ServerType = UNIX_SERVER;
-		*UseList = TRUE;
-		CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
-	    }
-	    return;
-	} else if (response_text[strlen(response_text) - 1] == ']') {
-	    /* path names ending with ] imply VMS, right? */
-	    *ServerType = VMS_SERVER;
-	    *UseList = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
-	} else {
-	    *ServerType = GENERIC_SERVER;
-	    CTRACE((tfp, "HTFTP: Treating as Generic server.\n"));
-	}
-
-	if ((*ServerType == NCSA_SERVER) ||
-	    (*ServerType == TCPC_SERVER) ||
-	    (*ServerType == PETER_LEWIS_SERVER) ||
-	    (*ServerType == NETPRESENZ_SERVER))
-	    set_mac_binary(*ServerType);
-    }
-}
-
-/* This function turns MSDOS-like directory output off for
- * Windows NT servers.
- */
-
-static void set_unix_dirstyle(eServerType *ServerType, BOOLEAN *UseList)
-{
-    char *cp;
-
-    /* This is a toggle.  It seems we have to toggle in order to see
-     * the current state (after toggling), so we may end up toggling
-     * twice.  - kw
-     */
-    int status = response("SITE DIRSTYLE\r\n");
-
-    if (status != 2) {
-	*ServerType = GENERIC_SERVER;
-	CTRACE((tfp, "HTFTP: DIRSTYLE failed, treating as Generic server.\n"));
-	return;
-    } else {
-	*UseList = TRUE;
-	/* Expecting one of:
-	 * 200 MSDOS-like directory output is off
-	 * 200 MSDOS-like directory output is on
-	 * The following code doesn't look for the full exact string -
-	 * who knows how the wording may change in some future version.
-	 * If the first response isn't recognized, we toggle again
-	 * anyway, under the assumption that it's more likely that
-	 * the MSDOS setting was "off" originally. - kw
-	 */
-	cp = strstr(response_text + 4, "MSDOS");
-	if (cp && strstr(cp, " off")) {
-	    return;		/* already off now. */
-	} else {
-	    response("SITE DIRSTYLE\r\n");
-	}
-    }
-}
-
-/*	Get a valid connection to the host
- *	----------------------------------
- *
- * On entry,
- *	arg	points to the name of the host in a hypertext address
- * On exit,
- *	returns <0 if error
- *		socket number if success
- *
- *	This routine takes care of managing timed-out connections, and
- *	limiting the number of connections in use at any one time.
- *
- *	It ensures that all connections are logged in if they exist.
- *	It ensures they have the port number transferred.
- */
-static int get_connection(const char *arg,
-			  HTParentAnchor *anchor)
-{
-    int status;
-    char *command = 0;
-    connection *con;
-    char *username = NULL;
-    char *password = NULL;
-    static BOOLEAN firstuse = TRUE;
-
-    if (firstuse) {
-	/*
-	 * Set up freeing at exit.  - FM
-	 */
-#ifdef LY_FIND_LEAKS
-	atexit(free_FTPGlobals);
-#endif
-	firstuse = FALSE;
-    }
-
-    if (control) {
-	/*
-	 * Reuse this object - KW, DW & FM
-	 */
-	if (control->socket != -1) {
-	    NETCLOSE(control->socket);
-	}
-	con = control;
-	con->addr = 0;
-	con->binary = FALSE;
-    } else {
-	/*
-	 * Allocate and init control struct.
-	 */
-	con = typecalloc(connection);
-	if (con == NULL)
-	    outofmem(__FILE__, "get_connection");
-
-	assert(con != NULL);
-    }
-    con->socket = -1;
-
-    if (!arg)
-	return -1;		/* Bad if no name specified     */
-    if (!*arg)
-	return -1;		/* Bad if name had zero length  */
-
-/* Get node name:
-*/
-    CTRACE((tfp, "get_connection(%s)\n", arg));
-    {
-	char *p1 = HTParse(arg, "", PARSE_HOST);
-	char *p2 = strrchr(p1, '@');	/* user? */
-	char *pw = NULL;
-
-	if (p2 != NULL) {
-	    username = p1;
-	    *p2 = '\0';		/* terminate */
-	    p1 = p2 + 1;	/* point to host */
-	    pw = strchr(username, ':');
-	    if (pw != NULL) {
-		*pw++ = '\0';
-		password = HTUnEscape(pw);
-	    }
-	    if (*username)
-		HTUnEscape(username);
-
-	    /*
-	     * If the password doesn't exist then we are going to have to ask
-	     * the user for it.  The only problem is that we don't want to ask
-	     * for it every time, so we will store away in a primitive fashion.
-	     */
-	    if (!password) {
-		char *tmp = NULL;
-
-		HTSprintf0(&tmp, "%s@%s", username, p1);
-		/*
-		 * If the user@host is not equal to the last time through or
-		 * user_entered_password has no data then we need to ask the
-		 * user for the password.
-		 */
-		if (!last_username_and_host ||
-		    strcmp(tmp, last_username_and_host) ||
-		    !user_entered_password) {
-
-		    StrAllocCopy(last_username_and_host, tmp);
-		    HTSprintf0(&tmp, gettext("Enter password for user %s@%s:"),
-			       username, p1);
-		    FREE(user_entered_password);
-		    user_entered_password = HTPromptPassword(tmp);
-
-		}		/* else we already know the password */
-		password = user_entered_password;
-		FREE(tmp);
-	    }
-	}
-
-	if (!username)
-	    FREE(p1);
-    }				/* scope of p1 */
-
-    status = HTDoConnect(arg, "FTP", IPPORT_FTP, (int *) &con->socket);
-
-    if (status < 0) {
-	if (status == HT_INTERRUPTED) {
-	    CTRACE((tfp, "HTFTP: Interrupted on connect\n"));
-	} else {
-	    CTRACE((tfp, "HTFTP: Unable to connect to remote host for `%s'.\n",
-		    arg));
-	}
-	if (status == HT_INTERRUPTED) {
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	    status = HT_NOT_LOADED;
-	} else {
-	    HTAlert(gettext("Unable to connect to FTP host."));
-	}
-	if (con->socket != -1) {
-	    NETCLOSE(con->socket);
-	}
-
-	FREE(username);
-	if (control == con)
-	    control = NULL;
-	FREE(con);
-	return status;		/* Bad return */
-    }
-
-    CTRACE((tfp, "FTP connected, socket %d  control %p\n",
-	    con->socket, (void *) con));
-    control = con;		/* Current control connection */
-
-    /* Initialise buffering for control connection */
-    HTInitInput(control->socket);
-    init_help_message_cache();	/* Clear the login message buffer. */
-
-/*	Now we log in		Look up username, prompt for pw.
-*/
-    status = response((char *) 0);	/* Get greeting */
-
-    if (status == HT_INTERRUPTED) {
-	CTRACE((tfp, "HTFTP: Interrupted at beginning of login.\n"));
-	_HTProgress(CONNECTION_INTERRUPTED);
-	NETCLOSE(control->socket);
-	control->socket = -1;
-	return HT_INTERRUPTED;
-    }
-    server_type = GENERIC_SERVER;	/* reset */
-    if (status == 2) {		/* Send username */
-	char *cp;		/* look at greeting text */
-
-	/* don't gettext() this -- incoming text: */
-	if (strlen(response_text) > 4) {
-	    if ((cp = strstr(response_text, " awaits your command")) ||
-		(cp = strstr(response_text, " ready."))) {
-		*cp = '\0';
-	    }
-	    cp = response_text + 4;
-	    if (!strncasecomp(cp, "NetPresenz", 10))
-		server_type = NETPRESENZ_SERVER;
-	} else {
-	    cp = response_text;
-	}
-	StrAllocCopy(anchor->server, cp);
-
-	status = send_cmd_2("USER", (username && *username)
-			    ? username
-			    : "anonymous");
-
-	if (status == HT_INTERRUPTED) {
-	    CTRACE((tfp, "HTFTP: Interrupted while sending username.\n"));
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	    NETCLOSE(control->socket);
-	    control->socket = -1;
-	    return HT_INTERRUPTED;
-	}
-    }
-    if (status == 3) {		/* Send password */
-	if (password) {
-	    /*
-	     * We have non-zero length password, so send it. - FM
-	     */
-	    HTSprintf0(&command, "PASS %s%c%c", password, CR, LF);
-	} else {
-	    /*
-	     * Create and send a mail address as the password. - FM
-	     */
-	    const char *the_address;
-	    char *user = NULL;
-	    const char *host = NULL;
-	    char *cp;
-
-	    the_address = anonftp_password;
-	    if (isEmpty(the_address))
-		the_address = personal_mail_address;
-	    if (isEmpty(the_address))
-		the_address = LYGetEnv("USER");
-	    if (isEmpty(the_address))
-		the_address = "WWWuser";
-
-	    StrAllocCopy(user, the_address);
-	    if ((cp = strchr(user, '@')) != NULL) {
-		*cp++ = '\0';
-		if (*cp == '\0')
-		    host = HTHostName();
-		else
-		    host = cp;
-	    } else {
-		host = HTHostName();
-	    }
-
-	    /*
-	     * If host is not fully qualified, suppress it
-	     * as ftp.uu.net prefers a blank to a bad name
-	     */
-	    if (!(host) || strchr(host, '.') == NULL)
-		host = "";
-
-	    HTSprintf0(&command, "PASS %s@%s%c%c", user, host, CR, LF);
-	    FREE(user);
-	}
-	status = response(command);
-	FREE(command);
-	if (status == HT_INTERRUPTED) {
-	    CTRACE((tfp, "HTFTP: Interrupted while sending password.\n"));
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	    NETCLOSE(control->socket);
-	    control->socket = -1;
-	    return HT_INTERRUPTED;
-	}
-    }
-    FREE(username);
-
-    if (status == 3) {
-	status = send_cmd_1("ACCT noaccount");
-	if (status == HT_INTERRUPTED) {
-	    CTRACE((tfp, "HTFTP: Interrupted while sending password.\n"));
-	    _HTProgress(CONNECTION_INTERRUPTED);
-	    NETCLOSE(control->socket);
-	    control->socket = -1;
-	    return HT_INTERRUPTED;
-	}
-
-    }
-    if (status != 2) {
-	CTRACE((tfp, "HTFTP: Login fail: %s", response_text));
-	/* if (control->socket > 0) close_connection(control->socket); */
-	return -1;		/* Bad return */
-    }
-    CTRACE((tfp, "HTFTP: Logged in.\n"));
-
-    /* Check for host type */
-    if (server_type != NETPRESENZ_SERVER)
-	server_type = GENERIC_SERVER;	/* reset */
-    use_list = FALSE;		/* reset */
-    if (response("SYST\r\n") == 2) {
-	/* we got a line -- what kind of server are we talking to? */
-	if (StrNCmp(response_text + 4,
-		    "UNIX Type: L8 MAC-OS MachTen", 28) == 0) {
-	    server_type = MACHTEN_SERVER;
-	    use_list = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as MachTen server.\n"));
-
-	} else if (strstr(response_text + 4, "UNIX") != NULL ||
-		   strstr(response_text + 4, "Unix") != NULL) {
-	    server_type = UNIX_SERVER;
-	    unsure_type = FALSE;	/* to the best of out knowledge... */
-	    use_list = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as Unix server.\n"));
-
-	} else if (strstr(response_text + 4, "MSDOS") != NULL) {
-	    server_type = MSDOS_SERVER;
-	    use_list = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as MSDOS (Unix emulation) server.\n"));
-
-	} else if (StrNCmp(response_text + 4, "VMS", 3) == 0) {
-	    char *tilde = strstr(arg, "/~");
-
-	    use_list = TRUE;
-	    if (tilde != 0
-		&& tilde[2] != 0
-		&& strstr(response_text + 4, "MadGoat") != 0) {
-		server_type = UNIX_SERVER;
-		CTRACE((tfp, "HTFTP: Treating VMS as UNIX server.\n"));
-	    } else {
-		server_type = VMS_SERVER;
-		CTRACE((tfp, "HTFTP: Treating as VMS server.\n"));
-	    }
-
-	} else if ((StrNCmp(response_text + 4, "VM/CMS", 6) == 0) ||
-		   (StrNCmp(response_text + 4, "VM ", 3) == 0)) {
-	    server_type = CMS_SERVER;
-	    use_list = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as CMS server.\n"));
-
-	} else if (StrNCmp(response_text + 4, "DCTS", 4) == 0) {
-	    server_type = DCTS_SERVER;
-	    CTRACE((tfp, "HTFTP: Treating as DCTS server.\n"));
-
-	} else if (strstr(response_text + 4, "MAC-OS TCP/Connect II") != NULL) {
-	    server_type = TCPC_SERVER;
-	    CTRACE((tfp, "HTFTP: Looks like a TCPC server.\n"));
-	    get_ftp_pwd(&server_type, &use_list);
-	    unsure_type = TRUE;
-
-	} else if (server_type == NETPRESENZ_SERVER) {	/* already set above */
-	    use_list = TRUE;
-	    set_mac_binary(server_type);
-	    CTRACE((tfp, "HTFTP: Treating as NetPresenz (MACOS) server.\n"));
-
-	} else if (StrNCmp(response_text + 4, "MACOS Peter's Server", 20) == 0) {
-	    server_type = PETER_LEWIS_SERVER;
-	    use_list = TRUE;
-	    set_mac_binary(server_type);
-	    CTRACE((tfp, "HTFTP: Treating as Peter Lewis (MACOS) server.\n"));
-
-	} else if (StrNCmp(response_text + 4, "Windows_NT", 10) == 0) {
-	    server_type = WINDOWS_NT_SERVER;
-	    CTRACE((tfp, "HTFTP: Treating as Window_NT server.\n"));
-	    set_unix_dirstyle(&server_type, &use_list);
-
-	} else if (StrNCmp(response_text + 4, "Windows2000", 11) == 0) {
-	    server_type = WINDOWS_2K_SERVER;
-	    CTRACE((tfp, "HTFTP: Treating as Window_2K server.\n"));
-	    set_unix_dirstyle(&server_type, &use_list);
-
-	} else if (StrNCmp(response_text + 4, "MS Windows", 10) == 0) {
-	    server_type = MS_WINDOWS_SERVER;
-	    use_list = TRUE;
-	    CTRACE((tfp, "HTFTP: Treating as MS Windows server.\n"));
-
-	} else if (StrNCmp(response_text + 4,
-			   "MACOS AppleShare IP FTP Server", 30) == 0) {
-	    server_type = APPLESHARE_SERVER;
-	    use_list = TRUE;
-	    set_mac_binary(server_type);
-	    CTRACE((tfp, "HTFTP: Treating as AppleShare server.\n"));
-
-	} else {
-	    server_type = GENERIC_SERVER;
-	    CTRACE((tfp, "HTFTP: Ugh!  A Generic server.\n"));
-	    get_ftp_pwd(&server_type, &use_list);
-	    unsure_type = TRUE;
-	}
-    } else {
-	/* SYST fails :(  try to get the type from the PWD command */
-	get_ftp_pwd(&server_type, &use_list);
-    }
-
-/*  Now we inform the server of the port number we will listen on
-*/
-#ifdef NOTREPEAT_PORT
-    {
-	int status = response(port_command);
-
-	if (status != 2) {
-	    if (control->socket)
-		close_connection(control->socket);
-	    return -status;	/* Bad return */
-	}
-	CTRACE((tfp, "HTFTP: Port defined.\n"));
-    }
-#endif /* NOTREPEAT_PORT */
-    return con->socket;		/* Good return */
-}
-
-static void reset_master_socket(void)
-{
-    have_socket = FALSE;
-}
-
-static void set_master_socket(int value)
-{
-    have_socket = (BOOLEAN) (value >= 0);
-    if (have_socket)
-	master_socket = (unsigned) value;
-}
-
-/*	Close Master (listening) socket
- *	-------------------------------
- *
- *
- */
-static int close_master_socket(void)
-{
-    int status;
-
-    if (have_socket)
-	FD_CLR(master_socket, &open_sockets);
-
-    status = NETCLOSE((int) master_socket);
-    CTRACE((tfp, "HTFTP: Closed master socket %u\n", master_socket));
-
-    reset_master_socket();
-
-    if (status < 0)
-	return HTInetStatus(gettext("close master socket"));
-    else
-	return status;
-}
-
-/*	Open a master socket for listening on
- *	-------------------------------------
- *
- *	When data is transferred, we open a port, and wait for the server to
- *	connect with the data.
- *
- * On entry,
- *	have_socket	Must be false, if master_socket is not setup already
- *	master_socket	Must be negative if not set up already.
- * On exit,
- *	Returns		socket number if good
- *			less than zero if error.
- *	master_socket	is socket number if good, else negative.
- *	port_number	is valid if good.
- */
-static int get_listen_socket(void)
-{
-#ifdef INET6
-    struct sockaddr_storage soc_address;	/* Binary network address */
-    struct sockaddr_in *soc_in = (struct sockaddr_in *) &soc_address;
-    int af;
-    LY_SOCKLEN slen;
-
-#else
-    struct sockaddr_in soc_address;	/* Binary network address */
-    struct sockaddr_in *soc_in = &soc_address;
-#endif /* INET6 */
-    int new_socket;		/* Will be master_socket */
-
-    FD_ZERO(&open_sockets);	/* Clear our record of open sockets */
-    num_sockets = 0;
-
-#ifndef REPEAT_LISTEN
-    if (have_socket)
-	return master_socket;	/* Done already */
-#endif /* !REPEAT_LISTEN */
-
-#ifdef INET6
-    /* query address family of control connection */
-    slen = (LY_SOCKLEN) sizeof(soc_address);
-    if (getsockname(control->socket, (struct sockaddr *) &soc_address,
-		    &slen) < 0) {
-	return HTInetStatus("getsockname failed");
-    }
-    af = ((struct sockaddr *) &soc_address)->sa_family;
-    memset(&soc_address, 0, sizeof(soc_address));
-#endif /* INET6 */
-
-/*  Create internet socket
-*/
-#ifdef INET6
-    new_socket = socket(af, SOCK_STREAM, IPPROTO_TCP);
-#else
-    new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-#endif /* INET6 */
-
-    if (new_socket < 0)
-	return HTInetStatus(gettext("socket for master socket"));
-
-    CTRACE((tfp, "HTFTP: Opened master socket number %d\n", new_socket));
-
-/*  Search for a free port.
-*/
-#ifdef INET6
-    memset(&soc_address, 0, sizeof(soc_address));
-    ((struct sockaddr *) &soc_address)->sa_family = af;
-    switch (af) {
-    case AF_INET:
-#ifdef SIN6_LEN
-	((struct sockaddr *) &soc_address)->sa_len = sizeof(struct sockaddr_in);
-#endif /* SIN6_LEN */
-	break;
-    case AF_INET6:
-#ifdef SIN6_LEN
-	((struct sockaddr *) &soc_address)->sa_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
-	break;
-    default:
-	HTInetStatus("AF");
-    }
-#else
-    soc_in->sin_family = AF_INET;	/* Family = internet, host order  */
-    soc_in->sin_addr.s_addr = INADDR_ANY;	/* Any peer address */
-#endif /* INET6 */
-#ifdef POLL_PORTS
-    {
-	PortNumber old_port_number = port_number;
-
-	for (port_number = (old_port_number + 1);; port_number++) {
-	    int status;
-
-	    if (port_number > LAST_TCP_PORT)
-		port_number = FIRST_TCP_PORT;
-	    if (port_number == old_port_number) {
-		return HTInetStatus("bind");
-	    }
-#ifdef INET6
-	    soc_in->sin_port = htons(port_number);
-#else
-	    soc_address.sin_port = htons(port_number);
-#endif /* INET6 */
-#ifdef SOCKS
-	    if (socks_flag)
-		if ((status = Rbind(new_socket,
-				    (struct sockaddr *) &soc_address,
-		/* Cast to generic sockaddr */
-				    SOCKADDR_LEN(soc_address)
-#ifndef SHORTENED_RBIND
-				    ,socks_bind_remoteAddr
-#endif /* !SHORTENED_RBIND */
-		     )) == 0) {
-		    break;
-		} else
-#endif /* SOCKS */
-		    if ((status = bind(new_socket,
-				       (struct sockaddr *) &soc_address,
-		    /* Cast to generic sockaddr */
-				       SOCKADDR_LEN(soc_address)
-			 )) == 0) {
-		    break;
-		}
-	    CTRACE((tfp, "TCP bind attempt to port %d yields %d, errno=%d\n",
-		    port_number, status, SOCKET_ERRNO));
-	}			/* for */
-    }
-#else
-    {
-	int status;
-	LY_SOCKLEN address_length = (LY_SOCKLEN) sizeof(soc_address);
-
-#ifdef SOCKS
-	if (socks_flag)
-	    status = Rgetsockname(control->socket,
-				  (struct sockaddr *) &soc_address,
-				  &address_length);
-	else
-#endif /* SOCKS */
-	    status = getsockname(control->socket,
-				 (struct sockaddr *) &soc_address,
-				 &address_length);
-	if (status < 0)
-	    return HTInetStatus("getsockname");
-#ifdef INET6
-	CTRACE((tfp, "HTFTP: This host is %s\n",
-		HTInetString((void *) soc_in)));
-
-	soc_in->sin_port = 0;	/* Unspecified: please allocate */
-#else
-	CTRACE((tfp, "HTFTP: This host is %s\n",
-		HTInetString(soc_in)));
-
-	soc_address.sin_port = 0;	/* Unspecified: please allocate */
-#endif /* INET6 */
-#ifdef SOCKS
-	if (socks_flag)
-	    status = Rbind(new_socket,
-			   (struct sockaddr *) &soc_address,
-	    /* Cast to generic sockaddr */
-			   sizeof(soc_address)
-#ifndef SHORTENED_RBIND
-			   ,socks_bind_remoteAddr
-#endif /* !SHORTENED_RBIND */
-		);
-	else
-#endif /* SOCKS */
-	    status = bind(new_socket,
-			  (struct sockaddr *) &soc_address,
-	    /* Cast to generic sockaddr */
-			  SOCKADDR_LEN(soc_address)
-		);
-	if (status < 0)
-	    return HTInetStatus("bind");
-
-	address_length = sizeof(soc_address);
-#ifdef SOCKS
-	if (socks_flag)
-	    status = Rgetsockname(new_socket,
-				  (struct sockaddr *) &soc_address,
-				  &address_length);
-	else
-#endif /* SOCKS */
-	    status = getsockname(new_socket,
-				 (struct sockaddr *) &soc_address,
-				 &address_length);
-	if (status < 0)
-	    return HTInetStatus("getsockname");
-    }
-#endif /* POLL_PORTS */
-
-#ifdef INET6
-    CTRACE((tfp, "HTFTP: bound to port %d on %s\n",
-	    (int) ntohs(soc_in->sin_port),
-	    HTInetString((void *) soc_in)));
-#else
-    CTRACE((tfp, "HTFTP: bound to port %d on %s\n",
-	    (int) ntohs(soc_in->sin_port),
-	    HTInetString(soc_in)));
-#endif /* INET6 */
-
-#ifdef REPEAT_LISTEN
-    if (have_socket)
-	(void) close_master_socket();
-#endif /* REPEAT_LISTEN */
-
-    set_master_socket(new_socket);
-
-/*	Now we must find out who we are to tell the other guy
-*/
-    (void) HTHostName();	/* Make address valid - doesn't work */
-#ifdef INET6
-    switch (((struct sockaddr *) &soc_address)->sa_family) {
-    case AF_INET:
-#endif /* INET6 */
-	sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
-		(int) *((unsigned char *) (&soc_in->sin_addr) + 0),
-		(int) *((unsigned char *) (&soc_in->sin_addr) + 1),
-		(int) *((unsigned char *) (&soc_in->sin_addr) + 2),
-		(int) *((unsigned char *) (&soc_in->sin_addr) + 3),
-		(int) *((unsigned char *) (&soc_in->sin_port) + 0),
-		(int) *((unsigned char *) (&soc_in->sin_port) + 1),
-		CR, LF);
-
-#ifdef INET6
-	break;
-
-    case AF_INET6:
-	{
-	    char hostbuf[MAXHOSTNAMELEN];
-	    char portbuf[MAXHOSTNAMELEN];
-
-	    getnameinfo((struct sockaddr *) &soc_address,
-			SOCKADDR_LEN(soc_address),
-			hostbuf,
-			(socklen_t) sizeof(hostbuf),
-			portbuf,
-			(socklen_t) sizeof(portbuf),
-			NI_NUMERICHOST | NI_NUMERICSERV);
-	    sprintf(port_command, "EPRT |%d|%s|%s|%c%c", 2, hostbuf, portbuf,
-		    CR, LF);
-	    break;
-	}
-    default:
-	sprintf(port_command, "JUNK%c%c", CR, LF);
-	break;
-    }
-#endif /* INET6 */
-
-    /*  Inform TCP that we will accept connections
-     */
-    {
-	int status;
-
-#ifdef SOCKS
-	if (socks_flag)
-	    status = Rlisten((int) master_socket, 1);
-	else
-#endif /* SOCKS */
-	    status = listen((int) master_socket, 1);
-	if (status < 0) {
-	    reset_master_socket();
-	    return HTInetStatus("listen");
-	}
-    }
-    CTRACE((tfp, "TCP: Master socket(), bind() and listen() all OK\n"));
-    FD_SET(master_socket, &open_sockets);
-    if ((master_socket + 1) > num_sockets)
-	num_sockets = master_socket + 1;
-
-    return (int) master_socket;	/* Good */
-
-}				/* get_listen_socket */
-
-static const char *months[12] =
-{
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-/*	Procedure: Set the current and last year strings and date integer
- *	-----------------------------------------------------------------
- *
- *	Bug:
- *		This code is for sorting listings by date, if that option
- *		is selected in Lynx, and doesn't take into account time
- *		zones or ensure resetting at midnight, so the sort may not
- *		be perfect, but the actual date isn't changed in the display,
- *		i.e., the date is still correct. - FM
- */
-static void set_years_and_date(void)
-{
-    char day[8], month[8], date[12];
-    time_t NowTime;
-    int i;
-
-    NowTime = time(NULL);
-    StrNCpy(day, (char *) ctime(&NowTime) + 8, 2);
-    day[2] = '\0';
-    if (day[0] == ' ') {
-	day[0] = '0';
-    }
-    StrNCpy(month, (char *) ctime(&NowTime) + 4, 3);
-    month[3] = '\0';
-    for (i = 0; i < 12; i++) {
-	if (!strcasecomp(month, months[i])) {
-	    break;
-	}
-    }
-    i++;
-    sprintf(date, "9999%02d%.2s", i, day);
-    TheDate = atoi(date);
-    strcpy(ThisYear, (char *) ctime(&NowTime) + 20);
-    ThisYear[4] = '\0';
-    sprintf(LastYear, "%d", (atoi(ThisYear) - 1));
-    HaveYears = TRUE;
-}
-
-typedef struct _EntryInfo {
-    char *filename;
-    char *linkname;		/* symbolic link, if any */
-    char *type;
-    char *date;
-    unsigned long size;
-    BOOLEAN display;		/* show this entry? */
-#ifdef LONG_LIST
-    unsigned long file_links;
-    char *file_mode;
-    char *file_user;
-    char *file_group;
-#endif
-} EntryInfo;
-
-static void free_entryinfo_struct_contents(EntryInfo *entry_info)
-{
-    if (entry_info) {
-	FREE(entry_info->filename);
-	FREE(entry_info->linkname);
-	FREE(entry_info->type);
-	FREE(entry_info->date);
-    }
-    /* dont free the struct */
-}
-
-/*
- * is_ls_date() --
- *	Return TRUE if s points to a string of the form:
- *		"Sep  1  1990 " or
- *		"Sep 11 11:59 " or
- *		"Dec 12 1989  " or
- *		"FCv 23 1990  " ...
- */
-static BOOLEAN is_ls_date(char *s)
-{
-    /* must start with three alpha characters */
-    if (!isalpha(UCH(*s++)) || !isalpha(UCH(*s++)) || !isalpha(UCH(*s++)))
-	return FALSE;
-
-    /* space or HT_NON_BREAK_SPACE */
-    if (!(*s == ' ' || *s == HT_NON_BREAK_SPACE)) {
-	return FALSE;
-    }
-    s++;
-
-    /* space or digit */
-    if (!(*s == ' ' || isdigit(UCH(*s)))) {
-	return FALSE;
-    }
-    s++;
-
-    /* digit */
-    if (!isdigit(UCH(*s++)))
-	return FALSE;
-
-    /* space */
-    if (*s++ != ' ')
-	return FALSE;
-
-    /* space or digit */
-    if (!(*s == ' ' || isdigit(UCH(*s)))) {
-	return FALSE;
-    }
-    s++;
-
-    /* digit */
-    if (!isdigit(UCH(*s++)))
-	return FALSE;
-
-    /* colon or digit */
-    if (!(*s == ':' || isdigit(UCH(*s)))) {
-	return FALSE;
-    }
-    s++;
-
-    /* digit */
-    if (!isdigit(UCH(*s++)))
-	return FALSE;
-
-    /* space or digit */
-    if (!(*s == ' ' || isdigit(UCH(*s)))) {
-	return FALSE;
-    }
-    s++;
-
-    /* space */
-    if (*s != ' ')
-	return FALSE;
-
-    return TRUE;
-}				/* is_ls_date() */
-
-/*
- * Extract the name, size, and date from an EPLF line.  - 08-06-96 DJB
- */
-static void parse_eplf_line(char *line,
-			    EntryInfo *info)
-{
-    char *cp = line;
-    char ct[26];
-    unsigned long size;
-    time_t secs;
-    static time_t base;		/* time() value on this OS in 1970 */
-    static int flagbase = 0;
-
-    if (!flagbase) {
-	struct tm t;
-
-	t.tm_year = 70;
-	t.tm_mon = 0;
-	t.tm_mday = 0;
-	t.tm_hour = 0;
-	t.tm_min = 0;
-	t.tm_sec = 0;
-	t.tm_isdst = -1;
-	base = mktime(&t);	/* could return -1 */
-	flagbase = 1;
-    }
-
-    while (*cp) {
-	switch (*cp) {
-	case '\t':
-	    StrAllocCopy(info->filename, cp + 1);
-	    return;
-	case 's':
-	    size = 0;
-	    while (*(++cp) && (*cp != ','))
-		size = (size * 10) + (unsigned long) (*cp - '0');
-	    info->size = size;
-	    break;
-	case 'm':
-	    secs = 0;
-	    while (*(++cp) && (*cp != ','))
-		secs = (secs * 10) + (*cp - '0');
-	    secs += base;	/* assumes that time_t is #seconds */
-	    strcpy(ct, ctime(&secs));
-	    ct[24] = 0;
-	    StrAllocCopy(info->date, ct);
-	    break;
-	case '/':
-	    StrAllocCopy(info->type, ENTRY_IS_DIRECTORY);
-	    /* FALLTHRU */
-	default:
-	    while (*cp) {
-		if (*cp++ == ',')
-		    break;
-	    }
-	    break;
-	}
-    }
-}				/* parse_eplf_line */
-
-/*
- * Extract the name, size, and date from an ls -l line.
- */
-static void parse_ls_line(char *line,
-			  EntryInfo *entry)
-{
-#ifdef LONG_LIST
-    char *next;
-    char *cp;
-#endif
-    int i, j;
-    unsigned long base = 1;
-    unsigned long size_num = 0;
-
-    for (i = (int) strlen(line) - 1;
-	 (i > 13) && (!isspace(UCH(line[i])) || !is_ls_date(&line[i - 12]));
-	 i--) {
-	;			/* null body */
-    }
-    line[i] = '\0';
-    if (i > 13) {
-	StrAllocCopy(entry->date, &line[i - 12]);
-	/* replace the 4th location with nbsp if it is a space or zero */
-	if (entry->date[4] == ' ' || entry->date[4] == '0')
-	    entry->date[4] = HT_NON_BREAK_SPACE;
-	/* make sure year or time is flush right */
-	if (entry->date[11] == ' ') {
-	    for (j = 11; j > 6; j--) {
-		entry->date[j] = entry->date[j - 1];
-	    }
-	}
-    }
-    j = i - 14;
-    while (isdigit(UCH(line[j]))) {
-	size_num += ((unsigned long) (line[j] - '0') * base);
-	base *= 10;
-	j--;
-    }
-    entry->size = size_num;
-    StrAllocCopy(entry->filename, &line[i + 1]);
-
-#ifdef LONG_LIST
-    line[j] = '\0';
-
-    /*
-     * Extract the file-permissions, as a string.
-     */
-    if ((cp = strchr(line, ' ')) != 0) {
-	if ((cp - line) == 10) {
-	    *cp = '\0';
-	    StrAllocCopy(entry->file_mode, line);
-	    *cp = ' ';
-	}
-
-	/*
-	 * Next is the link-count.
-	 */
-	next = 0;
-	entry->file_links = (unsigned long) strtol(cp, &next, 10);
-	if (next == 0 || *next != ' ') {
-	    entry->file_links = 0;
-	    next = cp;
-	} else {
-	    cp = next;
-	}
-	/*
-	 * Next is the user-name.
-	 */
-	while (isspace(UCH(*cp)))
-	    ++cp;
-	if ((next = strchr(cp, ' ')) != 0)
-	    *next = '\0';
-	if (*cp != '\0')
-	    StrAllocCopy(entry->file_user, cp);
-	/*
-	 * Next is the group-name (perhaps).
-	 */
-	if (next != NULL) {
-	    cp = (next + 1);
-	    while (isspace(UCH(*cp)))
-		++cp;
-	    if ((next = strchr(cp, ' ')) != 0)
-		*next = '\0';
-	    if (*cp != '\0')
-		StrAllocCopy(entry->file_group, cp);
-	}
-    }
-#endif
-}
-
-/*
- * Extract the name and size info and whether it refers to a directory from a
- * LIST line in "dls" format.
- */
-static void parse_dls_line(char *line,
-			   EntryInfo *entry_info,
-			   char **pspilledname)
-{
-    short j;
-    int base = 1;
-    int size_num = 0;
-    int len;
-    char *cps = NULL;
-
-    /* README              763  Information about this server\0
-       bin/                  -  \0
-       etc/                  =  \0
-       ls-lR                 0  \0
-       ls-lR.Z               3  \0
-       pub/                  =  Public area\0
-       usr/                  -  \0
-       morgan               14  -> ../real/morgan\0
-       TIMIT.mostlikely.Z\0
-       79215    \0
-     */
-
-    len = (int) strlen(line);
-    if (len == 0) {
-	FREE(*pspilledname);
-	entry_info->display = FALSE;
-	return;
-    }
-    cps = LYSkipNonBlanks(line);
-    if (*cps == '\0') {		/* only a filename, save it and return. */
-	StrAllocCopy(*pspilledname, line);
-	entry_info->display = FALSE;
-	return;
-    }
-    if (len < 24 || line[23] != ' ' ||
-	(isspace(UCH(line[0])) && !*pspilledname)) {
-	/* this isn't the expected "dls" format! */
-	if (!isspace(UCH(line[0])))
-	    *cps = '\0';
-	if (*pspilledname && !*line) {
-	    entry_info->filename = *pspilledname;
-	    *pspilledname = NULL;
-	    if (entry_info->filename[strlen(entry_info->filename) - 1] == '/')
-		StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	    else
-		StrAllocCopy(entry_info->type, "");
-	} else {
-	    StrAllocCopy(entry_info->filename, line);
-	    if (cps && cps != line && *(cps - 1) == '/')
-		StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	    else
-		StrAllocCopy(entry_info->type, "");
-	    FREE(*pspilledname);
-	}
-	return;
-    }
-
-    j = 22;
-    if (line[j] == '=' || line[j] == '-') {
-	StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-    } else {
-	while (isdigit(UCH(line[j]))) {
-	    size_num += (line[j] - '0') * base;
-	    base *= 10;
-	    j--;
-	}
-    }
-    entry_info->size = (unsigned long) size_num;
-
-    cps = LYSkipBlanks(&line[23]);
-    if (!StrNCmp(cps, "-> ", 3) && cps[3] != '\0' && cps[3] != ' ') {
-	StrAllocCopy(entry_info->type, ENTRY_IS_SYMBOLIC_LINK);
-	StrAllocCopy(entry_info->linkname, LYSkipBlanks(cps + 3));
-	entry_info->size = 0;	/* don't display size */
-    }
-
-    if (j > 0)
-	line[j] = '\0';
-
-    LYTrimTrailing(line);
-
-    len = (int) strlen(line);
-    if (len == 0 && *pspilledname && **pspilledname) {
-	line = *pspilledname;
-	len = (int) strlen(*pspilledname);
-    }
-    if (len > 0 && line[len - 1] == '/') {
-	/*
-	 * It's a dir, remove / and mark it as such.
-	 */
-	if (len > 1)
-	    line[len - 1] = '\0';
-	if (!entry_info->type)
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-    }
-
-    StrAllocCopy(entry_info->filename, line);
-    FREE(*pspilledname);
-}				/* parse_dls_line() */
-
-/*
- * parse_vms_dir_entry()
- *	Format the name, date, and size from a VMS LIST line
- *	into the EntryInfo structure - FM
- */
-static void parse_vms_dir_entry(char *line,
-				EntryInfo *entry_info)
-{
-    int i, j;
-    unsigned int ialloc;
-    char *cp, *cpd, *cps, date[16];
-    const char *sp = " ";
-
-    /* Get rid of blank lines, and information lines.  Valid lines have the ';'
-     * version number token.
-     */
-    if (!strlen(line) || (cp = strchr(line, ';')) == NULL) {
-	entry_info->display = FALSE;
-	return;
-    }
-
-    /* Cut out file or directory name at VMS version number. */
-    *cp++ = '\0';
-    StrAllocCopy(entry_info->filename, line);
-
-    /* Cast VMS non-README file and directory names to lowercase. */
-    if (strstr(entry_info->filename, "READ") == NULL) {
-	LYLowerCase(entry_info->filename);
-	i = (int) strlen(entry_info->filename);
-    } else {
-	i = (int) ((strstr(entry_info->filename, "READ")
-		    - entry_info->filename)
-		   + 4);
-	if (!StrNCmp(&entry_info->filename[i], "ME", 2)) {
-	    i += 2;
-	    while (entry_info->filename[i] && entry_info->filename[i] != '.') {
-		i++;
-	    }
-	} else if (!StrNCmp(&entry_info->filename[i], ".ME", 3)) {
-	    i = (int) strlen(entry_info->filename);
-	} else {
-	    i = 0;
-	}
-	LYLowerCase(entry_info->filename + i);
-    }
-
-    /* Uppercase terminal .z's or _z's. */
-    if ((--i > 2) &&
-	entry_info->filename[i] == 'z' &&
-	(entry_info->filename[i - 1] == '.' ||
-	 entry_info->filename[i - 1] == '_'))
-	entry_info->filename[i] = 'Z';
-
-    /* Convert any tabs in rest of line to spaces. */
-    cps = cp - 1;
-    while ((cps = strchr(cps + 1, '\t')) != NULL)
-	*cps = ' ';
-
-    /* Collapse serial spaces. */
-    i = 0;
-    j = 1;
-    cps = cp;
-    while (cps[j] != '\0') {
-	if (cps[i] == ' ' && cps[j] == ' ')
-	    j++;
-	else
-	    cps[++i] = cps[j++];
-    }
-    cps[++i] = '\0';
-
-    /* Set the years and date, if we don't have them yet. * */
-    if (!HaveYears) {
-	set_years_and_date();
-    }
-
-    /* Track down the date. */
-    if ((cpd = strchr(cp, '-')) != NULL &&
-	strlen(cpd) > 9 && isdigit(UCH(*(cpd - 1))) &&
-	isalpha(UCH(*(cpd + 1))) && *(cpd + 4) == '-') {
-
-	/* Month */
-	*(cpd + 2) = (char) TOLOWER(*(cpd + 2));
-	*(cpd + 3) = (char) TOLOWER(*(cpd + 3));
-	sprintf(date, "%.3s ", cpd + 1);
-
-	/* Day */
-	if (isdigit(UCH(*(cpd - 2))))
-	    sprintf(date + 4, "%.2s ", cpd - 2);
-	else
-	    sprintf(date + 4, "%c%.1s ", HT_NON_BREAK_SPACE, cpd - 1);
-
-	/* Time or Year */
-	if (!StrNCmp(ThisYear, cpd + 5, 4) &&
-	    strlen(cpd) > 15 && *(cpd + 12) == ':') {
-	    sprintf(date + 7, "%.5s", cpd + 10);
-	} else {
-	    sprintf(date + 7, " %.4s", cpd + 5);
-	}
-
-	StrAllocCopy(entry_info->date, date);
-    }
-
-    /* Track down the size */
-    if ((cpd = strchr(cp, '/')) != NULL) {
-	/* Appears be in used/allocated format */
-	cps = cpd;
-	while (isdigit(UCH(*(cps - 1))))
-	    cps--;
-	if (cps < cpd)
-	    *cpd = '\0';
-	entry_info->size = (unsigned long) atol(cps);
-	cps = cpd + 1;
-	while (isdigit(UCH(*cps)))
-	    cps++;
-	*cps = '\0';
-	ialloc = (unsigned) atoi(cpd + 1);
-	/* Check if used is in blocks or bytes */
-	if (entry_info->size <= ialloc)
-	    entry_info->size *= 512;
-
-    } else if (strtok(cp, sp) != NULL) {
-	/* We just initialized on the version number */
-	/* Now let's hunt for a lone, size number    */
-	while ((cps = strtok(NULL, sp)) != NULL) {
-	    cpd = cps;
-	    while (isdigit(UCH(*cpd)))
-		cpd++;
-	    if (*cpd == '\0') {
-		/* Assume it's blocks */
-		entry_info->size = ((unsigned long) atol(cps) * 512);
-		break;
-	    }
-	}
-    }
-
-    /* Wrap it up */
-    CTRACE((tfp, "HTFTP: VMS filename: %s  date: %s  size: %lu\n",
-	    entry_info->filename,
-	    NonNull(entry_info->date),
-	    entry_info->size));
-    return;
-}				/* parse_vms_dir_entry() */
-
-/*
- * parse_ms_windows_dir_entry() --
- *	Format the name, date, and size from an MS_WINDOWS LIST line into
- *	the EntryInfo structure (assumes Chameleon NEWT format). - FM
- */
-static void parse_ms_windows_dir_entry(char *line,
-				       EntryInfo *entry_info)
-{
-    char *cp = line;
-    char *cps, *cpd, date[16];
-    char *end = line + strlen(line);
-
-    /*  Get rid of blank or junk lines.  */
-    cp = LYSkipBlanks(cp);
-    if (!(*cp)) {
-	entry_info->display = FALSE;
-	return;
-    }
-
-    /* Cut out file or directory name. */
-    cps = LYSkipNonBlanks(cp);
-    *cps++ = '\0';
-    cpd = cps;
-    StrAllocCopy(entry_info->filename, cp);
-
-    /* Track down the size */
-    if (cps < end) {
-	cps = LYSkipBlanks(cps);
-	cpd = LYSkipNonBlanks(cps);
-	*cpd++ = '\0';
-	if (isdigit(UCH(*cps))) {
-	    entry_info->size = (unsigned long) atol(cps);
-	} else {
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	}
-    } else {
-	StrAllocCopy(entry_info->type, "");
-    }
-
-    /* Set the years and date, if we don't have them yet. * */
-    if (!HaveYears) {
-	set_years_and_date();
-    }
-
-    /* Track down the date. */
-    if (cpd < end) {
-	cpd = LYSkipBlanks(cpd);
-	if (strlen(cpd) > 17) {
-	    *(cpd + 6) = '\0';	/* Month and Day */
-	    *(cpd + 11) = '\0';	/* Year */
-	    *(cpd + 17) = '\0';	/* Time */
-	    if (strcmp(ThisYear, cpd + 7))
-		/* Not this year, so show the year */
-		sprintf(date, "%.6s  %.4s", cpd, (cpd + 7));
-	    else
-		/* Is this year, so show the time */
-		sprintf(date, "%.6s %.5s", cpd, (cpd + 12));
-	    StrAllocCopy(entry_info->date, date);
-	    if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
-		entry_info->date[4] = HT_NON_BREAK_SPACE;
-	    }
-	}
-    }
-
-    /* Wrap it up */
-    CTRACE((tfp, "HTFTP: MS Windows filename: %s  date: %s  size: %lu\n",
-	    entry_info->filename,
-	    NonNull(entry_info->date),
-	    entry_info->size));
-    return;
-}				/* parse_ms_windows_dir_entry */
-
-/*
- * parse_windows_nt_dir_entry() --
- *	Format the name, date, and size from a WINDOWS_NT LIST line into
- *	the EntryInfo structure (assumes Chameleon NEWT format). - FM
- */
-#ifdef NOTDEFINED
-static void parse_windows_nt_dir_entry(char *line,
-				       EntryInfo *entry_info)
-{
-    char *cp = line;
-    char *cps, *cpd, date[16];
-    char *end = line + strlen(line);
-    int i;
-
-    /*  Get rid of blank or junk lines.  */
-    cp = LYSkipBlanks(cp);
-    if (!(*cp)) {
-	entry_info->display = FALSE;
-	return;
-    }
-
-    /* Cut out file or directory name. */
-    cpd = cp;
-    cps = LYSkipNonBlanks(end - 1);
-    cp = (cps + 1);
-    if (!strcmp(cp, ".") || !strcmp(cp, "..")) {
-	entry_info->display = FALSE;
-	return;
-    }
-    StrAllocCopy(entry_info->filename, cp);
-    if (cps < cpd)
-	return;
-    *cp = '\0';
-    end = cp;
-
-    /* Set the years and date, if we don't have them yet. * */
-    if (!HaveYears) {
-	set_years_and_date();
-    }
-
-    /* Cut out the date. */
-    cp = cps = cpd;
-    cps = LYSkipNonBlanks(cps);
-    *cps++ = '\0';
-    if (cps > end) {
-	entry_info->display = FALSE;
-	return;
-    }
-    cps = LYSkipBlanks(cps);
-    cpd = LYSkipNonBlanks(cps);
-    *cps++ = '\0';
-    if (cps > end || cpd == cps || strlen(cpd) < 7) {
-	entry_info->display = FALSE;
-	return;
-    }
-    if (strlen(cp) == 8 &&
-	isdigit(*cp) && isdigit(*(cp + 1)) && *(cp + 2) == '-' &&
-	isdigit(*(cp + 3)) && isdigit(*(cp + 4)) && *(cp + 5) == '-') {
-	*(cp + 2) = '\0';	/* Month */
-	i = atoi(cp) - 1;
-	*(cp + 5) = '\0';	/* Day */
-	sprintf(date, "%.3s %.2s", months[i], (cp + 3));
-	if (date[4] == '0')
-	    date[4] = ' ';
-	cp += 6;		/* Year */
-	if (strcmp((ThisYear + 2), cp)) {
-	    /* Not this year, so show the year */
-	    if (atoi(cp) < 70) {
-		sprintf(&date[6], "  20%.2s", cp);
-	    } else {
-		sprintf(&date[6], "  19%.2s", cp);
-	    }
-	} else {
-	    /* Is this year, so show the time */
-	    *(cpd + 2) = '\0';	/* Hour */
-	    i = atoi(cpd);
-	    if (*(cpd + 5) == 'P' || *(cpd + 5) == 'p')
-		i += 12;
-	    sprintf(&date[6], " %02d:%.2s", i, (cpd + 3));
-	}
-	StrAllocCopy(entry_info->date, date);
-	if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
-	    entry_info->date[4] = HT_NON_BREAK_SPACE;
-	}
-    }
-
-    /* Track down the size */
-    if (cps < end) {
-	cps = LYSkipBlanks(cps);
-	cpd = LYSkipNonBlanks(cps);
-	*cpd = '\0';
-	if (isdigit(*cps)) {
-	    entry_info->size = atol(cps);
-	} else {
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	}
-    } else {
-	StrAllocCopy(entry_info->type, "");
-    }
-
-    /* Wrap it up */
-    CTRACE((tfp, "HTFTP: Windows NT filename: %s  date: %s  size: %d\n",
-	    entry_info->filename,
-	    NonNull(entry_info->date),
-	    entry_info->size));
-    return;
-}				/* parse_windows_nt_dir_entry */
-#endif /* NOTDEFINED */
-
-/*
- * parse_cms_dir_entry() --
- *	Format the name, date, and size from a VM/CMS line into
- *	the EntryInfo structure. - FM
- */
-static void parse_cms_dir_entry(char *line,
-				EntryInfo *entry_info)
-{
-    char *cp = line;
-    char *cps, *cpd, date[16];
-    char *end = line + strlen(line);
-    int RecordLength = 0;
-    int Records = 0;
-    int i;
-
-    /*  Get rid of blank or junk lines.  */
-    cp = LYSkipBlanks(cp);
-    if (!(*cp)) {
-	entry_info->display = FALSE;
-	return;
-    }
-
-    /* Cut out file or directory name. */
-    cps = LYSkipNonBlanks(cp);
-    *cps++ = '\0';
-    StrAllocCopy(entry_info->filename, cp);
-    if (strchr(entry_info->filename, '.') != NULL)
-	/* If we already have a dot, we did an NLST. */
-	return;
-    cp = LYSkipBlanks(cps);
-    if (!(*cp)) {
-	/* If we don't have more, we've misparsed. */
-	FREE(entry_info->filename);
-	FREE(entry_info->type);
-	entry_info->display = FALSE;
-	return;
-    }
-    cps = LYSkipNonBlanks(cp);
-    *cps++ = '\0';
-    if ((0 == strcasecomp(cp, "DIR")) && (cp - line) > 17) {
-	/* It's an SFS directory. */
-	StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	entry_info->size = 0;
-    } else {
-	/* It's a file. */
-	cp--;
-	*cp = '.';
-	StrAllocCat(entry_info->filename, cp);
-
-	/* Track down the VM/CMS RECFM or type. */
-	cp = cps;
-	if (cp < end) {
-	    cp = LYSkipBlanks(cp);
-	    cps = LYSkipNonBlanks(cp);
-	    *cps++ = '\0';
-	    /* Check cp here, if it's relevant someday. */
-	}
-    }
-
-    /* Track down the record length or dash. */
-    cp = cps;
-    if (cp < end) {
-	cp = LYSkipBlanks(cp);
-	cps = LYSkipNonBlanks(cp);
-	*cps++ = '\0';
-	if (isdigit(UCH(*cp))) {
-	    RecordLength = atoi(cp);
-	}
-    }
-
-    /* Track down the number of records or the dash. */
-    cp = cps;
-    if (cps < end) {
-	cp = LYSkipBlanks(cp);
-	cps = LYSkipNonBlanks(cp);
-	*cps++ = '\0';
-	if (isdigit(UCH(*cp))) {
-	    Records = atoi(cp);
-	}
-	if (Records > 0 && RecordLength > 0) {
-	    /* Compute an approximate size. */
-	    entry_info->size = ((unsigned long) Records * (unsigned long) RecordLength);
-	}
-    }
-
-    /* Set the years and date, if we don't have them yet. */
-    if (!HaveYears) {
-	set_years_and_date();
-    }
-
-    /* Track down the date. */
-    cpd = cps;
-    if (((cps < end) &&
-	 (cps = strchr(cpd, ':')) != NULL) &&
-	(cps < (end - 3) &&
-	 isdigit(UCH(*(cps + 1))) && isdigit(UCH(*(cps + 2))) && *(cps + 3) == ':')) {
-	cps += 3;
-	*cps = '\0';
-	if ((cps - cpd) >= 14) {
-	    cpd = (cps - 14);
-	    *(cpd + 2) = '\0';	/* Month */
-	    *(cpd + 5) = '\0';	/* Day */
-	    *(cpd + 8) = '\0';	/* Year */
-	    cps -= 5;		/* Time */
-	    if (*cpd == ' ')
-		*cpd = '0';
-	    i = atoi(cpd) - 1;
-	    sprintf(date, "%.3s %.2s", months[i], (cpd + 3));
-	    if (date[4] == '0')
-		date[4] = ' ';
-	    cpd += 6;		/* Year */
-	    if (strcmp((ThisYear + 2), cpd)) {
-		/* Not this year, so show the year. */
-		if (atoi(cpd) < 70) {
-		    sprintf(&date[6], "  20%.2s", cpd);
-		} else {
-		    sprintf(&date[6], "  19%.2s", cpd);
-		}
-	    } else {
-		/* Is this year, so show the time. */
-		*(cps + 2) = '\0';	/* Hour */
-		i = atoi(cps);
-		sprintf(&date[6], " %02d:%.2s", i, (cps + 3));
-	    }
-	    StrAllocCopy(entry_info->date, date);
-	    if (entry_info->date[4] == ' ' || entry_info->date[4] == '0') {
-		entry_info->date[4] = HT_NON_BREAK_SPACE;
-	    }
-	}
-    }
-
-    /* Wrap it up. */
-    CTRACE((tfp, "HTFTP: VM/CMS filename: %s  date: %s  size: %lu\n",
-	    entry_info->filename,
-	    NonNull(entry_info->date),
-	    entry_info->size));
-    return;
-}				/* parse_cms_dir_entry */
-
-/*
- * Given a line of LIST/NLST output in entry, return results and a file/dir
- * name in entry_info struct
- *
- * If first is true, this is the first name in a directory.
- */
-static EntryInfo *parse_dir_entry(char *entry,
-				  BOOLEAN *first,
-				  char **pspilledname)
-{
-    EntryInfo *entry_info;
-    int i;
-    int len;
-    BOOLEAN remove_size = FALSE;
-    char *cp;
-
-    entry_info = typecalloc(EntryInfo);
-
-    if (entry_info == NULL)
-	outofmem(__FILE__, "parse_dir_entry");
-
-    assert(entry_info != NULL);
-
-    entry_info->display = TRUE;
-
-    switch (server_type) {
-    case DLS_SERVER:
-
-	/*
-	 * Interpret and edit LIST output from a Unix server in "dls" format. 
-	 * This one must have claimed to be Unix in order to get here; if the
-	 * first line looks fishy, we revert to Unix and hope that fits better
-	 * (this recovery is untested).  - kw
-	 */
-
-	if (*first) {
-	    len = (int) strlen(entry);
-	    if (!len || entry[0] == ' ' ||
-		(len >= 24 && entry[23] != ' ') ||
-		(len < 24 && strchr(entry, ' '))) {
-		server_type = UNIX_SERVER;
-		CTRACE((tfp,
-			"HTFTP: Falling back to treating as Unix server.\n"));
-	    } else {
-		*first = FALSE;
-	    }
-	}
-
-	if (server_type == DLS_SERVER) {
-	    /* if still unchanged... */
-	    parse_dls_line(entry, entry_info, pspilledname);
-
-	    if (!entry_info->filename || *entry_info->filename == '\0') {
-		entry_info->display = FALSE;
-		return (entry_info);
-	    }
-	    if (!strcmp(entry_info->filename, "..") ||
-		!strcmp(entry_info->filename, "."))
-		entry_info->display = FALSE;
-	    if (entry_info->type && *entry_info->type == '\0') {
-		FREE(entry_info->type);
-		return (entry_info);
-	    }
-	    /*
-	     * Goto the bottom and get real type.
-	     */
-	    break;
-	}
-	/* fall through if server_type changed for *first == TRUE ! */
-    case UNIX_SERVER:
-    case PETER_LEWIS_SERVER:
-    case MACHTEN_SERVER:
-    case MSDOS_SERVER:
-    case WINDOWS_NT_SERVER:
-    case WINDOWS_2K_SERVER:
-    case APPLESHARE_SERVER:
-    case NETPRESENZ_SERVER:
-	/*
-	 * Check for EPLF output (local times).
-	 */
-	if (*entry == '+') {
-	    parse_eplf_line(entry, entry_info);
-	    break;
-	}
-
-	/*
-	 * Interpret and edit LIST output from Unix server.
-	 */
-	len = (int) strlen(entry);
-	if (*first) {
-	    /* don't gettext() this -- incoming text: */
-	    if (!strcmp(entry, "can not access directory .")) {
-		/*
-		 * Don't reset *first, nothing real will follow.  - KW
-		 */
-		entry_info->display = FALSE;
-		return (entry_info);
-	    }
-	    *first = FALSE;
-	    if (!StrNCmp(entry, "total ", 6) ||
-		strstr(entry, "not available") != NULL) {
-		entry_info->display = FALSE;
-		return (entry_info);
-	    } else if (unsure_type) {
-		/* this isn't really a unix server! */
-		server_type = GENERIC_SERVER;
-		entry_info->display = FALSE;
-		return (entry_info);
-	    }
-	}
-
-	/*
-	 * Check first character of ls -l output.
-	 */
-	if (TOUPPER(entry[0]) == 'D') {
-	    /*
-	     * It's a directory.
-	     */
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	    remove_size = TRUE;	/* size is not useful */
-	} else if (entry[0] == 'l') {
-	    /*
-	     * It's a symbolic link, does the user care about knowing if it is
-	     * symbolic?  I think so since it might be a directory.
-	     */
-	    StrAllocCopy(entry_info->type, ENTRY_IS_SYMBOLIC_LINK);
-	    remove_size = TRUE;	/* size is not useful */
-
-	    /*
-	     * Strip off " -> pathname".
-	     */
-	    for (i = len - 1; (i > 3) &&
-		 (!isspace(UCH(entry[i])) ||
-		  (entry[i - 1] != '>') ||
-		  (entry[i - 2] != '-') ||
-		  (entry[i - 3] != ' ')); i--) ;	/* null body */
-	    if (i > 3) {
-		entry[i - 3] = '\0';
-		StrAllocCopy(entry_info->linkname, LYSkipBlanks(entry + i));
-	    }
-	}
-	/* link */
-	parse_ls_line(entry, entry_info);
-
-	if (!strcmp(entry_info->filename, "..") ||
-	    !strcmp(entry_info->filename, "."))
-	    entry_info->display = FALSE;
-	/*
-	 * Goto the bottom and get real type.
-	 */
-	break;
-
-    case VMS_SERVER:
-	/*
-	 * Interpret and edit LIST output from VMS server and convert
-	 * information lines to zero length.
-	 */
-	parse_vms_dir_entry(entry, entry_info);
-
-	/*
-	 * Get rid of any junk lines.
-	 */
-	if (!entry_info->display)
-	    return (entry_info);
-
-	/*
-	 * Trim off VMS directory extensions.
-	 */
-	len = (int) strlen(entry_info->filename);
-	if ((len > 4) && !strcmp(&entry_info->filename[len - 4], ".dir")) {
-	    entry_info->filename[len - 4] = '\0';
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	    remove_size = TRUE;	/* size is not useful */
-	}
-	/*
-	 * Goto the bottom and get real type.
-	 */
-	break;
-
-    case MS_WINDOWS_SERVER:
-	/*
-	 * Interpret and edit LIST output from MS_WINDOWS server and convert
-	 * information lines to zero length.
-	 */
-	parse_ms_windows_dir_entry(entry, entry_info);
-
-	/*
-	 * Get rid of any junk lines.
-	 */
-	if (!entry_info->display)
-	    return (entry_info);
-	if (entry_info->type && *entry_info->type == '\0') {
-	    FREE(entry_info->type);
-	    return (entry_info);
-	}
-	/*
-	 * Goto the bottom and get real type.
-	 */
-	break;
-
-#ifdef NOTDEFINED
-    case WINDOWS_NT_SERVER:
-	/*
-	 * Interpret and edit LIST output from MS_WINDOWS server and convert
-	 * information lines to zero length.
-	 */
-	parse_windows_nt_dir_entry(entry, entry_info);
-
-	/*
-	 * Get rid of any junk lines.
-	 */
-	if (!entry_info->display)
-	    return (entry_info);
-	if (entry_info->type && *entry_info->type == '\0') {
-	    FREE(entry_info->type);
-	    return (entry_info);
-	}
-	/*
-	 * Goto the bottom and get real type.
-	 */
-	break;
-#endif /* NOTDEFINED */
-
-    case CMS_SERVER:
-	{
-	    /*
-	     * Interpret and edit LIST output from VM/CMS server and convert
-	     * any information lines to zero length.
-	     */
-	    parse_cms_dir_entry(entry, entry_info);
-
-	    /*
-	     * Get rid of any junk lines.
-	     */
-	    if (!entry_info->display)
-		return (entry_info);
-	    if (entry_info->type && *entry_info->type == '\0') {
-		FREE(entry_info->type);
-		return (entry_info);
-	    }
-	    /*
-	     * Goto the bottom and get real type.
-	     */
-	    break;
-	}
-
-    case NCSA_SERVER:
-    case TCPC_SERVER:
-	/*
-	 * Directories identified by trailing "/" characters.
-	 */
-	StrAllocCopy(entry_info->filename, entry);
-	len = (int) strlen(entry);
-	if (entry[len - 1] == '/') {
-	    /*
-	     * It's a dir, remove / and mark it as such.
-	     */
-	    entry[len - 1] = '\0';
-	    StrAllocCopy(entry_info->type, ENTRY_IS_DIRECTORY);
-	    remove_size = TRUE;	/* size is not useful */
-	}
-	/*
-	 * Goto the bottom and get real type.
-	 */
-	break;
-
-    default:
-	/*
-	 * We can't tell if it is a directory since we only did an NLST :( List
-	 * bad file types anyways?  NOT!
-	 */
-	StrAllocCopy(entry_info->filename, entry);
-	return (entry_info);	/* mostly empty info */
-
-    }				/* switch (server_type) */
-
-#ifdef LONG_LIST
-    (void) remove_size;
-#else
-    if (remove_size && entry_info->size) {
-	entry_info->size = 0;
-    }
-#endif
-
-    if (entry_info->filename && strlen(entry_info->filename) > 3) {
-	if (((cp = strrchr(entry_info->filename, '.')) != NULL &&
-	     0 == strncasecomp(cp, ".me", 3)) &&
-	    (cp[3] == '\0' || cp[3] == ';')) {
-	    /*
-	     * Don't treat this as application/x-Troff-me if it's a Unix server
-	     * but has the string "read.me", or if it's not a Unix server.  -
-	     * FM
-	     */
-	    if ((server_type != UNIX_SERVER) ||
-		(cp > (entry_info->filename + 3) &&
-		 0 == strncasecomp((cp - 4), "read.me", 7))) {
-		StrAllocCopy(entry_info->type, "text/plain");
-	    }
-	}
-    }
-
-    /*
-     * Get real types eventually.
-     */
-    if (!entry_info->type) {
-	const char *cp2;
-	HTFormat format;
-	HTAtom *encoding;	/* @@ not used at all */
-
-	format = HTFileFormat(entry_info->filename, &encoding, &cp2);
-
-	if (cp2 == NULL) {
-	    if (!StrNCmp(HTAtom_name(format), "application", 11)) {
-		cp2 = HTAtom_name(format) + 12;
-		if (!StrNCmp(cp2, "x-", 2))
-		    cp2 += 2;
-	    } else {
-		cp2 = HTAtom_name(format);
-	    }
-	}
-
-	StrAllocCopy(entry_info->type, cp2);
-    }
-
-    return (entry_info);
-}
-
-static int compare_EntryInfo_structs(EntryInfo *entry1, EntryInfo *entry2)
-{
-    int i, status;
-    char date1[16], date2[16], time1[8], time2[8], month[4];
-
-    switch (HTfileSortMethod) {
-    case FILE_BY_SIZE:
-	/* both equal or both 0 */
-	if (entry1->size == entry2->size)
-	    return (strcmp(entry1->filename, entry2->filename));
-	else if (entry1->size > entry2->size)
-	    return (1);
-	else
-	    return (-1);
-
-    case FILE_BY_TYPE:
-	if (entry1->type && entry2->type) {
-	    status = strcasecomp(entry1->type, entry2->type);
-	    if (status)
-		return (status);
-	    /* else fall to filename comparison */
-	}
-	return (strcmp(entry1->filename, entry2->filename));
-
-    case FILE_BY_DATE:
-	if (entry1->date && entry2->date) {
-	    /*
-	     * Make sure we have the correct length. - FM
-	     */
-	    if (strlen(entry1->date) != 12 || strlen(entry2->date) != 12) {
-		return (strcmp(entry1->filename, entry2->filename));
-	    }
-	    /*
-	     * Set the years and date,
-	     * if we don't have them yet.
-	     */
-	    if (!HaveYears) {
-		set_years_and_date();
-	    }
-	    /*
-	     * Set up for sorting in reverse
-	     * chronological order. - FM
-	     */
-	    if (entry1->date[9] == ':') {
-		strcpy(date1, "9999");
-		strcpy(time1, &entry1->date[7]);
-		if (time1[0] == ' ') {
-		    time1[0] = '0';
-		}
-	    } else {
-		strcpy(date1, &entry1->date[8]);
-		strcpy(time1, "00:00");
-	    }
-	    StrNCpy(month, entry1->date, 3);
-	    month[3] = '\0';
-	    for (i = 0; i < 12; i++) {
-		if (!strcasecomp(month, months[i])) {
-		    break;
-		}
-	    }
-	    i++;
-	    sprintf(month, "%02d", i);
-	    strcat(date1, month);
-	    StrNCat(date1, &entry1->date[4], 2);
-	    date1[8] = '\0';
-	    if (date1[6] == ' ' || date1[6] == HT_NON_BREAK_SPACE) {
-		date1[6] = '0';
-	    }
-	    /* If no year given, assume last year if it would otherwise be in
-	     * the future by more than one day.  The one day tolerance is to
-	     * account for a possible timezone difference.  - kw
-	     */
-	    if (date1[0] == '9' && atoi(date1) > TheDate + 1) {
-		for (i = 0; i < 4; i++) {
-		    date1[i] = LastYear[i];
-		}
-	    }
-	    strcat(date1, time1);
-	    if (entry2->date[9] == ':') {
-		strcpy(date2, "9999");
-		strcpy(time2, &entry2->date[7]);
-		if (time2[0] == ' ') {
-		    time2[0] = '0';
-		}
-	    } else {
-		strcpy(date2, &entry2->date[8]);
-		strcpy(time2, "00:00");
-	    }
-	    StrNCpy(month, entry2->date, 3);
-	    month[3] = '\0';
-	    for (i = 0; i < 12; i++) {
-		if (!strcasecomp(month, months[i])) {
-		    break;
-		}
-	    }
-	    i++;
-	    sprintf(month, "%02d", i);
-	    strcat(date2, month);
-	    StrNCat(date2, &entry2->date[4], 2);
-	    date2[8] = '\0';
-	    if (date2[6] == ' ' || date2[6] == HT_NON_BREAK_SPACE) {
-		date2[6] = '0';
-	    }
-	    /* If no year given, assume last year if it would otherwise be in
-	     * the future by more than one day.  The one day tolerance is to
-	     * account for a possible timezone difference.  - kw
-	     */
-	    if (date2[0] == '9' && atoi(date2) > TheDate + 1) {
-		for (i = 0; i < 4; i++) {
-		    date2[i] = LastYear[i];
-		}
-	    }
-	    strcat(date2, time2);
-	    /*
-	     * Do the comparison. - FM
-	     */
-	    status = strcasecomp(date2, date1);
-	    if (status)
-		return (status);
-	    /* else fall to filename comparison */
-	}
-	return (strcmp(entry1->filename, entry2->filename));
-
-    case FILE_BY_NAME:
-    default:
-	return (strcmp(entry1->filename, entry2->filename));
-    }
-}
-
-#ifdef LONG_LIST
-static char *FormatStr(char **bufp,
-		       char *start,
-		       const char *value)
-{
-    char fmt[512];
-
-    if (*start) {
-	sprintf(fmt, "%%%.*ss", (int) sizeof(fmt) - 3, start);
-	HTSprintf(bufp, fmt, value);
-    } else if (*bufp && !(value && *value)) {
-	;
-    } else if (value) {
-	StrAllocCat(*bufp, value);
-    }
-    return *bufp;
-}
-
-static char *FormatNum(char **bufp,
-		       char *start,
-		       unsigned long value)
-{
-    char fmt[512];
-
-    if (*start) {
-	sprintf(fmt, "%%%.*sld", (int) sizeof(fmt) - 3, start);
-	HTSprintf(bufp, fmt, value);
-    } else {
-	sprintf(fmt, "%lu", value);
-	StrAllocCat(*bufp, fmt);
-    }
-    return *bufp;
-}
-
-static void FlushParse(HTStructured * target, char **buf)
-{
-    if (*buf && **buf) {
-	PUTS(*buf);
-	**buf = '\0';
-    }
-}
-
-static void LYListFmtParse(const char *fmtstr,
-			   EntryInfo *data,
-			   HTStructured * target,
-			   char *tail)
-{
-    char c;
-    char *s;
-    char *end;
-    char *start;
-    char *str = NULL;
-    char *buf = NULL;
-    BOOL is_directory = (BOOL) (data->file_mode != 0 &&
-				(TOUPPER(data->file_mode[0]) == 'D'));
-    BOOL is_symlinked = (BOOL) (data->file_mode != 0 &&
-				(TOUPPER(data->file_mode[0]) == 'L'));
-    BOOL remove_size = (BOOL) (is_directory || is_symlinked);
-
-    StrAllocCopy(str, fmtstr);
-    s = str;
-    end = str + strlen(str);
-    while (*s) {
-	start = s;
-	while (*s) {
-	    if (*s == '%') {
-		if (*(s + 1) == '%')	/* literal % */
-		    s++;
-		else
-		    break;
-	    }
-	    s++;
-	}
-	/* s is positioned either at a % or at \0 */
-	*s = '\0';
-	if (s > start) {	/* some literal chars. */
-	    StrAllocCat(buf, start);
-	}
-	if (s == end)
-	    break;
-	start = ++s;
-	while (isdigit(UCH(*s)) || *s == '.' || *s == '-' || *s == ' ' ||
-	       *s == '#' || *s == '+' || *s == '\'')
-	    s++;
-	c = *s;			/* the format char. or \0 */
-	*s = '\0';
-
-	switch (c) {
-	case '\0':
-	    StrAllocCat(buf, start);
-	    continue;
-
-	case 'A':
-	case 'a':		/* anchor */
-	    FlushParse(target, &buf);
-	    HTDirEntry(target, tail, data->filename);
-	    FormatStr(&buf, start, data->filename);
-	    PUTS(buf);
-	    END(HTML_A);
-	    *buf = '\0';
-	    if (c != 'A' && data->linkname != 0) {
-		PUTS(" -> ");
-		PUTS(data->linkname);
-	    }
-	    break;
-
-	case 'T':		/* MIME type */
-	case 't':		/* MIME type description */
-	    if (is_directory) {
-		if (c != 'T') {
-		    FormatStr(&buf, start, ENTRY_IS_DIRECTORY);
-		} else {
-		    FormatStr(&buf, start, "");
-		}
-	    } else if (is_symlinked) {
-		if (c != 'T') {
-		    FormatStr(&buf, start, ENTRY_IS_SYMBOLIC_LINK);
-		} else {
-		    FormatStr(&buf, start, "");
-		}
-	    } else {
-		const char *cp2;
-		HTFormat format;
-
-		format = HTFileFormat(data->filename, NULL, &cp2);
-
-		if (c != 'T') {
-		    if (cp2 == NULL) {
-			if (!StrNCmp(HTAtom_name(format),
-				     "application", 11)) {
-			    cp2 = HTAtom_name(format) + 12;
-			    if (!StrNCmp(cp2, "x-", 2))
-				cp2 += 2;
-			} else {
-			    cp2 = HTAtom_name(format);
-			}
-		    }
-		    FormatStr(&buf, start, cp2);
-		} else {
-		    FormatStr(&buf, start, HTAtom_name(format));
-		}
-	    }
-	    break;
-
-	case 'd':		/* date */
-	    if (data->date) {
-		FormatStr(&buf, start, data->date);
-	    } else {
-		FormatStr(&buf, start, " * ");
-	    }
-	    break;
-
-	case 's':		/* size in bytes */
-	    FormatNum(&buf, start, data->size);
-	    break;
-
-	case 'K':		/* size in Kilobytes but not for directories */
-	    if (remove_size) {
-		FormatStr(&buf, start, "");
-		StrAllocCat(buf, " ");
-		break;
-	    }
-	    /* FALL THROUGH */
-	case 'k':		/* size in Kilobytes */
-	    /* FIXME - this is inconsistent with HTFile.c, but historical */
-	    if (data->size < 1024) {
-		FormatNum(&buf, start, data->size);
-		StrAllocCat(buf, " bytes");
-	    } else {
-		FormatNum(&buf, start, data->size / 1024);
-		StrAllocCat(buf, "Kb");
-	    }
-	    break;
-
-#ifdef LONG_LIST
-	case 'p':		/* unix-style permission bits */
-	    FormatStr(&buf, start, NonNull(data->file_mode));
-	    break;
-
-	case 'o':		/* owner */
-	    FormatStr(&buf, start, NonNull(data->file_user));
-	    break;
-
-	case 'g':		/* group */
-	    FormatStr(&buf, start, NonNull(data->file_group));
-	    break;
-
-	case 'l':		/* link count */
-	    FormatNum(&buf, start, data->file_links);
-	    break;
-#endif
-
-	case '%':		/* literal % with flags/width */
-	    FormatStr(&buf, start, "%");
-	    break;
-
-	default:
-	    fprintf(stderr,
-		    "Unknown format character `%c' in list format\n", c);
-	    break;
-	}
-
-	s++;
-    }
-    if (buf) {
-	LYTrimTrailing(buf);
-	FlushParse(target, &buf);
-	FREE(buf);
-    }
-    PUTC('\n');
-    FREE(str);
-}
-#endif /* LONG_LIST */
-/*	Read a directory into an hypertext object from the data socket
- *	--------------------------------------------------------------
- *
- * On entry,
- *	anchor		Parent anchor to link the this node to
- *	address		Address of the directory
- * On exit,
- *	returns		HT_LOADED if OK
- *			<0 if error.
- */
-static int read_directory(HTParentAnchor *parent,
-			  const char *address,
-			  HTFormat format_out,
-			  HTStream *sink)
-{
-    int status;
-    BOOLEAN WasInterrupted = FALSE;
-    HTStructured *target = HTML_new(parent, format_out, sink);
-    char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
-    EntryInfo *entry_info;
-    BOOLEAN first = TRUE;
-    char *lastpath = NULL;	/* prefix for link, either "" (for root) or xxx  */
-    BOOL tildeIsTop = FALSE;
-
-#ifndef LONG_LIST
-    char string_buffer[64];
-#endif
-
-    _HTProgress(gettext("Receiving FTP directory."));
-
-    /*
-     * Force the current Date and Year (TheDate, ThisYear, and LastYear) to be
-     * recalculated for each directory request.  Otherwise we have a problem
-     * with long-running sessions assuming the wrong date for today.  - kw
-     */
-    HaveYears = FALSE;
-    /*
-     * Check whether we always want the home directory treated as Welcome.  -
-     * FM
-     */
-    if (server_type == VMS_SERVER)
-	tildeIsTop = TRUE;
-
-    /*
-     * This should always come back FALSE, since the flag is set only for local
-     * directory listings if LONG_LIST was defined on compilation, but we could
-     * someday set up an equivalent listing for Unix ftp servers.  - FM
-     */
-    (void) HTDirTitles(target, parent, format_out, tildeIsTop);
-
-    data_read_pointer = data_write_pointer = data_buffer;
-
-    if (*filename == '\0') {	/* Empty filename: use root. */
-	StrAllocCopy(lastpath, "/");
-    } else if (!strcmp(filename, "/")) {	/* Root path. */
-	StrAllocCopy(lastpath, "/foo/..");
-    } else {
-	char *p = strrchr(filename, '/');	/* Find the lastslash. */
-	char *cp;
-
-	if (server_type == CMS_SERVER) {
-	    StrAllocCopy(lastpath, filename);	/* Use absolute path for CMS. */
-	} else {
-	    StrAllocCopy(lastpath, p + 1);	/* Take slash off the beginning. */
-	}
-	if ((cp = strrchr(lastpath, ';')) != NULL) {	/* Trim type= param. */
-	    if (!strncasecomp((cp + 1), "type=", 5)) {
-		if (TOUPPER(*(cp + 6)) == 'D' ||
-		    TOUPPER(*(cp + 6)) == 'A' ||
-		    TOUPPER(*(cp + 6)) == 'I')
-		    *cp = '\0';
-	    }
-	}
-    }
-    FREE(filename);
-
-    {
-	HTBTree *bt = HTBTree_new((HTComparer) compare_EntryInfo_structs);
-	int ic;
-	HTChunk *chunk = HTChunkCreate(128);
-	int BytesReceived = 0;
-	int BytesReported = 0;
-	char NumBytes[64];
-	char *spilledname = NULL;
-
-	PUTC('\n');		/* prettier LJM */
-	for (ic = 0; ic != EOF;) {	/* For each entry in the directory */
-	    HTChunkClear(chunk);
-
-	    if (HTCheckForInterrupt()) {
-		CTRACE((tfp,
-			"read_directory: interrupted after %d bytes\n",
-			BytesReceived));
-		WasInterrupted = TRUE;
-		if (BytesReceived) {
-		    goto unload_btree;	/* unload btree */
-		} else {
-		    ABORT_TARGET;
-		    HTBTreeAndObject_free(bt);
-		    FREE(spilledname);
-		    return HT_INTERRUPTED;
-		}
-	    }
-
-	    /*   read directory entry
-	     */
-	    interrupted_in_next_data_char = FALSE;
-	    for (;;) {		/* Read in one line as filename */
-		ic = NEXT_DATA_CHAR;
-	      AgainForMultiNet:
-		if (interrupted_in_next_data_char) {
-		    CTRACE((tfp,
-			    "read_directory: interrupted_in_next_data_char after %d bytes\n",
-			    BytesReceived));
-		    WasInterrupted = TRUE;
-		    if (BytesReceived) {
-			goto unload_btree;	/* unload btree */
-		    } else {
-			ABORT_TARGET;
-			HTBTreeAndObject_free(bt);
-			FREE(spilledname);
-			return HT_INTERRUPTED;
-		    }
-		} else if ((char) ic == CR || (char) ic == LF) {	/* Terminator? */
-		    if (chunk->size != 0) {	/* got some text */
-			/* Deal with MultiNet's wrapping of long lines */
-			if (server_type == VMS_SERVER) {
-			    /* Deal with MultiNet's wrapping of long lines - F.M. */
-			    if (data_read_pointer < data_write_pointer &&
-				*(data_read_pointer + 1) == ' ')
-				data_read_pointer++;
-			    else if (data_read_pointer >= data_write_pointer) {
-				status = NETREAD(data_soc, data_buffer,
-						 DATA_BUFFER_SIZE);
-				if (status == HT_INTERRUPTED) {
-				    interrupted_in_next_data_char = 1;
-				    goto AgainForMultiNet;
-				}
-				if (status <= 0) {
-				    ic = EOF;
-				    break;
-				}
-				data_write_pointer = data_buffer + status;
-				data_read_pointer = data_buffer;
-				if (*data_read_pointer == ' ')
-				    data_read_pointer++;
-				else
-				    break;
-			    } else
-				break;
-			} else
-			    break;	/* finish getting one entry */
-		    }
-		} else if (ic == EOF) {
-		    break;	/* End of file */
-		} else {
-		    HTChunkPutc(chunk, UCH(ic));
-		}
-	    }
-	    HTChunkTerminate(chunk);
-
-	    BytesReceived += chunk->size;
-	    if (BytesReceived > BytesReported + 1024) {
-#ifdef _WINDOWS
-		sprintf(NumBytes, gettext("Transferred %d bytes (%5d)"),
-			BytesReceived, ws_read_per_sec);
-#else
-		sprintf(NumBytes, TRANSFERRED_X_BYTES, BytesReceived);
-#endif
-		HTProgress(NumBytes);
-		BytesReported = BytesReceived;
-	    }
-
-	    if (ic == EOF && chunk->size == 1)
-		/* 1 means empty: includes terminating 0 */
-		break;
-	    CTRACE((tfp, "HTFTP: Line in %s is %s\n",
-		    lastpath, chunk->data));
-
-	    entry_info = parse_dir_entry(chunk->data, &first, &spilledname);
-	    if (entry_info->display) {
-		FREE(spilledname);
-		CTRACE((tfp, "Adding file to BTree: %s\n",
-			entry_info->filename));
-		HTBTree_add(bt, entry_info);
-	    } else {
-		free_entryinfo_struct_contents(entry_info);
-		FREE(entry_info);
-	    }
-
-	}			/* next entry */
-
-      unload_btree:
-
-	HTChunkFree(chunk);
-	FREE(spilledname);
-
-	/* print out the handy help message if it exists :) */
-	if (help_message_cache_non_empty()) {
-	    START(HTML_PRE);
-	    START(HTML_HR);
-	    PUTC('\n');
-	    PUTS(help_message_cache_contents());
-	    init_help_message_cache();	/* to free memory */
-	    START(HTML_HR);
-	    PUTC('\n');
-	} else {
-	    START(HTML_PRE);
-	    PUTC('\n');
-	}
-
-	/* Run through tree printing out in order
-	 */
-	{
-#ifndef LONG_LIST
-#ifdef SH_EX			/* 1997/10/18 (Sat) 14:14:28 */
-	    char *p, name_buff[256];
-	    int name_len, dot_len;
-
-#define	FNAME_WIDTH	30
-#define	FILE_GAP	1
-
-#endif
-	    int i;
-#endif
-	    HTBTElement *ele;
-
-	    for (ele = HTBTree_next(bt, NULL);
-		 ele != NULL;
-		 ele = HTBTree_next(bt, ele)) {
-		entry_info = (EntryInfo *) HTBTree_object(ele);
-
-#ifdef LONG_LIST
-		LYListFmtParse(ftp_format,
-			       entry_info,
-			       target,
-			       lastpath);
-#else
-		if (entry_info->date) {
-		    PUTS(entry_info->date);
-		    PUTS("  ");
-		} else {
-		    PUTS("     * ");
-		}
-
-		if (entry_info->type) {
-		    for (i = 0; entry_info->type[i] != '\0' && i < 16; i++)
-			PUTC(entry_info->type[i]);
-		    for (; i < 17; i++)
-			PUTC(' ');
-		}
-		/* start the anchor */
-		HTDirEntry(target, lastpath, entry_info->filename);
-#ifdef SH_EX			/* 1997/10/18 (Sat) 16:00 */
-		name_len = strlen(entry_info->filename);
-
-		sprintf(name_buff, "%-*s", FNAME_WIDTH, entry_info->filename);
-
-		if (name_len < FNAME_WIDTH) {
-		    dot_len = FNAME_WIDTH - FILE_GAP - name_len;
-		    if (dot_len > 0) {
-			p = name_buff + name_len + 1;
-			while (dot_len-- > 0)
-			    *p++ = '.';
-		    }
-		} else {
-		    name_buff[FNAME_WIDTH] = '\0';
-		}
-
-		PUTS(name_buff);
-#else
-		PUTS(entry_info->filename);
-#endif
-		END(HTML_A);
-
-		if (entry_info->size) {
-#ifdef SH_EX			/* 1998/02/02 (Mon) 16:34:52 */
-		    if (entry_info->size < 1024)
-			sprintf(string_buffer, "%6ld bytes",
-				entry_info->size);
-		    else
-			sprintf(string_buffer, "%6ld Kb",
-				entry_info->size / 1024);
-#else
-		    if (entry_info->size < 1024)
-			sprintf(string_buffer, "  %lu bytes",
-				entry_info->size);
-		    else
-			sprintf(string_buffer, "  %luKb",
-				entry_info->size / 1024);
-#endif
-		    PUTS(string_buffer);
-		} else if (entry_info->linkname != 0) {
-		    PUTS(" -> ");
-		    PUTS(entry_info->linkname);
-		}
-
-		PUTC('\n');	/* end of this entry */
-#endif
-
-		free_entryinfo_struct_contents(entry_info);
-	    }
-	}
-	END(HTML_PRE);
-	END(HTML_BODY);
-	FREE_TARGET;
-	HTBTreeAndObject_free(bt);
-    }
-
-    FREE(lastpath);
-
-    if (WasInterrupted || data_soc != -1) {	/* should always be true */
-	/*
-	 * Without closing the data socket first, the response(0) later may
-	 * hang.  Some servers expect the client to fin/ack the close of the
-	 * data connection before proceeding with the conversation on the
-	 * control connection.  - kw
-	 */
-	CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
-	status = NETCLOSE(data_soc);
-	if (status == -1)
-	    HTInetStatus("close");	/* Comment only */
-	data_soc = -1;
-    }
-
-    if (WasInterrupted || HTCheckForInterrupt()) {
-	_HTProgress(TRANSFER_INTERRUPTED);
-    }
-    return HT_LOADED;
-}
-
-/*
- * Setup an FTP connection.
- */
-static int setup_connection(const char *name,
-			    HTParentAnchor *anchor)
-{
-    int retry;			/* How many times tried? */
-    int status = HT_NO_CONNECTION;
-
-    CTRACE((tfp, "setup_connection(%s)\n", name));
-
-    /* set use_list to NOT since we don't know what kind of server
-     * this is yet.  And set the type to GENERIC
-     */
-    use_list = FALSE;
-    server_type = GENERIC_SERVER;
-    Broken_RETR = FALSE;
-
-#ifdef INET6
-    Broken_EPSV = FALSE;
-#endif
-
-    for (retry = 0; retry < 2; retry++) {	/* For timed out/broken connections */
-	status = get_connection(name, anchor);
-	if (status < 0) {
-	    break;
-	}
-
-	if (!ftp_local_passive) {
-	    status = get_listen_socket();
-	    if (status < 0) {
-		NETCLOSE(control->socket);
-		control->socket = -1;
-#ifdef INET6
-		if (have_socket)
-		    (void) close_master_socket();
-#else
-		close_master_socket();
-#endif /* INET6 */
-		/* HT_INTERRUPTED would fall through, if we could interrupt
-		   somehow in the middle of it, which we currently can't. */
-		break;
-	    }
-#ifdef REPEAT_PORT
-	    /*  Inform the server of the port number we will listen on
-	     */
-	    status = response(port_command);
-	    if (status == HT_INTERRUPTED) {
-		CTRACE((tfp, "HTFTP: Interrupted in response (port_command)\n"));
-		_HTProgress(CONNECTION_INTERRUPTED);
-		NETCLOSE(control->socket);
-		control->socket = -1;
-		close_master_socket();
-		status = HT_INTERRUPTED;
-		break;
-	    }
-	    if (status != 2) {	/* Could have timed out */
-		if (status < 0)
-		    continue;	/* try again - net error */
-		status = -status;	/* bad reply */
-		break;
-	    }
-	    CTRACE((tfp, "HTFTP: Port defined.\n"));
-#endif /* REPEAT_PORT */
-	} else {		/* Tell the server to be passive */
-	    char *command = NULL;
-	    const char *p = "?";
-	    int h0, h1, h2, h3, p0, p1;		/* Parts of reply */
-
-#ifdef INET6
-	    char dst[LINE_LENGTH + 1];
-#endif
-
-	    data_soc = status;
-
-#ifdef INET6
-	    /* see RFC 2428 */
-	    if (Broken_EPSV)
-		status = 1;
-	    else
-		status = send_cmd_1(p = "EPSV");
-	    if (status < 0)	/* retry or Bad return */
-		continue;
-	    else if (status != 2) {
-		status = send_cmd_1(p = "PASV");
-		if (status < 0) {	/* retry or Bad return */
-		    continue;
-		} else if (status != 2) {
-		    status = -status;	/* bad reply */
-		    break;
-		}
-	    }
-
-	    if (strcmp(p, "PASV") == 0) {
-		for (p = response_text; *p && *p != ','; p++) {
-		    ;		/* null body */
-		}
-
-		while (--p > response_text && '0' <= *p && *p <= '9') {
-		    ;		/* null body */
-		}
-		status = sscanf(p + 1, "%d,%d,%d,%d,%d,%d",
-				&h0, &h1, &h2, &h3, &p0, &p1);
-		if (status < 4) {
-		    fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
-		    status = HT_NO_CONNECTION;
-		    break;
-		}
-		passive_port = (PortNumber) ((p0 << 8) + p1);
-		sprintf(dst, "%d.%d.%d.%d", h0, h1, h2, h3);
-	    } else if (strcmp(p, "EPSV") == 0) {
-		char c0, c1, c2, c3;
-		struct sockaddr_storage ss;
-		LY_SOCKLEN sslen;
-
-		/*
-		 * EPSV bla (|||port|)
-		 */
-		for (p = response_text; *p && !isspace(UCH(*p)); p++) {
-		    ;		/* null body */
-		}
-		for ( /*nothing */ ;
-		     *p && *p && *p != '(';
-		     p++) {	/*) */
-		    ;		/* null body */
-		}
-		status = sscanf(p, "(%c%c%c%d%c)", &c0, &c1, &c2, &p0, &c3);
-		if (status != 5) {
-		    fprintf(tfp, "HTFTP: EPSV reply has invalid format!\n");
-		    status = HT_NO_CONNECTION;
-		    break;
-		}
-		passive_port = (PortNumber) p0;
-
-		sslen = (LY_SOCKLEN) sizeof(ss);
-		if (getpeername(control->socket, (struct sockaddr *) &ss,
-				&sslen) < 0) {
-		    fprintf(tfp, "HTFTP: getpeername(control) failed\n");
-		    status = HT_NO_CONNECTION;
-		    break;
-		}
-		if (getnameinfo((struct sockaddr *) &ss,
-				sslen,
-				dst,
-				(socklen_t) sizeof(dst),
-				NULL, 0, NI_NUMERICHOST)) {
-		    fprintf(tfp, "HTFTP: getnameinfo failed\n");
-		    status = HT_NO_CONNECTION;
-		    break;
-		}
-	    }
-#else
-	    status = send_cmd_1("PASV");
-	    if (status != 2) {
-		if (status < 0)
-		    continue;	/* retry or Bad return */
-		status = -status;	/* bad reply */
-		break;
-	    }
-	    for (p = response_text; *p && *p != ','; p++) {
-		;		/* null body */
-	    }
-
-	    while (--p > response_text && '0' <= *p && *p <= '9') {
-		;		/* null body */
-	    }
-
-	    status = sscanf(p + 1, "%d,%d,%d,%d,%d,%d",
-			    &h0, &h1, &h2, &h3, &p0, &p1);
-	    if (status < 4) {
-		fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");
-		status = HT_NO_CONNECTION;
-		break;
-	    }
-	    passive_port = (PortNumber) ((p0 << 8) + p1);
-#endif /* INET6 */
-	    CTRACE((tfp, "HTFTP: Server is listening on port %d\n",
-		    passive_port));
-
-	    /* Open connection for data:  */
-
-#ifdef INET6
-	    HTSprintf0(&command, "%s//%s:%d/", STR_FTP_URL, dst, passive_port);
-#else
-	    HTSprintf0(&command, "%s//%d.%d.%d.%d:%d/",
-		       STR_FTP_URL, h0, h1, h2, h3, passive_port);
-#endif
-	    status = HTDoConnect(command, "FTP data", passive_port, &data_soc);
-	    FREE(command);
-
-	    if (status < 0) {
-		(void) HTInetStatus(gettext("connect for data"));
-		NETCLOSE(data_soc);
-		break;
-	    }
-
-	    CTRACE((tfp, "FTP data connected, socket %d\n", data_soc));
-	}
-	status = 0;
-	break;			/* No more retries */
-
-    }				/* for retries */
-    CTRACE((tfp, "setup_connection returns %d\n", status));
-    return status;
-}
-
-/*	Retrieve File from Server
- *	-------------------------
- *
- * On entry,
- *	name		WWW address of a file: document, including hostname
- * On exit,
- *	returns		Socket number for file if good.
- *			<0 if bad.
- */
-int HTFTPLoad(const char *name,
-	      HTParentAnchor *anchor,
-	      HTFormat format_out,
-	      HTStream *sink)
-{
-    BOOL isDirectory = NO;
-    HTAtom *encoding = NULL;
-    int status, final_status;
-    int outstanding = 1;	/* outstanding control connection responses
-
-				   that we are willing to wait for, if we
-				   get to the point of reading data - kw */
-    HTFormat format;
-
-    CTRACE((tfp, "HTFTPLoad(%s) %s connection\n",
-	    name,
-	    (ftp_local_passive
-	     ? "passive"
-	     : "normal")));
-
-    HTReadProgress((off_t) 0, (off_t) 0);
-
-    status = setup_connection(name, anchor);
-    if (status < 0)
-	return status;		/* Failed with this code */
-
-    /*  Ask for the file:
-     */
-    {
-	char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
-	char *fname = filename;	/* Save for subsequent free() */
-	char *vmsname = NULL;
-	BOOL binary;
-	const char *type = NULL;
-	char *types = NULL;
-	char *cp;
-
-	if (server_type == CMS_SERVER) {
-	    /* If the unescaped path has a %2f, reject it as illegal. - FM */
-	    if (((cp = strstr(filename, "%2")) != NULL) &&
-		TOUPPER(cp[2]) == 'F') {
-		FREE(fname);
-		init_help_message_cache();	/* to free memory */
-		NETCLOSE(control->socket);
-		control->socket = -1;
-		CTRACE((tfp,
-			"HTFTP: Rejecting path due to illegal escaped slash.\n"));
-		return -1;
-	    }
-	}
-
-	if (!*filename) {
-	    StrAllocCopy(filename, "/");
-	    type = "D";
-	} else if ((type = types = strrchr(filename, ';')) != NULL) {
-	    /*
-	     * Check and trim the type= parameter.  - FM
-	     */
-	    if (!strncasecomp((type + 1), "type=", 5)) {
-		switch (TOUPPER(*(type + 6))) {
-		case 'D':
-		    *types = '\0';
-		    type = "D";
-		    break;
-		case 'A':
-		    *types = '\0';
-		    type = "A";
-		    break;
-		case 'I':
-		    *types = '\0';
-		    type = "I";
-		    break;
-		default:
-		    type = "";
-		    break;
-		}
-		if (!*filename) {
-		    *filename = '/';
-		    *(filename + 1) = '\0';
-		}
-	    }
-	    if (*type != '\0') {
-		CTRACE((tfp, "HTFTP: type=%s\n", type));
-	    }
-	}
-	HTUnEscape(filename);
-	CTRACE((tfp, "HTFTP: UnEscaped %s\n", filename));
-	if (filename[1] == '~') {
-	    /*
-	     * Check if translation of HOME as tilde is supported,
-	     * and adjust filename if so. - FM
-	     */
-	    char *cp2 = NULL;
-	    char *fn = NULL;
-
-	    if ((cp2 = strchr((filename + 1), '/')) != NULL) {
-		*cp2 = '\0';
-	    }
-	    status = send_cmd_1("PWD");
-	    if (status == 2 && response_text[5] == '/') {
-		status = send_cwd(filename + 1);
-		if (status == 2) {
-		    StrAllocCopy(fn, (filename + 1));
-		    if (cp2) {
-			*cp2 = '/';
-			if (fn[strlen(fn) - 1] != '/') {
-			    StrAllocCat(fn, cp2);
-			} else {
-			    StrAllocCat(fn, (cp2 + 1));
-			}
-			cp2 = NULL;
-		    }
-		    FREE(fname);
-		    fname = filename = fn;
-		}
-	    }
-	    if (cp2) {
-		*cp2 = '/';
-	    }
-	}
-	if (strlen(filename) > 3) {
-	    char *cp2;
-
-	    if (((cp2 = strrchr(filename, '.')) != NULL &&
-		 0 == strncasecomp(cp2, ".me", 3)) &&
-		(cp2[3] == '\0' || cp2[3] == ';')) {
-		/*
-		 * Don't treat this as application/x-Troff-me if it's a Unix
-		 * server but has the string "read.me", or if it's not a Unix
-		 * server.  - FM
-		 */
-		if ((server_type != UNIX_SERVER) ||
-		    (cp2 > (filename + 3) &&
-		     0 == strncasecomp((cp2 - 4), "read.me", 7))) {
-		    *cp2 = '\0';
-		    format = HTFileFormat(filename, &encoding, NULL);
-		    *cp2 = '.';
-		} else {
-		    format = HTFileFormat(filename, &encoding, NULL);
-		}
-	    } else {
-		format = HTFileFormat(filename, &encoding, NULL);
-	    }
-	} else {
-	    format = HTFileFormat(filename, &encoding, NULL);
-	}
-	format = HTCharsetFormat(format, anchor, -1);
-	binary = (BOOL) (encoding != HTAtom_for("8bit") &&
-			 encoding != HTAtom_for("7bit"));
-	if (!binary &&
-	/*
-	 * Force binary if we're in source, download or dump mode and this is
-	 * not a VM/CMS server, so we don't get CRLF instead of LF (or CR) for
-	 * newlines in text files.  Can't do this for VM/CMS or we'll get raw
-	 * EBCDIC.  - FM
-	 */
-	    (format_out == WWW_SOURCE ||
-	     format_out == HTAtom_for("www/download") ||
-	     format_out == HTAtom_for("www/dump")) &&
-	    (server_type != CMS_SERVER))
-	    binary = TRUE;
-	if (!binary && type && *type == 'I') {
-	    /*
-	     * Force binary if we had ;type=I - FM
-	     */
-	    binary = TRUE;
-	} else if (binary && type && *type == 'A') {
-	    /*
-	     * Force ASCII if we had ;type=A - FM
-	     */
-	    binary = FALSE;
-	}
-	if (binary != control->binary) {
-	    /*
-	     * Act on our setting if not already set.  - FM
-	     */
-	    const char *mode = binary ? "I" : "A";
-
-	    status = send_cmd_2("TYPE", mode);
-	    if (status != 2) {
-		init_help_message_cache();	/* to free memory */
-		return ((status < 0) ? status : -status);
-	    }
-	    control->binary = binary;
-	}
-	switch (server_type) {
-	    /*
-	     * Handle what for Lynx are special case servers, e.g., for which
-	     * we respect RFC 1738, or which have known conflicts in suffix
-	     * mappings.  - FM
-	     */
-	case VMS_SERVER:
-	    {
-		char *cp1, *cp2;
-		BOOL included_device = FALSE;
-		BOOL found_tilde = FALSE;
-
-		/* Accept only Unix-style filename */
-		if (strchr(filename, ':') != NULL ||
-		    strchr(filename, '[') != NULL) {
-		    FREE(fname);
-		    init_help_message_cache();	/* to free memory */
-		    NETCLOSE(control->socket);
-		    control->socket = -1;
-		    CTRACE((tfp,
-			    "HTFTP: Rejecting path due to non-Unix-style syntax.\n"));
-		    return -1;
-		}
-		/* Handle any unescaped "/%2F" path */
-		if (!StrNCmp(filename, "//", 2)) {
-		    int i;
-
-		    included_device = TRUE;
-		    for (i = 0; filename[(i + 1)]; i++)
-			filename[i] = filename[(i + 1)];
-		    filename[i] = '\0';
-		    CTRACE((tfp, "HTFTP: Trimmed '%s'\n", filename));
-		    cp = HTVMS_name("", filename);
-		    CTRACE((tfp, "HTFTP: VMSized '%s'\n", cp));
-		    if ((cp1 = strrchr(cp, ']')) != NULL) {
-			strcpy(filename, ++cp1);
-			CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
-			*cp1 = '\0';
-			status = send_cwd(cp);
-			if (status != 2) {
-			    char *dotslash = 0;
-
-			    if ((cp1 = strchr(cp, '[')) != NULL) {
-				*cp1++ = '\0';
-				status = send_cwd(cp);
-				if (status != 2) {
-				    FREE(fname);
-				    init_help_message_cache();	/* to free memory */
-				    NETCLOSE(control->socket);
-				    control->socket = -1;
-				    return ((status < 0) ? status : -status);
-				}
-				HTSprintf0(&dotslash, "[.%s", cp1);
-				status = send_cwd(dotslash);
-				FREE(dotslash);
-				if (status != 2) {
-				    FREE(fname);
-				    init_help_message_cache();	/* to free memory */
-				    NETCLOSE(control->socket);
-				    control->socket = -1;
-				    return ((status < 0) ? status : -status);
-				}
-			    } else {
-				FREE(fname);
-				init_help_message_cache();	/* to free memory */
-				NETCLOSE(control->socket);
-				control->socket = -1;
-				return ((status < 0) ? status : -status);
-			    }
-			}
-		    } else if ((cp1 = strchr(cp, ':')) != NULL &&
-			       strchr(cp, '[') == NULL &&
-			       strchr(cp, ']') == NULL) {
-			cp1++;
-			if (*cp1 != '\0') {
-			    int cplen = (int) (cp1 - cp);
-
-			    strcpy(filename, cp1);
-			    CTRACE((tfp, "HTFTP: Filename '%s'\n", filename));
-			    HTSprintf0(&vmsname, "%.*s[%s]", cplen, cp, filename);
-			    status = send_cwd(vmsname);
-			    if (status != 2) {
-				HTSprintf(&vmsname, "%.*s[000000]", cplen, cp);
-				status = send_cwd(vmsname);
-				if (status != 2) {
-				    HTSprintf(&vmsname, "%.*s", cplen, cp);
-				    status = send_cwd(vmsname);
-				    if (status != 2) {
-					FREE(fname);
-					init_help_message_cache();
-					NETCLOSE(control->socket);
-					control->socket = -1;
-					return ((status < 0) ? status : -status);
-				    }
-				}
-			    } else {
-				HTSprintf0(&vmsname, "000000");
-				filename = vmsname;
-			    }
-			}
-		    } else if (0 == strcmp(cp, (filename + 1))) {
-			status = send_cwd(cp);
-			if (status != 2) {
-			    HTSprintf0(&vmsname, "%s:", cp);
-			    status = send_cwd(vmsname);
-			    if (status != 2) {
-				FREE(fname);
-				init_help_message_cache();	/* to free memory */
-				NETCLOSE(control->socket);
-				control->socket = -1;
-				return ((status < 0) ? status : -status);
-			    }
-			}
-			HTSprintf0(&vmsname, "000000");
-			filename = vmsname;
-		    }
-		}
-		/* Trim trailing slash if filename is not the top directory */
-		if (strlen(filename) > 1 && filename[strlen(filename) - 1] == '/')
-		    filename[strlen(filename) - 1] = '\0';
-
-#ifdef MAINTAIN_CONNECTION	/* Don't need this if always new connection - F.M. */
-		if (!included_device) {
-		    /* Get the current default VMS device:[directory] */
-		    status = send_cmd_1("PWD");
-		    if (status != 2) {
-			FREE(fname);
-			init_help_message_cache();	/* to free memory */
-			NETCLOSE(control->socket);
-			control->socket = -1;
-			return ((status < 0) ? status : -status);
-		    }
-		    /* Go to the VMS account's top directory */
-		    if ((cp = strchr(response_text, '[')) != NULL &&
-			(cp1 = strrchr(response_text, ']')) != NULL) {
-			char *tmp = 0;
-			unsigned len = 4;
-
-			StrAllocCopy(tmp, cp);
-			if ((cp2 = strchr(cp, '.')) != NULL && cp2 < cp1) {
-			    len += (cp2 - cp);
-			} else {
-			    len += (cp1 - cp);
-			}
-			tmp[len] = 0;
-			StrAllocCat(tmp, "]");
-
-			status = send_cwd(tmp);
-			FREE(tmp);
-
-			if (status != 2) {
-			    FREE(fname);
-			    init_help_message_cache();	/* to free memory */
-			    NETCLOSE(control->socket);
-			    control->socket = -1;
-			    return ((status < 0) ? status : -status);
-			}
-		    }
-		}
-#endif /* MAINTAIN_CONNECTION */
-
-		/* If we want the VMS account's top directory, list it now */
-		if (!(strcmp(filename, "/~")) ||
-		    (included_device && 0 == strcmp(filename, "000000")) ||
-		    (strlen(filename) == 1 && *filename == '/')) {
-		    isDirectory = YES;
-		    status = send_cmd_1("LIST");
-		    FREE(fname);
-		    if (status != 1) {
-			/* Action not started */
-			init_help_message_cache();	/* to free memory */
-			NETCLOSE(control->socket);
-			control->socket = -1;
-			return ((status < 0) ? status : -status);
-		    }
-		    /* Big goto! */
-		    goto listen;
-		}
-		/* Otherwise, go to appropriate directory and doctor filename */
-		if (!StrNCmp(filename, "/~", 2)) {
-		    filename += 2;
-		    found_tilde = TRUE;
-		}
-		CTRACE((tfp, "check '%s' to translate x/y/ to [.x.y]\n", filename));
-		if (!included_device &&
-		    (cp = strchr(filename, '/')) != NULL &&
-		    (cp1 = strrchr(cp, '/')) != NULL &&
-		    (cp1 - cp) > 1) {
-		    char *tmp = 0;
-
-		    HTSprintf0(&tmp, "[.%.*s]", (int) (cp1 - cp - 1), cp + 1);
-
-		    CTRACE((tfp, "change path '%s'\n", tmp));
-		    while ((cp2 = strrchr(tmp, '/')) != NULL)
-			*cp2 = '.';
-		    CTRACE((tfp, "...to  path '%s'\n", tmp));
-
-		    status = send_cwd(tmp);
-		    FREE(tmp);
-
-		    if (status != 2) {
-			FREE(fname);
-			init_help_message_cache();	/* to free memory */
-			NETCLOSE(control->socket);
-			control->socket = -1;
-			return ((status < 0) ? status : -status);
-		    }
-		    filename = cp1 + 1;
-		} else {
-		    if (!included_device && !found_tilde) {
-			filename += 1;
-		    }
-		}
-		break;
-	    }
-	case CMS_SERVER:
-	    {
-		/*
-		 * If we want the CMS account's top directory, or a base SFS or
-		 * anonymous directory path (i.e., without a slash), list it
-		 * now.  FM
-		 */
-		if ((strlen(filename) == 1 && *filename == '/') ||
-		    ((0 == strncasecomp((filename + 1), "vmsysu:", 7)) &&
-		     (cp = strchr((filename + 1), '.')) != NULL &&
-		     strchr(cp, '/') == NULL) ||
-		    (0 == strncasecomp(filename + 1, "anonymou.", 9) &&
-		     strchr(filename + 1, '/') == NULL)) {
-		    if (filename[1] != '\0') {
-			status = send_cwd(filename + 1);
-			if (status != 2) {
-			    /* Action not started */
-			    init_help_message_cache();	/* to free memory */
-			    NETCLOSE(control->socket);
-			    control->socket = -1;
-			    return ((status < 0) ? status : -status);
-			}
-		    }
-		    isDirectory = YES;
-		    if (use_list)
-			status = send_cmd_1("LIST");
-		    else
-			status = send_cmd_1("NLST");
-		    FREE(fname);
-		    if (status != 1) {
-			/* Action not started */
-			init_help_message_cache();	/* to free memory */
-			NETCLOSE(control->socket);
-			control->socket = -1;
-			return ((status < 0) ? status : -status);
-		    }
-		    /* Big goto! */
-		    goto listen;
-		}
-		filename++;
-
-		/* Otherwise, go to appropriate directory and adjust filename */
-		while ((cp = strchr(filename, '/')) != NULL) {
-		    *cp++ = '\0';
-		    status = send_cwd(filename);
-		    if (status == 2) {
-			if (*cp == '\0') {
-			    isDirectory = YES;
-			    if (use_list)
-				status = send_cmd_1("LIST");
-			    else
-				status = send_cmd_1("NLST");
-			    FREE(fname);
-			    if (status != 1) {
-				/* Action not started */
-				init_help_message_cache();	/* to free memory */
-				NETCLOSE(control->socket);
-				control->socket = -1;
-				return ((status < 0) ? status : -status);
-			    }
-			    /* Clear any messages from the login directory */
-			    init_help_message_cache();
-			    /* Big goto! */
-			    goto listen;
-			}
-			filename = cp;
-		    }
-		}
-		break;
-	    }
-	default:
-	    /* Shift for any unescaped "/%2F" path */
-	    if (!StrNCmp(filename, "//", 2))
-		filename++;
-	    break;
-	}
-	/*
-	 * Act on a file or listing request, or try to figure out which we're
-	 * dealing with if we don't know yet.  - FM
-	 */
-	if (!(type) || (type && *type != 'D')) {
-	    status = send_cmd_2("RETR", filename);
-	    if (status >= 5) {
-		int check;
-
-		if (Broken_RETR) {
-		    CTRACE((tfp, "{{reconnecting...\n"));
-		    close_connection(control);
-		    check = setup_connection(name, anchor);
-		    CTRACE((tfp, "...done }}reconnecting\n"));
-		    if (check < 0)
-			return check;
-		}
-	    }
-	} else {
-	    status = 5;		/* Failed status set as flag. - FM */
-	}
-	if (status != 1) {	/* Failed : try to CWD to it */
-	    /* Clear any login messages if this isn't the login directory */
-	    if (strcmp(filename, "/"))
-		init_help_message_cache();
-
-	    status = send_cwd(filename);
-	    if (status == 2) {	/* Succeeded : let's NAME LIST it */
-		isDirectory = YES;
-		if (use_list)
-		    status = send_cmd_1("LIST");
-		else
-		    status = send_cmd_1("NLST");
-	    }
-	}
-	FREE(fname);
-	FREE(vmsname);
-	if (status != 1) {
-	    init_help_message_cache();	/* to free memory */
-	    NETCLOSE(control->socket);
-	    control->socket = -1;
-	    if (status < 0)
-		return status;
-	    else
-		return -status;
-	}
-    }
-
-  listen:
-    if (!ftp_local_passive) {
-	/* Wait for the connection */
-#ifdef INET6
-	struct sockaddr_storage soc_address;
-
-#else
-	struct sockaddr_in soc_address;
-#endif /* INET6 */
-	LY_SOCKLEN soc_addrlen = (LY_SOCKLEN) sizeof(soc_address);
-
-#ifdef SOCKS
-	if (socks_flag)
-	    status = Raccept((int) master_socket,
-			     (struct sockaddr *) &soc_address,
-			     &soc_addrlen);
-	else
-#endif /* SOCKS */
-	    status = accept((int) master_socket,
-			    (struct sockaddr *) &soc_address,
-			    &soc_addrlen);
-	if (status < 0) {
-	    init_help_message_cache();	/* to free memory */
-	    return HTInetStatus("accept");
-	}
-	CTRACE((tfp, "TCP: Accepted new socket %d\n", status));
-	data_soc = status;
-    }
-    /* !ftp_local_passive */
-#if 0				/* no - this makes the data connection go away too soon (2.8.3dev.22) */
-    if ((status = send_cmd_nowait("QUIT")) == 1)
-	outstanding++;
-#endif
-
-    if (isDirectory) {
-	if (server_type == UNIX_SERVER && !unsure_type &&
-	    !strcmp(response_text,
-		    "150 Opening ASCII mode data connection for /bin/dl.\n")) {
-	    CTRACE((tfp, "HTFTP: Treating as \"dls\" server.\n"));
-	    server_type = DLS_SERVER;
-	}
-	final_status = read_directory(anchor, name, format_out, sink);
-	if (final_status > 0) {
-	    if (server_type != CMS_SERVER)
-		if (outstanding-- > 0) {
-		    status = response(0);
-		    if (status < 0 ||
-			(status == 2 && !StrNCmp(response_text, "221", 3)))
-			outstanding = 0;
-		}
-	} else {		/* HT_INTERRUPTED */
-	    /* User may have pressed 'z' to give up because no
-	       packets got through, so let's not make them wait
-	       any longer - kw */
-	    outstanding = 0;
-	}
-
-	if (data_soc != -1) {	/* normally done in read_directory */
-	    CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
-	    status = NETCLOSE(data_soc);
-	    if (status == -1)
-		HTInetStatus("close");	/* Comment only */
-	}
-	status = final_status;
-    } else {
-	int rv;
-	char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
-
-	/* Clear any login messages */
-	init_help_message_cache();
-
-	/* Fake a Content-Encoding for compressed files. - FM */
-	HTUnEscape(FileName);
-	if (!IsUnityEnc(encoding)) {
-	    /*
-	     * We already know from the call to HTFileFormat above that this is
-	     * a compressed file, no need to look at the filename again.  - kw
-	     */
-	    StrAllocCopy(anchor->content_type, format->name);
-	    StrAllocCopy(anchor->content_encoding, HTAtom_name(encoding));
-	    format = HTAtom_for("www/compressed");
-
-	} else {
-	    int rootlen;
-	    CompressFileType cft = HTCompressFileType(FileName, "._-", &rootlen);
-
-	    if (cft != cftNone) {
-		FileName[rootlen] = '\0';
-		format = HTFileFormat(FileName, &encoding, NULL);
-		format = HTCharsetFormat(format, anchor, -1);
-		StrAllocCopy(anchor->content_type, format->name);
-		format = HTAtom_for("www/compressed");
-
-		switch (cft) {
-		case cftCompress:
-		    StrAllocCopy(anchor->content_encoding, "x-compress");
-		    break;
-		case cftGzip:
-		    StrAllocCopy(anchor->content_encoding, "x-gzip");
-		    break;
-		case cftDeflate:
-		    StrAllocCopy(anchor->content_encoding, "x-deflate");
-		    break;
-		case cftBzip2:
-		    StrAllocCopy(anchor->content_encoding, "x-bzip2");
-		    break;
-		case cftNone:
-		    break;
-		}
-	    }
-	}
-	FREE(FileName);
-
-	_HTProgress(gettext("Receiving FTP file."));
-	rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
-
-	HTInitInput(control->socket);
-	/* Reset buffering to control connection DD 921208 */
-
-	if (rv < 0) {
-#if 0				/* any known servers where ABOR would work this way? */
-	    if (rv == HT_INTERRUPTED || rv == -501)
-		if (send_cmd_nowait("ABOR") == 1) {
-		    outstanding++;
-		    CTRACE((tfp, "HTFTP: outstanding responses: %d\n", outstanding));
-		}
-#endif
-	    if (rv == -2)	/* weird error, don't expect much response */
-		outstanding--;
-	    else if (rv == HT_INTERRUPTED || rv == -1)
-		/* User may have pressed 'z' to give up because no
-		   packets got through, so let's not make them wait
-		   longer - kw */
-		outstanding = 0;
-	    CTRACE((tfp, "HTFTP: Closing data socket %d\n", data_soc));
-	    status = NETCLOSE(data_soc);
-	} else
-	    status = 2;		/* data_soc already closed in HTCopy - kw */
-
-	if (status < 0 && rv != HT_INTERRUPTED && rv != -1) {
-	    (void) HTInetStatus("close");	/* Comment only */
-	} else {
-	    if (rv != HT_LOADED && outstanding--) {
-		status = response(0);	/* Pick up final reply */
-		if (status != 2 && rv != HT_INTERRUPTED && rv != -1) {
-		    data_soc = -1;	/* invalidate it */
-		    init_help_message_cache();	/* to free memory */
-		    return HTLoadError(sink, 500, response_text);
-		} else if (status <= 0) {
-		    outstanding = 0;
-		} else if (status == 2 && !StrNCmp(response_text, "221", 3))
-		    outstanding = 0;
-	    }
-	}
-	final_status = HT_LOADED;
-    }
-    while (outstanding-- > 0 &&
-	   (status > 0)) {
-	status = response(0);
-	if (status == 2 && !StrNCmp(response_text, "221", 3))
-	    break;
-    }
-    data_soc = -1;		/* invalidate it */
-    CTRACE((tfp, "HTFTPLoad: normal end; "));
-    if (control->socket < 0) {
-	CTRACE((tfp, "control socket is %d\n", control->socket));
-    } else {
-	CTRACE((tfp, "closing control socket %d\n", control->socket));
-	status = NETCLOSE(control->socket);
-	if (status == -1)
-	    HTInetStatus("control connection close");	/* Comment only */
-    }
-    control->socket = -1;
-    init_help_message_cache();	/* to free memory */
-    /* returns HT_LOADED (always for file if we get here) or error */
-    return final_status;
-}				/* open_file_read */
-
-/*
- *  This function frees any user entered password, so that
- *  it must be entered again for a future request. - FM
- */
-void HTClearFTPPassword(void)
-{
-    /*
-     * Need code to check cached documents from non-anonymous ftp accounts and
-     * do something to ensure that they no longer can be accessed without a new
-     * retrieval.  - FM
-     */
-
-    /*
-     * Now free the current user entered password, if any.  - FM
-     */
-    FREE(user_entered_password);
-}
-
-#endif /* ifndef DISABLE_FTP */