#include "HTUtils.h"
#include "tcp.h"
#include "LYCurses.h"
#include "HTParse.h"
#include "HTAccess.h"
#include "LYUtils.h"
#include "LYStrings.h"
#include "LYGlobalDefs.h"
#include "LYSignal.h"
#include "GridText.h"
#ifdef VMS
#include <descrip.h>
#include <libclidef.h>
#include <lib$routines.h>
#include "HTVMSUtils.h"
#endif /* VMS */
#ifdef UNIX
#include <pwd.h>
#include <utmp.h>
#endif /* UNIX */

#include "LYLeaks.h"

#ifdef SVR4_BSDSELECT
extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
	 		     fd_set * exceptfds, struct timeval * timeout));
#ifdef select
#undef select
#endif /* select */
#define select BSDselect
#ifdef SOCKS
#ifdef Rselect
#undef Rselect
#endif /* Rselect */
#define Rselect BSDselect
#endif /* SOCKS */
#endif /* SVR4_BSDSELECT */

#ifndef FD_SETSIZE
#define FD_SETSIZE 256
#endif /* !FD_SETSIZE */

#ifndef UTMP_FILE
#if defined(__FreeBSD__) || defined(__bsdi__)
#define UTMP_FILE _PATH_UTMP
#else
#define UTMP_FILE "/etc/utmp"
#endif /* __FreeBSD__ || __bsdi__ */
#endif /* !UTMP_FILE */

#define FREE(x) if (x) {free(x); x = NULL;}

extern BOOL HTJapanese;

PRIVATE HTList * localhost_aliases = NULL;	/* Hosts to treat as local */

/*
 * highlight (or unhighlight) a given link
 */
PUBLIC void highlight ARGS2(int,flag, int,cur)
{
    char buffer[200];
    int i;
    char tmp[3];

    tmp[0] = tmp[1] = tmp[2] = '\0';

    /* Bug in history code can cause -1 to be sent, which will yield
    ** an ACCVIO when LYstrncpy() is called with a nonsense pointer.
    ** This works around the bug, for now. -- FM
    */
    if (cur < 0)
        cur = 0;

    if (nlinks > 0) {
	move(links[cur].ly, links[cur].lx);
	if (flag == ON) { 
	    /* makes some terminals work wrong because
	     * they can't handle two attributes at the 
	     * same time
	     */
	    /* start_bold();  */
	    start_reverse();
#ifdef USE_SLANG
	    start_underline ();
#endif /* USE_SLANG */
	} else {
	    start_bold();
	}

	if (links[cur].type == WWW_FORM_LINK_TYPE) {
	    int len;
	    int avail_space = (LYcols-links[cur].lx)-1;

	    LYstrncpy(buffer,
	    	      links[cur].hightext, 
		      (avail_space > links[cur].form->size ? 
				      links[cur].form->size : avail_space));
	    addstr(buffer);  

	    len = strlen(buffer);
	    for (; len < links[cur].form->size && len < avail_space; len++)
	        addch('_');

	} else {

	    /* copy into the buffer only what will fit within the
	     * width of the screen
	     */
	    LYstrncpy(buffer,links[cur].hightext, LYcols-links[cur].lx-1);
	    addstr(buffer);  
	}

	/* display a second line as well */
	if (links[cur].hightext2 && links[cur].ly < display_lines) {
	    if (flag == ON) {
	        stop_reverse();
#ifdef USE_SLANG
		stop_underline ();
#endif /* USE_SLANG */
	    } else {
	        stop_bold();
	    }

	    addch('\n');
	    for (i=0; i < links[cur].hightext2_offset; i++)
	        addch(' ');

	    if (flag == ON) {
	        start_reverse();
#ifdef USE_SLANG
		start_underline ();
#endif /* USE_SLANG */
	    } else {
	        start_bold();
	    }

	    for (i=0; (tmp[0] = links[cur].hightext2[i]) != '\0' &&
	    	      i+links[cur].hightext2_offset < LYcols; i++) {
		if (!IsSpecialAttrChar(links[cur].hightext2[i])) {
		    /* For Japanese strings, by Masanobu Kimura */
		    if (HTJapanese && !isascii(tmp[0])) {
		        tmp[1] = links[cur].hightext2[++i];
			addstr(tmp);
			tmp[1] = '\0';
		    } else {
		        addstr(tmp);
		    }
		 }
	    }
	}

	if (flag == ON) {
	    stop_reverse();
#ifdef USE_SLANG
	    stop_underline ();
#endif /* USE_SLANG */
	} else {
	    stop_bold();
	}

#if defined(FANCY_CURSES) || defined(USE_SLANG)
	if (!LYShowCursor)
	    move(LYlines-1, LYcols-1);  /* get cursor out of the way */
	else
#endif /* FANCY CURSES || USE_SLANG */
	    /* never hide the cursor if there's no FANCY CURSES or SLANG */
	    move(links[cur].ly, links[cur].lx - 1);

	if (flag)
	    refresh();
    }
    return;
}

/*
 * free_and_clear will free a pointer if it is non-zero and
 * then set it to zero
 */
PUBLIC void free_and_clear ARGS1(char **,pointer)
{
    if (*pointer) {
	free(*pointer);
        *pointer = 0;
    }
    return;
}

/*
 * Collapse (REMOVE) all spaces in the string. 
 */
PUBLIC void collapse_spaces ARGS1(char *,string)
{
    int i=0;
    int j=0;

    if (!string)
        return;

    for (; string[i] != '\0'; i++) 
	if (!isspace(string[i])) 
	    string[j++] = string[i];

    string[j] = '\0';  /* terminate */
    return;
}

/*
 * Convert single or serial newlines to single spaces throughout a string
 * (ignore newlines if the preceding character is a space) and convert
 * tabs to single spaces (but don't ignore any explicit tabs or spaces).
 */
PUBLIC void convert_to_spaces ARGS1(char *,string)
{
    char *s = string;
    char *ns = string;
    BOOL last_is_space = FALSE;

    if (!string)
        return;

    while (*s) {
	switch (*s) {
	    case ' ':
	    case '\t':
		*(ns++) = ' ';
		last_is_space = TRUE;
		break;

	    case '\r':
	    case '\n':
	        if (!last_is_space) {
		    *(ns++) = ' ';
		    last_is_space = TRUE;
		}
		break;

	    default:
		*(ns++) = *s;
		last_is_space = FALSE;
		break;
	}
	s++;
    }
    *ns = '\0';
    return;
}

/*
 * display (or hide) the status line
 */
BOOLEAN mustshow = FALSE;

PUBLIC void statusline ARGS1(char *,text)
{
    char buffer[256];
    extern BOOLEAN no_statusline;
    int max_length;

    if (!text || text==NULL)
	return;

	/* don't print statusline messages if dumping to stdout
	 */
    if (dump_output_immediately)
	return;

    /* Don't print status line if turned off. */
    if (mustshow != TRUE) {
	if (no_statusline == TRUE) {
	    return;
	}
    }
    mustshow = FALSE;

    /* deal with any newlines or tabs in the string */
    LYstrncpy(buffer, text, 255);
    convert_to_spaces(buffer);

    /* make sure text is not longer than the statusline window */
    max_length = ((LYcols - 2) < 256) ? (LYcols - 2) : 255;
    buffer[max_length] = '\0';

    if (user_mode == NOVICE_MODE)
        move(LYlines-3,0);
    else
        move(LYlines-1,0);
    clrtoeol();
    if (text != NULL) {
	start_reverse();
	addstr(buffer);
	stop_reverse();
    }

    refresh();
    return;
}

static char *novice_lines[] = {
#ifndef	NOVICE_LINE_TWO_A
#define	NOVICE_LINE_TWO_A	NOVICE_LINE_TWO
#define	NOVICE_LINE_TWO_B	""
#define	NOVICE_LINE_TWO_C	""
#endif /* !NOVICE_LINE_TWO_A */
  NOVICE_LINE_TWO_A,
  NOVICE_LINE_TWO_B,
  NOVICE_LINE_TWO_C,
  ""
};
static int lineno = 0;

PUBLIC void toggle_novice_line NOARGS
{
	lineno++;
	if (*novice_lines[lineno] == '\0')
		lineno = 0;
	return;
}

PUBLIC void noviceline ARGS1(int,more)
{

    if (dump_output_immediately)
	return;

    move(LYlines-2,0);
    stop_reverse();
    clrtoeol();
    addstr(NOVICE_LINE_ONE);
    clrtoeol();

#if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE)
    if (lynx_edit_mode && !no_dired_support) 
       addstr(DIRED_NOVICELINE);
    else
#endif /* DIRED_SUPPORT && OK_OVERRIDE */

    if (LYUseNoviceLineTwo)
        addstr(NOVICE_LINE_TWO);
    else
        addstr(novice_lines[lineno]);

#ifdef NOTDEFINED
    if (is_www_index && more) {
        addstr("This is a searchable index.  Use ");
	addstr(key_for_func(LYK_INDEX_SEARCH));
	addstr(" to search:");
	stop_reverse();
	addstr("                ");
	start_reverse();
        addstr("space for more");

    } else if (is_www_index) {
        addstr("This is a searchable index.  Use ");
	addstr(key_for_func(LYK_INDEX_SEARCH));
	addstr(" to search:");
    } else {
        addstr("Type a command or ? for help:");                   

        if (more) {
	    stop_reverse();
	    addstr("                       ");
	    start_reverse();
            addstr("Press space for next page");
	}
    }

#endif /* NOTDEFINED */

    refresh();
    return;
}

PUBLIC int HTCheckForInterrupt NOARGS
{
#ifndef VMS /* UNIX stuff: */
    int c;
#ifndef USE_SLANG
    struct timeval socket_timeout;
    int ret = 0;
    fd_set readfds;
#endif /* !USE_SLANG */

    /** Curses or slang setup was not invoked **/
    if (dump_output_immediately)
	return((int)FALSE);

#ifdef USE_SLANG
    /** No keystroke was entered
        Note that this isn't taking possible SOCKSification
	and the socks_flag into account, and may fail on the
	slang library's select() when SOCKSified. - FM **/
    if (0 == SLang_input_pending(0))
        return(FALSE);

#else /* Unix curses: */

    socket_timeout.tv_sec = 0;
    socket_timeout.tv_usec = 100;
    FD_ZERO(&readfds);
    FD_SET(0, &readfds);
#ifdef __hpux
#ifdef SOCKS
    if (socks_flag)
        ret = Rselect(FD_SETSIZE, (int *)&readfds, NULL, NULL,
	  		&socket_timeout);
    else
#endif /* SOCKS */
         ret = select(FD_SETSIZE, (int *)&readfds, NULL, NULL,
	  		&socket_timeout);
#else
#ifdef SOCKS
    if (socks_flag)
        ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL,
	  		&socket_timeout);
    else
#endif /* SOCKS */
        ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL,
	  		&socket_timeout);
#endif /* __hpux */

    /** No keystroke was entered **/
    if (!FD_ISSET(0,&readfds))
	 return((int)FALSE); 
#endif /* USE_SLANG */

    /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
    c = LYgetch();
    if (TOUPPER(c) == 'Z' || c == 7 || c == 3)
	return((int)TRUE);

    /** Other keystrokes **/
    return((int)FALSE);

#else /* VMS: */

    int c;
    extern BOOLEAN HadVMSInterrupt;
    extern int typeahead();

    /** Curses or slang setup was not invoked **/
    if (dump_output_immediately)
	  return((int)FALSE);

    /** Control-C or Control-Y and a 'N'o reply to exit query **/
    if (HadVMSInterrupt) {
        HadVMSInterrupt = FALSE;
        return((int)TRUE);
    }

    /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
    c = typeahead();
    if (TOUPPER(c) == 'Z' || c == 7 || c == 3)
        return((int)TRUE);

    /** Other or no keystrokes **/
    return((int)FALSE);
#endif /* !VMS */
}

/*
 * A file URL for a remote host is an obsolete ftp URL.
 * Return YES only if we're certain it's a local file. - FM
 */
PUBLIC BOOLEAN LYisLocalFile ARGS1(char *,filename)
{
    char *host=NULL;
    char *access=NULL;
    char *cp;

    if (!filename || !(host = HTParse(filename, "", PARSE_HOST)))
        return NO;

    if (!*host) {
        FREE(host);
	return NO;
    }

    if ((cp=strchr(host, ':')) != NULL)
        *cp = '\0';

    if ((access = HTParse(filename, "", PARSE_ACCESS))) {
        if (0==strcmp("file", access) &&
	    (0==strcmp(host, "localhost") ||
#ifdef VMS
             0==strcasecomp(host, HTHostName())))
#else
             0==strcmp(host, HTHostName())))
#endif /* VMS */
        {
	    FREE(host);
	    FREE(access);
	    return YES;
	}
    }

    FREE(host);
    FREE(access);
    return NO;
}

/*
 * Utility for checking URLs with a host field.
 * Return YES only if we're certain it's the local host. - FM
 */
PUBLIC BOOLEAN LYisLocalHost ARGS1(char *,filename)
{
    char *host=NULL;
    char *cp;

    if (!filename || !(host = HTParse(filename, "", PARSE_HOST)))
        return NO;

    if (!*host) {
        FREE(host);
	return NO;
    }

    if ((cp=strchr(host, ':')) != NULL)
        *cp = '\0';

#ifdef VMS
    if ((0==strcasecomp(host, "localhost") ||
	 0==strcasecomp(host, LYHostName) ||
         0==strcasecomp(host, HTHostName()))) {
#else
    if ((0==strcmp(host, "localhost") ||
	 0==strcmp(host, LYHostName) ||
         0==strcmp(host, HTHostName()))) {
#endif /* VMS */
	    FREE(host);
	    return YES;
    }

    FREE(host);
    return NO;
}

/* 
 * Utility for freeing the list of local host aliases. - FM
 */
PUBLIC void LYLocalhostAliases_free NOARGS
{
    char *alias;
    HTList *cur = localhost_aliases;

    if (!cur)
        return;

    while (NULL != (alias = (char *)HTList_nextObject(cur))) {
	FREE(alias);
    }
    HTList_delete(localhost_aliases);
    localhost_aliases = NULL;
    return;
}

/* 
 * Utility for listing hosts to be treated as local aliases. - FM
 */
PUBLIC void LYAddLocalhostAlias ARGS1(char *, alias)
{
    char *LocalAlias;

    if (!(alias && *alias))
        return;

    if (!localhost_aliases) {
        localhost_aliases = HTList_new();
	atexit(LYLocalhostAliases_free);
    }

    if ((LocalAlias = (char *)calloc(1, (strlen(alias) + 1))) == NULL)
    	outofmem(__FILE__, "HTAddLocalhosAlias");
    strcpy(LocalAlias, alias);
    HTList_addObject(localhost_aliases, LocalAlias);

    return;
}

/*
 * Utility for checking URLs with a host field.
 * Return YES only if we've listed the host as a local alias. - FM
 */
PUBLIC BOOLEAN LYisLocalAlias ARGS1(char *,filename)
{
    char *host=NULL;
    char *alias;
    char *cp;
    HTList *cur = localhost_aliases;

    if (!cur || !filename || !(host = HTParse(filename, "", PARSE_HOST)))
        return NO;

    if (!(*host)) {
        FREE(host);
	return NO;
    }

    if ((cp=strchr(host, ':')) != NULL)
        *cp = '\0';

    while (NULL != (alias = (char *)HTList_nextObject(cur))) {
#ifdef VMS
        if (0==strcasecomp(host, alias)) {
#else
        if (0==strcmp(host, alias)) {
#endif /* VMS */
	    FREE(host);
	    return YES;
	}
    }

    FREE(host);
    return NO;
}

/*
 * This function checks for a URL with an unknown scheme,
 * but for which proxying has been set up. - FM
 */
PUBLIC int LYCheckForProxyURL ARGS1(char *, filename) {
    char *cp=filename;
    char *cp1;
    char *cp2 = NULL;

    /* don't crash on an empty argument */
    if (cp == NULL || *cp == '\0')
        return(0);

    /* kill beginning spaces */
    while (isspace(*cp))
        cp++;

    /*
     * Check for a colon, and if present,
     * see if we have proxying set up.
     */
    if ((cp1=strchr((cp+1), ':')) != NULL) {
	*cp1 = '\0';
	StrAllocCopy(cp2, cp);
	*cp1 = ':';
	StrAllocCat(cp2, "_proxy");
	if (getenv(cp2) != NULL) {
	    FREE(cp2);
	    return(PROXY_URL_TYPE);
	}
	FREE(cp2);
    }

    return(0);
}

/* must recognize a URL and return the type.
 */
PUBLIC int is_url ARGS1(char *,filename)
{
    char *cp=filename;
    char *cp2;
    int i;

    /* don't crash on an empty argument */
    if (cp == NULL || *cp == '\0')
        return(0);

    /* kill beginning spaces */
    while (isspace(*cp))
        cp++;

    if (!strncasecomp(cp, "news:", 5)) {
        if (strncmp(cp, "news", 4)) {
	    for (i = 0; i < 4; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(NEWS_URL_TYPE);

    } else if (!strncasecomp(cp, "snews:", 6)) {
        if (strncmp(cp, "snews", 5)) {
	    for (i = 0; i < 5; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(SNEWS_URL_TYPE);

    } else if (!strncasecomp(cp, "mailto:", 7)) {
        if (strncmp(cp, "mailto", 6)) {
	    for (i = 0; i < 6; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(MAILTO_URL_TYPE);

	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXPRINT:", 10)) {
        if (strncmp(cp, "LYNXPRINT", 9)) {
	    for (i = 0; i < 9; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXPRINT_URL_TYPE);


	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXDOWNLOAD:", 13)) {
        if (strncmp(cp, "LYDOWNLOAD", 12)) {
	    for (i = 0; i < 12; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXDOWNLOAD_URL_TYPE);

#ifdef DIRED_SUPPORT
	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXDIRED:", 10)) {
        if (strncmp(cp, "LYNXDIRED", 9)) {
	    for (i = 0; i < 9; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXDIRED_URL_TYPE);
#endif /* DIRED_SUPPORT */

	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXHIST:", 9)) {
        if (strncmp(cp, "LYNXHIST", 8)) {
	    for (i = 0; i < 8; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXHIST_URL_TYPE);

	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXKEYMAP:", 11)) {
        if (strncmp(cp, "LYNXKEYMAP", 10)) {
	    for (i = 0; i < 10; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXKEYMAP_URL_TYPE);

	/* special internal lynx type */
    } else if (!strncasecomp(cp, "LYNXIMGMAP:", 11)) {
        if (strncmp(cp, "LYNXIMGMAP", 10)) {
	    for (i = 0; i < 10; i++)
	        cp[i] = TOUPPER(cp[i]);
	}
	return(LYNXIMGMAP_URL_TYPE);

	/* special internal lynx type to handle exec links :(((((((*/
    } else if (!strncasecomp(cp, "lynxexec:", 9)) {
        if (strncmp(cp, "lynxexec", 8)) {
	    for (i = 0; i < 8; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(LYNXEXEC_URL_TYPE);

	/* special internal lynx type to handle program links :(((((((*/
    } else if (!strncasecomp(cp, "lynxprog:", 9)) {
        if (strncmp(cp, "lynxprog", 8)) {
	    for (i = 0; i < 8; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(LYNXPROG_URL_TYPE);

	/* special internal lynx type to handle cgi scripts */
    } else if (!strncasecomp(cp, "lynxcgi:", 8)) {
        if (strncmp(cp, "lynxcgi", 7)) {
	    for (i = 0; i < 7; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(LYNXCGI_URL_TYPE);

	/* special internal lynx type to handle news posts */
    } else if (!strncasecomp(cp, "newspost:", 9)) {
        if (strncmp(cp, "newspost", 8)) {
	    for (i = 0; i < 8; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(NEWSPOST_URL_TYPE);

	/* special internal lynx type to handle news replies (followups) */
    } else if (!strncasecomp(cp, "newsreply:", 10)) {
        if (strncmp(cp, "newsreply", 9)) {
	    for (i = 0; i < 9; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(NEWSREPLY_URL_TYPE);

    } else if (!strstr((cp+3), ":/")) {  
	/*
	 * If it doesn't contain ":/", and it's not one of the
	 * the above, it can't be a url with a scheme we know,
	 * so check if it's an unknown scheme for which proxying
	 * has been set up. - FM
	 */
	return(LYCheckForProxyURL(filename));

    } else if (!strncasecomp(cp, "https:", 6)) {
        if (strncmp(cp, "https", 5)) {
	    for (i = 0; i < 5; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(HTTPS_URL_TYPE);

    } else if (!strncasecomp(cp, "http:", 5)) {
        if (strncmp(cp, "http", 4)) {
	    for (i = 0; i < 4; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(HTTP_URL_TYPE);

    } else if (!strncasecomp(cp, "file:", 5)) {
        if (strncmp(cp, "file", 4)) {
	    for (i = 0; i < 4; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
        /*
	 *  We won't expend the overhead here of
	 *  determining whether it's really an
	 *  ftp URL unless we are restricting
	 *  ftp access, in which case getfile()
	 *  needs to know in order to issue an
	 *  appropriate statusline message and
	 *  and return NULLFILE.
	 */
        if ((ftp_ok) || LYisLocalFile(cp))
	    return(FILE_URL_TYPE);
	else
	    return(FTP_URL_TYPE);

    } else if (!strncasecomp(cp, "gopher:", 7)) {
        if (strncmp(cp, "gopher", 6)) {
	    for (i = 0; i < 6; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	if ((cp2 = strchr(cp+11,'/')) != NULL) {

	    if (TOUPPER(*(cp2+1)) == 'H' || *(cp2+1) == 'w')
		/* if this is a gopher html type */
	        return(HTML_GOPHER_URL_TYPE);
	    else if (*(cp2+1) == 'T' || *(cp2+1) == '8')
	        return(TELNET_GOPHER_URL_TYPE);
	    else if (*(cp2+1) == '7')
	        return(INDEX_GOPHER_URL_TYPE);
	    else
	        return(GOPHER_URL_TYPE);
	} else {
	    return(GOPHER_URL_TYPE);
	}

    } else if (!strncasecomp(cp, "ftp:", 4)) {
        if (strncmp(cp, "ftp", 3)) {
	    for (i = 0; i < 3; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(FTP_URL_TYPE);

    } else if (!strncasecomp(cp, "wais:", 5)) {
        if (strncmp(cp, "wais", 4)) {
	    for (i = 0; i < 4; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(WAIS_URL_TYPE);

    } else if (!strncasecomp(cp, "telnet:", 7)) {
        if (strncmp(cp, "telnet", 6)) {
	    for (i = 0; i < 6; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(TELNET_URL_TYPE);

    } else if (!strncasecomp(cp, "tn3270:", 7)) {
        if (strncmp(cp, "tn", 2)) {
	    for (i = 0; i < 2; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(TN3270_URL_TYPE);

    } else if (!strncasecomp(cp, "rlogin:", 7)) {
        if (strncmp(cp, "rlogin", 6)) {
	    for (i = 0; i < 6; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(RLOGIN_URL_TYPE);

    } else if (!strncasecomp(cp, "nntp:", 5)) {
        if (strncmp(cp, "nntp", 4)) {
	    for (i = 0; i < 4; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(NNTP_URL_TYPE);

    } else if (!strncasecomp(cp, "cso:", 4)) {
        if (strncmp(cp, "cso", 3)) {
	    for (i = 0; i < 3; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(CSO_URL_TYPE);

    } else if (!strncasecomp(cp, "finger:", 7)) {
        if (strncmp(cp, "finger", 6)) {
	    for (i = 0; i < 6; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(FINGER_URL_TYPE);

    } else if (!strncasecomp(cp, "afs:", 4)) {
        if (strncmp(cp, "afs", 3)) {
	    for (i = 0; i < 3; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(AFS_URL_TYPE);

    } else if (!strncasecomp(cp, "prospero:", 9)) {
        if (strncmp(cp, "prospero", 8)) {
	    for (i = 0; i < 8; i++)
	        cp[i] = TOLOWER(cp[i]);
	}
	return(PROSPERO_URL_TYPE);

    } else {
        /*
	 * Check if it's an unknown scheme for which
	 * proxying has been set up. - FM
	 */
	return(LYCheckForProxyURL(filename));
    }
}

/*
 * remove backslashes from any string
 */

PUBLIC void remove_backslashes ARGS1(char *,buf)
{
    char *cp;

    for (cp = buf; *cp != '\0' ; cp++) {

	if (*cp != '\\') { /* don't print slashes */
	    *buf = *cp;
	    buf++;
	} else if (*cp == '\\' &&      /* print one slash if there */
		   *(cp+1) == '\\') {  /* are two in a row         */
	    *buf = *cp;
	    buf++;
	}
    }
    *buf = '\0';
    return;
}

/* Quote the path to make it safe for shell command processing. */
/* We use a simple technique which involves quoting the entire
   string using single quotes, escaping the real single quotes
   with double quotes. This may be gross but it seems to work. */

PUBLIC char * quote_pathname ARGS1(char *, pathname)
{
    int i, n = 0;
    char * result;

    for (i=0; i < strlen(pathname); ++i)
        if (pathname[i] == '\'') ++n;

    result = (char *)malloc(strlen(pathname) + 5*n + 3);
    if (result == NULL)
        outofmem(__FILE__, "quote_pathname");

    result[0] = '\'';
    for (i = 0, n = 1; i < strlen(pathname); i++)
        if (pathname[i] == '\'') {
            result[n++] = '\'';
            result[n++] = '"';
            result[n++] = '\'';
            result[n++] = '"';
            result[n++] = '\'';
        } else {
            result[n++] = pathname[i];
	}
    result[n++] = '\'';
    result[n] = '\0';
    return result;
}

/*
 * checks to see if the current process is attached via a terminal in the
 * local domain
 *
 */
#ifdef VMS
#ifndef NO_UTMP
#define NO_UTMP
#endif /* NO_UTMP */
#endif /* VMS */

PUBLIC BOOLEAN inlocaldomain NOARGS
{
#ifdef NO_UTMP
    return(TRUE);
#else
    int n;
    FILE *fp;
    struct utmp me;
    char *cp, *mytty=NULL;
    char *ttyname();

    if ((cp=ttyname(0)))
	mytty = strrchr(cp, '/');

    if (mytty && (fp=fopen(UTMP_FILE, "r")) != NULL) {
	    mytty++;
	    do {
		n = fread((char *) &me, sizeof(struct utmp), 1, fp);
	    } while (n>0 && !STREQ(me.ut_line,mytty));
	    (void) fclose(fp);

	    if (n > 0 &&
	        strlen(me.ut_host) > strlen(LOCAL_DOMAIN) &&
	        STREQ(LOCAL_DOMAIN,
		  me.ut_host+strlen(me.ut_host)-strlen(LOCAL_DOMAIN)) )
		return(TRUE);
#ifdef LINUX
/* Linux fix to check for local user. J.Cullen 11Jul94          */
		if ((n > 0) && (strlen(me.ut_host) == 0))
			return(TRUE);
#endif /* LINUX */

    } else {
	if (TRACE)
	   fprintf(stderr,"Could not get ttyname or open UTMP file");
    }

    return(FALSE);
#endif /* NO_UTMP */
}

/**************
** This bit of code catches window size change signals
**/

#if defined(VMS) || defined(SNAKE)
#define NO_SIZECHANGE
#endif /* VMS || SNAKE */

#if !defined(VMS) && !defined(ISC)
#include <sys/ioctl.h>
#endif /* !VMS && !ISC */

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#else
#ifdef HAVE_TERMIO_H
#include <termio.h>
#endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */

PUBLIC void size_change ARGS1(int,sig)
{
#ifndef NO_SIZECHANGE
#ifdef TIOCGSIZE
    struct ttysize win;
#else
#ifdef TIOCGWINSZ
    struct winsize win;
#endif /* TIOCGWINSZ */
#endif /* TIOCGSIZE */

#ifdef TIOCGSIZE
    if (ioctl(0, TIOCGSIZE, &win) == 0) {
        if (win.ts_lines != 0) {
#ifdef USE_SLANG
	    LYlines = win.ts_lines;
#else
	    LYlines = win.ts_lines - 1;
#endif /* USE_SLANG */
	}
	if (win.ts_cols != 0) {
	    LYcols = win.ts_cols;
	}
    }
#else
#ifdef TIOCGWINSZ
    if (ioctl(0, TIOCGWINSZ, &win) == 0) {
        if (win.ws_row != 0) {
#ifdef USE_SLANG
	    LYlines = win.ws_row;
#else
	    LYlines = win.ws_row - 1;
#endif /* USE_SLANG */
	}
	if (win.ws_col != 0) {
	    LYcols = win.ws_col;
	}
    }
#endif /* TIOCGWINSZ */
#endif /* TIOCGSIZE */
#endif /* !NO_SIZECHANGE */

    if (LYlines <= 0)
        LYlines = 24;
    if (LYcols <= 0)
        LYcols = 80;

#ifdef USE_SLANG
    SLtt_Screen_Rows = LYlines; 
    SLtt_Screen_Cols = LYcols;
    if (sig == 0)
        return;	/* called from start_curses */
#endif /* USE_SLANG */

    recent_sizechange = TRUE; 
#ifdef SIGWINCH
    (void)signal (SIGWINCH, size_change);
#endif /* SIGWINCH */

    return;
}

/*
 *  CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993
 *	Upgraded for use with Lynx2.2 - FM 17-Jan-1994
 */

PUBLIC void change_sug_filename ARGS1(char *,fname)
{
     char *temp, *cp, *cp1, *end;
     int len;
#ifdef VMS
     char *dot;
     int j,k;
#endif /* VMS */

     /*** establish the current end of fname ***/
     end = fname + strlen(fname);

     /*** unescape fname ***/
     HTUnEscape(fname);

     /*** rename any temporary files ***/
     temp = (char *)calloc(1, (strlen(lynx_temp_space) + 60));
     if (*lynx_temp_space == '/')
         sprintf(temp, "file://localhost%sL%d", lynx_temp_space, getpid());
     else
         sprintf(temp, "file://localhost/%sL%d", lynx_temp_space, getpid());
     len = strlen(temp);
     if (0==strncmp(fname, temp, len)) {
         cp = strrchr(fname, '.');
	 if (strlen(cp) > (len - 4))
	     cp = NULL;
	 strcpy(temp, (cp ? cp : ""));
	 strcpy(fname, "temp");
	 strcat(fname, temp);
     }
     FREE(temp);

     /*** remove everything up the the last_slash if there is one ***/
     if ((cp = strrchr(fname,'/')) != NULL && strlen(cp) > 1) {
	 cp1=fname;
	 cp++; /* go past the slash */
	 for (; *cp != '\0'; cp++, cp1++)
	    *cp1 = *cp;

	 *cp1 = '\0'; /* terminate */
     }

     /*** Trim off date-size suffix, if present ***/
     if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
         (cp > fname) && *(--cp) == ' ')
	  while (*cp == ' ')
	       *(cp--) = '\0';

     /*** Trim off VMS device and/or directory specs, if present ***/
     if ((cp=strchr(fname,'[')) != NULL &&
         (cp1=strrchr(cp,']')) != NULL && strlen(cp1) > 1) {
	  cp1++;
	  for (cp=fname; *cp1 != '\0'; cp1++)
	       *(cp++) = *cp1;
	  *cp = '\0';
     }

#ifdef VMS
     /*** Replace illegal or problem characters ***/
     dot = fname + strlen(fname);
     for (cp = fname; cp < dot; cp++) {

	  /** Replace with underscores **/
	  if (*cp == ' ' || *cp == '/' || *cp == ':' ||
	      *cp == '[' || *cp == ']' || *cp == '&')
	       *cp = '_';

	  /** Replace with dashes **/
	  else if (*cp == '!' || *cp == '?' || *cp == '\'' || 
	           *cp == ',' || *cp == ':' || *cp == '\"' ||
	           *cp == '+' || *cp == '@' || *cp == '\\' ||
	           *cp == '(' || *cp == ')' || *cp == '=' ||
	           *cp == '<' || *cp == '>' || *cp == '#' ||
	           *cp == '%' || *cp == '*' || *cp == '`' ||
	           *cp == '~' || *cp == '^' || *cp == '|' ||
		   *cp <  ' ' || ((unsigned char)*cp) > 126)
	       *cp = '-';
     }

     /** Collapse any serial underscores **/
     cp = fname + 1;
     j = 0;
     while (cp < dot) {
	  if (fname[j] == '_' && *cp == '_')
	       cp++;
	  else
	       fname[++j] = *cp++;
     }
     fname[++j] = '\0';

     /** Collapse any serial dashes **/
     dot = fname + (strlen(fname));
     cp = fname + 1;
     j = 0;
     while (cp < dot) {
          if (fname[j] == '-' && *cp == '-')
	       cp++;
	  else
	       fname[++j] = *cp++;
     }
     fname[++j] = '\0';

     /** Trim any trailing or leading **/
     /** underscrores or dashes       **/
     cp = fname + (strlen(fname)) - 1;
     while (*cp == '_' || *cp == '-')
          *cp-- = '\0';
     if (fname[0] == '_' || fname[0] == '-') {
          dot = fname + (strlen(fname));
          cp = fname;
          while ((*cp == '_' || *cp == '-') && cp < dot)
	       cp++;
	  j = 0;
          while (cp < dot)
	       fname[j++] = *cp++;
	  fname[j] = '\0';
     }

     /** Replace all but the last period with _'s, or second **/
     /** to last if last is followed by a terminal Z or z,   **/
     /** e.g., convert foo.tar.Z to                          **/
     /**               foo.tar_Z                             **/
     j = strlen(fname) - 1;
     if ((dot = strrchr(fname, '.')) != NULL) {
	  if (((fname[j] == 'Z' || fname[j] == 'z') && fname[j-1] == '.') &&
	      (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
	       *dot = '_';
	       dot = strrchr(fname, '.');
	  }
	  cp = fname;
	  while ((cp = strchr(cp, '.')) != NULL && cp < dot)
	       *cp = '_';

          /** But if the root is > 39 characters, move **/
          /** the period appropriately to the left     **/
	  while (dot - fname > 39) {
	       *dot = '\0';
	       if ((cp = strrchr(fname, '_')) != NULL) {
		    *cp  = '.';
		    *dot = '_';
	       } 
	       else if ((cp = strrchr(fname, '-')) != NULL) {
		    *cp  = '.';
		    *dot = '_';
	       }
	       else {
		    *dot = '_';
		    j = strlen(fname);
		    fname[j+1] = '\0';
		    while (j > 39)
			 fname[j--] = fname[j];
		    fname[j] = '.';
	       }
               dot = strrchr(fname, '.');
	  }

          /** Make sure the extension is < 40 characters **/
          if ((fname + strlen(fname) - dot) > 39)
	       *(dot+40) = '\0';

	  /** Trim trailing dashes or underscores **/
	  j = strlen(fname) - 1;
	  while (fname[j] == '_' || fname[j] == '-')
	       fname[j--] = '\0';
     }
     else {
	  /** No period, so put one on the end, or after   **/
	  /** the 39th character, trimming trailing dashes **/
	  /** or underscrores                              **/
	  if (strlen(fname) > 39)
	       fname[39] = '\0';
	  j = strlen(fname) - 1;
	  while ((fname[j] == '_') || (fname[j] == '-'))
	       j--;
	  fname[++j] = '.';
	  fname[++j] = '\0';
     }

#else /* Not VMS (UNIX): */

     /*** Replace problem characters ***/
     for (cp = fname; *cp != '\0'; cp++) {
	  switch (*cp) {
	  case '\'':
	  case '\"':
	  case '/':
	  case ' ':
	       *cp = '-';
	  }
     }
#endif /* VMS (UNIX) */

     /** Make sure the rest of the original string in nulled. **/
     cp = fname + strlen(fname);
     while (cp < end)
          *cp++ = '\0';

    return;
}

/*
 *	To create standard temporary file names
 */
PUBLIC void tempname ARGS2(char *,namebuffer, int,action)
{
	static int counter = 0;


	if (action == REMOVE_FILES) { /* REMOVE ALL FILES */ 
	    for (; counter > 0; counter--) {
#ifdef NeXT
	        sprintf(namebuffer, "%sL%d%u.txt", lynx_temp_space,
						   getpid(), counter-1);
		remove(namebuffer);
#endif /* NeXT */
	        sprintf(namebuffer, "%sL%d%u.html", lynx_temp_space,
						    getpid(), counter-1);
#ifdef SCO
		unlink(namebuffer);
#else
		remove(namebuffer);
#endif /* SCO */
	    }
	} else /* add a file */ {
	/*
	 * 	Create name
	 */
	    sprintf(namebuffer, "%sL%d%u.html", lynx_temp_space,
	    					getpid(), counter++);
	}
	return;
}

/* convert 4, 6, 2, 8 to left, right, down, up, etc. */
PUBLIC int number2arrows ARGS1(int,number)
{
      switch(number) {
            case '1':
                number=END;
                  break;
            case '2':
                number=DNARROW;
                  break;
            case '3':
                number=PGDOWN;
                  break;
            case '4':
                number=LTARROW;
                  break;
	    case '5':
		number=DO_NOTHING;
		break;
            case '6':
                number=RTARROW;
                  break;
            case '7':
                number=HOME;
                  break;
 	    case '8':
                number=UPARROW;
                  break;
            case '9':
                number=PGUP;
                  break;
      }

      return(number);
}

/*
 * parse_restrictions takes a string of comma-separated restrictions
 * and sets the corresponding flags to restrict the facilities available
 */
PRIVATE char *restrict_name[] = {
       "inside_telnet" ,
       "outside_telnet",
       "telnet_port"   ,
       "inside_ftp"    ,
       "outside_ftp"   ,
       "inside_rlogin" ,
       "outside_rlogin",
       "suspend"       ,
       "editor"        ,
       "shell"         ,
       "bookmark"      ,
       "option_save"   ,
       "print"         ,
       "download"      ,
       "disk_save"     ,
       "exec"          ,
       "lynxcgi"       ,
       "exec_frozen"   ,
       "bookmark_exec" ,
       "goto"          ,
       "jump"          ,
       "file_url"      ,
       "news_post"     ,
       "inside_news"   ,
       "outside_news"  ,
       "mail"          ,
       "dotfiles"      ,
#ifdef DIRED_SUPPORT
       "dired_support" ,
#ifdef OK_PERMIT
       "change_exec_perms",
#endif /* OK_PERMIT */
#endif /* DIRED_SUPPORT */
       (char *) 0     };

	/* restrict_name and restrict_flag structure order
	 * must be maintained exactly!
	 */

PRIVATE BOOLEAN *restrict_flag[] = {
       &no_inside_telnet,
       &no_outside_telnet,
       &no_telnet_port,
       &no_inside_ftp,
       &no_outside_ftp,
       &no_inside_rlogin,
       &no_outside_rlogin,
       &no_suspend  ,
       &no_editor   ,
       &no_shell    ,
       &no_bookmark ,
       &no_option_save,
       &no_print    ,
       &no_download ,
       &no_disk_save,
       &no_exec     ,
       &no_lynxcgi  ,
       &exec_frozen ,
       &no_bookmark_exec,
       &no_goto     ,
       &no_jump     ,
       &no_file_url ,
       &no_newspost ,
       &no_inside_news,
       &no_outside_news,
       &no_mail     ,
       &no_dotfiles ,
#ifdef DIRED_SUPPORT
       &no_dired_support,
#ifdef OK_PERMIT
       &no_change_exec_perms,
#endif /* OK_PERMIT */
#endif /* DIRED_SUPPORT */
       (BOOLEAN *) 0  };

PUBLIC void parse_restrictions ARGS1(char *,s)
{
      char *p;
      char *word;
      int i;

      if (STREQ("all", s)) {
	   /* set all restrictions */
          for (i=0; restrict_flag[i]; i++) 
              *restrict_flag[i] = TRUE;
          return;
      }

      if (STREQ("default", s)) {
	   /* set all restrictions */
          for (i=0; restrict_flag[i]; i++) 
              *restrict_flag[i] = TRUE;

	     /* reset these to defaults */
             no_inside_telnet = !(CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET);
            no_outside_telnet = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET);
	       no_inside_news = !(CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS);
	      no_outside_news = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS);
                no_inside_ftp = !(CAN_ANONYMOUS_INSIDE_DOMAIN_FTP);
               no_outside_ftp = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP);
             no_inside_rlogin = !(CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN);
            no_outside_rlogin = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN);
		      no_goto = !(CAN_ANONYMOUS_GOTO);
	       no_telnet_port = !(CAN_ANONYMOUS_GOTO_TELNET_PORT);
		      no_jump = !(CAN_ANONYMOUS_JUMP);
                      no_mail = !(CAN_ANONYMOUS_MAIL);
                     no_print = !(CAN_ANONYMOUS_PRINT);
#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
		      no_exec = LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS;
#endif /* EXEC_LINKS || EXEC_SCRIPTS */
          return;
      }

      p = s;
      while (*p) {
          while (isspace(*p))
              p++;
          if (*p == '\0')
              break;
          word = p;
          while (*p != ',' && *p != '\0')
              p++;
          if (*p)
              *p++ = '\0';

	  for (i=0; restrict_name[i]; i++) 
             if (STREQ(word, restrict_name[i])) {
                 *restrict_flag[i] = TRUE;
		 break;
	     }
      }
      return;
}

#ifdef VMS
#include <jpidef.h>
#include <maildef.h>
#include <starlet.h>

typedef struct _VMSMailItemList
{
  short buffer_length;
  short item_code;
  void *buffer_address;
  long *return_length_address;
} VMSMailItemList;

PUBLIC int LYCheckMail NOARGS
{
    static BOOL firsttime = TRUE, failure = FALSE;
    static char user[13], dir[252];
    static long userlen = 0, dirlen;
    static time_t lastcheck = 0;
    time_t now;
    static short new, lastcount;
    long ucontext = 0, status;
    short flags = MAIL$M_NEWMSG;
    VMSMailItemList
      null_list[] = {{0,0,0,0}},
      jpi_list[]  = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen},
		     {0,0,0,0}},
      uilist[]    = {{0,MAIL$_USER_USERNAME,0,0},
    		     {0,0,0,0}},
      uolist[]    = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0},
                     {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen},
                     {0,0,0,0}};
    extern long mail$user_begin();
    extern long mail$user_get_info(); 
    extern long mail$user_end();

    if (failure)
        return 0;

    if (firsttime) {
        firsttime = FALSE;
        /* Get the username. */
        status = sys$getjpiw(0,0,0,jpi_list,0,0,0);
        if (!(status & 1)) {
            failure = TRUE;
            return 0;
        }
        user[userlen] = '\0';
        while (user[0] &&
	       isspace(user[--userlen])) /* suck up trailing spaces */
            user[userlen] = '\0';
    }

    /* Minimum report interval is 60 sec. */
    time(&now);
    if (now - lastcheck < 60)
	return 0;
    lastcheck = now;

    /* Get the current newmail count. */
    status = mail$user_begin(&ucontext,null_list,null_list);
    if (!(status & 1)) {
        failure = TRUE;
        return 0;
    }
    uilist[0].buffer_length = strlen(user);
    uilist[0].buffer_address = user;
    status = mail$user_get_info(&ucontext,uilist,uolist);
    if (!(status & 1)) {
        failure = TRUE;
        return 0;
    }

    /* Should we report anything to the user? */
    if (new > 0) {
	if (lastcount == 0)
	    /* Have newmail at startup of Lynx. */
	    _statusline("*** You have unread mail. ***");
	else if (new > lastcount)
	    /* Have additional mail since last report. */
	    _statusline("*** You have new mail. ***");
        lastcount = new;
	return 1;
    }
    lastcount = new;

    /* Clear the context */
    mail$user_end((long *)&ucontext,null_list,null_list);
    return 0;
}
#else
PUBLIC int LYCheckMail NOARGS
{
    static BOOL firsttime = TRUE;
    static char *mf;
    static time_t lastcheck;
    static long lastsize;
    time_t now;
    struct stat st;

    if (firsttime) {
	mf = getenv("MAIL");
	firsttime = FALSE;
    }

    if (mf == NULL)
	return 0;

    time(&now);
    if (now - lastcheck < 60)
	return 0;
    lastcheck = now;

    if (stat(mf,&st) < 0) {
	mf = NULL;
	return 0;
    }

    if (st.st_size > 0) {
	if (st.st_mtime > st.st_atime ||
	    (lastsize && st.st_size > lastsize))
	    _statusline("*** You have new mail. ***");
	else if (lastsize == 0)
	    _statusline("*** You have mail. ***");
        lastsize = st.st_size;
	return 1;
    }
    lastsize = st.st_size;
    return 0;
}
#endif /* VMS */

/*
 * Rewrite and reallocate a previously allocated string
 * as a file URL if the string resolves to a file or
 * directory on the local system, otherwise as an
 * http URL. - FM
 */
PUBLIC void LYConvertToURL ARGS1(char **, AllocatedString)
{
    char *old_string = *AllocatedString;

    if (!old_string || *old_string == '\0')
        return;

    *AllocatedString = NULL;  /* so StrAllocCopy doesn't free it */
    StrAllocCopy(*AllocatedString,"file://localhost");
    if (*old_string != '/') {
#ifdef VMS
	/*
	 *  Not a SHELL pathspec.  Get the full VMS spec and convert it.
	 */
	char *cp, *cur_dir=NULL, *tmp_dir=NULL;
	static char url_file[256], file_name[256];
	unsigned long context = 0;
	$DESCRIPTOR(url_file_dsc, url_file);
	$DESCRIPTOR(file_name_dsc, file_name);
	if (*old_string == '~') {
	    /*
	     *  On VMS, we'll accept '~' on the command line as
	     *  $HOME, and assume the rest of the path, if any,
	     *  has SHELL syntax.
	     */
	    strcpy(url_file, HTVMS_wwwName(getenv("HOME")));
	    if (old_string[1])
		strcat(url_file, (old_string+1));
	    goto have_VMS_fileURL;
	} else {
	    strcpy(url_file, old_string);
	}
	url_file_dsc.dsc$w_length = (short) strlen(url_file);
	if (1&lib$find_file(&url_file_dsc, &file_name_dsc, &context,
    			    0, 0, 0, 0)) {
	    /*
	     *  We found the file.  Convert to a URL pathspec.
	     */
	    if ((cp=strchr(file_name, ';')) != NULL)
		*cp = '\0';
	    for (cp = file_name; *cp; cp++)
		*cp = TOLOWER(*cp);
	    strcpy(url_file, HTVMS_wwwName(file_name));
	    if ((cp=strchr(old_string, ';')) != NULL)
		strcat(url_file, cp);
	    if (TRACE)
		fprintf(stderr, "Converted '%s' to '%s'\n",
		    		old_string, url_file);
	} else if ((cur_dir=getcwd(cur_dir, 256, 0)) &&
		   0==chdir(old_string)) {
	    /*
	     * Probably a directory.  Try converting that.
	     */
	    tmp_dir = getcwd(tmp_dir, 256, 0);
	    if (tmp_dir) {
		/*
		 * Yup, we got it!
		 */
		strcpy(url_file, tmp_dir);
		FREE(tmp_dir);
		if (TRACE)
		fprintf(stderr, "Converted '%s' to '%s'\n",
				old_string, url_file);
	    } else {
		/*
		 *  Nope.  Assume it's an http URL with
		 *  the "http://" defaulted, if we can't
		 *  rule out a bad VMS path.
		 */
		if (strchr(old_string, '[') ||
		    ((cp=strchr(old_string, ':')) &&
		     !isdigit(*(cp+1)))) {
		    /*
		     *  Probably a bad VMS path (but can't be
		     *  sure).  Use original pathspec for the
		     *  error message that will result.
		     */
		    strcpy(url_file, "/");
		    strcat(url_file, old_string);
		    if (TRACE)
			fprintf(stderr,
			    "Can't find '%s'  Will assume it's a bad path.\n",
			        old_string);
		} else {
		    /*
		     *  Assume an http URL is wanted.
		     */
		    StrAllocCopy(*AllocatedString, "http://");
		    strcpy(url_file, old_string);
		    if (TRACE)
			fprintf(stderr,
			        "Can't find '%s'  Will assume it's a host.\n",
			        old_string);
		}
	    }
	} else {
	    /*
	     *  Nothing found.  Assume it's an http URL
	     *  with the "http://" defaulted, if we can't
	     *  rule out a bad VMS path.
	     */
	    if (strchr(old_string, '[') ||
		((cp=strchr(old_string, ':')) &&
		 !isdigit(*(cp+1)))) {
		/*
		 *  Probably a bad VMS path (but can't be
		 *  sure).  Use original pathspec for the
		 *  error message that will result.
		 */
		strcpy(url_file, "/");
		strcat(url_file, old_string);
		if (TRACE)
		    fprintf(stderr,
			    "Can't find '%s'  Will assume it's a bad path.\n",
			    old_string);
	    } else {
		/*
		 *  Assume an http URL is wanted.
		 */
		StrAllocCopy(*AllocatedString, "http://");
		strcpy(url_file, old_string);
		if (TRACE)
		    fprintf(stderr,
			    "Can't find '%s'  Will assume it's a host.\n",
			    old_string);
	    }
	}
	lib$find_file_end(&context);
	if (cur_dir) {
	    chdir(cur_dir);
	    FREE(cur_dir);
	}
have_VMS_fileURL:
	StrAllocCat(*AllocatedString, url_file);
	if (TRACE)
	    fprintf(stderr, "Converted '%s' to '%s'\n",
	    		    old_string, *AllocatedString);
#else /* Unix: */
	if (*old_string == '~') {
	    /*
	     * On Unix, covert '~' to $HOME.
	     */
	    StrAllocCat(*AllocatedString, getenv("HOME"));
	    if (old_string[1])
		StrAllocCat(*AllocatedString, (old_string+1));
	    if (TRACE)
		fprintf(stderr, "Converted '%s' to '%s'\n",
		    		old_string, *AllocatedString);
	} else {
	    /*
	     *  Create a full path to the current default directory.
	     */
	    char curdir[DIRNAMESIZE];
	    char *temp=NULL;
	    struct stat st;
	    FILE *fptemp=NULL;
#ifdef NO_GETCWD
	    getwd (curdir);
#else
	    getcwd (curdir, DIRNAMESIZE);
#endif /* NO_GETCWD */
	    StrAllocCopy(temp, curdir);
	    StrAllocCat(temp, "/");
	    StrAllocCat(temp, old_string);
	    if (TRACE)
		fprintf(stderr, "Converted '%s' to '%s'\n", old_string, temp);
	    if ((stat(temp, &st) < 0) &&
		!(fptemp=fopen(temp, "r"))) {
		/*
		 *  It's not an accessible subdirectory or file on the
		 *  local system, so assume it's an http request with
		 *  the "http://" defaulted.
		 */
		StrAllocCopy(*AllocatedString, "http://");
		StrAllocCat(*AllocatedString, old_string);
		if (TRACE) {
		    fprintf(stderr, "Can't stat() or fopen() '%s'\n", temp);
		    fprintf(stderr, "Converted '%s' to '%s'\n",
				    old_string, *AllocatedString);
		}
	    } else {
		/*
		 *  It is a subdirectory or file on the local system.
		 */
		StrAllocCat(*AllocatedString, temp);
		if (TRACE)
		    fprintf(stderr, "Converted '%s' to '%s'\n",
				    old_string, *AllocatedString);
	    }
	    FREE(temp);
	    if (fptemp)
		fclose(fptemp);
	}
#endif /* VMS */
    } else { 
	/*
	 *  Path begins with a slash.  Use it as is.
	 */
	StrAllocCat(*AllocatedString, old_string);
	if (TRACE)
	    fprintf(stderr, "Converted '%s' to '%s'\n",
			    old_string, *AllocatedString);
    }
    if (old_string)
	FREE(old_string);
    if (TRACE)
	/* Pause so we can read the messages before invoking curses */
	sleep(AlertSecs);
}

#ifdef VMS
/*
 *  Define_VMSLogical -- Fote Macrides 04-Apr-1995
 *	Define VMS logicals in the process table.
 */
PUBLIC void Define_VMSLogical ARGS2(char *, LogicalName, char *, LogicalValue)
{
    $DESCRIPTOR(lname, "");
    $DESCRIPTOR(lvalue, "");
    $DESCRIPTOR(ltable, "LNM$PROCESS");

    lname.dsc$w_length = strlen(LogicalName);
    lname.dsc$a_pointer = LogicalName;
    lvalue.dsc$w_length = strlen(LogicalValue);
    lvalue.dsc$a_pointer = LogicalValue;
    lib$set_logical(&lname, &lvalue, &ltable, 0, 0);
    return;
}
#endif /* VMS */

PUBLIC CONST char * Home_Dir NOARGS
{
    static CONST char *homedir;

    if (!homedir) {
	if ((homedir = getenv("HOME")) == NULL) {
#ifdef VMS
	    if ((homedir = getenv("SYS$LOGIN")) == NULL) {
	        if ((homedir = getenv("SYS$SCRATCH")) == NULL)
		    homedir = "sys$scratch:";
	    }
#else
	    /* One could use getlogin() and getpwnam() here instead */
	    struct passwd *pw = getpwuid(geteuid());
	    if (pw && pw->pw_dir) {
		homedir = (CONST char *)malloc(1+strlen(pw->pw_dir));
		if (homedir == NULL)
		    outofmem(__FILE__, "Home_Dir");
		strcpy((char *)homedir, pw->pw_dir);
	    }
	    else {
		/* Use /tmp; it should be writable. */
		homedir = "/tmp";
	    }
#endif /* VMS */
	}
    }
    return homedir;
}

#ifdef NO_PUTENV
/* no putenv on the next so we use this code instead!
 */

/* Copyright (C) 1991 Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can  redistribute it and/or
modify it under the terms of the GNU Library General  Public License as
published by the Free Software Foundation; either  version 2 of the
License, or (at your option) any later version.

The GNU C Library is distributed in the hope that it  will be useful,
but WITHOUT ANY WARRANTY; without even the implied  warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library  General Public
License along with the GNU C Library; see the file  COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675  Mass Ave,
Cambridge, MA 02139, USA.  */

#include <sys/types.h>
#include <errno.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#else
extern int errno;
#endif /* STDC_HEADERS */

#if defined(STDC_HEADERS) || defined(USG)
#include <string.h>
#define index strchr
#define bcopy(s, d, n) memcpy((d), (s), (n))
#else /* Not (STDC_HEADERS or USG): */
#include <strings.h>
#endif /* STDC_HEADERS or USG */

#ifndef NULL
#define NULL 0
#endif /* !NULL */

#if !__STDC__
#define const
#endif /* !__STDC__ */

extern char **environ;

/* Put STRING, which is of the form "NAME=VALUE", in  the environment.  */
int
putenv (string)
     const char *string;
{
  char *name_end = index (string, '=');
  register size_t size;
  register char **ep;

  if (name_end == NULL)
    {
      /* Remove the variable from the environment.  */
      size = strlen (string);
      for (ep = environ; *ep != NULL; ++ep)
	if (!strncmp (*ep, string, size) && (*ep)[size]  == '=')
	  {
	    while (ep[1] != NULL)
	      {
		ep[0] = ep[1];
		++ep;
	      }
	    *ep = NULL;
	    return 0;
	  }
    }

  size = 0;
  for (ep = environ; *ep != NULL; ++ep)
    if (!strncmp (*ep, string, name_end - string) && (*ep)[name_end - string] == '=')
      break;
    else
      ++size;

  if (*ep == NULL)
    {
      static char **last_environ = NULL;
      char **new_environ = (char **) malloc ((size + 2)  * sizeof (char *));
      if (new_environ == NULL)
	return -1;
      (void) bcopy ((char *) environ, (char *)  new_environ, size * sizeof (char *));
      new_environ[size] = (char *) string;
      new_environ[size + 1] = NULL;
      if (last_environ != NULL)
	free ((char *) last_environ);
      last_environ = new_environ;
      environ = new_environ;
    }
  else
    *ep = (char *) string;

  return 0;
}
#endif /* NO_PUTENV */
