about summary refs log tree commit diff stats
path: root/src/GridText.c
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
committerThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
commite087f6d44e87f489fcb3056e86319ebba4218156 (patch)
treed045b58011bfbbf5186d34c4fed9e0dedb363275 /src/GridText.c
downloadlynx-snapshots-e087f6d44e87f489fcb3056e86319ebba4218156.tar.gz
snapshot of project "lynx", label v2_6
Diffstat (limited to 'src/GridText.c')
-rw-r--r--src/GridText.c4368
1 files changed, 4368 insertions, 0 deletions
diff --git a/src/GridText.c b/src/GridText.c
new file mode 100644
index 00000000..2c35304d
--- /dev/null
+++ b/src/GridText.c
@@ -0,0 +1,4368 @@
+/*		Character grid hypertext object
+**		===============================
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "LYCurses.h" /* lynx defined curses */
+
+#include <assert.h>
+#include <ctype.h>
+#include "HTString.h"
+#include "GridText.h"
+#include "HTFont.h"
+#include "HTAccess.h"
+#include "HTParse.h"
+#include "HTTP.h"
+#include "HTAlert.h"
+#include "HTCJK.h"
+
+/* lynx specific defines */
+#include "LYUtils.h"
+#include "LYStrings.h"
+#include "LYStructs.h"
+#include "LYGlobalDefs.h"
+#include "LYGetFile.h"
+#include "LYSignal.h"
+#include "LYMail.h"
+#include "LYList.h"
+#include "LYCharSets.h"
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+#ifndef VMS
+#ifdef SYSLOG_REQUESTED_URLS
+#include <syslog.h>
+#endif /* SYSLOG_REQUESTED_URLS */
+#endif /* !VMS */
+
+struct _HTStream {                      /* only know it as object */
+    CONST HTStreamClass *       isa;
+    /* ... */
+};
+
+#define TITLE_LINES  1
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern BOOL HTPassHighCtrlRaw;
+extern HTkcode kanji_code;
+extern HTCJKlang HTCJK;
+
+/*	From default style sheet:
+*/
+extern HTStyleSheet * styleSheet;	/* Default or overridden */
+
+extern int display_lines; /* number of lines in display */
+extern BOOLEAN ignore_excess; /* flag to ignore chararcters at wrap column */
+extern char HTML_Last_Char;
+
+/*	Exports
+*/ 
+PUBLIC HText * HTMainText = NULL;		/* Equivalent of main window */
+PUBLIC HTParentAnchor * HTMainAnchor = NULL;	/* Anchor for HTMainText */
+
+PUBLIC char * HTAppName = "Lynx";      /* Application name */
+PUBLIC char * HTAppVersion = LYNX_VERSION;        /* Application version */
+
+PUBLIC int HTFormNumber = 0;
+PUBLIC int HTFormFields = 0;
+PUBLIC char * HTCurSelectGroup = NULL;		/* form select group name */
+PUBLIC int HTCurSelectGroupType = F_RADIO_TYPE;	/* group type */
+PUBLIC char * HTCurSelectGroupSize = NULL;	/* length of select */
+PRIVATE char * HTCurSelectedOptionValue = NULL;	/* select choice */
+
+PUBLIC char * checked_box = "[X]";
+PUBLIC char * unchecked_box = "[ ]";
+PUBLIC char * checked_radio = "(*)";
+PUBLIC char * unchecked_radio = "( )";
+
+PUBLIC BOOLEAN underline_on = OFF;
+PUBLIC BOOLEAN bold_on      = OFF;
+
+typedef struct _line {
+	struct _line	*next;
+	struct _line	*prev;
+	int unsigned	offset;		/* Implicit initial spaces */
+	int unsigned	size;		/* Number of characters */
+	BOOL	split_after;		/* Can we split after? */
+	BOOL	bullet;			/* Do we bullet? */
+	char	data[1];		/* Space for terminator at least! */
+} HTLine;
+
+#define LINE_SIZE(l) (sizeof(HTLine)+(l))	/* allow for terminator */
+
+typedef struct _TextAnchor {
+	struct _TextAnchor *	next;
+	int			number;		/* For user interface */
+	int			start;		/* Characters */
+        int			line_pos;       /* position in text */
+	int			extent;		/* Characters */
+	int			line_num;       /* place in document */
+	char *		        hightext;       /* the link text */
+	char *		        hightext2;       /* a second line*/
+        int 		    	hightext2offset; /* offset from left */
+	int			link_type;	/* normal or form? */
+	FormInfo *		input_field;	/* info for form links */
+	BOOL			show_anchor;    /* show the anchor? */
+	HTChildAnchor *		anchor;
+} TextAnchor;
+
+typedef struct _HTTabID {
+	char *			name;		/* ID value of TAB */
+	int			column;		/* Zero-based column value */
+} HTTabID;
+
+
+/*	Notes on struct _Htext:
+**	next_line is valid if state is false.
+**	top_of_screen line means the line at the top of the screen
+**			or just under the title if there is one.
+*/
+struct _HText {
+	HTParentAnchor *	node_anchor;
+	HTLine * 		last_line;
+	int			lines;		/* Number of them */
+	int			chars;		/* Number of them */
+	TextAnchor *		first_anchor;	/* Singly linked list */
+	TextAnchor *		last_anchor;
+	int			last_anchor_number;	/* user number */
+	BOOL			source;		/* is the text source? */
+	BOOL			toolbar;	/* Toolbar set? */
+	HTList *		tabs;		/* TAB IDs */
+	BOOL			no_cache;	/* Always refresh? */
+/* For Internal use: */	
+	HTStyle *		style;			/* Current style */
+	int			display_on_the_fly;	/* Lines left */
+	int			top_of_screen;		/* Line number */
+	HTLine *		top_of_screen_line;	/* Top */
+	HTLine *		next_line;		/* Bottom + 1 */
+	int			permissible_split;	/* in last line */
+	BOOL			in_line_1;		/* of paragraph */
+	BOOL			stale;			/* Must refresh */
+
+        HTkcode			kcode;			/* Kanji code? */
+	enum grid_state	      { S_text, S_esc, S_dollar, S_paren,
+				S_nonascii_text, S_dollar_paren,
+				S_jisx0201_text }
+				state;			/* Escape sequence? */
+	char			kanji_buf;		/* Lead multibyte */
+	int			in_sjis;		/* SJIS flag */
+
+        HTStream*               target;                 /* Output stream */
+        HTStreamClass           targetClass;            /* Output routines */
+};
+
+/*
+ *  Boring static variable used for moving cursor across
+ */
+#define UNDERSCORES(n) (&underscore_string[(MAX_LINE-1) - (n)])
+
+/*
+ *	Memory leak fixed.
+ *	05-29-94 Lynx 2-3-1 Garrett Arch Blythe
+ *	Changed to arrays.
+ */
+PRIVATE char underscore_string[MAX_LINE + 1];
+PUBLIC char star_string[MAX_LINE + 1];
+
+PRIVATE int ctrl_chars_on_this_line = 0; /* num of ctrl chars in current line */
+
+PRIVATE HTStyle default_style =
+	{ 0,  "(Unstyled)", "",
+	(HTFont)0, 1.0, HT_BLACK,		0, 0,
+	0, 0, 0, HT_LEFT,		1, 0,	0, 
+	NO, NO, 0, 0,			0 };	
+
+
+
+PRIVATE HTList * loaded_texts = NULL;	 /* A list of all those in memory */
+PUBLIC  HTList * search_queries = NULL;  /* isindex and whereis queries   */
+PRIVATE void free_all_texts NOARGS;
+PRIVATE int HText_TrueLineSize PARAMS((HTLine *line));
+
+/*			Creation Method
+**			---------------
+*/
+PUBLIC HText *	HText_new ARGS1(HTParentAnchor *,anchor)
+{
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+#include <lib$routines.h>
+    int status, VMType=3, VMTotal;
+#endif /* VMS && VAXC && !__DECC */
+    HTLine * line = NULL;
+    HText * self = (HText *) calloc(sizeof(*self),1);
+    if (!self) return self;
+    
+#if defined(VMS) && defined (VAXC) && !defined(__DECC)
+    status = lib$stat_vm(&VMType, &VMTotal);
+    if (TRACE)
+        fprintf(stderr, "GritText: VMTotal = %d\n", VMTotal);
+#endif /* VMS && VAXC && !__DECC */
+
+    if (!loaded_texts)	{
+	loaded_texts = HTList_new();
+	atexit(free_all_texts);
+    }
+
+    /*
+     *  Links between anchors & documents are a 1-1 relationship. If
+     *  an anchor is already linked to a document we didn't call
+     *  HTuncache_current_document(), e.g., for the showinfo, options,
+     *  dowload, print, etc., temporary file URLs, so we'll check now
+     *  and free it before reloading. - Dick Wesseling (ftu@fi.ruu.nl)
+     */
+    if (anchor->document) {
+       HTList_removeObject(loaded_texts, anchor->document);
+       if (TRACE)
+           fprintf(stderr, "GridText: Auto-uncaching\n") ;
+       ((HText *)anchor->document)->node_anchor = NULL;
+       HText_free((HText *)anchor->document);
+       anchor->document = NULL;
+    }
+
+    HTList_addObject(loaded_texts, self);
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+    while (HTList_count(loaded_texts) > HTCacheSize &&
+    	   VMTotal > HTVirtualMemorySize) {
+#else
+    if (HTList_count(loaded_texts) > HTCacheSize) {
+#endif /* VMS && VAXC && !__DECC */
+        if (TRACE)
+	    fprintf(stderr, "GridText: Freeing off cached doc.\n"); 
+        HText_free((HText *)HTList_removeFirstObject(loaded_texts));
+#if defined(VMS) && defined (VAXC) && !defined(__DECC)
+        status = lib$stat_vm(&VMType, &VMTotal);
+        if (TRACE)
+            fprintf(stderr, "GridText: VMTotal reduced to %d\n", VMTotal);
+#endif /* VMS && VAXC && !__DECC */
+    }
+    
+    line = self->last_line = (HTLine *)calloc(sizeof(char),LINE_SIZE(MAX_LINE));
+    if (line == NULL)
+        outofmem(__FILE__, "HText_New");
+    line->next = line->prev = line;
+    line->offset = line->size = 0;
+    self->lines = self->chars = 0;
+    self->first_anchor = self->last_anchor = NULL;
+    self->style = &default_style;
+    self->top_of_screen = 0;
+    self->node_anchor = anchor;
+    self->last_anchor_number = 0;	/* Numbering of them for references */
+    self->stale = YES;
+    self->toolbar = NO;
+    self->tabs = NULL;
+    self->no_cache = ((anchor->no_cache || anchor->post_data) ?
+    							  YES : NO);
+    if (HTOutputFormat == WWW_SOURCE)
+        self->source = YES;
+    else
+        self->source = NO;
+    HTAnchor_setDocument(anchor, (HyperDoc *)self);
+    HTFormNumber = 0;  /* no forms started yet */
+    HTMainText = self;
+    HTMainAnchor = anchor;
+    self->display_on_the_fly = 0;
+    self->kcode = NOKANJI;
+    self->state = S_text;
+    self->kanji_buf = '\0';
+    self->in_sjis = 0;
+
+    /*
+     *  Check the kcode setting if the anchor has a charset element. - FM
+     */
+    if (anchor->charset)
+        HText_setKcode(self, anchor->charset);
+
+    /*
+     *	Memory leak fixed.
+     *  05-29-94 Lynx 2-3-1 Garrett Arch Blythe
+     *	Check to see if our underline and star_string need initialization
+     *		if the underline is not filled with dots.
+     */ 
+    if (underscore_string[0] != '.') { /* Make a line */
+        char *p;
+        for (p=underscore_string; p<underscore_string+(MAX_LINE-1); p++)
+            *p = '.';           /* Used for printfs later */
+        underscore_string[(MAX_LINE-1)] = '\0';
+        for (p=star_string; p<star_string+(LINESIZE-1); p++)
+            *p = '_';           /* Used for printfs later */
+        star_string[(LINESIZE-1)] = '\0';
+    }
+
+    underline_on = FALSE; /* reset */
+    bold_on = FALSE;
+    
+    return self;
+}
+
+/*                      Creation Method 2
+**                      ---------------
+**
+**      Stream is assumed open and left open.
+*/
+PUBLIC HText *  HText_new2 ARGS2(
+                HTParentAnchor *,       anchor,
+                HTStream*,              stream)
+
+{
+    HText * this = HText_new(anchor);
+
+    if (stream) {
+        this->target = stream;
+        this->targetClass = *stream->isa;       /* copy action procedures */
+    }
+    return this;
+}
+
+/*	Free Entire Text
+**	----------------
+*/
+PUBLIC void HText_free ARGS1(HText *,self)
+{
+    if (!self)
+        return;
+
+    HTAnchor_setDocument(self->node_anchor, (HyperDoc *)0);
+    
+    while (YES) {	/* Free off line array */
+        HTLine * l = self->last_line;
+	if (l) {
+	    l->next->prev = l->prev;
+	    l->prev->next = l->next;	/* Unlink l */
+	    self->last_line = l->prev;
+	    if (l != self->last_line) {
+	        FREE(l);
+	    } else {
+	        free(l);
+	    }
+	}
+	if (l == self->last_line) {	/* empty */
+	    l = NULL;
+	    break;
+	}
+    };
+    
+    while (self->first_anchor) {		/* Free off anchor array */
+        TextAnchor * l = self->first_anchor;
+	self->first_anchor = l->next;
+
+	if (l->link_type == INPUT_ANCHOR && l->input_field) {
+	    /*
+	     *  Free form fields.
+	     */
+	    if (l->input_field->type == F_OPTION_LIST_TYPE) {
+		/*
+		 *  Free off option lists.
+		 */
+		OptionType *optptr = l->input_field->select_list;
+		OptionType *tmp;
+		while (optptr) {
+		    tmp = optptr;
+		    optptr = tmp->next;
+		    FREE(tmp->name);
+		    FREE(tmp->cp_submit_value);
+		    FREE(tmp);
+		}
+		l->input_field->select_list = NULL;
+		/* 
+		 *  Don't free the value field on option
+		 *  lists since it points to a option value
+		 *  same for orig value.
+		 */
+		l->input_field->value = NULL;
+		l->input_field->orig_value = NULL;
+		l->input_field->cp_submit_value = NULL;
+		l->input_field->orig_submit_value = NULL;
+	    } else {
+                FREE(l->input_field->value);
+                FREE(l->input_field->orig_value);
+                FREE(l->input_field->cp_submit_value);
+                FREE(l->input_field->orig_submit_value);
+	    }
+	    FREE(l->input_field->name);
+            FREE(l->input_field->submit_action);
+            FREE(l->input_field->submit_enctype);
+            FREE(l->input_field->submit_title);
+		
+	    FREE(l->input_field);
+	}
+
+	FREE(l->hightext);
+	FREE(l->hightext2);
+
+	FREE(l);
+    }
+
+    /*
+     *  Free the tabs list. - FM
+     */
+    if (self->tabs) {
+        HTTabID * tab = NULL;
+	HTList * cur = self->tabs;
+
+	while (NULL != (tab = (HTTabID *)HTList_nextObject(cur))) {
+	    FREE(tab->name);
+	}
+	HTList_delete(self->tabs);
+    }
+
+    /*
+     *  Invoke HTAnchor_delete() to free the node_anchor
+     *  if it is not a destination of other links. - FM
+     */
+    if (self->node_anchor) {
+        HTAnchor_delete(self->node_anchor);
+    }
+
+    FREE(self);
+}
+
+/*		Display Methods
+**		---------------
+*/
+
+
+/*	Output a line
+**	-------------
+*/
+PRIVATE int display_line ARGS1(HTLine *,line)
+{
+    register int i,j;
+    char buffer[3];
+    char *data;
+
+    buffer[0] = buffer[1] = buffer[2] = '\0';
+    clrtoeol();
+    /* make sure that we don't go over the COLS limit on the display! */
+
+    /* add offset */
+    j = (int)line->offset;
+    if (j > (int)LYcols - 1)
+        j = (int)LYcols - 1;
+#ifdef USE_SLANG
+    SLsmg_forward (j);
+    i = j;
+#else
+    for (i = 0; i < j; i++)
+        addch (' ');
+#endif /* USE_SLANG */
+
+    /* add data */
+    data = line->data;
+    i++;
+    while ((i < LYcols) && ((buffer[0] = *data) != '\0')) {
+	data++;
+	i++;
+
+   	switch (buffer[0]) {
+
+	    case LY_UNDERLINE_START_CHAR:
+	        if (!(dump_output_immediately && use_underscore)) {
+		    start_underline();
+		    i--;
+		} else {
+		    addch('_');
+		}
+		break;
+
+	    case LY_UNDERLINE_END_CHAR:
+	        if (!(dump_output_immediately && use_underscore)) {
+		    stop_underline();
+		    i--;
+		} else {
+		    addch('_');
+		}
+		break;
+
+	    case LY_BOLD_START_CHAR:
+	        start_bold();
+		i--;
+		break;
+
+	    case LY_BOLD_END_CHAR:
+	        stop_bold ();
+		i--;
+		break;
+
+	    case LY_SOFT_HYPHEN:
+	        if (*data != '\0') {
+		    /*
+		     *  Ignore the soft hyphen if it is not
+		     *  the last character in the line. - FM
+		     */
+		    i--;
+		    break;
+		} else {
+		    /*
+		     *  Make it a hard hyphen and fall through. - FM
+		     */
+		    buffer[0] = '-';
+		}
+
+	    default:
+		/* For CJK strings, by Masanobu Kimura */
+		if (HTCJK != NOCJK && !isascii(buffer[0])) { 
+		    buffer[1] = *data;
+		    data++;
+		    i++;
+		    addstr(buffer);
+		    buffer[1] = '\0';
+		} else {
+		    addstr(buffer);
+		}
+	} /* end of switch */
+    } /* end of while */
+
+    /* add the return */
+    addch('\n');
+
+    stop_underline();
+    stop_bold();
+    return(0);
+}
+
+/*	Output the title line
+**	---------------------
+*/
+PRIVATE void display_title ARGS1(HText *,text)
+{
+    char *title = NULL;
+    char percent[20], format[20];
+    char *cp = NULL;
+    unsigned char *tmp = NULL;
+    int i = 0, j = 0;
+
+    /*
+     *  Make sure we have a text structure. - FM
+     */
+    if (!text)
+        return;
+
+    /*
+     *  Load the title field. - FM
+     */
+    StrAllocCopy(title,
+    		 (HTAnchor_title(text->node_anchor) ?
+		  HTAnchor_title(text->node_anchor) : ""));
+
+    /*
+     *  There shouldn't be any \n in the title field,
+     *  but if there is, lets kill it now!
+     */
+    if ((cp = strchr(title,'\n')) != NULL)
+	*cp = '\0';
+
+    /*
+     *  Generate the page indicator (percent) string.
+     */
+    if ((text->lines + 1) > (display_lines)) {
+	/*
+	 *  In a small attempt to correct the number of pages counted....
+	 *    GAB 07-14-94
+	 *
+	 *  In a bigger attempt (hope it holds up 8-)....
+	 *    FM 02-08-95
+	 */
+	int total_pages =
+	 	(((text->lines + 1) + (display_lines - 1))/(display_lines));
+	int start_of_last_page =
+		((text->lines + 1) < display_lines) ? 0 :
+		((text->lines + 1) - display_lines);	
+
+	sprintf(percent, " (p%d of %d)",
+		((text->top_of_screen >= start_of_last_page) ?
+						 total_pages :
+	            ((text->top_of_screen + display_lines)/(display_lines))),
+		total_pages);
+    } else {
+	strcpy(percent, "");	/* Null string */
+    }
+
+    /*
+     *  Generate format string.
+     */
+    sprintf(format, "%%%d.%ds%%s\n",
+		    (LYcols-1)-strlen(percent),
+		    (LYcols-1)-strlen(percent));
+
+    /*
+     *  Generate and display the complete title string.
+     */
+    cp = (char *)calloc(1, (LYcols * 2));
+    if (cp == NULL)
+        outofmem(__FILE__, "display_title");
+    if (HTCJK != NOCJK) {
+        if (*title && (tmp = (unsigned char *)calloc(1, strlen(title) + 1))) {
+	    if (kanji_code == EUC) {
+	        TO_EUC((unsigned char *)title, tmp);
+	    } else if (kanji_code == SJIS) {
+	        TO_SJIS((unsigned char *)title, tmp);
+	    } else {
+	        for (i = 0, j = 0; title[i]; i++) {
+		    if (title[i] != '\033') {
+		        tmp[j++] = title[i];
+		    }
+		}
+	    }
+	    sprintf(cp, format, tmp, percent);
+	    FREE(tmp);
+        } else {
+	    sprintf(cp, format, title, percent);
+        }
+    } else {
+        sprintf(cp, format, title, percent);
+    }
+    move(0,0);
+    addstr(cp);
+    FREE(cp);
+    FREE(title);
+    return;
+}
+
+
+/*	Output a page
+**	-------------
+*/
+PRIVATE void display_page ARGS3(HText *,text, int,line_number, char *, target)
+{
+    HTLine * line = NULL;
+    int i;
+    char *cp, tmp[3];
+    int last_screen;
+    TextAnchor *Anchor_ptr = NULL;
+    FormInfo *FormInfo_ptr;
+    BOOL display_flag = FALSE;
+    HTAnchor *link_dest;
+
+    lynx_mode = NORMAL_LYNX_MODE;
+ 
+    if (text == NULL) {
+	/*
+	 *  Check whether to force a screen clear to enable scrollback,
+	 *  or as a hack to fix a reverse clear screen problem for some
+	 *  curses packages. - shf@access.digex.net & seldon@eskimo.com
+	 */
+	if (enable_scrollback) {
+	    addch('*');
+	    refresh();
+	    clear();
+	}
+	addstr("\n\nError accessing document\nNo data available\n");
+	refresh();
+	nlinks = 0;  /* set number of links to 0 */
+	return;
+    }
+
+    tmp[0] = tmp[1] = tmp[2] = '\0';
+    last_screen = text->lines - (display_lines-2);
+    line = text->last_line->prev;
+
+    /*
+     *  Constrain the line number to be within the document.
+     */
+    if (text->lines < (display_lines))
+        line_number = 0;
+    else if (line_number > text->lines)
+        line_number = last_screen;
+    else if (line_number < 0)
+        line_number = 0;
+    
+    for (i = 0, line = text->last_line->next;		/* Find line */
+    	 i < line_number && (line != text->last_line);
+         i++, line = line->next) 			/* Loop */
+	assert(line->next != NULL);
+
+    /*
+     *  Check whether to force a screen clear to enable scrollback,
+     *  or as a hack to fix a reverse clear screen problem for some
+     *  curses packages. - shf@access.digex.net & seldon@eskimo.com
+     */
+    if (enable_scrollback) {
+	addch('*');
+	refresh();
+	clear();
+    }
+
+    text->top_of_screen = line_number;
+    display_title(text);  /* will move cursor to top of screen */
+    display_flag=TRUE;
+    
+    /*
+     *  Print it.
+     */
+    if (line) {
+      for (i = 0; i < (display_lines); i++)  {
+
+        assert(line != NULL);
+        display_line(line);
+
+        /*
+	 *  If the target is on this line, underline it.
+	 */
+        if (strlen(target) > 0 &&
+	    (case_sensitive ?  
+	    (cp = LYno_attr_char_strstr(line->data, target)) != NULL : 
+	    (cp = LYno_attr_char_case_strstr(line->data, target)) != NULL) &&
+            ((int)(cp - (char *)line->data) +
+	     (int)line->offset + strlen(target)) < LYcols) {
+
+	    int itmp = 0;
+	    int written = 0;
+	    int x_pos=(int)line->offset + (int)(cp - line->data);
+	    int len = strlen(target);
+
+	    start_underline();
+		/* underline string */
+	    for (; written < len && (tmp[0] = line->data[itmp]) != '\0';
+		   itmp++)  {
+		if (IsSpecialAttrChar(tmp[0])) {
+		   /* ignore special characters */
+		   x_pos--;
+
+		} else if (cp == &line->data[itmp]) {
+		    /* first character of target */
+            	    move(i+1, x_pos);
+		    if (HTCJK != NOCJK && !isascii(tmp[0])) {
+		        /* For CJK strings, by Masanobu Kimura */
+		        tmp[1] = line->data[++itmp];
+			addstr(tmp);
+			tmp[1] = '\0';
+			written += 2;
+		    } else {
+		        addstr(tmp);
+			written++;
+		    }
+
+		} else if (&line->data[itmp] > cp) { 
+		    /* print all the other target chars */
+		    if (HTCJK != NOCJK && !isascii(tmp[0])) {
+		        /* For CJK strings, by Masanobu Kimura */
+		        tmp[1] = line->data[++itmp];
+			addstr(tmp);
+			tmp[1] = '\0';
+			written += 2;
+		    } else {
+		        addstr(tmp);
+			written++;
+		    }
+		}
+	    }
+
+	    stop_underline();
+	    move(i+2, 0);
+	}
+
+	/*
+	 *  Stop if at the last line.
+	 */
+	if (line == text->last_line)  {
+	    /* clr remaining lines of display */
+	    for (i++; i < (display_lines); i++) {
+		move(i+1,0);
+		clrtoeol();
+	    }
+	    break;
+	}
+
+	display_flag=TRUE;
+	line = line->next;
+      }
+    }
+
+    text->next_line = line;	/* Line after screen */
+    text->stale = NO;		/* Display is up-to-date */
+
+    /*
+     *  Add the anchors to lynx structures.
+     */
+    nlinks = 0;
+    for (Anchor_ptr=text->first_anchor;  Anchor_ptr != NULL &&
+		Anchor_ptr->line_num <= line_number+(display_lines);
+					    Anchor_ptr = Anchor_ptr->next) {
+
+	if (Anchor_ptr->line_num >= line_number &&
+		Anchor_ptr->line_num < line_number+(display_lines)) {
+
+		/* load normal hypertext anchors */
+	    if (Anchor_ptr->show_anchor && Anchor_ptr->hightext && 
+			strlen(Anchor_ptr->hightext)>0 && 
+			Anchor_ptr->link_type == HYPERTEXT_ANCHOR) {
+
+                links[nlinks].hightext  = Anchor_ptr->hightext;
+                links[nlinks].hightext2 = Anchor_ptr->hightext2;
+                links[nlinks].hightext2_offset = Anchor_ptr->hightext2offset;
+
+                links[nlinks].anchor_number = Anchor_ptr->number;
+
+		link_dest = HTAnchor_followMainLink(
+					     (HTAnchor *)Anchor_ptr->anchor);
+		{
+		    /*
+		     *	Memory leak fixed 05-27-94
+		     *	Garrett Arch Blythe
+		     */
+	            auto char *cp_AnchorAddress = NULL;
+		    if (traversal)
+		        cp_AnchorAddress = stub_HTAnchor_address(link_dest);
+		    else
+			cp_AnchorAddress = HTAnchor_address(link_dest);
+
+		    FREE(links[nlinks].lname);
+
+		    if (cp_AnchorAddress != NULL)
+			links[nlinks].lname = cp_AnchorAddress;
+		    else
+			StrAllocCopy(links[nlinks].lname, empty_string);
+		}
+
+      	        links[nlinks].lx = Anchor_ptr->line_pos;
+      	        links[nlinks].ly = (Anchor_ptr->line_num+1)-line_number;
+	        links[nlinks].type = WWW_LINK_TYPE;
+		links[nlinks].target = empty_string;
+
+	        nlinks++;
+		display_flag = TRUE;
+
+	    } else if (Anchor_ptr->link_type == INPUT_ANCHOR
+			&& Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
+
+		lynx_mode = FORMS_LYNX_MODE;
+
+		FormInfo_ptr = Anchor_ptr->input_field;
+
+                links[nlinks].anchor_number = Anchor_ptr->number;
+
+	   	links[nlinks].form = FormInfo_ptr;
+		links[nlinks].lx = Anchor_ptr->line_pos;
+		links[nlinks].ly = (Anchor_ptr->line_num+1)-line_number;
+		links[nlinks].type = WWW_FORM_LINK_TYPE;
+		links[nlinks].target = empty_string;
+		StrAllocCopy(links[nlinks].lname, empty_string);
+
+		if (FormInfo_ptr->type == F_RADIO_TYPE) {
+		    if (FormInfo_ptr->num_value)
+			links[nlinks].hightext = checked_radio;
+		    else
+			links[nlinks].hightext = unchecked_radio;
+
+		} else if (FormInfo_ptr->type == F_CHECKBOX_TYPE) {
+		    if (FormInfo_ptr->num_value)
+			links[nlinks].hightext = checked_box;
+		    else
+			links[nlinks].hightext = unchecked_box;
+
+		} else if (FormInfo_ptr->type == F_PASSWORD_TYPE) {
+		    links[nlinks].hightext = STARS(strlen(FormInfo_ptr->value));
+
+		} else {  /* TEXT type */
+		    links[nlinks].hightext = FormInfo_ptr->value;
+		}
+
+  		/* never a second line on form types */
+		links[nlinks].hightext2 = NULL;
+
+		nlinks++;
+	         /* bold the link after incrementing nlinks */
+		highlight(OFF,nlinks-1);
+	
+		display_flag = TRUE;
+
+	    } else { /* not showing anchor */
+		if (TRACE &&
+		    Anchor_ptr->hightext && *Anchor_ptr->hightext) 
+		    fprintf(stderr,
+		    	    "\nGridText: Not showing link, hightext=%s\n",
+			    Anchor_ptr->hightext);
+	    }
+	} 
+
+	if (Anchor_ptr == text->last_anchor)
+	    break;
+    }
+
+    /*
+     *  If Anchor_ptr is not NULL and is not pointing to the last
+     *  anchor, then there are anchors farther down in the document,
+     *  and we need to flag this for traversals.
+     */
+    more_links = FALSE;
+    if (traversal && Anchor_ptr) {
+        if (Anchor_ptr->next)
+            more_links = TRUE;
+    }
+
+    if (!display_flag) /* nothing on the page */
+	addstr("\n     Document is empty");
+
+    if (HTCJK != NOCJK || TRACE) {
+        /* for non-multibyte curses ;_; */
+        clearok(curscr, TRUE);
+    }
+    refresh();
+
+}
+
+
+/*			Object Building methods
+**			-----------------------
+**
+**	These are used by a parser to build the text in an object
+*/
+PUBLIC void HText_beginAppend ARGS1(HText *,text)
+{
+    text->permissible_split = 0;
+    text->in_line_1 = YES;
+
+}
+
+
+/*	Add a new line of text
+**	----------------------
+**
+** On entry,
+**
+**	split	is zero for newline function, else number of characters
+**		before split.
+**	text->display_on_the_fly
+**		may be set to indicate direct output of the finished line.
+** On exit,
+**		A new line has been made, justified according to the
+**		current style. Text after the split (if split nonzero)
+**		is taken over onto the next line.
+**
+**		If display_on_the_fly is set, then it is decremented and
+**		the finished line is displayed.
+*/
+#define new_line(text) split_line(text, 0)
+
+PRIVATE void split_line ARGS2(HText *,text, int,split)
+{
+    HTStyle * style = text->style;
+#if defined(AIX) || defined(ultrix)
+    HTLine * temp; /* for realloc() substitute. */
+#endif /* AIX || ultrix */	
+    int spare;
+    int indent = text->in_line_1 ?
+    	  text->style->indent1st : text->style->leftIndent;
+
+    /*
+     *  Make new line.
+     */
+    HTLine * previous = text->last_line;
+    int ctrl_chars_on_previous_line = 0;
+    char * cp;
+    HTLine * line = (HTLine *)calloc(sizeof(char), LINE_SIZE(MAX_LINE));
+
+    ctrl_chars_on_this_line = 0; /*reset since we are going to a new line*/
+    HTML_Last_Char = ' ';
+
+    if (TRACE)
+	fprintf(stderr,"GridText: split_line called\n");
+    
+    if (line == NULL)
+        outofmem(__FILE__, "split_line");
+    text->lines++;
+    
+    previous->next->prev = line;
+    line->prev = previous;
+    line->next = previous->next;
+    previous->next = line;
+    text->last_line = line;
+    line->size = 0;
+    line->offset = 0;
+    text->permissible_split = 0;  /* 12/13/93 */
+    line->data[0] = '\0';
+
+    /*
+     *  If we are not splitting and need an underline char, add it now. - FM
+     */
+    if ((split < 1) &&
+        !(dump_output_immediately && use_underscore) && underline_on) {
+	line->data[line->size++] = LY_UNDERLINE_START_CHAR;
+	line->data[line->size] = '\0';
+	ctrl_chars_on_this_line++;
+    }
+    /*
+     *  If we are not splitting and need a bold char, add it now. - FM
+     */
+    if ((split < 1) && bold_on) {
+	line->data[line->size++] = LY_BOLD_START_CHAR;
+	line->data[line->size] = '\0';
+	ctrl_chars_on_this_line++;
+    }
+
+    /*
+     *  Split at required point
+     */    
+    if (split > 0) {	/* Delete space at "split" splitting line */
+        char *p, *prevdata = previous->data, *linedata = line->data;
+        unsigned int plen;
+	int i;
+
+        /*
+	 *  Split the line. - FM
+	 */
+	prevdata[previous->size] = '\0';
+	previous->size = split;
+
+	/*
+	 *  Trim any spaces or soft hyphens from the beginning
+	 *  of our new line. - FM
+	 */
+	p = prevdata + split;
+        while (*p == ' ' || *p == LY_SOFT_HYPHEN)
+	    p++;
+        plen = strlen(p);
+
+	/*
+	 *  Add underline char if needed. - FM
+	 */
+        if (!(dump_output_immediately && use_underscore)) {
+	    /*
+	     * Make sure our global flag is correct. - FM
+	     */
+	    underline_on = NO;
+	    for (i = (split-1); i >= 0; i--) {
+	        if (prevdata[i] == LY_UNDERLINE_END_CHAR) {
+		    break;
+		}
+		if (prevdata[i] == LY_UNDERLINE_START_CHAR) {
+		    underline_on = YES;
+		    break;
+		}
+	    }
+	    /*
+	     *  Act on the global flag if set above. - FM
+	     */
+	    if (underline_on && *p != LY_UNDERLINE_END_CHAR) {
+	        linedata[line->size++] = LY_UNDERLINE_START_CHAR;
+		linedata[line->size] = '\0';
+		ctrl_chars_on_this_line++;
+	    }
+	    for (i = (plen - 1); i >= 0; i--) {
+		if (p[i] == LY_UNDERLINE_START_CHAR) {
+		    underline_on = YES;
+		    break;
+		}
+		if (p[i] == LY_UNDERLINE_END_CHAR) {
+		    underline_on = NO;
+		    break;
+		}
+	    }
+	    for (i = (plen - 1); i >= 0; i--) {
+	        if (p[i] == LY_UNDERLINE_START_CHAR ||
+		    p[i] == LY_UNDERLINE_END_CHAR) {
+		    ctrl_chars_on_this_line++;
+		}
+	    }
+	}
+
+	/*
+	 *  Add bold char if needed, first making
+	 *  sure that our global flag is correct. - FM
+	 */
+	bold_on = NO;
+	for (i = (split - 1); i >= 0; i--) {
+	    if (prevdata[i] == LY_BOLD_END_CHAR) {
+		break;
+	    }
+	    if (prevdata[i] == LY_BOLD_START_CHAR) {
+	        bold_on = YES;
+		break;
+	    }
+	}
+	/*
+	 *  Act on the global flag if set above. - FM
+	 */
+	if (bold_on && *p != LY_BOLD_END_CHAR) {
+	    linedata[line->size++] = LY_BOLD_START_CHAR;
+	    linedata[line->size] = '\0';
+	    ctrl_chars_on_this_line++;
+	}
+	for (i = (plen - 1); i >= 0; i--) {
+	    if (p[i] == LY_BOLD_START_CHAR) {
+	        bold_on = YES;
+		break;
+	    }
+	    if (p[i] == LY_BOLD_END_CHAR) {
+		bold_on = NO;
+		break;
+	    }
+	}
+	for (i = (plen - 1); i >= 0; i--) {
+	    if (p[i] == LY_BOLD_START_CHAR ||
+	        p[i] == LY_BOLD_END_CHAR ||
+		p[i] == LY_SOFT_HYPHEN) {
+	        ctrl_chars_on_this_line++;
+	    }
+	    if (p[i] == LY_SOFT_HYPHEN && text->permissible_split < i) {
+	        text->permissible_split = i + 1;
+	    }
+	}
+
+	/*
+	 *  Add the data to the new line. - FM
+	 */
+	strcat(linedata, p);
+	line->size += plen;
+    }
+    
+    /*
+     *  Economize on space.
+     */
+    while ((previous->size > 0) &&
+    	(previous->data[previous->size-1] == ' '))	/* Strip trailers */
+        previous->size--;
+	
+#if !defined(AIX) && !defined(ultrix)	
+    previous = (HTLine *) realloc (previous, LINE_SIZE(previous->size));
+    if (previous == NULL)
+        outofmem(__FILE__, "split_line");
+#else
+    /*
+     *  RS6000 has a chaotic bug in realloc argument passing.  Same
+     *  problem with Ultrix (4.2) : realloc() is not declared properly.
+     *  So we'll use a substitute for realloc.
+     */
+    temp = (HTLine *)calloc(sizeof(char), LINE_SIZE(previous->size));
+    if (temp == NULL)
+        outofmem(__FILE__, "split_line");
+    memcpy(temp, previous, LINE_SIZE(previous->size));
+    FREE(previous);
+    previous = temp;
+#endif /* !AIX && !ultrix */
+
+    previous->prev->next = previous;	/* Link in new line */
+    previous->next->prev = previous;	/* Could be same node of course */
+
+    /*
+     *  Terminate finished line for printing.
+     */
+    previous->data[previous->size] = '\0';
+     
+    
+    /*
+     *  Align left, right or center.
+     */
+    for (cp = previous->data; *cp; cp++) {
+        if (*cp == LY_UNDERLINE_START_CHAR ||
+	    *cp == LY_UNDERLINE_END_CHAR ||
+	    *cp == LY_BOLD_START_CHAR ||
+	    *cp == LY_BOLD_END_CHAR ||
+	    *cp == LY_SOFT_HYPHEN)
+	    ctrl_chars_on_previous_line++;
+    }
+    /* @@ first line indent */
+    spare =  (LYcols-1) -
+    		(int)style->rightIndent - indent +
+    		ctrl_chars_on_previous_line - previous->size -
+		((previous->size > 0) &&
+		 (int)(previous->data[previous->size-1] ==
+		 			    LY_SOFT_HYPHEN ?
+							 1 : 0));
+
+    switch (style->alignment) {
+	case HT_CENTER :
+	    previous->offset = previous->offset + indent + spare/2;
+	    break;
+	case HT_RIGHT :
+	    previous->offset = previous->offset + indent + spare;
+	    break;
+	case HT_LEFT :
+	case HT_JUSTIFY :		/* Not implemented */
+	default:
+	    previous->offset = previous->offset + indent;
+	    break;
+    } /* switch */
+
+    text->chars = text->chars + previous->size + 1;	/* 1 for the line */
+    text->in_line_1 = NO;		/* unless caller sets it otherwise */
+    
+} /* split_line */
+
+
+/*	Allow vertical blank space
+**	--------------------------
+*/
+PRIVATE void blank_lines ARGS2(HText *,text, int,newlines)
+{
+    if (!HText_LastLineSize(text)) {	/* No text on current line */
+	HTLine * line = text->last_line->prev;
+	while ((line != text->last_line) &&
+	       (HText_TrueLineSize(line) == 0)) {
+	    if (newlines == 0) break;
+	    newlines--;		/* Don't bother: already blank */
+	    line = line->prev;
+	}
+    } else {
+	newlines++;			/* Need also to finish this line */
+    }
+
+    for (; newlines; newlines--) {
+	new_line(text);
+    }
+    text->in_line_1 = YES;
+}
+
+
+/*	New paragraph in current style
+**	------------------------------
+** See also: setStyle.
+*/
+PUBLIC void HText_appendParagraph ARGS1(HText *,text)
+{
+    int after = text->style->spaceAfter;
+    int before = text->style->spaceBefore;
+    blank_lines(text, ((after > before) ? after : before));
+}
+
+
+/*	Set Style
+**	---------
+**
+**	Does not filter unnecessary style changes.
+*/
+PUBLIC void HText_setStyle ARGS2(HText *,text, HTStyle *,style)
+{
+    int after, before;
+
+    if (!style)
+        return;				/* Safety */
+    after = text->style->spaceAfter;
+    before = style->spaceBefore;
+    if (TRACE)
+        fprintf(stderr, "GridText: Change to style %s\n", style->name);
+
+    blank_lines (text, ((after > before) ? after : before));
+
+    text->style = style;
+}
+
+/*	Append a character to the text object
+**	-------------------------------------
+*/
+PUBLIC void HText_appendCharacter ARGS2(HText *,text, char,ch)
+{
+    HTLine * line;
+    HTStyle * style;
+    int indent;
+
+    /*
+     *  Make sure we don't crash on NULLs.
+     */
+    if (!text)
+	return;
+
+#ifdef NOTDEFINED
+    /* Make sure nbsp is handled properly. */
+    if ((unsigned char)ch == 160)
+        ch = HT_NON_BREAK_SPACE;
+#endif /* NOTDEFINED */
+
+    /*
+     *  Make sure we don't hang on escape sequences.
+     */
+    if (ch == '\033' && HTCJK == NOCJK)			/* decimal 27 */
+	return;
+    if ((unsigned char)ch == 155 && HTCJK == NOCJK) {	/* octal 233 */
+        if (!HTPassHighCtrlRaw &&
+	    strncmp(LYchar_set_names[current_char_set],
+		    "IBM PC character set", 20) &&
+	    strncmp(LYchar_set_names[current_char_set],
+		    "IBM PC codepage 850", 19) &&
+	    strncmp(LYchar_set_names[current_char_set],
+		    "Macintosh (8 bit)", 17) &&
+	    strncmp(LYchar_set_names[current_char_set],
+		    "NeXT character set", 18)) {
+	    return;
+	}
+    }
+
+    line = text->last_line;
+    style = text->style;
+
+    indent = text->in_line_1 ? (int)style->indent1st : (int)style->leftIndent;
+    
+    if (HTCJK != NOCJK) {
+	switch(text->state) {
+	    case S_text:
+		if (ch == '\033') {
+		    text->state = S_esc;
+		    text->kanji_buf = '\0';
+		    return;
+		}
+		break;
+
+		case S_esc:
+		if (ch == '$') {
+		    text->state = S_dollar;
+		    return;
+		} else if (ch == '(') {
+		    text->state = S_paren;
+		    return;
+		} else {
+		    text->state = S_text;
+		}
+
+		case S_dollar:
+		if (ch == '@' || ch == 'B' || ch=='A') {
+		    text->state = S_nonascii_text;
+		    return;
+		} else if (ch == '(') {
+		    text->state = S_dollar_paren;
+		    return;
+		} else {
+		    text->state = S_text;
+		}
+		break;
+
+		case S_dollar_paren:
+		if (ch == 'C') {
+		    text->state = S_nonascii_text;
+		    return;
+		} else {
+		    text->state = S_text;
+		}
+		break;
+
+		case S_paren:
+		if (ch == 'B' || ch == 'J' || ch == 'T')  {
+		    text->state = S_text;
+		    return;
+		} else if (ch == 'I')  {
+		    text->state = S_jisx0201_text;
+		    return;
+		} else {
+		    text->state = S_text;
+		}
+		break;
+
+		case S_nonascii_text:
+		if (ch == '\033') {
+		    text->state = S_esc;
+		    text->kanji_buf = '\0';
+		    return;
+		} else {
+		    ch |= 0200;
+		}
+		break;
+
+		/*
+		 *  JIS X0201 Kana in JIS support. - by ASATAKU
+		 */
+		case S_jisx0201_text:
+		if (ch == '\033') {
+		    text->state = S_esc;
+		    text->kanji_buf = '\0';
+		    return;
+		} else {
+		    text->kanji_buf = '\x8E';
+		    ch |= 0200;
+		}
+		break;
+	}
+
+        if (!text->kanji_buf) {
+	    if ((ch & 0200) != 0) {
+		/*
+		 *  JIS X0201 Kana in SJIS support. - by ASATAKU
+		 */
+	        if ((text->kcode == SJIS) &&
+		    ((unsigned char)ch >= 0xA1) &&
+		    ((unsigned char)ch <= 0xDF)) {
+		    unsigned char c = (unsigned char)ch;
+		    unsigned char kb = (unsigned char)text->kanji_buf;
+		    JISx0201TO0208_SJIS(c,
+		    			(unsigned char *)&kb,
+					(unsigned char *)&c);
+		    ch = (char)c;
+		    text->kanji_buf = (char)kb;
+	        } else {
+		    text->kanji_buf = ch;
+		    text->permissible_split = line->size;   /* Can split here */
+		    return;
+	        }
+	    }
+	} else {
+	    goto check_ignore_excess;
+	}
+    } else if (ch == '\033') {
+	return;
+    }
+
+    if (ch != LY_SOFT_HYPHEN && IsSpecialAttrChar(ch)) {
+        if (ch == LY_UNDERLINE_START_CHAR) { 
+            line->data[line->size++] = LY_UNDERLINE_START_CHAR;
+	    line->data[line->size] = '\0';
+	    underline_on = ON;
+	    if (!(dump_output_immediately && use_underscore))
+		ctrl_chars_on_this_line++;
+	    return;
+        } else if (ch == LY_UNDERLINE_END_CHAR) {
+            line->data[line->size++] = LY_UNDERLINE_END_CHAR;
+	    line->data[line->size] = '\0';
+	    underline_on = OFF;
+	    if (!(dump_output_immediately && use_underscore))
+	    	ctrl_chars_on_this_line++;
+	    return;
+        } else if (ch == LY_BOLD_START_CHAR) {
+            line->data[line->size++] = LY_BOLD_START_CHAR;
+	    line->data[line->size] = '\0';
+            bold_on = ON;
+	    ctrl_chars_on_this_line++;
+            return;
+        } else if (ch == LY_BOLD_END_CHAR) {
+            line->data[line->size++] = LY_BOLD_END_CHAR;
+	    line->data[line->size] = '\0';
+            bold_on = OFF;
+	    ctrl_chars_on_this_line++;
+            return;
+	}
+    }
+
+
+    /*
+     *  New Line.
+     */
+    if (ch == '\n') {
+	    new_line(text);
+	    text->in_line_1 = YES;	/* First line of new paragraph */
+	    return;
+    }
+
+    /*
+     *  Convert EM_SPACE to a space here so that it doesn't get collapsed.
+     */
+    if (ch == HT_EM_SPACE)
+	ch = ' ';
+
+    /*
+     *  I'm going to cheat here in a BIG way.  Since I know that all
+     *  \r's will be trapped by HTML_put_character I'm going to use
+     *  \r to mean go down a line but don't start a new paragraph.  
+     *  i.e. use the second line indenting.
+     */
+    if (ch == '\r') {
+	new_line(text);
+	text->in_line_1 = NO;
+	return;
+    }
+
+
+    /*
+     *  Tabs.
+     */
+    if (ch == '\t') {
+        HTTabStop * tab;
+	int target;	/* Where to tab to */
+	int here;
+
+	if (line->size > 0 && line->data[line->size-1] == LY_SOFT_HYPHEN) {
+	    /*
+	     *  A tab shouldn't follow a soft hyphen, so
+	     *  if one does, we'll dump the soft hyphen. - FM
+	     */
+	    line->data[--line->size] = '\0';
+	    ctrl_chars_on_this_line--;
+	}
+	here = (((int)line->size + (int)line->offset) + indent)
+		- ctrl_chars_on_this_line; /* Consider special chars GAB */
+        if (style->tabs) {	/* Use tab table */
+	    for (tab = style->tabs;
+	    	tab->position <= here;
+		tab++)
+		if (!tab->position) {
+		    new_line(text);
+		    return;
+		}
+	    target = tab->position;
+	} else if (text->in_line_1) {	/* Use 2nd indent */
+	    if (here >= (int)style->leftIndent) {
+	        new_line(text); /* wrap */
+		return;
+	    } else {
+	        target = (int)style->leftIndent;
+	    }
+	} else {		/* Default tabs align with left indent mod 8 */
+#ifdef DEFAULT_TABS_8
+	    target = (((int)line->offset + (int)line->size + 8) & (-8))
+	    		+ (int)style->leftIndent;
+#else
+	    new_line(text);
+	    return;
+#endif
+	}
+
+	if (target > (LYcols-1) - (int)style->rightIndent &&
+	    HTOutputFormat != WWW_SOURCE) {
+	    new_line(text);
+	    return;
+	} else {
+            text->permissible_split = (int)line->size;	/* Can split here */
+	    if (line->size == 0) {
+	        line->offset = line->offset + target - here;
+	    } else {
+	        for (; here<target; here++) {
+		    /* Put character into line */
+                    line->data[line->size++] = ' ';
+		    line->data[line->size] = '\0';
+	        }
+	    }
+	    return;
+	}
+	/*NOTREACHED*/
+    } /* if tab */ 
+
+    
+    if (ch == ' ') {
+        text->permissible_split = (int)line->size;	/* Can split here */
+	/* 
+	 *  There are some pages witten in
+	 *  different kanji codes. - TA
+	 */
+	if (HTCJK == JAPANESE)
+	    text->kcode = NOKANJI;
+    }
+
+    /*
+     *  Check if we should ignore characters at the wrap point.
+     */    
+check_ignore_excess:
+    if (ignore_excess &&
+        ((indent + (int)line->offset + (int)line->size) + 
+	(int)style->rightIndent - ctrl_chars_on_this_line) >= (LYcols-1))
+        return;
+
+    /*
+     *  Check for end of line.
+     */
+    if (((indent + (int)line->offset + (int)line->size) + 
+	(int)style->rightIndent - ctrl_chars_on_this_line +
+	(int)(line->data[line->size] == LY_SOFT_HYPHEN ?
+						     1 : 0)) >= (LYcols-1)) {
+
+        if (style->wordWrap && HTOutputFormat != WWW_SOURCE) {
+	    split_line(text, text->permissible_split);
+	    if (ch == ' ') return;	/* Ignore space causing split */
+
+	}  else if (HTOutputFormat == WWW_SOURCE) {
+		 /*
+		  *  For source output we dont want to wrap this stuff
+		  *  unless absolutely neccessary. - LJM 
+		  *  !
+		  *  If we don't wrap here we might get a segmentation fault.
+		  *  but let's see what happens
+		  */
+		if ((int)line->size >= (int)(MAX_LINE-1))
+		   new_line(text);  /* try not to linewrap */
+	} else {
+		/*
+		 *  For normal stuff like pre let's go ahead and
+		 *  wrap so the user can see all of the text.
+		 */
+		new_line(text);  
+	}
+    }
+
+    /*
+     *  Insert normal characters.
+     */
+    if (ch == HT_NON_BREAK_SPACE) {
+        ch = ' ';
+    }
+
+    {
+        HTLine * line = text->last_line;	/* May have changed */
+        HTFont font = style->font;
+	unsigned char hi, lo, tmp[2];
+
+	if (HTCJK != NOCJK && text->kanji_buf) {
+	    hi = (unsigned char)text->kanji_buf, lo = (unsigned char)ch;
+	    if (HTCJK == JAPANESE && text->kcode == NOKANJI) {
+		if (IS_SJIS(hi, lo, text->in_sjis) && IS_EUC(hi, lo)) {
+		    text->kcode = NOKANJI;
+		} else if (IS_SJIS(hi, lo, text->in_sjis)) {
+		    text->kcode = SJIS;
+		} else if (IS_EUC(hi, lo)) {
+		    text->kcode = EUC;
+		}
+	    }
+	    if (HTCJK == JAPANESE &&
+		(kanji_code == EUC) && (text->kcode == SJIS)) {
+		SJIS_TO_EUC1(hi, lo, tmp);
+		line->data[line->size++] = tmp[0];
+		line->data[line->size++] = tmp[1];
+	    } else if (HTCJK == JAPANESE &&
+		       (kanji_code == EUC) && (text->kcode == EUC)) {
+		JISx0201TO0208_EUC(hi, lo, &hi, &lo);
+		line->data[line->size++] = hi;
+		line->data[line->size++] = lo;
+	    } else if (HTCJK == JAPANESE &&
+		       (kanji_code == SJIS) && (text->kcode == EUC)) {
+		EUC_TO_SJIS1(hi, lo, tmp);
+		line->data[line->size++] = tmp[0];
+		line->data[line->size++] = tmp[1];
+	    } else {
+		line->data[line->size++] = hi;
+		line->data[line->size++] = lo;
+	    }
+	    text->kanji_buf = 0;
+	} else if (HTCJK != NOCJK) {
+	    line->data[line->size++] = (kanji_code != NOKANJI) ?
+	    						    ch :
+					  (font & HT_CAPITALS) ?
+					    	   TOUPPER(ch) : ch;
+	} else {
+            line->data[line->size++] =	/* Put character into line */
+	    	font & HT_CAPITALS ? TOUPPER(ch) : ch;
+	}
+	line->data[line->size] = '\0';
+        if (font & HT_DOUBLE)		/* Do again if doubled */
+            HText_appendCharacter(text, HT_NON_BREAK_SPACE);
+	    /* NOT a permissible split */ 
+    }
+
+    if (ch == LY_SOFT_HYPHEN) {
+        ctrl_chars_on_this_line++;
+        text->permissible_split = (int)line->size;	/* Can split here */
+    }
+}
+
+/*		Anchor handling
+**		---------------
+*/
+/*	Start an anchor field
+*/
+PUBLIC void HText_beginAnchor ARGS2(HText *,text, HTChildAnchor *,anc)
+{
+    char marker[16];
+
+    TextAnchor * a = (TextAnchor *) calloc(sizeof(*a),1);
+    
+    if (a == NULL)
+        outofmem(__FILE__, "HText_beginAnchor");
+    a->hightext  = 0;
+    a->hightext2 = 0;
+    a->start = text->chars + text->last_line->size;
+
+    a->line_pos = text->last_line->size;
+    if (text->last_anchor) {
+        text->last_anchor->next = a;
+    } else {
+        text->first_anchor = a;
+    }
+    a->next = 0;
+    a->anchor = anc;
+    a->extent = 0;
+    a->link_type = HYPERTEXT_ANCHOR;
+    text->last_anchor = a;
+    
+    if (HTAnchor_followMainLink((HTAnchor*)anc)) {
+        a->number = ++(text->last_anchor_number);
+    } else {
+        a->number = 0;
+    }
+
+    /* if we are doing link_numbering add the link number */
+    if (keypad_mode == LINKS_ARE_NUMBERED && a->number > 0) {
+	sprintf(marker,"[%d]", a->number);
+        HText_appendText(text, marker);
+	a->start += strlen(marker);
+    }
+}
+
+
+PUBLIC void HText_endAnchor ARGS1(HText *,text)
+{
+    TextAnchor * a = text->last_anchor;
+    if (a->number) {
+        /*
+	 * If it goes somewhere...
+	 */
+        a->extent += text->chars + text->last_line->size - a->start;
+	if (a->extent <= 2) {
+	    /*
+	     * Might be a blank anchor from an ALT="". - FM
+	     */
+    	    int j;
+	    a->show_anchor = NO;
+	    for (j = (text->last_line->size - a->extent);
+	         text->last_line->data[j] != '\0'; j++) {
+		if (!IsSpecialAttrChar(text->last_line->data[j])) {
+		    a->show_anchor = YES;
+		    break;
+		}
+	    }
+	} else {
+	    a->show_anchor = YES;
+	}
+    } else {
+        a->show_anchor = NO;
+	a->extent = 0;
+    }
+ 
+}
+
+
+PUBLIC void HText_appendText ARGS2(HText *,text, CONST char *,str)
+{
+    CONST char *p;
+
+    if (str == NULL)
+	return;
+
+    for (p = str; *p; p++) {
+        HText_appendCharacter(text, *p);
+    }
+}
+
+
+PRIVATE void remove_special_attr_chars ARGS1(char *,buf)
+{
+    register char *cp;
+
+    for (cp=buf; *cp != '\0' ; cp++) {
+         /* don't print underline chars */
+        if (!IsSpecialAttrChar(*cp)) {
+           *buf = *cp, 
+           buf++;
+	}
+    }
+    *buf = '\0';
+}
+
+
+/*
+**  This function trims blank lines from the end of the document, and
+**  then get the hightext from the text by finding the char position,
+**  and brings the anchors in line with the text by adding the text
+**  offset to each of the anchors
+*/
+PUBLIC void HText_endAppend ARGS1(HText *,text)
+{
+    int cur_line, cur_char;
+    TextAnchor *anchor_ptr;
+    HTLine *line_ptr;
+    unsigned char ch;
+
+    if (!text)
+	return;
+
+    new_line(text);
+    
+    /*
+     *  Get the first line
+     */
+    line_ptr = text->last_line->next;
+    cur_char = line_ptr->size;;
+    cur_line = 0;
+
+    /*
+     *  Remove the blank lines at the end of document.
+     */
+    while (text->last_line->data[0] == '\0' && text->lines > 2) {
+        HTLine *next_to_the_last_line;
+
+        if (TRACE)
+            fprintf(stderr, "GridText: Removing bottom blank line: %s\n",
+                            text->last_line->data);
+    
+        next_to_the_last_line = text->last_line->prev;
+
+        /* line_ptr points to the first line */
+        next_to_the_last_line->next = line_ptr;
+        line_ptr->prev = next_to_the_last_line;
+	FREE(text->last_line);
+        text->last_line = next_to_the_last_line;
+        text->lines--;
+
+        if (TRACE)
+            fprintf(stderr, "GridText: New bottom line: %s\n",
+                            text->last_line->data);
+
+    }
+
+    if (TRACE)
+	fprintf(stderr,"Gridtext: Entering HText_endAppend\n");
+
+    for (anchor_ptr = text->first_anchor;
+         anchor_ptr; anchor_ptr=anchor_ptr->next) {
+
+re_parse:
+	/*
+	 *  Find the right line.
+	 */
+	for (; anchor_ptr->start >= cur_char;
+	       line_ptr = line_ptr->next,
+	       cur_char += line_ptr->size+1,
+	       cur_line++) 
+	    ; /* null body */
+
+	if (anchor_ptr->start == cur_char)
+	    anchor_ptr->line_pos = line_ptr->size;
+	else
+	    anchor_ptr->line_pos = anchor_ptr->start-(cur_char-line_ptr->size);
+
+	if (anchor_ptr->line_pos < 0)
+	    anchor_ptr->line_pos = 0;
+
+	if (TRACE)
+	    fprintf(stderr, "Gridtext: Anchor found on line:%d col:%d\n",
+			    cur_line, anchor_ptr->line_pos);
+
+	/* 
+	 *  Strip off any spaces or SpecialAttrChars at the beginning,
+	 *  if they exist, but only on HYPERTEXT_ANCHORS.
+	 */
+	if (anchor_ptr->link_type == HYPERTEXT_ANCHOR) {
+	    ch = (unsigned char)line_ptr->data[anchor_ptr->line_pos];
+            while (isspace(ch) ||
+	           IsSpecialAttrChar(ch)) {
+		anchor_ptr->line_pos++;
+		anchor_ptr->extent--;
+		ch = (unsigned char)line_ptr->data[anchor_ptr->line_pos];
+	    }
+	}
+
+	if (anchor_ptr->extent < 0)
+	    anchor_ptr->extent = 0;
+
+	if (TRACE)
+	    fprintf(stderr, "anchor text: '%s'   pos: %d\n",
+	    		    line_ptr->data, anchor_ptr->line_pos);
+
+	/* 
+	 *  If the link begins with a end of line and we have more
+	 *  lines, then start the highlighting on the next line.
+	 */
+	if (anchor_ptr->line_pos >= strlen(line_ptr->data) &&
+	    cur_line < text->lines) {
+	    anchor_ptr->start++;
+
+	    if (TRACE)
+		fprintf(stderr, "found anchor at end of line\n");
+	    goto re_parse;
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "anchor text: '%s'   pos: %d\n",
+	    		    line_ptr->data, anchor_ptr->line_pos);
+
+	/*
+	 *  Copy the link name into the data structure.
+	 */
+	if (line_ptr->data &&
+	    anchor_ptr->extent > 0 && anchor_ptr->line_pos >= 0) {
+
+	    StrnAllocCopy(anchor_ptr->hightext,
+	    		  &line_ptr->data[anchor_ptr->line_pos],
+			  anchor_ptr->extent);
+	} else {
+	    StrAllocCopy(anchor_ptr->hightext, ""); 
+	}
+
+	/*
+	 *  If true the anchor extends over two lines,
+	 *  copy that into the data structure.
+	 */
+	if (anchor_ptr->extent > strlen(anchor_ptr->hightext)) {
+            HTLine *line_ptr2 = line_ptr->next;
+	    /* double check! */
+	    if (line_ptr) {
+		StrnAllocCopy(anchor_ptr->hightext2, line_ptr2->data, 
+			 (anchor_ptr->extent - strlen(anchor_ptr->hightext))-1);
+	        anchor_ptr->hightext2offset = line_ptr2->offset;
+	 	remove_special_attr_chars(anchor_ptr->hightext2);
+	    }
+	}   
+
+	remove_special_attr_chars(anchor_ptr->hightext);
+
+        /*
+	 *  Subtract any formatting characters from the x position
+         *  of the link.
+         */
+        if (anchor_ptr->line_pos > 0) {
+            register int offset = 0, i = 0;
+            for (; i < anchor_ptr->line_pos; i++)
+                if (IsSpecialAttrChar(line_ptr->data[i]))
+                    offset++;
+            anchor_ptr->line_pos -= offset;
+        }
+
+        anchor_ptr->line_pos += line_ptr->offset;  /* add the offset */
+        anchor_ptr->line_num  = cur_line;
+
+
+	if (TRACE)
+	    fprintf(stderr,
+	    	    "GridText: adding link on line %d in HText_endAppend\n",
+		    cur_line);
+
+ 	if (anchor_ptr == text->last_anchor)
+	    break;
+    }
+}
+
+
+/* 	Dump diagnostics to stderr
+*/
+PUBLIC void HText_dump ARGS1(HText *,text)
+{
+    fprintf(stderr, "HText: Dump called\n");
+}
+	
+
+/*	Return the anchor associated with this node
+*/
+PUBLIC HTParentAnchor * HText_nodeAnchor ARGS1(HText *,text)
+{
+    return text->node_anchor;
+}
+
+/*				GridText specials
+**				=================
+*/
+/*	Return the anchor with index N
+**
+**	The index corresponds to the number we print in the anchor.
+*/
+PUBLIC HTChildAnchor * HText_childNumber ARGS1(int,number)
+{
+    TextAnchor * a;
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+        if (a->number == number) return(a->anchor);
+    }
+    return (HTChildAnchor *)0;	/* Fail */
+}
+
+/* HTGetLinkInfo returns some link info based on the number
+ */
+PUBLIC int HTGetLinkInfo ARGS3(int, number, char **, hightext, char **, lname)
+{
+    TextAnchor * a;
+    HTAnchor *link_dest;
+
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+        if (a->number == number) {
+	    *hightext= a->hightext;
+            link_dest = HTAnchor_followMainLink(
+                                               (HTAnchor *)a->anchor);
+	    {
+		/*
+		 *	Memory Leak fixed.
+		 *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+	 	 */
+		auto char *cp_freeme = NULL;
+		if (traversal)
+		    cp_freeme = stub_HTAnchor_address(link_dest);
+		else
+		    cp_freeme = HTAnchor_address(link_dest);
+            	StrAllocCopy(*lname, cp_freeme);
+		FREE(cp_freeme);
+	    }
+	    return(YES);
+	}
+    }
+    return(NO);
+}
+
+/* HText_getNumOfLines returns the number of lines in the
+ * current document
+ */
+PUBLIC int HText_getNumOfLines NOARGS
+{
+     return(HTMainText->lines);
+}
+
+/* HText_getTitle returns the title of the
+ * current document
+ */
+PUBLIC char * HText_getTitle NOARGS
+{
+   return((char *) HTAnchor_title(HTMainText->node_anchor));
+}
+
+/*
+ * HText_pageDisplay displays a screen of text
+ * starting from the line 'line_num'-1
+ * this is the primary call for lynx
+ */
+extern char is_www_index;
+
+PUBLIC void HText_pageDisplay ARGS2(int,line_num, char *, target)
+{
+    display_page(HTMainText, line_num-1, target);
+
+    is_www_index = HTAnchor_isIndex(HTMainAnchor);
+} 
+
+/*
+ * HText_LinksInLines returns the number of links in the
+ * 'lines' number of lines beginning with 'line_num'-1. - FM
+ */
+PUBLIC int HText_LinksInLines ARGS3(HText *,text, int,line_num, int,lines)
+{
+    int total = 0;
+    int start = (line_num - 1);
+    int end = (start + lines);
+    TextAnchor *Anchor_ptr = NULL;
+
+    if (!text)
+        return total;
+
+    for (Anchor_ptr = text->first_anchor;
+		Anchor_ptr != NULL && Anchor_ptr->line_num <= end;
+			Anchor_ptr = Anchor_ptr->next) {
+	if (Anchor_ptr->line_num >= start &&
+	    Anchor_ptr->line_num < end &&
+	    Anchor_ptr->show_anchor)
+	    ++total;
+	if (Anchor_ptr == text->last_anchor)
+	    break;
+    }
+
+    return total;
+}
+
+PUBLIC void HText_setStale ARGS1(HText *,text)
+{
+    text->stale = YES;
+}
+
+PUBLIC void HText_refresh ARGS1(HText *,text)
+{
+    if (text->stale) display_page(text, text->top_of_screen, "");
+}
+
+PUBLIC int HText_sourceAnchors ARGS1(HText *,text)
+{
+    return (text ? text->last_anchor_number : -1);
+}
+
+PUBLIC BOOL HText_canScrollUp ARGS1(HText *,text)
+{
+    return (text->top_of_screen != 0);
+}
+
+PUBLIC BOOL HText_canScrollDown NOARGS
+{
+    HText * text = HTMainText;
+
+    return ((text->top_of_screen + display_lines) < text->lines+1);
+}
+
+/*		Scroll actions
+*/
+PUBLIC void HText_scrollTop ARGS1(HText *,text)
+{
+    display_page(text, 0, "");
+}
+
+PUBLIC void HText_scrollDown ARGS1(HText *,text)
+{
+    display_page(text, text->top_of_screen + display_lines, "");
+}
+
+PUBLIC void HText_scrollUp ARGS1(HText *,text)
+{
+    display_page(text, text->top_of_screen - display_lines, "");
+}
+
+PUBLIC void HText_scrollBottom ARGS1(HText *,text)
+{
+    display_page(text, text->lines - display_lines, "");
+}
+
+
+/*		Browsing functions
+**		==================
+*/
+
+/* Bring to front and highlight it
+*/
+PRIVATE int line_for_char ARGS2(HText *,text, int,char_num)
+{
+    int line_number = 0;
+    int characters = 0;
+    HTLine * line = text->last_line->next;
+    for (;;) {
+	if (line == text->last_line) return 0;	/* Invalid */
+        characters = characters + line->size + 1;
+	if (characters > char_num) return line_number;
+	line_number ++;
+	line = line->next;
+    }
+}
+
+PUBLIC BOOL HText_select ARGS1(HText *,text)
+{
+    if (text != HTMainText) {
+        HTMainText = text;
+	HTMainAnchor = text->node_anchor;
+	/*
+	 * Make this text the most current in the loaded texts list. - FM
+	 */
+	if (loaded_texts && HTList_removeObject(loaded_texts, text))
+	    HTList_addObject(loaded_texts, text);
+	  /* let lynx do it */
+	/* display_page(text, text->top_of_screen, ""); */
+    }
+    return YES;
+}
+
+PUBLIC BOOL HTFindPoundSelector ARGS1(char *,selector)
+{
+    TextAnchor * a;
+
+    for (a=HTMainText->first_anchor; a; a=a->next) {
+
+        if (a->anchor && a->anchor->tag)
+            if (!strcmp(a->anchor->tag, selector)) {
+
+                 www_search_result = a->line_num+1;
+                 if (TRACE) 
+		    fprintf(stderr, 
+		"HText: Selecting anchor [%d] at character %d, line %d\n",
+                                     a->number, a->start, www_search_result);
+		if (!strcmp(selector, LYToolbarName))
+		    --www_search_result;
+
+                 return(YES);
+            }
+    }
+
+    return(NO);
+
+}
+
+PUBLIC BOOL HText_selectAnchor ARGS2(HText *,text, HTChildAnchor *,anchor)
+{
+    TextAnchor * a;
+
+/* This is done later, hence HText_select is unused in GridText.c
+   Should it be the contrary ? @@@
+    if (text != HTMainText) {
+        HText_select(text);
+    }
+*/
+
+    for (a=text->first_anchor; a; a=a->next) {
+        if (a->anchor == anchor) break;
+    }
+    if (!a) {
+        if (TRACE) fprintf(stderr, "HText: No such anchor in this text!\n");
+        return NO;
+    }
+
+    if (text != HTMainText) {		/* Comment out by ??? */
+        HTMainText = text;		/* Put back in by tbl 921208 */
+	HTMainAnchor = text->node_anchor;
+    }
+
+    {
+	 int l = line_for_char(text, a->start);
+	if (TRACE) fprintf(stderr,
+	    "HText: Selecting anchor [%d] at character %d, line %d\n",
+	    a->number, a->start, l);
+
+	if ( !text->stale &&
+	     (l >= text->top_of_screen) &&
+	     ( l < text->top_of_screen + display_lines+1))
+	         return YES;
+
+	www_search_result = l - (display_lines/3); /* put in global variable */
+    }
+    
+    return YES;
+}
+ 
+
+/*		Editing functions		- NOT IMPLEMENTED
+**		=================
+**
+**	These are called from the application. There are many more functions
+**	not included here from the orginal text object.
+*/
+
+/*	Style handling:
+*/
+/*	Apply this style to the selection
+*/
+PUBLIC void HText_applyStyle ARGS2(HText *, me, HTStyle *,style)
+{
+    
+}
+
+
+/*	Update all text with changed style.
+*/
+PUBLIC void HText_updateStyle ARGS2(HText *, me, HTStyle *,style)
+{
+    
+}
+
+
+/*	Return style of  selection
+*/
+PUBLIC HTStyle * HText_selectionStyle ARGS2(
+	HText *,me,
+	HTStyleSheet *,sheet)
+{
+    return 0;
+}
+
+
+/*	Paste in styled text
+*/
+PUBLIC void HText_replaceSel ARGS3(
+	HText *,me,
+	CONST char *,aString, 
+	HTStyle *,aStyle)
+{
+}
+
+
+/*	Apply this style to the selection and all similarly formatted text
+**	(style recovery only)
+*/
+PUBLIC void HTextApplyToSimilar ARGS2(HText *,me, HTStyle *,style)
+{
+    
+}
+
+ 
+/*	Select the first unstyled run.
+**	(style recovery only)
+*/
+PUBLIC void HTextSelectUnstyled ARGS2(HText *,me, HTStyleSheet *,sheet)
+{
+    
+}
+
+
+/*	Anchor handling:
+*/
+PUBLIC void		HText_unlinkSelection ARGS1(HText *,me)
+{
+    
+}
+
+PUBLIC HTAnchor *	HText_referenceSelected ARGS1(HText *,me)
+{
+     return 0;   
+}
+
+
+PUBLIC int HText_getTopOfScreen NOARGS
+{
+      HText * text = HTMainText;
+      return text->top_of_screen;
+}
+
+PUBLIC int HText_getLines ARGS1(HText *,text)
+{
+      return text->lines;
+}
+
+PUBLIC HTAnchor * HText_linkSelTo ARGS2(HText *,me, HTAnchor *,anchor)
+{
+    return 0;
+}
+
+/* 
+ * Utility for freeing the list of previous isindex and whereis queries. - FM
+ */
+PUBLIC void HTSearchQueries_free NOARGS
+{
+    char *query;
+    HTList *cur = search_queries;
+
+    if (!cur)
+        return;
+
+    while (NULL != (query = (char *)HTList_nextObject(cur))) {
+	FREE(query);
+    }
+    HTList_delete(search_queries);
+    search_queries = NULL;
+    return;
+}
+
+/* 
+ * Utility for listing isindex and whereis queries, making
+ * any repeated queries the most current in the list. - FM
+ */
+PUBLIC void HTAddSearchQuery ARGS1(char *, query)
+{
+    char *new;
+    char *old;
+    HTList *cur;
+
+    if (!(query && *query))
+        return;
+
+    if ((new = (char *)calloc(1, (strlen(query) + 1))) == NULL)
+    	outofmem(__FILE__, "HTAddSearchQuery");
+    strcpy(new, query);
+
+    if (!search_queries) {
+        search_queries = HTList_new();
+	atexit(HTSearchQueries_free);
+	HTList_addObject(search_queries, new);
+	return;
+    }
+
+    cur = search_queries;
+    while (NULL != (old = (char *)HTList_nextObject(cur))) {
+	if (!strcmp(old, new)) {
+	    HTList_removeObject(search_queries, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(search_queries, new);
+
+    return;
+}
+
+PUBLIC int do_www_search ARGS1(document *,doc)
+{
+    char searchstring[256], temp[256], *cp, *tmpaddress = NULL;
+    int ch, recall, i;
+    int QueryTotal;
+    int QueryNum;
+    BOOLEAN PreviousSearch = FALSE;
+
+    /*
+     * Load the default query buffer
+     */
+    if ((cp=strchr(doc->address, '?')) != NULL) {
+        /*
+	 * This is an index from a previous search.
+	 * Use its query as the default.
+	 */
+	PreviousSearch = TRUE;
+	strcpy(searchstring, ++cp);
+	for (cp=searchstring; *cp; cp++)
+	    if (*cp == '+')
+	        *cp = ' ';
+	HTUnEscape(searchstring);
+	strcpy(temp, searchstring);
+	/*
+	 * Make sure it's treated as the most recent query. - FM
+	 */
+	HTAddSearchQuery(searchstring);
+    } else {
+        /*
+	 * New search; no default.
+	 */
+        searchstring[0] = '\0';
+	temp[0] = '\0';
+    }
+
+    /*
+     * Prompt for a query string.
+     */
+    if (searchstring[0] == '\0') {
+        if (HTMainAnchor->isIndexPrompt)
+            _statusline(HTMainAnchor->isIndexPrompt);
+	else
+            _statusline(ENTER_DATABASE_QUERY);
+    } else
+        _statusline(EDIT_CURRENT_QUERY);
+    QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
+    recall = (((PreviousSearch && QueryTotal >= 2) ||
+    	       (!PreviousSearch && QueryTotal >= 1)) ? RECALL : NORECALL);
+    QueryNum = QueryTotal;
+get_query:
+    if ((ch=LYgetstr(searchstring, VISIBLE,
+    		     sizeof(searchstring), recall)) < 0 ||
+        *searchstring == '\0' || ch == UPARROW || ch == DNARROW) {
+	if (recall && ch == UPARROW) {
+	    if (PreviousSearch) {
+	        /*
+		 * Use the second to last query in the list. - FM
+		 */
+	        QueryNum = 1;
+		PreviousSearch = FALSE;
+	    } else {
+	        /*
+		 * Go back to the previous query in the list. - FM
+		 */
+	        QueryNum++;
+	    }
+	    if (QueryNum >= QueryTotal)
+	        /*
+		 * Roll around to the last query in the list. - FM
+		 */
+	        QueryNum = 0;
+	    if ((cp=(char *)HTList_objectAt(search_queries,
+	    				    QueryNum)) != NULL) {
+		strcpy(searchstring, cp);
+		if (*temp && !strcmp(temp, searchstring)) {
+		    _statusline(EDIT_CURRENT_QUERY);
+		} else if ((*temp && QueryTotal == 2) ||
+			   (!(*temp) && QueryTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_QUERY);
+		} else {
+		    _statusline(EDIT_A_PREV_QUERY);
+		}
+		goto get_query;
+	    }
+	} else if (recall && ch == DNARROW) {
+	    if (PreviousSearch) {
+	        /*
+		 * Use the first query in the list. - FM
+		 */
+	        QueryNum = QueryTotal - 1;
+		PreviousSearch = FALSE;
+	    } else {
+	        /*
+		 * Advance to the next query in the list. - FM
+		 */
+	        QueryNum--;
+	    }
+	    if (QueryNum < 0)
+	        /*
+		 * Roll around to the first query in the list. - FM
+		 */
+		QueryNum = QueryTotal - 1;
+	    if ((cp=(char *)HTList_objectAt(search_queries,
+	    				    QueryNum)) != NULL) {
+		strcpy(searchstring, cp);
+		if (*temp && !strcmp(temp, searchstring)) {
+		    _statusline(EDIT_CURRENT_QUERY);
+		} else if ((*temp && QueryTotal == 2) ||
+			   (!(*temp) && QueryTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_QUERY);
+		} else {
+		    _statusline(EDIT_A_PREV_QUERY);
+		}
+		goto get_query;
+	    }
+	}
+
+        /*
+	 * Search cancelled.
+	 */
+        _statusline(CANCELLED);
+        sleep(InfoSecs);
+	return(NULLFILE);
+    }
+
+    /*
+     * Strip leaders and trailers. - FM
+     */
+    cp = searchstring;
+    while (*cp && isspace((unsigned char)*cp))
+        cp++;
+    if (!(*cp)) {
+        _statusline(CANCELLED);
+        sleep(InfoSecs);
+        return(NULLFILE);
+    }
+    if (cp > searchstring) {
+        for (i = 0; *cp; i++)
+	    searchstring[i] = *cp++;
+	searchstring[i] = '\0';
+    }
+    cp = searchstring + strlen(searchstring) - 1;
+    while ((cp > searchstring) && isspace((unsigned char)*cp))
+        *cp-- = '\0';
+
+    /*
+     * Don't resubmit the same query unintentionally.
+     */
+    if (!LYforce_no_cache && 0 == strcmp(temp, searchstring)) {
+	_statusline(USE_C_R_TO_RESUB_CUR_QUERY);
+	sleep(MessageSecs);
+	return(NULLFILE);
+    }
+
+    /*
+     * Add searchstring to the query list,
+     * or make it the most current. - FM
+     */
+    HTAddSearchQuery(searchstring);
+
+    /*
+     * Show the URL with the new query.
+     */
+    if ((cp=strchr(doc->address, '?')) != NULL)
+        *cp = '\0';
+    StrAllocCopy(tmpaddress, doc->address);
+    StrAllocCat(tmpaddress, "?");
+    StrAllocCat(tmpaddress, searchstring);
+    user_message(WWW_WAIT_MESSAGE, tmpaddress);
+#ifndef VMS
+#ifdef SYSLOG_REQUESTED_URLS
+    syslog(LOG_INFO|LOG_LOCAL5, "%s", tmpaddress);
+#endif /* SYSLOG_REQUESTED_URLS */
+#endif /* !VMS */
+    FREE(tmpaddress);
+    if (cp)
+        *cp = '?';
+
+    /*
+     * OK, now we do the search.
+     */
+    if (HTSearch(searchstring, HTMainAnchor)) {
+	/*
+	 *	Memory leak fixed.
+	 *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+	 */
+	auto char *cp_freeme = NULL;
+	if (traversal)
+	    cp_freeme = stub_HTAnchor_address((HTAnchor *)HTMainAnchor);
+	else
+	    cp_freeme = HTAnchor_address((HTAnchor *)HTMainAnchor);
+        StrAllocCopy(doc->address, cp_freeme);
+	FREE(cp_freeme);
+
+	if (TRACE)
+	    fprintf(stderr,"\ndo_www_search: newfile: %s\n",doc->address);
+
+        /*
+	 * Yah, the search succeeded.
+	 */
+	return(NORMAL);
+    }
+
+    /*
+     * Either the search failed (Yuk), or we got redirection.
+     * If it's redirection, use_this_url_instead is set, and
+     * mainloop() will deal with it such that security features
+     * and restrictions are checked before acting on the URL, or
+     * rejecting it. - FM
+     */
+    return(NOT_FOUND);
+}
+
+/* print the contents of the file in HTMainText to
+ * the file descripter fp.
+ * if is_reply is true add ">" to the beginning of each
+ * line to specify the file is a replied to message
+ */
+PUBLIC void print_wwwfile_to_fd ARGS2(FILE *,fp, int,is_reply)
+{
+      register int i;
+      HTLine * line = HTMainText->last_line->next;
+#ifdef VMS
+      extern BOOLEAN HadVMSInterrupt;
+#endif /* VMS */
+
+      for (;; line = line->next) {
+
+	  if (is_reply)
+             fputc('>',fp);
+
+            /* add offset */
+          for (i = 0; i < (int)line->offset; i++)
+             fputc(' ',fp);
+
+            /* add data */
+          for (i = 0; line->data[i] != '\0'; i++)
+             if (!IsSpecialAttrChar(line->data[i]))
+                 fputc(line->data[i],fp);
+	     else if (dump_output_immediately && use_underscore) {
+		switch (line->data[i]) {
+		    case LY_UNDERLINE_START_CHAR:
+		    case LY_UNDERLINE_END_CHAR:
+			fputc('_', fp);
+			break;
+		    case LY_BOLD_START_CHAR:
+		    case LY_BOLD_END_CHAR:
+			break;
+		}
+	     } 
+
+         /* add the return */
+         fputc('\n',fp);
+
+	 if (line == HTMainText->last_line)
+	    break;
+
+#ifdef VMS
+	if (HadVMSInterrupt)
+	    break;
+#endif /* VMS */
+    }
+
+}
+
+/* print the contents of the file in HTMainText to
+ * the file descripter fp.
+ * First output line is "thelink", ie, the URL for this file
+ */
+PUBLIC void print_crawl_to_fd ARGS3(FILE *, fp, char *, thelink,
+				    char *, thetitle)
+{
+    register int i;
+    HTLine * line = HTMainText->last_line->next;
+#ifdef VMS
+    extern BOOLEAN HadVMSInterrupt;
+#endif /* VMS */
+
+    fprintf(fp,"THE_URL:%s\n",thelink);
+    if (thetitle != NULL)fprintf(fp,"THE_TITLE:%s\n",thetitle);;
+      
+    for (;; line = line->next) {
+        /* add offset */
+        for (i = 0; i < (int)line->offset; i++)
+            fputc(' ',fp);
+
+        /* add data */
+        for (i = 0; line->data[i] != '\0'; i++)
+            if (!IsSpecialAttrChar(line->data[i]))
+                fputc(line->data[i],fp);
+
+        /* add the return */
+        fputc('\n',fp);
+
+	if (line == HTMainText->last_line)
+	    break;
+    }
+
+    /* add the References list if appropriate */
+    if (keypad_mode == LINKS_ARE_NUMBERED && !nolist)
+        printlist(fp,FALSE);
+
+#ifdef VMS
+    HadVMSInterrupt = FALSE;
+#endif /* VMS */
+}
+
+PUBLIC void www_user_search ARGS2(int,start_line, char *,target)
+{
+    register HTLine * line = HTMainText->last_line->next;
+    register int count;
+    extern BOOLEAN case_sensitive;
+
+    /* advance to the start line */
+    for (count = 1; count <= start_line; line = line->next, count++) {
+        if (line == HTMainText->last_line) {
+	    line = HTMainText->last_line->next; /* set to first line */
+	    count = 1;
+	    break;
+	}
+    }
+
+    for (;;) {
+	if (case_sensitive && LYno_attr_char_strstr(line->data, target)) {
+	    www_search_result=count;
+	    return;
+	} else if (!case_sensitive &&
+		   LYno_attr_char_case_strstr(line->data, target)) {
+	    www_search_result=count;
+	    return;
+	} else if (line == HTMainText->last_line) {  /* next line */
+	    break;
+	} else {			/* end */
+	    line = line->next;
+	    count++;
+	}
+    }
+
+	/* search from the beginning */
+    line = HTMainText->last_line->next; /* set to first line */
+    count = 1;
+
+    for (;;) {
+	    if (case_sensitive && LYno_attr_char_strstr(line->data, target)) {
+	        www_search_result=count;
+		return;
+	    } else if (!case_sensitive &&
+	    	       LYno_attr_char_case_strstr(line->data, target)) {
+	        www_search_result=count;
+		return;
+	    } else if (count > start_line) {  /* next line */
+    		_user_message("\"%s\" could not be found in this document",
+			      target);
+    		sleep(MessageSecs);
+	        return;			/* end */
+	    } else {
+	        line = line->next;
+		count++;
+	    }
+    }
+
+
+}
+
+PUBLIC  void  user_message ARGS2(char *,message, char *,argument) 
+{
+    char *temp = NULL;
+    char temp_arg[256];
+
+    if (message == NULL) {
+        mustshow = FALSE;
+	return;
+    }
+
+   /* make sure we don't overun any buffers */
+    LYstrncpy(temp_arg, ((argument == NULL) ? "" : argument), 255);
+    temp_arg[255] = '\0';
+    temp = (char *)malloc(strlen(message) + strlen(temp_arg) + 1);
+    if (temp == NULL)
+        outofmem(__FILE__, "user_message");
+    sprintf(temp, message, temp_arg);
+
+    statusline(temp);
+   
+    FREE(temp);
+    return;
+}
+
+/* HText_getOwner returns the owner of the
+ * current document
+ */
+PUBLIC char * HText_getOwner NOARGS
+{
+   return((char *)HTAnchor_owner(HTMainText->node_anchor));
+}
+
+/* HText_getRevTitle returns the RevTitle element of the
+ * current document, used as the subject for mailto comments
+ * to the owner.
+ */
+PUBLIC char * HText_getRevTitle NOARGS
+{
+   return((char *)HTAnchor_RevTitle(HTMainText->node_anchor));
+}
+
+PUBLIC void HTuncache_current_document NOARGS
+{
+    /* should remove current document from memory */
+    HTList_removeObject(loaded_texts, HTMainText);
+    HText_free(HTMainText);
+    HTMainText = NULL;
+}
+
+PUBLIC int HTisDocumentSource NOARGS
+{
+   return(HTMainText->source);
+}
+
+PUBLIC char * HTLoadedDocumentURL NOARGS
+{
+   if (!HTMainText)
+	return ("");
+
+   if (HTMainText->node_anchor && HTMainText->node_anchor->address) 
+       	return(HTMainText->node_anchor->address);
+   else
+	return ("");
+}
+
+PUBLIC char * HTLoadedDocumentPost_data NOARGS
+{
+   if (!HTMainText)
+	return ("");
+
+   if (HTMainText->node_anchor && HTMainText->node_anchor->post_data) 
+       	return(HTMainText->node_anchor->post_data);
+   else
+	return ("");
+}
+
+PUBLIC char * HTLoadedDocumentTitle NOARGS
+{
+   if (!HTMainText)
+	return ("");
+
+   if (HTMainText->node_anchor && HTMainText->node_anchor->title) 
+       	return(HTMainText->node_anchor->title);
+   else
+	return ("");
+}
+
+PUBLIC BOOLEAN HTLoadedDocumentIsHEAD NOARGS
+{
+   if (!HTMainText)
+	return (FALSE);
+
+   if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD) 
+       	return(HTMainText->node_anchor->isHEAD);
+   else
+	return (FALSE);
+}
+
+PUBLIC int HText_LastLineSize ARGS1(HText *,text)
+{
+    if (!text || !text->last_line || !text->last_line->size)
+        return 0;
+    return HText_TrueLineSize(text->last_line);
+}
+
+PUBLIC int HText_PreviousLineSize ARGS1(HText *,text)
+{
+    HTLine * line;
+
+    if (!text || !text->last_line)
+        return 0;
+    if (!(line = text->last_line->prev))
+        return 0;
+    return HText_TrueLineSize(line);
+}
+
+PRIVATE int HText_TrueLineSize ARGS1(HTLine *,line)
+{
+    int i, true_size = 0;
+
+    if (!line || !line->size)
+        return 0;
+
+    for (i = 0; i < line->size; i++) {
+    	if (!IsSpecialAttrChar(line->data[i])) {
+	    true_size++;
+	}
+    }
+    return true_size;
+}
+
+PUBLIC void HText_NegateLineOne ARGS1(HText *,text)
+{
+    if (text) {
+        text->in_line_1 = NO;
+    }
+    return;
+}
+
+/*
+ * NOTE: This function presently is correct only if the
+ *	 alignment is HT_LEFT.  The offset is still zero,
+ *	 because that's not determined for HT_CENTER or
+ *	 HT_RIGHT until subsequent characters are received
+ *	 and split_line() is called. - FM
+ */
+PUBLIC int HText_getCurrentColumn ARGS1(HText *,text)
+{
+    int column = 0;
+
+    if (text) {
+        column = (text->in_line_1 ?
+		  (int)text->style->indent1st : (int)text->style->leftIndent)
+		  + HText_LastLineSize(text) + (int)text->last_line->offset;
+    }
+    return column;
+}
+
+PUBLIC int HText_getMaximumColumn ARGS1(HText *,text)
+{
+    int column = (LYcols-2);
+    if (text) {
+        column = ((int)text->style->rightIndent ? (LYcols-2) :
+		  ((LYcols-1) - (int)text->style->rightIndent));
+    }
+    return column;
+}
+
+/*
+ * NOTE: This function uses HText_getCurrentColumn() which
+ *	 presently is correct only if the alignment is
+ *	 HT_LEFT. - FM
+ */
+PUBLIC void HText_setTabID ARGS2(HText *,text, CONST char *,name)
+{
+    HTTabID * tab = NULL;
+    HTList * cur = text->tabs;
+    HTList * last = NULL;
+
+    if (!text || !name || !*name)
+    	return;
+
+    if (!cur) {
+        cur = text->tabs = HTList_new();
+    } else {
+        while (NULL != (tab = (HTTabID *)HTList_nextObject(cur))) {
+	    if (tab->name && !strcmp(tab->name, name))
+	        return; /* Already set.  Keep the first value. */
+	    last = cur;
+	}
+	cur = last;
+    }
+    if (!tab) { /* New name.  Create a new node */
+	tab = (HTTabID *)calloc(1, sizeof(HTTabID));
+	if (tab == NULL)
+	    outofmem(__FILE__, "HText_setTabID");
+	HTList_addObject(cur, tab);
+	StrAllocCopy(tab->name, name);
+    }
+    tab->column = HText_getCurrentColumn(text);
+    return;
+}
+
+PUBLIC int HText_getTabIDColumn ARGS2(HText *,text, CONST char *,name)
+{
+    int column = 0;
+    HTTabID * tab;
+    HTList * cur = text->tabs;
+
+    if (text && name && *name && cur) {
+        while (NULL != (tab = (HTTabID *)HTList_nextObject(cur))) {
+	    if (tab->name && !strcmp(tab->name, name))
+	        break;
+	}
+	if (tab)
+	    column = tab->column;
+    }
+    return column;
+}
+
+
+/*  Form methods
+ *    These routines are used to build forms consisting
+ *    of input fields 
+ */
+PRIVATE int HTFormMethod;
+PRIVATE char * HTFormAction = NULL;
+PRIVATE char * HTFormEnctype = NULL;
+PRIVATE char * HTFormTitle = NULL;
+
+PUBLIC void HText_beginForm ARGS4(
+	char *,		action,
+	char *,		method,
+	char *,		enctype,
+	char *,		title)
+{
+    HTFormMethod = URL_GET_METHOD;
+    HTFormNumber++;
+    HTFormFields = 0;
+
+    if (action != NULL) {
+	if (!strncmp(action, "mailto:", 7)) {
+	    HTFormMethod = URL_MAIL_METHOD;
+	}
+        StrAllocCopy(HTFormAction, action);
+    }
+    else
+	StrAllocCopy(HTFormAction, HTLoadedDocumentURL());
+    
+    if (method != NULL)
+	if (!strcasecomp(method,"post") && HTFormMethod != URL_MAIL_METHOD)
+	   HTFormMethod = URL_POST_METHOD;
+
+    if ((enctype != NULL) && *enctype)
+        StrAllocCopy(HTFormEnctype, enctype);
+    else
+        FREE(HTFormEnctype);
+
+    if ((title != NULL) && *title)
+        StrAllocCopy(HTFormTitle, title);
+    else
+        FREE(HTFormTitle);
+
+    if (TRACE)
+	fprintf(stderr,
+		"BeginForm: action:%s Method:%d%s%s%s%s\n",
+		HTFormAction, HTFormMethod,
+		(HTFormTitle ? " Title:" : ""),
+		(HTFormTitle ? HTFormTitle : ""),
+		(HTFormEnctype ? " Enctype:" : ""),
+		(HTFormEnctype ? HTFormEnctype : ""));
+}
+
+PUBLIC void HText_endForm ARGS1(HText *,text)
+{
+    if (HTFormFields == 1 && text && text->first_anchor) {
+        /*
+	 * Support submission of a single text input field in
+	 * the form via <return> instead of a submit botton. - FM
+	 */
+        TextAnchor * a = text->first_anchor;
+	/*
+	 * Go through list of anchors and get our input field. - FM
+	 */
+	while (1) {
+	    if (a->link_type == INPUT_ANCHOR &&
+	        a->input_field->number == HTFormNumber &&
+		a->input_field->type == F_TEXT_TYPE) {
+		/*
+		 * Got it.  Make it submitting. - FM
+		 */
+		a->input_field->submit_action = NULL;
+		StrAllocCopy(a->input_field->submit_action, HTFormAction);
+		if (HTFormEnctype != NULL)
+		    StrAllocCopy(a->input_field->submit_enctype,
+		    		 HTFormEnctype);
+		if (HTFormTitle != NULL)
+		    StrAllocCopy(a->input_field->submit_title, HTFormTitle);
+		a->input_field->submit_method = HTFormMethod;
+		a->input_field->type = F_TEXT_SUBMIT_TYPE;
+		break;
+	    }
+	    if (a == text->last_anchor)
+	        break;
+	    a = a->next;
+	}
+    }
+    FREE(HTCurSelectGroup);
+    FREE(HTCurSelectGroupSize);
+    FREE(HTCurSelectedOptionValue);
+    FREE(HTFormAction);
+    FREE(HTFormEnctype);
+    FREE(HTFormTitle);
+    HTFormFields = 0;
+}
+
+PUBLIC void HText_beginSelect ARGS3(char *,name, BOOLEAN,multiple, char *, size)
+{
+   /*
+    *  Save the group name.
+    */
+   StrAllocCopy(HTCurSelectGroup, name);
+
+   /*
+    *  If multiple then all options are actually checkboxes.
+    */
+   if (multiple)
+      HTCurSelectGroupType = F_CHECKBOX_TYPE;
+   /*
+    *  If not multiple then all options are radio buttons.
+    */
+   else
+      HTCurSelectGroupType = F_RADIO_TYPE;
+
+    /*
+     * Length of an option list.
+     */
+    StrAllocCopy(HTCurSelectGroupSize, size);
+
+   if (TRACE)
+       fprintf(stderr,"HText_beginSelect: name=%s type=%d size=%s\n",
+	       ((HTCurSelectGroup == NULL) ? 
+	       			  "<NULL>" : HTCurSelectGroup),
+		HTCurSelectGroupType,
+	       ((HTCurSelectGroupSize == NULL) ? 
+	       			      "<NULL>" : HTCurSelectGroupSize));
+} 
+
+/*
+**  We couln't set the value field for the previous option
+**  tag so we have to do it now.  Assume that the last anchor
+**  was the previous options tag.
+*/
+PUBLIC char * HText_setLastOptionValue ARGS5(HText *, text, char *, value,
+						char*, submit_value,
+						int, order, BOOLEAN, checked)
+{
+    char *cp;
+    unsigned char *tmp = NULL;
+    int number = 0, i, j;
+
+    if (!(text && text->last_anchor &&
+    	  text->last_anchor->link_type == INPUT_ANCHOR))
+	return NULL;
+
+    if (TRACE)
+	fprintf(stderr,
+		"Entering HText_setLastOptionValue: value:%s, checked:%s\n",
+		value, (checked ? "on" : "off"));
+
+    /*
+     *  Strip end spaces, newline is also whitespace.
+     */
+    if (*value) {
+        cp = &value[strlen(value)-1];
+        while ((cp >= value) && (isspace((unsigned char)*cp) ||
+       				 IsSpecialAttrChar((unsigned char)*cp)))
+            cp--;
+        *(cp+1) = '\0';
+    }
+
+    /*
+     *  Find first non space
+     */
+    cp = value;
+    while (isspace((unsigned char)*cp) ||
+   	   IsSpecialAttrChar((unsigned char)*cp))
+        cp++;
+
+    if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+        StrAllocCopy(text->last_anchor->input_field->value, cp);
+        /*
+	 *  Put the text on the screen as well.
+	 */
+        HText_appendText(text, cp);
+ 
+    } else {
+	/*
+	 *  Create a linked list of option values.
+	 */
+	OptionType * op_ptr = text->last_anchor->input_field->select_list;
+	OptionType * new_ptr = NULL;
+	BOOLEAN first_option = FALSE;
+
+	/*
+	 *  Deal with newlines or tabs.
+	 */
+	convert_to_spaces(value);
+
+	if (!op_ptr) {
+	    /*
+	     *  No option items yet.
+	     */
+	    new_ptr = text->last_anchor->input_field->select_list = 
+				(OptionType *) calloc(1,sizeof(OptionType));
+	    if (new_ptr == NULL)
+	        outofmem(__FILE__, "HText_setLastOptionValue");
+
+	    first_option = TRUE;
+	} else {
+	    while (op_ptr->next) {
+		number++;
+		op_ptr=op_ptr->next;
+	    }
+	    number++;  /* add one more */
+
+	    op_ptr->next = new_ptr =
+	    			(OptionType *) calloc(1,sizeof(OptionType));
+	    if (new_ptr == NULL)
+	        outofmem(__FILE__, "HText_setLastOptionValue");
+	}
+
+	new_ptr->name = NULL;
+	new_ptr->cp_submit_value = NULL;
+	new_ptr->next = NULL;
+	for (i = 0, j = 0; cp[i]; i++) {
+	    if (cp[i] == HT_NON_BREAK_SPACE ||
+	        cp[i] == HT_EM_SPACE) {
+		cp[j++] = ' ';
+	    } else if (cp[i] != LY_SOFT_HYPHEN &&
+	    	       !IsSpecialAttrChar((unsigned char)cp[i])) {
+		cp[j++] = cp[i];
+	    }
+	}
+	cp[j] = '\0';
+	if (HTCJK != NOCJK) {
+	    if (cp &&
+	        (tmp = (unsigned char *)calloc(1, strlen(cp)+1))) {
+		if (kanji_code == EUC) {
+		    TO_EUC((unsigned char *)cp, tmp);
+		} else if (kanji_code == SJIS) {
+		    TO_SJIS((unsigned char *)cp, tmp);
+		} else {
+		    for (i = 0, j = 0; cp[i]; i++) {
+		        if (cp[i] != '\033') {
+			    tmp[j++] = cp[i];
+			}
+		    }
+		}
+		StrAllocCopy(new_ptr->name, (CONST char *)tmp);
+		FREE(tmp);
+	    }
+	} else {
+	    StrAllocCopy(new_ptr->name, cp);
+	}
+	StrAllocCopy(new_ptr->cp_submit_value,
+	    	     (submit_value ? submit_value : new_ptr->name));
+	
+	if (first_option) {
+	    StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
+	    text->last_anchor->input_field->num_value = 0;
+	    text->last_anchor->input_field->value = 
+		text->last_anchor->input_field->select_list->name;
+	    text->last_anchor->input_field->orig_value = 
+		text->last_anchor->input_field->select_list->name;
+	    text->last_anchor->input_field->cp_submit_value = 
+		text->last_anchor->input_field->select_list->cp_submit_value;
+	    text->last_anchor->input_field->orig_submit_value = 
+		text->last_anchor->input_field->select_list->cp_submit_value;
+	} else {
+	    int newlen = strlen(new_ptr->name);
+	    int curlen = strlen(HTCurSelectedOptionValue);
+		/*
+		 *  Make the selected Option Value as long as
+		 *  the longest option.
+		 */
+	    if (newlen > curlen)
+		StrAllocCat(HTCurSelectedOptionValue,
+			    UNDERSCORES(newlen-curlen));
+	}
+
+	if (checked) {
+	    int curlen = strlen(new_ptr->name);
+	    int newlen = strlen(HTCurSelectedOptionValue);
+	    /*
+	     *  Set the default option as this one.
+	     */
+	    text->last_anchor->input_field->num_value = number;
+	    text->last_anchor->input_field->value = new_ptr->name;
+	    text->last_anchor->input_field->orig_value = new_ptr->name;
+	    text->last_anchor->input_field->cp_submit_value =
+	    			   new_ptr->cp_submit_value;
+	    text->last_anchor->input_field->orig_submit_value =
+	    			   new_ptr->cp_submit_value;
+	    StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
+	    if (newlen > curlen)
+		StrAllocCat(HTCurSelectedOptionValue,
+			    UNDERSCORES(newlen-curlen));
+	}
+	     
+	/*
+	 *  Return the selected Option value to be sent to the screen.
+	 */
+	if (order == LAST_ORDER) {
+	    /*
+	     *  Change the value.
+	     */
+	    text->last_anchor->input_field->size =
+	    			strlen(HTCurSelectedOptionValue); 
+	    return(HTCurSelectedOptionValue);
+	} else 
+	   return(NULL);
+    }
+
+    if (TRACE)
+	fprintf(stderr,"HText_setLastOptionValue: value=%s\n", value);
+
+    return(NULL);
+}
+
+/*
+ *  Assign a form input anchor
+ *  returns the number of charactors to leave blank
+ *  so that the input field can fit
+ */
+PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
+{
+	
+    TextAnchor * a = (TextAnchor *) calloc(1, sizeof(*a));
+    FormInfo * f = (FormInfo *) calloc(1, sizeof(*f)); 
+    char *cp_option = NULL;
+    char *IValue = NULL;
+    unsigned char *tmp = NULL;
+    int i, j;
+
+    if (TRACE)
+	fprintf(stderr,"Entering HText_beginInput\n");
+
+    if (a == NULL || f == NULL)
+        outofmem(__FILE__, "HText_beginInput");
+
+    a->start = text->chars + text->last_line->size;
+    a->line_pos = text->last_line->size;
+
+
+    /*
+     *  If this is a radio button, and it's the first with this name,
+     *  make sure it's checked by default.  Otherwise, if it's checked,
+     *  uncheck the default or any preceding radio button with this name
+     *  that was checked. - FM
+     */
+    if (I->name && I->type && !strcasecomp(I->type, "radio")) {
+        if (!text->last_anchor) {
+	    I->checked = TRUE;
+	} else {
+	    TextAnchor * b = text->first_anchor;
+	    int i = 0;
+	    while (1) {
+	        if (b->link_type == INPUT_ANCHOR &&
+		    b->input_field->type == F_RADIO_TYPE &&
+                    b->input_field->number == HTFormNumber) {
+		    if (!strcmp(b->input_field->name, I->name)) {
+			if (I->checked && b->input_field->num_value) {
+			    b->input_field->num_value = 0;
+			    StrAllocCopy(b->input_field->orig_value, "0");
+			    break;
+			}
+			i++;
+		    }
+		}
+		if (b == text->last_anchor) {
+		    if (i == 0)
+		       I->checked = TRUE;
+		    break;
+		}
+		b = b->next;
+	    }
+	}
+    }
+
+    if (text->last_anchor) {
+        text->last_anchor->next = a;
+    } else {
+        text->first_anchor = a;
+    }
+    a->next = 0;
+    a->anchor = NULL;
+    a->link_type = INPUT_ANCHOR;
+    a->show_anchor = YES;
+
+    a->hightext = NULL;
+    a->extent = 2;
+
+    a->input_field = f;
+
+    f->select_list = 0;
+    f->number = HTFormNumber;
+    f->disabled = I->disabled;
+    f->no_cache = NO;
+
+    HTFormFields++;
+
+
+    /*
+     *  Set the no_cache flag if the METHOD is POST. - FM
+     */
+    if (HTFormMethod == URL_POST_METHOD)
+        f->no_cache = TRUE;
+
+    /*
+     *  Disable if the ENCTYPE is multipart/form-data
+     *  until we add code to handle it. - FM
+     */
+    if (HTFormEnctype) {
+         if (!strcmp(HTFormEnctype, "multipart/form-data")) {
+	     f->disabled = YES;
+	 }
+    }
+
+    /*
+     *  Set up VALUE.
+     */
+    if (I->value)
+        StrAllocCopy(IValue, I->value);
+    if (IValue && HTCJK != NOCJK) {
+	if ((tmp = (unsigned char *)calloc(1, strlen(IValue)+1))) {
+	    if (kanji_code == EUC) {
+		TO_EUC((unsigned char *)IValue, tmp);
+	    } else if (kanji_code == SJIS) {
+		TO_SJIS((unsigned char *)IValue, tmp);
+	    } else {
+		for (i = 0, j = 0; IValue[i]; i++) {
+		    if (IValue[i] != '\033') {
+		        tmp[j++] = IValue[i];
+		    }
+		}
+	    }
+	    StrAllocCopy(IValue, (CONST char *)tmp);
+	    FREE(tmp);
+	}
+    }
+
+    /*
+     *  Special case of OPTION.
+     */
+    /* set the values and let the parsing below do the work */
+    if (I->type != NULL && !strcmp(I->type,"OPTION")) {
+	cp_option = I->type;
+ 	if (HTCurSelectGroupType == F_RADIO_TYPE)
+	    I->type = "OPTION_LIST";
+	else
+	    I->type = "CHECKBOX";
+	I->name = HTCurSelectGroup;
+
+	/*
+	 *  The option's size parameter actually gives the length and not
+	 *    the width of the list.  Perform the conversion here
+	 *    and get rid of the allocated HTCurSelect....
+	 *  0 is ok as it means any length (arbitrary decision).
+	 */
+	if (HTCurSelectGroupSize != NULL) {
+	    f->size_l = atoi(HTCurSelectGroupSize);
+	    FREE(HTCurSelectGroupSize);
+	    HTCurSelectGroupSize = NULL;
+	}
+    }
+
+    /*
+     *  Set SIZE.
+     */
+    if (I->size != NULL) {
+	f->size = atoi(I->size);
+	/* Leave at zero for option lists.
+	 */
+	if (f->size == 0 && cp_option == NULL) {
+	   f->size = 20;  /* default */
+	}
+    } else {
+	f->size = 20;  /* default */
+    }
+
+    /*
+     *  Set MAXLENGTH.
+     */
+    if (I->maxlength != NULL) {
+	f->maxlength = atoi(I->maxlength);
+    } else {
+	f->maxlength = 0;  /* 0 means infinite */
+    }
+
+    /*
+     *  Set CHECKED
+     *  (num_value is only relevent to check and radio types).
+     */
+    if (I->checked == TRUE)
+ 	f->num_value = 1; 
+    else
+ 	f->num_value = 0;
+
+    /*
+     *  Set TYPE.
+     */
+    if (I->type != NULL) {
+	if (!strcasecomp(I->type,"password")) {
+	    f->type = F_PASSWORD_TYPE;
+	} else if (!strcasecomp(I->type,"checkbox")) {
+	    f->type = F_CHECKBOX_TYPE;
+	} else if (!strcasecomp(I->type,"radio")) {
+	    f->type = F_RADIO_TYPE;
+	} else if (!strcasecomp(I->type,"submit")) {
+	    f->type = F_SUBMIT_TYPE;
+	} else if (!strcasecomp(I->type,"image")) {
+	    /*
+	     *  Ugh, we have a clickable image submit button.
+	     *  Set the type to submit, and fake an ALT string.
+	     *  If the user activates it, we'll send a 0,0
+	     *  coordinate pair, which typically will return
+	     *  the image's default. FM
+	     */
+	    f->type = F_SUBMIT_TYPE;
+	    StrAllocCopy(f->value, "[IMAGE]-Submit");
+	} else if (!strcasecomp(I->type,"reset")) {
+	    f->type = F_RESET_TYPE;
+	} else if (!strcasecomp(I->type,"OPTION_LIST")) {
+	    f->type = F_OPTION_LIST_TYPE;
+	} else if (!strcasecomp(I->type,"hidden")) {
+	    f->type = F_HIDDEN_TYPE;
+	    HTFormFields--;
+	    f->size = 0;
+	} else if (!strcasecomp(I->type,"textarea")) {
+	    f->type = F_TEXTAREA_TYPE;
+	} else if (!strcasecomp(I->type,"range")) {
+	    f->type = F_RANGE_TYPE;
+	} else if (!strcasecomp(I->type,"file")) {
+	    f->type = F_FILE_TYPE;
+	} else {
+	    /* Note that TYPE="scribble" defaults to TYPE="text". - FM */
+	    f->type = F_TEXT_TYPE; /* default */
+	}
+    } else {
+	f->type = F_TEXT_TYPE;
+    }
+
+    /*
+     *  Set NAME.
+     */
+    if (I->name != NULL) {
+        StrAllocCopy(f->name,I->name);
+    } else {
+	if (f->type == F_RESET_TYPE || f->type == F_SUBMIT_TYPE) {
+	    /* set name to empty string */
+	    StrAllocCopy(f->name, "");
+	} else {
+	    /* error! name must be present */
+	    if (TRACE)
+		fprintf(stderr,
+		  "GridText: No name present in input field; not displaying\n");
+	    FREE(a);
+	    FREE(f);
+	    FREE(IValue);
+	    return(0);
+	}
+    }
+
+    /* 
+     *  Set VALUE (if it exists).
+     */
+    if (IValue != NULL) {
+	/*
+	 *  OPTION VALUE is not actually the value to be seen but is to
+	 *    be sent....
+	 */
+	if (f->type != F_OPTION_LIST_TYPE && f->type != F_CHECKBOX_TYPE) {
+	    StrAllocCopy(f->value, IValue);
+	} else {
+	    /*
+	     *  Fill both with the value.  The f->value may be
+	     *  overwritten in HText_setLastOptionValue....
+	     */
+	    if (!f->value || strcmp(f->value, "[IMAGE]-Submit"))
+		StrAllocCopy(f->value, IValue);
+	    StrAllocCopy(f->cp_submit_value, IValue);
+	}
+    } else if (!f->value || strcmp(f->value, "[IMAGE]-Submit")) {
+        if (f->type != F_OPTION_LIST_TYPE) {
+	    StrAllocCopy(f->value, "");
+	}
+    }
+
+    /*
+     *  Run checks and fill in neccessary values.
+     */
+    if (f->type == F_RESET_TYPE) {
+	if (IValue != NULL) {
+	    f->size = strlen(IValue);
+	} else {
+	    StrAllocCopy(f->value, "Reset");
+	    f->size = 5;
+	}
+    } else if (f->type == F_SUBMIT_TYPE) {
+        if (f->value && !strcmp(f->value, "[IMAGE]-Submit")) {
+	    f->size = strlen(f->value);
+	} else if (IValue != NULL) {
+	    f->size = strlen(IValue);
+	} else {
+	    StrAllocCopy(f->value, "Submit");
+	    f->size = 6;
+	}
+	f->submit_action = NULL;
+	StrAllocCopy(f->submit_action, HTFormAction);
+	if (HTFormEnctype != NULL)
+	    StrAllocCopy(f->submit_enctype, HTFormEnctype);
+	if (HTFormTitle != NULL)
+	    StrAllocCopy(f->submit_title, HTFormTitle);
+	f->submit_method = HTFormMethod;
+
+    } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE ) {
+	f->size=3;
+	if (IValue == NULL)
+	   StrAllocCopy(f->value, "on");
+
+    }
+    FREE(IValue); 
+
+    /*
+     *  Set original values.
+     */
+    if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE ) {
+	if (f->num_value)
+            StrAllocCopy(f->orig_value, "1");
+	else
+            StrAllocCopy(f->orig_value, "0");
+    } else if (f->type == F_OPTION_LIST_TYPE) {
+	f->orig_value = NULL;
+    } else {
+        StrAllocCopy(f->orig_value, f->value);
+    }
+
+    /*
+     *  Restrict SIZE to maximum allowable size.
+     */
+    if (f->size > LYcols-10)
+	f->size = LYcols-10;  /* maximum */
+
+    /*
+     *  Add this anchor to the anchor list
+     */
+    text->last_anchor = a;
+
+    if (TRACE)
+	fprintf(stderr,"Input link: name=%s\nvalue=%s\nsize=%d\n",
+		 	f->name,
+			((f->value != NULL) ? f->value : ""),
+			f->size);
+	
+    /*
+     *  Return the SIZE of the input field.
+     */
+    return(f->size);
+}
+
+
+PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
+				   char *,link_name, char *, link_value)
+{
+    TextAnchor *anchor_ptr = HTMainText->first_anchor;
+    int form_number = submit_item->number;
+    FormInfo *form_ptr;
+    int len, i;
+    char *query = NULL;
+    char *escaped1 = NULL, *escaped2 = NULL;
+    int first_one = 1;
+    char *last_textarea_name = NULL;
+    char *previous_blanks = NULL;
+    BOOLEAN PlainText = FALSE;
+    BOOLEAN SemiColon = FALSE;
+
+    if (submit_item->submit_action) {
+        /*
+	 *  Set length plus breathing room.
+	 */
+        len = strlen(submit_item->submit_action) + 2048;
+    } else {
+	return;
+    }
+
+    /*
+     *  Go through list of anchors and get size first.
+     */
+    while (1) {
+        if (anchor_ptr->link_type == INPUT_ANCHOR) {
+   	    if (anchor_ptr->input_field->number == form_number) {
+
+	        form_ptr = anchor_ptr->input_field;
+	
+	        len += strlen(form_ptr->name)+10;
+		/*
+		 *	Calculate by the option submit value if present.
+		 */
+		if (form_ptr->cp_submit_value != NULL)	{
+			len += strlen(form_ptr->cp_submit_value) + 10;
+		}
+		else	{
+	        	len += strlen(form_ptr->value)+10;
+		}
+	        len += 32; /* plus and ampersand + safty net */
+
+	    } else if (anchor_ptr->input_field->number > form_number) {
+	        break;
+	    }
+	}
+
+	if (anchor_ptr == HTMainText->last_anchor)
+	    break;
+
+	anchor_ptr = anchor_ptr->next;
+    }
+
+    /*
+     *  Get query ready.
+     */
+    query = (char *)calloc (sizeof(char), len);
+    if (query == NULL)
+        outofmem(__FILE__, "HText_SubmitForm");
+
+    if (submit_item->submit_enctype &&
+	!strncasecomp(submit_item->submit_enctype, "text/plain", 10)) {
+	/*
+	 *  Do not hex escape, and use physical newlines
+	 *  to separate name=value pairs. - FM
+	 */
+	PlainText = TRUE;
+    } else if (submit_item->submit_enctype &&
+	       !strncasecomp(submit_item->submit_enctype,
+			     "application/sgml-form-urlencoded", 32)) {
+	/*
+	 *  Use semicolons instead of ampersands as the
+	 *  separators for name=value pairs. - FM
+	 */
+	SemiColon = TRUE;
+    }
+
+    if (submit_item->submit_method == URL_GET_METHOD) {
+       	strcpy (query, submit_item->submit_action);
+       	/*
+	 *  Method is GET.  Clip out any anchor in the current URL.
+	 */
+       	strtok (query, "#");
+       	/*
+	 *  Clip out any old query in the current URL.
+	 */
+       	strtok (query, "?");
+	/*
+	 *  Add the lead question mark for the new URL.
+	 */  
+	strcat(query,"?");
+    } else {
+        query[0] = '\0';
+	if (submit_item->submit_method != URL_MAIL_METHOD) {
+	    /*
+	     *  We are submitting POST content to a server,
+	     *  so load the post_content_type element. - FM
+	     */
+	    if (SemiColon == TRUE) {
+	        StrAllocCopy(doc->post_content_type,
+			     "application/sgml-form-urlencoded");
+	    } else if (PlainText == TRUE) {
+	        StrAllocCopy(doc->post_content_type,
+			     "text/plain");
+	    } else {
+	        StrAllocCopy(doc->post_content_type,
+	        	     "application/x-www-form-urlencoded");
+	    }
+
+	    /*
+	     *  Append the exended charset info if known, and it is
+	     *  not ISO-8859-1 or US-ASCII.  We'll assume the user
+	     *  has the matching character set selected, or a
+	     *  download offer would have been forced and we would
+	     *  not be processing the form here.  We don't yet want
+	     *  to do this unless the server indicated the charset
+	     *  in the original transmission, because otherwise it
+	     *  might be an old server and CGI script which will
+	     *  not parse out the extended charset info, and reject
+	     *  the POST Content-Type as invalid. - FM
+	     */
+	    if (HTMainText->node_anchor->charset != NULL &&
+	        *HTMainText->node_anchor->charset != '\0') {
+		if (strcasecomp(HTMainText->node_anchor->charset,
+				"iso-8859-1") &&
+		    strcasecomp(HTMainText->node_anchor->charset,
+				"us-ascii")) {
+		    StrAllocCat(doc->post_content_type, ";charset=");
+		    StrAllocCat(doc->post_content_type,
+				HTMainText->node_anchor->charset);
+		}
+	    }
+	}
+    }
+
+    /*
+     *  Reset anchor->ptr.
+     */
+    anchor_ptr = HTMainText->first_anchor;
+    /*
+     *  Go through list of anchors and assemble URL query.
+     */
+    while (1) {
+        if (anchor_ptr->link_type == INPUT_ANCHOR) {
+	    if (anchor_ptr->input_field->number == form_number) {
+
+                form_ptr = anchor_ptr->input_field;
+
+                switch(form_ptr->type) {
+
+	        case F_RESET_TYPE:
+		    break;
+
+	        case F_SUBMIT_TYPE:
+	        case F_TEXT_SUBMIT_TYPE:
+		    /*
+		     *  If it has a non-zero length name (e.g., because
+		     *  it's really a type="image" that's been converted
+		     *  to SUBMIT_TYPE, or one of multiple submit buttons,
+		     *  or a single type="text" that's been converted to
+		     *  a TEXT_SUBMIT_TYPE), include the name=value pair. - FM
+		     */
+		    if ((form_ptr->name && *form_ptr->name != '\0' &&
+		        !strcmp(form_ptr->name, link_name)) &&
+		       (form_ptr->type == F_TEXT_SUBMIT_TYPE ||
+		        (form_ptr->value && *form_ptr->value != '\0' &&
+		         !strcmp(form_ptr->value, link_value)))) {
+		        if (first_one) {
+                            first_one=FALSE;
+                        } else {
+			    if (PlainText) {
+			        strcat(query, "\n");
+			    } else if (SemiColon) {
+			        strcat(query, ";");
+			    } else {
+                                strcat(query, "&");
+			    }
+			}
+
+			if (PlainText) {
+			    StrAllocCopy(escaped1, (form_ptr->name ?
+			    			    form_ptr->name : ""));
+			} else {
+		            escaped1 = HTEscape(form_ptr->name,URL_XALPHAS);
+			}
+
+		        /*
+		         * Be sure to actually look at the option submit value.
+		         */
+		        if (form_ptr->cp_submit_value != NULL) {
+			    for (i = 0; form_ptr->cp_submit_value[i]; i++) {
+			        if (form_ptr->cp_submit_value[i] ==
+					HT_NON_BREAK_SPACE ||
+				    form_ptr->cp_submit_value[i] ==
+				    	HT_EM_SPACE) {
+				    if (PlainText) {
+				        form_ptr->cp_submit_value[i] = ' ';
+				    } else {
+				        form_ptr->cp_submit_value[i] = 160;
+				    }
+				} else if (form_ptr->cp_submit_value[i] ==
+					LY_SOFT_HYPHEN) {
+				    form_ptr->cp_submit_value[i] = 173;
+				}
+			    }
+			    if (PlainText) {
+			        StrAllocCopy(escaped2,
+					     (form_ptr->cp_submit_value ?
+					      form_ptr->cp_submit_value : ""));
+			    } else {
+		    	        escaped2 = HTEscapeSP(form_ptr->cp_submit_value,
+						      URL_XALPHAS);
+			    }
+		        } else {
+			    for (i = 0; form_ptr->value[i]; i++) {
+			        if (form_ptr->value[i] ==
+					HT_NON_BREAK_SPACE ||
+				    form_ptr->value[i] ==
+				    	HT_EM_SPACE) {
+				    if (PlainText) {
+				        form_ptr->value[i] = ' ';
+				    } else {
+				        form_ptr->value[i] = 160;
+				    }
+				} else if (form_ptr->value[i] ==
+					LY_SOFT_HYPHEN) {
+				    form_ptr->value[i] = 173;
+				}
+			    }
+			    if (PlainText) {
+			        StrAllocCopy(escaped2, (form_ptr->value ?
+							form_ptr->value : ""));
+			    } else {
+			        escaped2 = HTEscapeSP(form_ptr->value,
+						      URL_XALPHAS);
+			    }
+		        }
+
+			if (!strcmp(form_ptr->value, "[IMAGE]-Submit"))
+			    /*
+			     * It's a clickable image submit button.
+			     * Fake a 0,0 coordinate pair, which
+			     * typically returns the image's default. - FM
+			     */
+			    sprintf(&query[strlen(query)],
+				    "%s.x=0%s%s.y=0%s",
+				    escaped1,
+				    (PlainText ?
+				          "\n" : (SemiColon ?
+					  		";" : "&")),
+				    escaped1,
+				    ((PlainText && *escaped1) ?
+				    			 "\n" : ""));
+			else
+			    /*
+			     * It's a standard submit button.
+			     * Use the name=value pair. = FM
+			     */
+			    sprintf(&query[strlen(query)],
+				    "%s=%s%s%s",
+				    escaped1,
+				    (PlainText ?
+				          "\n" : ""),
+				    escaped2,
+				    ((PlainText && *escaped2) ?
+				    			 "\n" : ""));
+		        FREE(escaped1);
+		        FREE(escaped2);
+		    }
+		    break;
+
+	        case F_RADIO_TYPE:
+                case F_CHECKBOX_TYPE:
+		    /* only add if selected */
+		    if (form_ptr->num_value) {
+	                if (first_one) {
+		            first_one=FALSE;
+	                } else {
+			    if (PlainText) {
+			        strcat(query, "\n");
+			    } else if (SemiColon) {
+			        strcat(query, ";");
+			    } else {
+		                strcat(query, "&");
+			    }
+			}
+
+			if (PlainText) {
+			    StrAllocCopy(escaped1, (form_ptr->name ?
+			    			    form_ptr->name : ""));
+			} else {
+		            escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
+			}
+			/*
+			 *	Be sure to use the submit option value.
+			 */
+			if (form_ptr->cp_submit_value != NULL) {
+			    for (i = 0; form_ptr->cp_submit_value[i]; i++) {
+			        if (form_ptr->cp_submit_value[i] ==
+					HT_NON_BREAK_SPACE ||
+				    form_ptr->cp_submit_value[i] ==
+				    	HT_EM_SPACE) {
+				    if (PlainText) {
+				        form_ptr->cp_submit_value[i] = ' ';
+				    } else {
+				        form_ptr->cp_submit_value[i] = 160;
+				    }
+				 } else if (form_ptr->cp_submit_value[i] ==
+				    	LY_SOFT_HYPHEN) {
+				    form_ptr->cp_submit_value[i] = 173;
+				 }
+			    }
+			    if (PlainText) {
+			        StrAllocCopy(escaped2,
+					     (form_ptr->cp_submit_value ?
+					      form_ptr->cp_submit_value : ""));
+			    } else {
+			        escaped2 = HTEscapeSP(form_ptr->cp_submit_value,
+						      URL_XALPHAS);
+			    }
+			} else {
+			    for (i = 0; form_ptr->value[i]; i++) {
+				if (form_ptr->value[i] ==
+					HT_NON_BREAK_SPACE ||
+				    form_ptr->value[i] ==
+				    	HT_EM_SPACE) {
+				    if (PlainText) {
+				        form_ptr->value[i] = ' ';
+				    } else {
+				        form_ptr->value[i] = 160;
+				    }
+				} else if (form_ptr->value[i] ==
+				    	LY_SOFT_HYPHEN) {
+				    form_ptr->value[i] = 173;
+
+				}
+			    }
+			    if (PlainText) {
+			        StrAllocCopy(escaped2, (form_ptr->value ?
+							form_ptr->value : ""));
+			    } else {
+		                escaped2 = HTEscapeSP(form_ptr->value,
+						      URL_XALPHAS);
+			    }
+			}
+
+                        sprintf(&query[strlen(query)],
+				"%s=%s%s%s",
+				escaped1,
+				(PlainText ?
+				      "\n" : ""),
+				escaped2,
+				((PlainText && *escaped2) ?
+						     "\n" : ""));
+		        FREE(escaped1);
+		        FREE(escaped2);
+		    }
+		    break;
+		
+		case F_TEXTAREA_TYPE:
+		    for (i = 0; form_ptr->value[i]; i++) {
+			if (form_ptr->value[i] == HT_NON_BREAK_SPACE ||
+			    form_ptr->value[i] == HT_EM_SPACE) {
+			    if (PlainText) {
+			        form_ptr->value[i] = ' ';
+			    } else {
+			        form_ptr->value[i] = 160;
+			    }
+			} else if (form_ptr->value[i] == LY_SOFT_HYPHEN) {
+			    form_ptr->value[i] = 173;
+			}
+		    }
+		    if (PlainText) {
+		        StrAllocCopy(escaped2, (form_ptr->value ? 
+						form_ptr->value : ""));
+		    } else {
+                        escaped2 = HTEscapeSP(form_ptr->value, URL_XALPHAS);
+		    }
+
+		    if (!last_textarea_name || 
+			strcmp(last_textarea_name, form_ptr->name)) {
+			/*
+			 *  Names are different so this is the first
+			 *  textarea or a different one from any before
+			 *  it.
+			 */
+			FREE(previous_blanks);
+		        if (first_one) {
+                            first_one=FALSE;
+                        } else {
+			    if (PlainText) {
+			        strcat(query, "\n");
+			    } else if (SemiColon) {
+			        strcat(query, ";");
+			    } else {
+                                strcat(query, "&");
+			    }
+			}
+			if (PlainText) {
+			    StrAllocCopy(escaped1, (form_ptr->name ?
+			    			    form_ptr->name : ""));
+			} else {
+                            escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
+			}
+                        sprintf(&query[strlen(query)],
+				"%s=%s%s%s",
+				escaped1,
+				(PlainText ?
+				      "\n" : ""),
+				escaped2,
+				((PlainText && *escaped2) ?
+						     "\n" : ""));
+                        FREE(escaped1);
+			last_textarea_name = form_ptr->name;
+		    } else {
+			/*
+			 *  This is a continuation of a previous textarea
+			 *  add %0a (\n) and the escaped string.
+			 */
+			if (escaped2[0] != '\0') {
+			    if (previous_blanks) {
+				strcat(query, previous_blanks);
+				FREE(previous_blanks);
+			    }
+			    if (PlainText) {
+			        sprintf(&query[strlen(query)], "%s\n",
+							       escaped2);
+			    } else {
+			        sprintf(&query[strlen(query)], "%%0a%s",
+							       escaped2);
+			    }
+			} else {
+			    if (PlainText) {
+			        StrAllocCat(previous_blanks, "\n");
+			    } else {
+			        StrAllocCat(previous_blanks, "%0a");
+			    }
+			}
+		    }
+                    FREE(escaped2);
+                    break;
+
+                case F_PASSWORD_TYPE:
+	        case F_TEXT_TYPE:
+		case F_OPTION_LIST_TYPE:
+		case F_HIDDEN_TYPE:
+	            if (first_one) {
+		        first_one=FALSE;
+	            } else {
+		        if (PlainText) {
+			    strcat(query, "\n");
+			} else if (SemiColon) {
+			    strcat(query, ";");
+			} else {
+		            strcat(query, "&");
+			}
+		    }
+    
+		    if (PlainText) {
+		       StrAllocCopy(escaped1, (form_ptr->name ?
+		       			       form_ptr->name : ""));
+		    } else {
+		        escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
+		    }
+
+		    /*
+		     *	Be sure to actually look at the option submit value.
+		     */
+		    if (form_ptr->cp_submit_value != NULL) {
+			for (i = 0; form_ptr->cp_submit_value[i]; i++) {
+			    if (form_ptr->cp_submit_value[i] ==
+			    		HT_NON_BREAK_SPACE ||
+			        form_ptr->cp_submit_value[i] ==
+					HT_EM_SPACE) {
+				if (PlainText) {
+				    form_ptr->cp_submit_value[i] = ' ';
+				} else {
+				    form_ptr->cp_submit_value[i] = 160;
+				}
+			    } else if (form_ptr->cp_submit_value[i] ==
+					LY_SOFT_HYPHEN) {
+				form_ptr->cp_submit_value[i] = 173;
+			    }
+			}
+			if (PlainText) {
+			    StrAllocCopy(escaped2,
+			    		 (form_ptr->cp_submit_value ?
+					  form_ptr->cp_submit_value : ""));
+			} else {
+		    	    escaped2 = HTEscapeSP(form_ptr->cp_submit_value,
+					          URL_XALPHAS);
+			}
+		    } else {
+			for (i = 0; form_ptr->value[i]; i++) {
+			    if (form_ptr->value[i] ==
+			    		HT_NON_BREAK_SPACE ||
+			        form_ptr->value[i] ==
+					HT_EM_SPACE) {
+				if (PlainText) {
+				    form_ptr->value[i] = ' ';
+				} else {
+				    form_ptr->value[i] = 160;
+				}
+			    } else if (form_ptr->value[i] ==
+					LY_SOFT_HYPHEN) {
+				form_ptr->value[i] = 173;
+			    }
+			}
+			if (PlainText) {
+			    StrAllocCopy(escaped2, (form_ptr->value ?
+			    			    form_ptr->value : ""));
+			} else {
+			    escaped2 = HTEscapeSP(form_ptr->value,
+			    			  URL_XALPHAS);
+			}
+		    }
+
+                    sprintf(&query[strlen(query)],
+		    	    "%s=%s%s%s",
+			    escaped1,
+			    (PlainText ?
+				  "\n" : ""),
+			    escaped2,
+			    ((PlainText && *escaped2) ?
+		    				 "\n" : ""));
+		    FREE(escaped1);
+		    FREE(escaped2);
+		    break;
+	        }
+	    } else if (anchor_ptr->input_field->number > form_number) {
+	        break;
+	    }
+        }
+
+        if (anchor_ptr == HTMainText->last_anchor)
+            break;
+
+	anchor_ptr = anchor_ptr->next;
+    }
+    FREE(previous_blanks);
+
+    if (submit_item->submit_method == URL_MAIL_METHOD) {
+	if (strncmp(submit_item->submit_action, "mailto:", 7)) {
+	    HTAlert(BAD_FORM_MAILTO);
+	    FREE(query);
+	    return;
+	}
+	_user_message("submitting %s", submit_item->submit_action);
+	if (TRACE) {
+	    fprintf(stderr, "\nGridText - mailto_address: %s\n",
+	    		    (submit_item->submit_action+7));
+	    fprintf(stderr, "GridText - mailto_subject: %s\n",
+	    		    ((submit_item->submit_title &&
+			      *submit_item->submit_title) ?
+			      (submit_item->submit_title) :
+			      	        (HText_getTitle() ?
+				         HText_getTitle() : "")));
+	    fprintf(stderr,"GridText - mailto_content: %s\n",query);
+	}
+	sleep(MessageSecs);
+	mailform((submit_item->submit_action+7),
+		 ((submit_item->submit_title &&
+		   *submit_item->submit_title) ?
+		   (submit_item->submit_title) :
+		   	     (HText_getTitle() ?
+			      HText_getTitle() : "")),
+		 query);
+	FREE(query);
+	return;
+    } else {
+        _statusline(SUBMITTING_FORM);
+    }
+   
+    if (submit_item->submit_method == URL_POST_METHOD) {
+        StrAllocCopy(doc->post_data, query);
+        if (TRACE)
+	    fprintf(stderr,"GridText - post_data: %s\n",doc->post_data);
+        StrAllocCopy(doc->address, submit_item->submit_action);
+        FREE(query);
+        return;
+    } else { /* GET_METHOD */ 
+        StrAllocCopy(doc->address, query);
+        FREE(doc->post_data);
+        FREE(doc->post_content_type);
+        FREE(query);
+        return;
+    }
+}
+
+PUBLIC void HText_ResetForm ARGS1(FormInfo *,form)
+{
+    TextAnchor * anchor_ptr = HTMainText->first_anchor;
+
+    _statusline(RESETTING_FORM);
+
+   /* go through list of anchors and reset values */
+   while (1) {
+        if (anchor_ptr->link_type == INPUT_ANCHOR) {
+            if (anchor_ptr->input_field->number == form->number) {
+
+                 if (anchor_ptr->input_field->type == F_RADIO_TYPE ||
+                     anchor_ptr->input_field->type == F_CHECKBOX_TYPE) {
+
+		    if (anchor_ptr->input_field->orig_value[0] == '0')
+		        anchor_ptr->input_field->num_value = 0;
+		    else
+		        anchor_ptr->input_field->num_value = 1;
+		
+		 } else if (anchor_ptr->input_field->type ==
+		 	    F_OPTION_LIST_TYPE) {
+		    anchor_ptr->input_field->value =
+				anchor_ptr->input_field->orig_value;
+		    
+		    anchor_ptr->input_field->cp_submit_value =
+		    		anchor_ptr->input_field->orig_submit_value;
+
+	         } else {
+		    StrAllocCopy(anchor_ptr->input_field->value,
+					anchor_ptr->input_field->orig_value);
+		 }
+	     } else if (anchor_ptr->input_field->number > form->number) {
+                 break;
+	     }
+
+        }
+
+        if (anchor_ptr == HTMainText->last_anchor)
+            break;
+
+
+        anchor_ptr = anchor_ptr->next;
+   }
+
+
+}
+
+PUBLIC void HText_activateRadioButton ARGS1(FormInfo *,form)
+{
+    TextAnchor * anchor_ptr = HTMainText->first_anchor;
+    int form_number = form->number;
+
+    while (1) {
+        if (anchor_ptr->link_type == INPUT_ANCHOR &&
+                anchor_ptr->input_field->type == F_RADIO_TYPE) {
+                    
+	    if (anchor_ptr->input_field->number == form_number) {
+
+		    /* if it has the same name and its on */
+	         if (!strcmp(anchor_ptr->input_field->name, form->name) &&
+		     anchor_ptr->input_field->num_value) {
+		    anchor_ptr->input_field->num_value = 0;
+		    break;
+	         }
+	    } else if (anchor_ptr->input_field->number > form_number) {
+	            break;
+	    }
+
+        }
+
+        if (anchor_ptr == HTMainText->last_anchor)
+            break;
+
+        anchor_ptr = anchor_ptr->next;
+   }
+
+   form->num_value = 1;
+}
+
+/*
+ *	Purpose:	Free all currently loaded HText objects in memory.
+ *	Arguments:	void
+ *	Return Value:	void
+ *	Remarks/Portability/Dependencies/Restrictions:
+ *		Usage of this function should really be limited to program
+ *			termination.
+ *	Revision History:
+ *		05-27-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+PRIVATE void free_all_texts NOARGS
+{
+    HText *cur = NULL;
+
+    if (!loaded_texts)
+	return;
+
+    /*
+     *  Simply loop through the loaded texts list killing them off.
+     */
+    while (loaded_texts && !HTList_isEmpty(loaded_texts)) {
+        if ((cur = (HText *)HTList_removeLastObject(loaded_texts)) != NULL) {
+	    HText_free(cur);
+	}
+    }
+
+    /*
+     *  Get rid of the text list.
+     */
+    if (loaded_texts) {
+	HTList_delete(loaded_texts);
+    }
+
+    /*
+     *  Insurance for bad HTML.
+     */
+    FREE(HTCurSelectGroup);
+    FREE(HTCurSelectGroupSize);
+    FREE(HTCurSelectedOptionValue);
+    FREE(HTFormAction);
+    FREE(HTFormEnctype);
+    FREE(HTFormTitle);
+
+    return;
+}
+
+/*
+**  stub_HTAnchor_address is like HTAnchor_address, but it returns the
+**  parent address for child links.  This is only useful for traversal's
+**  where one does not want to index a text file N times, once for each
+**  of N internal links.  Since the parent link has already been taken,
+**  it won't go again, hence the (incorrect) links won't cause problems.
+*/
+PUBLIC char * stub_HTAnchor_address ARGS1 (HTAnchor *,me)
+{
+    char *addr = NULL;
+    if (me)
+	StrAllocCopy (addr, me->parent->address);
+    return addr;
+}
+
+PUBLIC void HText_setToolbar ARGS1 (HText *, text)
+{
+    if (text)
+        text->toolbar = TRUE;
+    return;
+}
+
+PUBLIC BOOL HText_hasToolbar ARGS1 (HText *, text)
+{
+    return ((text && text->toolbar) ? TRUE : FALSE);
+}
+
+PUBLIC void HText_setNoCache ARGS1 (HText *, text)
+{
+    if (text)
+        text->no_cache = TRUE;
+    return;
+}
+
+PUBLIC BOOL HText_hasNoCacheSet ARGS1 (HText *, text)
+{
+    return ((text && text->no_cache) ? TRUE : FALSE);
+}
+
+/*
+**  Check charset and set the kcode element. - FM
+*/
+PUBLIC void HText_setKcode ARGS2(
+	HText *,	text,
+	CONST char *,	charset)
+{
+    if (!text)
+        return;
+
+    /*
+    **  Check whether we have a sepecified charset. - FM
+    */
+    if (!charset || *charset == '\0') {
+	return;
+    }
+
+    /*
+    **  We've included the charset, and not forced a download offer,
+    **  only if the currently selected character set can handle it,
+    **  so check the charset value and set the text->kcode element
+    **  appropriately. - FM
+    */
+    if (!strcmp(charset, "shift_jis")) {
+	text->kcode = SJIS;
+    } else if (!strcmp(charset, "euc-jp") ||
+	       !strcmp(charset, "iso-2022-jp") ||
+	       !strcmp(charset, "iso-2022-jp-2") ||
+	       !strcmp(charset, "euc-kr") ||
+	       !strcmp(charset, "iso-2022-kr") ||
+	       !strcmp(charset, "big5") ||
+	       !strcmp(charset, "euc-cn") ||
+	       !strcmp(charset, "gb2312") ||
+	       !strcmp(charset, "iso-2022-cn")) {
+	text->kcode = EUC;
+    } else {
+        /*
+	**  If we get to here, it's not CJK, so disable that. - FM
+	*/
+	text->kcode = NOKANJI;
+	HTCJK = NOCJK;
+    }
+
+    return;
+}
+