about summary refs log tree commit diff stats
path: root/src/GridText.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/GridText.c')
-rw-r--r--src/GridText.c545
1 files changed, 278 insertions, 267 deletions
diff --git a/src/GridText.c b/src/GridText.c
index a76c7faa..54bd8ebd 100644
--- a/src/GridText.c
+++ b/src/GridText.c
@@ -104,8 +104,6 @@ struct _HTStream {			/* only know it as object */
 #define UTF8_XNEGLEN(c) (c&0xC0? 0 :c&32? 1 :c&16? 2 :c&8? 3 :c&4? 4 :c&2? 5:0)
 #define UTF_XLEN(c) UTF8_XNEGLEN(((char)~(c)))
 
-extern BOOL HTPassHighCtrlRaw;
-
 #ifdef KANJI_CODE_OVERRIDE
 PUBLIC HTkcode last_kcode = NOKANJI;	/* 1997/11/14 (Fri) 09:09:26 */
 #endif
@@ -151,8 +149,9 @@ PUBLIC int LYsb_begin = -1;
 PUBLIC int LYsb_end = -1;
 #endif
 
-#if defined(USE_COLOR_STYLE)
-#define MAX_STYLES_ON_LINE 64
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
 
     /*try to fit in 2 shorts*/
 typedef struct {
@@ -161,49 +160,39 @@ typedef struct {
 	    /* horizontal position of this change */
 	unsigned short	style;		/* which style to change to */
 } HTStyleChange;
-#endif
 
-typedef struct _line {
-	struct _line	*next;
-	struct _line	*prev;
-	unsigned	offset;		/* Implicit initial spaces */
-	unsigned	size;		/* Number of characters */
 #if defined(USE_COLOR_STYLE)
-	HTStyleChange* styles;
-	int	numstyles;
+#define MAX_STYLES_ON_LINE   64
+  /* buffers used when current line is being aggregated, in split_line() */
+static HTStyleChange stylechanges_buffers[2][MAX_STYLES_ON_LINE];
 #endif
-	char	data[1];		/* Space for terminator at least! */
-} HTLine;
 
-#if defined(USE_COLOR_STYLE)
-typedef struct _HTStyleChangePool {
-	HTStyleChange	data[4092];
-	struct _HTStyleChangePool* next;
-	int free_items;
-} HTStyleChangePool;
-
-/*these are used when current line is being aggregated. */
-HTStyleChange stylechanges_buffers[2][MAX_STYLES_ON_LINE];
-int stylechanges_buffers_free;/*this is an index of the free buffer.
-    Can be 0 or 1*/
-
-/* These are generic macros for any pools (provided those structures have the
-same members as HTStyleChangePool).  Pools are used for allocation of groups of
+#define POOL_SIZE (8192 - 4*sizeof(void*) - sizeof(struct _HTPool*) + sizeof(int)) / sizeof(HTStyleChange)
+
+typedef struct _HTPool {
+    HTStyleChange   data[POOL_SIZE];
+    struct _HTPool* prev;
+    int used;
+} HTPool;
+
+/************************************************************************
+These are generic macros for any pools (provided those structures have the
+same members as HTPool).  Pools are used for allocation of groups of
 objects of the same type T.  Pools are represented as a list of structures of
 type P (called pool chunks here).  Structure P has an array of N objects of
 type T named 'data' (the number N in the array can be chosen arbitrary),
-pointer to the next pool chunk named 'pool', and the number of free items in
-that pool chunk named 'free_items'.  Here is a definition of the structure P:
+pointer to the previous pool chunk named 'prev', and the number of used items
+in that pool chunk named 'used'.  Here is a definition of the structure P:
 	struct P
 	{
 	    T data[N];
-	    struct P* next;
-	    int free_items;
+	    struct P* prev;
+	    int used;
 	};
  It's recommended that sizeof(P) be memory page size minus 32 in order malloc'd
 chunks to fit in machine page size.
- Allocation of 'n' items in the pool is implemented by decrementing member
-'free_items' by 'n' if 'free_items' >= 'n', or allocating a new pool chunk and
+ Allocation of 'n' items in the pool is implemented by incrementing member
+'used' by 'n' if (used+n <= N), or malloc a new pool chunk and
 allocating 'n' items in that new chunk.  It's the task of the programmer to
 assert that 'n' is <= N.  Only entire pool may be freed - this limitation makes
 allocation algorithms trivial and fast - so the use of pools is limited to
@@ -214,6 +203,9 @@ speed due to the simple algorithms used.  Due to the fact that memory is
 'allocated' in array, alignment overhead is minimal.  Allocating strings in a
 pool provided their length will never exceed N and is much smaller than N seems
 to be very efficient.
+ [Several types of memory-hungry objects are stored in the pool now:  styles,
+lines, anchors, and FormInfo. Arrays of HTStyleChange are stored as is,
+other objects are aligned to sizeof(void*) bytes and stored using a cast.]
 
  Pool are referenced by pointer to the chunk that contains free slots. Macros
 that allocate memory in pools update that pointer if needed.
@@ -222,7 +214,7 @@ ALLOC_IN_POOL.
  Here is a description of those macros as C++ functions (with names mentioned
 above and with use of C++ references)
 
-void ALLOC_IN_POOL( P*& pool, pool_type, int toalloc, T*& ptr)
+void ALLOC_IN_POOL( P*& pool, pool_type, int toalloc, T*& ptr, int align=1)
     - allocates 'toalloc' items in the pool of type 'pool_type' pointed by
     'pool', sets the pointer 'ptr' to the "allocated" memory and updates 'pool'
     if necessary. Sets 'ptr' to NULL if fails.
@@ -231,67 +223,104 @@ void POOL_NEW( pool_type  , P*& ptr)
     Initializes a pool of type 'pool_type' pointed by 'ptr', updating 'ptr'.
     Sets 'ptr' to NULL if fails.
 
-void POOL_FREE( pool_type , P* ptr)
-    Frees a pool of type 'pool_type' pointed by ptr.
+void POOL_FREE( pool_type , P*& ptr)
+    Frees a pool of type 'pool_type' pointed by ptr. Sets ptr to NULL.
 
-      - VH */
+      - VH
 
+*************************************************************************/
 /*
-void ALLOC_IN_POOL( P*& pool, pool_type, int toalloc, T*& ptr)
-    - allocates 'toalloc' items in the pool of type 'pool_type' pointed by
-    'pool', sets the pointer 'ptr' to the "allocated" memory and updates 'pool'
-    if necessary. Sets 'ptr' to NULL if fails.
-*/
-#define ALLOC_IN_POOL(pool,pool_type,toalloc,ptr)     \
-if (!pool)  \
-    ptr = NULL; \
-else { \
-    if ((pool)->free_items > toalloc) { \
-	(pool)->free_items -= toalloc; \
-	ptr = (pool)->data + (pool)->free_items; \
-    } else { \
-	pool_type* newpool = (pool_type*)malloc(sizeof(pool_type)); \
-	if (!newpool) { \
-	    ptr = NULL; \
-	} else { \
-	    newpool->next = pool; \
-	    newpool->free_items = sizeof newpool->data/ \
-		    sizeof newpool->data[0] - toalloc; \
-	    pool = newpool; \
-	    ptr = newpool->data + sizeof newpool->data/sizeof newpool->data[0] - toalloc; \
-	} \
-    } \
-}
-/*
-void POOL_NEW( pool_type  , P*& ptr)
-    Initializes a pool of type 'pool_type' pointed by 'ptr', updating 'ptr'.
-    Sets 'ptr' to NULL if fails.
-*/
-#define POOL_NEW(pool_type,ptr) \
-    { \
-	pool_type* newpool = (pool_type*)malloc(sizeof(pool_type)); \
-	if (!newpool) { \
-	    ptr = NULL; \
-	} else { \
-	    newpool->next = NULL; \
-	    newpool->free_items = sizeof newpool->data/sizeof newpool->data[0]; \
-	    ptr = newpool; \
-	} \
+ * void ALLOC_IN_POOL( P*& pool, pool_type, int toalloc, T*& ptr, int align=1)
+ *     - allocates 'toalloc' items in the pool of type 'pool_type' pointed by
+ *     'pool', sets the pointer 'ptr' to the "allocated" memory and updates
+ *     'pool' if necessary.  Sets 'ptr' to NULL if fails.
+ */
+#define ALLOC_IN_POOL(pool,pool_type,toalloc,ptr,align) \
+    if (!pool) {					\
+	ptr = NULL;					\
+    } else {						\
+	if (align)					\
+	    pool->used += (pool->used % align);		\
+	if (POOL_SIZE - pool->used >= toalloc) { 	\
+	    ptr = pool->data + pool->used;		\
+	    pool->used += toalloc;			\
+	} else {					\
+	    pool_type* newpool = (pool_type*)LY_CALLOC(1, sizeof(pool_type)); \
+	    if (!newpool) {				\
+		ptr = NULL;				\
+	    } else {					\
+		newpool->prev = pool;			\
+		newpool->used = toalloc;		\
+		ptr = newpool->data;			\
+		pool = newpool;				\
+	   }						\
+	}						\
     }
 /*
-void POOL_FREE( pool_type , P* ptr)
-    Frees a pool of type 'pool_type' pointed by ptr.
-*/
-#define POOL_FREE(pool_type,xptr) \
-    { \
-	pool_type* ptr = xptr; \
-	do { \
-	    pool_type* prevpool = ptr; \
-	    ptr = ptr->next; \
-	    FREE(prevpool); \
-	} while (ptr); \
+ * void POOL_NEW( pool_type  , P*& ptr)
+ *     Initializes a pool of type 'pool_type' pointed by 'ptr', updating 'ptr'.
+ *     Sets 'ptr' to NULL if fails.
+ */
+#define POOL_NEW(pool_type,ptr)				\
+    {							\
+	ptr = (pool_type*)LY_CALLOC(1, sizeof(pool_type)); \
+	if (ptr) {					\
+	    ptr->prev = NULL;				\
+	    ptr->used = 0;				\
+	}						\
     }
+/*
+ * void POOL_FREE( pool_type, P*& ptr)
+ *     Frees a pool of type 'pool_type' pointed by ptr. Sets ptr to NULL.
+ */
+#define POOL_FREE(pool_type,ptr)			\
+    {							\
+	pool_type* cur = ptr;				\
+	pool_type* prev;				\
+	while (cur) {					\
+	    prev = cur->prev;				\
+	    FREE(cur);					\
+	    cur = prev;					\
+	}						\
+	ptr = NULL;					\
+    }
+/**************************************************************************/
+
+#define _sz_        sizeof(HTStyleChange)      /* 4 */
+#define _align_     (sizeof(void*)/_sz_)       /*64bit OS!*/
+#define _round_(x)  (x%_sz_ ? x/_sz_ + 1: x/_sz_)
+
+#define POOLallocstyles(ptr, N)     ALLOC_IN_POOL(HTMainText->pool,HTPool,\
+					N,				\
+					ptr,				\
+					1)
+#define POOLallocHTLine(ptr, size)  { HTStyleChange* _tmp_;		\
+				      ALLOC_IN_POOL(HTMainText->pool,HTPool,\
+					_round_(LINE_SIZE(size)),	\
+					_tmp_,				\
+					_align_);			\
+				      ptr = (HTLine*)_tmp_;		\
+				    }
+#define POOLtypecalloc(T,ptr)	    { HTStyleChange* _tmp_;		\
+				      ALLOC_IN_POOL(HTMainText->pool,HTPool,\
+					_round_(sizeof(T)),		\
+					_tmp_,				\
+					_align_);			\
+				      ptr = (T*)_tmp_;			\
+				    }
+
+typedef struct _line {
+	struct _line    *next;
+	struct _line    *prev;
+	unsigned short	offset;	/* Implicit initial spaces */
+	unsigned short	size;	/* Number of characters */
+#if defined(USE_COLOR_STYLE)
+	HTStyleChange*  styles;
+	unsigned short  numstyles;
 #endif
+	char	data[1];	/* Space for terminator at least! */
+} HTLine;
+
 
 #define LINE_SIZE(l) (sizeof(HTLine)+(l))	/* Allow for terminator */
 #define allocHTLine(l) (HTLine *)calloc(1, LINE_SIZE(l))
@@ -300,15 +329,16 @@ typedef struct _TextAnchor {
 	struct _TextAnchor *	next;
 	struct _TextAnchor *	prev;		/* www_user_search only! */
 	int			number;		/* For user interface */
-	int			line_pos;	/* Bytes/chars - extent too */
-	int			extent;		/* (see HText_trimHightext) */
 	int			line_num;	/* Place in document */
-	HiliteList		lites;
-	int			link_type;	/* Normal, internal, or form? */
-	FormInfo *		input_field;	/* Info for form links */
+	short			line_pos;	/* Bytes/chars - extent too */
+	short			extent;		/* (see HText_trimHightext) */
 	BOOL			show_anchor;	/* Show the anchor? */
 	BOOL			inUnderline;	/* context is underlined */
 	BOOL			expansion_anch; /* TEXTAREA edit new anchor */
+	char			link_type;	/* Normal, internal, or form? */
+	FormInfo *		input_field;	/* Info for form links */
+	HiliteList		lites;
+
 	HTChildAnchor *		anchor;
 } TextAnchor;
 
@@ -325,27 +355,13 @@ typedef struct {
 */
 struct _HText {
 	HTParentAnchor *	node_anchor;
-#ifdef SOURCE_CACHE
-	/*
-	 * Parse settings when this HText was generated.
-	 */
-	BOOLEAN			clickable_images;
-	BOOLEAN			pseudo_inline_alts;
-	BOOLEAN			verbose_img;
-	BOOLEAN			raw_mode;
-	BOOLEAN			historical_comments;
-	BOOLEAN			minimal_comments;
-	BOOLEAN			soft_dquotes;
-	int			old_dtd;
-	int			keypad_mode;
-	int			disp_lines;	/* Screen size */
-	int			disp_cols;	/* Used for reports only */
-#endif
+
 	HTLine *		last_line;
 	int			Lines;		/* Number of them */
-	TextAnchor *		first_anchor;	/* Singly linked list */
+	TextAnchor *		first_anchor;	/* double-linked on demand */
 	TextAnchor *		last_anchor;
 	TextAnchor *		last_anchor_before_stbl;
+	TextAnchor *		last_anchor_before_split;
 	HTList *		forms;		/* also linked internally */
 	int			last_anchor_number;	/* user number */
 	BOOL			source;		/* Is the text source? */
@@ -389,20 +405,36 @@ struct _HText {
 	enum grid_state       { S_text, S_esc, S_dollar, S_paren,
 				S_nonascii_text, S_dollar_paren,
 				S_jisx0201_text }
-				state;			/* Escape sequence? */
-	int			kanji_buf;		/* Lead multibyte */
-	int			in_sjis;		/* SJIS flag */
-	int			halted;			/* emergency halt */
-
-	BOOL			have_8bit_chars;   /* Any non-ASCII chars? */
-	LYUCcharset *		UCI;		   /* node_anchor UCInfo */
-	int			UCLYhndl;	   /* charset we are fed */
+				state;		/* Escape sequence? */
+	int			kanji_buf;	/* Lead multibyte */
+	int			in_sjis;	/* SJIS flag */
+	int			halted;		/* emergency halt */
+
+	BOOL			have_8bit_chars; /* Any non-ASCII chars? */
+	LYUCcharset *		UCI;		/* node_anchor UCInfo */
+	int			UCLYhndl;	/* charset we are fed */
 	UCTransParams		T;
 
-	HTStream *		target;			/* Output stream */
-	HTStreamClass		targetClass;		/* Output routines */
-#if defined(USE_COLOR_STYLE)
-	HTStyleChangePool*	styles_pool;
+	HTStream *		target;		/* Output stream */
+	HTStreamClass		targetClass;	/* Output routines */
+
+	HTPool*			pool;		/* this HText memory pool */
+
+#ifdef SOURCE_CACHE
+	/*
+	* Parse settings when this HText was generated.
+	*/
+	BOOL			clickable_images;
+	BOOL			pseudo_inline_alts;
+	BOOL			verbose_img;
+	BOOL			raw_mode;
+	BOOL			historical_comments;
+	BOOL			minimal_comments;
+	BOOL			soft_dquotes;
+	short			old_dtd;
+	short			keypad_mode;
+	short			disp_lines;	/* Screen size */
+	short			disp_cols;	/* Used for reports only */
 #endif
 };
 
@@ -560,9 +592,9 @@ PRIVATE void LYAddHiText ARGS3(
     unsigned want = ++(a->lites.hl_len) * sizeof(HiliteInfo);
 
     if (have != NULL) {
-	have = realloc(have, want);
+	have = (HiliteInfo *) realloc(have, want);
     } else {
-	have = malloc(want);
+	have = (HiliteInfo *) malloc(want);
     }
     a->lites.hl_info = have;
 
@@ -696,7 +728,7 @@ PRIVATE void * LY_check_calloc ARGS2(
     }
     n = HTList_count(loaded_texts);
     for (i = n - 1; i > 0; i--) {
-	HText * t = HTList_objectAt(loaded_texts, i);
+	HText * t = (HText *) HTList_objectAt(loaded_texts, i);
 	if (t == HTMainText)
 	    t = NULL;		/* shouldn't happen */
 	{
@@ -853,6 +885,9 @@ PUBLIC HText *	HText_new ARGS1(
 #endif /* VMS && VAXC && !__DECC */
     }
 
+    POOL_NEW(HTPool, self->pool);
+    if (!self->pool)
+	outofmem(__FILE__, "HText_New");
     line = self->last_line = allocHTLine(MAX_LINE);
     if (line == NULL)
 	outofmem(__FILE__, "HText_New");
@@ -860,14 +895,11 @@ PUBLIC HText *	HText_new ARGS1(
     line->offset = line->size = 0;
 #ifdef USE_COLOR_STYLE
     line->numstyles = 0;
-    POOL_NEW(HTStyleChangePool,self->styles_pool);
-    if (!self->styles_pool)
-	outofmem(__FILE__, "HText_New");
-    stylechanges_buffers_free = 0;
     line->styles = stylechanges_buffers[0];
 #endif
     self->Lines = 0;
     self->first_anchor = self->last_anchor = NULL;
+    self->last_anchor_before_split = NULL;
     self->style = &default_style;
     self->top_of_screen = 0;
     self->node_anchor = anchor;
@@ -1033,26 +1065,6 @@ PUBLIC void HText_free ARGS1(
 	return;
 
     HTAnchor_setDocument(self->node_anchor, (HyperDoc *)0);
-#if defined(USE_COLOR_STYLE)
-    POOL_FREE(HTStyleChangePool,self->styles_pool);
-#endif
-    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 = self->last_line = NULL;
-	    break;
-	}
-    }
 
     while (self->first_anchor) {		/* Free off anchor array */
 	TextAnchor * l = self->first_anchor;
@@ -1102,13 +1114,9 @@ PUBLIC void HText_free ARGS1(
 	    FREE(l->input_field->submit_title);
 
 	    FREE(l->input_field->accept_cs);
-
-	    FREE(l->input_field);
 	}
 
 	LYSetHiText(l, NULL, 0);
-
-	FREE(l);
     }
     FormList_delete(self->forms);
 
@@ -1166,6 +1174,7 @@ PUBLIC void HText_free ARGS1(
 	    HTMainAnchor = NULL;
     }
 
+    POOL_FREE(HTPool, self->pool);
     FREE(self);
 }
 
@@ -1769,19 +1778,6 @@ PRIVATE void display_scrollbar ARGS1(
 #define display_scrollbar(text) /*nothing*/
 #endif /* USE_SCROLLBAR */
 
-/*
- * Utility to let us use for-loops on the anchor-pointers.
- */
-PRIVATE TextAnchor * next_anchor ARGS2(
-	HText *,	text,
-	TextAnchor *,	anchor_ptr)
-{
-    if (anchor_ptr == text->last_anchor)
-	anchor_ptr = NULL;
-    else
-	anchor_ptr = anchor_ptr->next;
-    return anchor_ptr;
-}
 
 /*	Output a page
 **	-------------
@@ -1846,6 +1842,7 @@ PRIVATE void display_page ARGS3(
 	 *  an xterm is temporarily made very small. - kw */
 	return;
     }
+
     last_screen = text->Lines - (display_lines - 2);
     line = text->last_line->prev;
 
@@ -2123,7 +2120,7 @@ PRIVATE void display_page ARGS3(
     nlinks = 0;
     for (Anchor_ptr = text->first_anchor;
 	 Anchor_ptr != NULL && Anchor_ptr->line_num <= stop_before_for_anchors;
-	 Anchor_ptr = next_anchor(text, Anchor_ptr)) {
+	 Anchor_ptr = Anchor_ptr->next) {
 
 	if (Anchor_ptr->line_num >= line_number
 	 && Anchor_ptr->line_num < stop_before_for_anchors) {
@@ -2167,7 +2164,7 @@ PRIVATE void display_page ARGS3(
 #ifndef DONT_TRACK_INTERNAL_LINKS
 			if (Anchor_ptr->link_type == INTERNAL_LINK_ANCHOR) {
 			    link_dest_intl = HTAnchor_followTypedLink(
-				(HTAnchor *)Anchor_ptr->anchor, LINK_INTERNAL);
+				(HTAnchor *)Anchor_ptr->anchor, HTInternalLink);
 			    if (link_dest_intl && link_dest_intl != link_dest) {
 
 				CTRACE((tfp,
@@ -2430,20 +2427,19 @@ PRIVATE void move_anchors_in_region ARGS7(
     int,		shift)		/* Likewise */
 {
     /*
-     *  Update anchor positions for anchors that start on this line.
-     *  Note: we rely on a->line_pos counting bytes, not
-     *  characters.  That's one reason why HText_trimHightext
-     *  has to be prevented from acting on these anchors in
-     *  partial display mode before we get a chance to
-     *  deal with them here.
+     * Update anchor positions for anchors that start on this line.  Note:  we
+     * rely on a->line_pos counting bytes, not characters.  That's one reason
+     * why HText_trimHightext has to be prevented from acting on these anchors
+     * in partial display mode before we get a chance to deal with them here.
      */
     TextAnchor *a;
     int head_processed = *prev_head_processed;
 
-    /* We need to know whether (*prev_anchor)->line_pos is "in new
-       coordinates" or in old ones.  If prev_anchor' head was touched
-       on the previous iteraction, we set head_processed.  The tail
-       may need to be treated now. */
+    /*
+     * We need to know whether (*prev_anchor)->line_pos is "in new coordinates"
+     * or in old ones.  If prev_anchor' head was touched on the previous
+     * iteration, we set head_processed.  The tail may need to be treated now.
+     */
     for (a = *prev_anchor;
 	 a && a->line_num <= line_number;
 	 a = a->next, head_processed = 0) {
@@ -2492,7 +2488,7 @@ PRIVATE void move_anchors_in_region ARGS7(
  *  Some necessary changes for anchors starting on this line are also done
  *  here if needed.
  *  Returns a newly allocated HTLine* if changes were made
- *    (caller has to free the old one).
+ *    (lines allocated in pool, caller should not free the old one).
  *  Returns NULL if no changes needed.  (Remove-spaces code may be buggy...)
  * - kw
  */
@@ -2536,7 +2532,7 @@ PRIVATE HTLine * insert_blanks_in_line ARGS7(
     if (!prev_anchor)
 	prev_anchor = text->first_anchor;
     head_processed = (prev_anchor && prev_anchor->line_num < line_number);
-    memcpy(mod_line, line, LINE_SIZE(1));
+    memcpy(mod_line, line, LINE_SIZE(0));
     t = newdata = mod_line->data;
     ip = 0;
     while (ip <= ninserts) {
@@ -2637,7 +2633,6 @@ PRIVATE void split_line ARGS2(
 	unsigned,	split)
 {
     HTStyle * style = text->style;
-    HTLine * temp;
     int spare;
     int indent = text->in_line_1 ?
 	  text->style->indent1st : text->style->leftIndent;
@@ -2657,10 +2652,12 @@ PRIVATE void split_line ARGS2(
     HTLine * line = (HTLine *)LY_CALLOC(1, LINE_SIZE(MAX_LINE)+2);
 
     /*
-     *  Make new line.
+     *  Set new line.
      */
     if (line == NULL)
 	outofmem(__FILE__, "split_line_1");
+    memset(line, 0, LINE_SIZE(0));
+
     ctrl_chars_on_this_line = 0; /*reset since we are going to a new line*/
     utfxtra_on_this_line = 0;	/*reset too, we'll count them*/
     text->LastChar = ' ';
@@ -2852,7 +2849,10 @@ PRIVATE void split_line ARGS2(
 #endif
 
 #if defined(USE_COLOR_STYLE)
-    line->styles = stylechanges_buffers[stylechanges_buffers_free = (stylechanges_buffers_free + 1) &1];
+    if (previous->styles == stylechanges_buffers[0])
+	line->styles = stylechanges_buffers[1];
+    else
+	line->styles = stylechanges_buffers[0];
     line->numstyles = 0;
     {
 	HTStyleChange *from = previous->styles + previous->numstyles - 1;
@@ -2946,18 +2946,20 @@ PRIVATE void split_line ARGS2(
     }
 #endif /*USE_COLOR_STYLE*/
 
-    temp = (HTLine *)LY_CALLOC(1, LINE_SIZE(previous->size));
-    if (temp == NULL)
+    {
+    HTLine* temp;
+    POOLallocHTLine(temp, previous->size);
+    if (!temp)
 	outofmem(__FILE__, "split_line_2");
     memcpy(temp, previous, LINE_SIZE(previous->size));
 #if defined(USE_COLOR_STYLE)
-    ALLOC_IN_POOL((text->styles_pool),HTStyleChangePool,previous->numstyles,temp->styles);
-    memcpy(temp->styles, previous->styles, sizeof(HTStyleChange)*previous->numstyles);
+    POOLallocstyles(temp->styles, previous->numstyles);
     if (!temp->styles)
 	outofmem(__FILE__, "split_line_2");
+    memcpy(temp->styles, previous->styles, sizeof(HTStyleChange)*previous->numstyles);
 #endif
-    FREE(previous);
     previous = temp;
+    }
 
     previous->prev->next = previous;	/* Link in new line */
     previous->next->prev = previous;	/* Could be same node of course */
@@ -3085,7 +3087,6 @@ PRIVATE void split_line ARGS2(
      */
 
     if (s > 0) {			/* if not completely empty */
-	TextAnchor * prev_a = NULL;
 	int moved = 0;
 
 	/* In the algorithm below we move or not move anchors between
@@ -3097,11 +3098,16 @@ PRIVATE void split_line ARGS2(
 	 */
 	/* Our operations can make a non-empty all-whitespace link
 	   empty.  So what? */
-	for (a = text->first_anchor; a; prev_a = a, a = a->next) {
+	if ((a = text->last_anchor_before_split) == 0)
+	    a = text->first_anchor;
+
+	for ( ; a; a = a->next) {
 	    if (a->line_num == CurLine) {
 		int len = a->extent, n = a->number, start = a->line_pos;
 		int end = start + len;
 
+		text->last_anchor_before_split = a;
+
 		/* Which anchors do we leave on the previous line?
 		   a) empty finished (We need a cut-off value.
 		      "Just because": those before s;
@@ -3236,8 +3242,6 @@ PRIVATE void split_line ARGS2(
 	    previous->next->prev = jline;
 	    previous->prev->next = jline;
 
-	    FREE(previous);
-
 	    previous = jline;
 	}
 	{ /* (ht_num_runs==1) */
@@ -3252,11 +3256,10 @@ PRIVATE void split_line ARGS2(
 	    if (!a2)
 		a2 = text->first_anchor;
 	    else
-		a2 = next_anchor(text, a2); /* 1st anchor on line we justify */
+		a2 = a2->next; /* 1st anchor on line we justify */
 
-	    if (a2)
-		for (; a2 && a2->line_num <= text->Lines-1;
-		    last_anchor_of_previous_line = a2, a2 = a2->next);
+	    for (; a2 && a2->line_num <= text->Lines-1;
+		last_anchor_of_previous_line = a2, a2 = a2->next);
 	}
     } else {
 	if (REALLY_CAN_JUSTIFY(text) ) {
@@ -4122,8 +4125,8 @@ check_WrapSource:
      *  Check for end of line.
      */
     actual = ((indent + (int)line->offset + (int)line->size) +
-       ((line->size > 0) &&
-	(int)(line->data[line->size-1] == LY_SOFT_HYPHEN ? 1 : 0)));
+	      ((line->size > 0) &&
+	       (int)(line->data[line->size-1] == LY_SOFT_HYPHEN ? 1 : 0)));
 
     if (text->T.output_utf8) {
 	actual += (UTFXTRA_ON_THIS_LINE - ctrl_chars_on_this_line + UTF_XLEN(ch));
@@ -4516,7 +4519,6 @@ PRIVATE int HText_insertBlanksInStblLines ARGS2(
 	    lines_changed++;
 	    if (line == first_line)
 		first_line = mod_line;
-	    free(line);
 	    line = mod_line;
 #ifdef DISP_PARTIAL
 	    /*
@@ -4902,8 +4904,9 @@ PUBLIC int HText_beginAnchor ARGS3(
 	BOOL,			underline,
 	HTChildAnchor *,	anc)
 {
-    TextAnchor * a = typecalloc(TextAnchor);
+    TextAnchor * a;
 
+    POOLtypecalloc(TextAnchor, a);
     if (a == NULL)
 	outofmem(__FILE__, "HText_beginAnchor");
     a->inUnderline = underline;
@@ -4922,7 +4925,7 @@ PUBLIC int HText_beginAnchor ARGS3(
     text->last_anchor = a;
 
 #ifndef DONT_TRACK_INTERNAL_LINKS
-    if (HTAnchor_followTypedLink((HTAnchor*)anc, LINK_INTERNAL)) {
+    if (HTAnchor_followTypedLink((HTAnchor*)anc, HTInternalLink)) {
 	a->number = ++(text->last_anchor_number);
 	a->link_type = INTERNAL_LINK_ANCHOR;
     } else
@@ -5526,7 +5529,6 @@ PUBLIC void HText_endAppend ARGS1(
 	 */
 	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--;
 	CTRACE((tfp, "GridText: New bottom line: `%s'\n",
@@ -5607,7 +5609,7 @@ PRIVATE void HText_trimHightext ARGS3(
      */
     for (anchor_ptr = text->first_anchor;
 	anchor_ptr != NULL;
-	prev_a = anchor_ptr, anchor_ptr = next_anchor(text, anchor_ptr)) {
+	prev_a = anchor_ptr, anchor_ptr = anchor_ptr->next) {
 	int have_soft_newline_in_1st_line = 0;
 re_parse:
 	/*
@@ -5817,23 +5819,33 @@ PUBLIC HTParentAnchor * HText_nodeAnchor ARGS1(
 /*				GridText specials
 **				=================
 */
+
 /*
- *  HTChildAnchor() returns the anchor with index N.
- *  The index corresponds to the [number] we print for the anchor.
+ *  HText_childNextNumber() returns the anchor with index [number],
+ *  using a pointer from the previous number (=optimization) or NULL.
  */
-PUBLIC HTChildAnchor * HText_childNumber ARGS1(
-	int,		number)
+PUBLIC HTChildAnchor * HText_childNextNumber ARGS2(
+	int,		number,
+	void**,		prev)
 {
-    TextAnchor * a;
+    /* Sorry, TextAnchor is not declared outside this file, use a cast. */
+    TextAnchor * a = *prev;
 
-    if (!(HTMainText && HTMainText->first_anchor) || number <= 0)
+    if (!HTMainText || number <= 0)
 	return (HTChildAnchor *)0;	/* Fail */
+    if (number == 1 || !a)
+	a = HTMainText->first_anchor;
 
-    for (a = HTMainText->first_anchor; a; a = a->next) {
-	if (a->number == number)
-	    return(a->anchor);
-    }
-    return (HTChildAnchor *)0;	/* Fail */
+    /* a strange thing:  positive a->number's are sorted,
+     * and between them several a->number's may be 0 -- skip them
+     */
+    for( ; a && a->number != number; a = a->next)
+	;
+
+    if (!a)
+	return (HTChildAnchor *)0;	/* Fail */
+    *prev = (void*)a;
+    return a->anchor;
 }
 
 /*
@@ -6122,7 +6134,7 @@ PUBLIC int HTGetLinkInfo ARGS6(
 #ifndef DONT_TRACK_INTERNAL_LINKS
 			if (a->link_type == INTERNAL_LINK_ANCHOR) {
 			    link_dest_intl = HTAnchor_followTypedLink(
-				(HTAnchor *)a->anchor, LINK_INTERNAL);
+				(HTAnchor *)a->anchor, HTInternalLink);
 			    if (link_dest_intl && link_dest_intl != link_dest) {
 
 				CTRACE((tfp, "HTGetLinkInfo: unexpected typed link to %s!\n",
@@ -6224,7 +6236,7 @@ PUBLIC BOOL HText_TAHasMoreLines ARGS2(
 	}
 	return NO;
     } else {
-	for (a = HTMainText->first_anchor; a; a = next_anchor(HTMainText, a)) {
+	for (a = HTMainText->first_anchor; a; a = a->next) {
 	    if (a->link_type == INPUT_ANCHOR &&
 		links[curlink].l_form == a->input_field) {
 		return same_anchors(a, a->next, TRUE);
@@ -6469,11 +6481,12 @@ PUBLIC BOOL HText_getFirstTargetInLine ARGS7(
      *  Make sure we have an HText structure, that line_num is
      *  in its range, and that we have a target string. -FM
      */
-    if (!(text
-       && line_num >= 0
-       && line_num <= text->Lines
-       && non_empty(target)))
+    if (!(text &&
+	line_num >= 0 &&
+	line_num <= text->Lines &&
+	non_empty(target))) {
 	return(FALSE);
+    }
 
     /*
      *  Find the line and set up its data and offset -FM
@@ -6834,8 +6847,8 @@ PUBLIC int HText_LinksInLines ARGS3(
 	return total;
 
     for (Anchor_ptr = text->first_anchor;
-		Anchor_ptr != NULL && Anchor_ptr->line_num <= end;
-			Anchor_ptr = next_anchor(text, Anchor_ptr)) {
+	 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 &&
@@ -7012,7 +7025,7 @@ PUBLIC BOOL HText_POSTReplyLoaded ARGS1(
 }
 
 PUBLIC BOOL HTFindPoundSelector ARGS1(
-	char *,		selector)
+	CONST char *,		selector)
 {
     TextAnchor * a;
 
@@ -7748,7 +7761,7 @@ PRIVATE BOOL anchor_has_target ARGS2(
 		return TRUE;
 	    }
 	    FREE(stars);
-       } else if (a->input_field->type == F_OPTION_LIST_TYPE) {
+	} else if (a->input_field->type == F_OPTION_LIST_TYPE) {
 	    /*
 	     *  Search the option strings that are displayed
 	     *  when the popup is invoked. -FM
@@ -8503,18 +8516,15 @@ PUBLIC void HText_RemovePreviousLine ARGS1(
 	HText *,	text)
 {
     HTLine *line, *previous;
-    char *data;
 
     if (!(text && text->Lines > 1))
 	return;
 
     line = text->last_line->prev;
-    data = line->data;
     previous = line->prev;
     previous->next = text->last_line;
     text->last_line->prev = previous;
     text->Lines--;
-    FREE(line);
 }
 
 /*
@@ -8797,7 +8807,7 @@ PUBLIC void HText_endForm ARGS1(
 	/*
 	 *  Go through list of anchors and get our input field. -FM
 	 */
-	for (a = text->first_anchor; a != NULL; a = next_anchor(text, a)) {
+	for (a = text->first_anchor; a != NULL; a = a->next) {
 	    if (a->link_type == INPUT_ANCHOR &&
 		a->input_field->number == HTFormNumber &&
 		a->input_field->type == F_TEXT_TYPE) {
@@ -9249,8 +9259,8 @@ PUBLIC int HText_beginInput ARGS3(
 	BOOL,			underline,
 	InputFieldData *,	I)
 {
-    TextAnchor * a = typecalloc(TextAnchor);
-    FormInfo * f = typecalloc(FormInfo);
+    TextAnchor * a;
+    FormInfo * f;
     CONST char *cp_option = NULL;
     char *IValue = NULL;
     unsigned char *tmp = NULL;
@@ -9261,6 +9271,8 @@ PUBLIC int HText_beginInput ARGS3(
 
     CTRACE((tfp, "GridText: Entering HText_beginInput\n"));
 
+    POOLtypecalloc(TextAnchor, a);
+    POOLtypecalloc(FormInfo, f);
     if (a == NULL || f == NULL)
 	outofmem(__FILE__, "HText_beginInput");
 
@@ -9287,7 +9299,7 @@ PUBLIC int HText_beginInput ARGS3(
 	} else {
 	    TextAnchor * b;
 	    int i2 = 0;
-	    for (b = text->first_anchor; b != NULL; b = next_anchor(text, b)) {
+	    for (b = text->first_anchor; b != NULL; b = b->next) {
 		if (b->link_type == INPUT_ANCHOR &&
 		    b->input_field->type == F_RADIO_TYPE &&
 		    b->input_field->number == HTFormNumber) {
@@ -9474,8 +9486,6 @@ PUBLIC int HText_beginInput ARGS3(
 	     */
 	    CTRACE((tfp,
 		  "GridText: No name present in input field; not displaying\n"));
-	    FREE(a);
-	    FREE(f);
 	    FREE(IValue);
 	    return(0);
 	}
@@ -9957,6 +9967,16 @@ PRIVATE unsigned check_form_specialchars ARGS1(
     return result;
 }
 
+PRIVATE CONST char *guess_content_type ARGS1(CONST char *, filename)
+{
+    HTAtom *encoding;
+    CONST char *desc;
+    HTFormat format = HTFileFormat (filename, &encoding, &desc);
+    return (format != 0 && non_empty(format->name))
+	    ? format->name
+	    : "text/plain";
+}
+
 /*
  *  HText_SubmitForm - generate submit data from form fields.
  *  For mailto forms, send the data.
@@ -10111,7 +10131,7 @@ PUBLIC int HText_SubmitForm ARGS4(
      */
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 
 	if (anchor_ptr->link_type != INPUT_ANCHOR)
 	    continue;
@@ -10263,7 +10283,7 @@ PUBLIC int HText_SubmitForm ARGS4(
      */
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 
 	if (anchor_ptr->link_type != INPUT_ANCHOR)
 	    continue;
@@ -10373,7 +10393,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 		    out_csname = LYCharSet_UC[out_cs].MIMEname;
 		if (Boundary) {
 		    StrAllocCopy(MultipartContentType,
-				 "\r\nContent-Type: text/plain");
+				 "\r\nContent-Type: %s");
 		    if (!success && form_ptr->value_cs < 0) {
 			/*  This is weird. */
 			out_csname = "UNKNOWN-8BIT";
@@ -10482,8 +10502,9 @@ PUBLIC int HText_SubmitForm ARGS4(
 						  SemiColon,
 						  PlainText,
 						  Boundary);
-		if ((escaped2 = load_a_file(&use_mime, val_used)) == NULL)
-		    goto exit_disgracefully;
+		if ((escaped2 = load_a_file(&use_mime, val_used)) == NULL) {
+		    StrAllocCopy(escaped2, "");
+		}
 
 		/* FIXME: we need to modify the mime-type here - rp */
 		/* Note: could use LYGetFileInfo for that and for
@@ -10496,7 +10517,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 		    HTSprintf(&escaped1, "; name=\"%s\"", name_used);
 		    HTSprintf(&escaped1, "; filename=\"%s\"", val_used);
 		    if (MultipartContentType) {
-			StrAllocCat(escaped1, MultipartContentType);
+			HTSprintf(&escaped1, MultipartContentType, guess_content_type(val_used));
 			if (use_mime)
 			    StrAllocCat(escaped1, "\r\nContent-Transfer-Encoding: base64");
 		    }
@@ -10545,7 +10566,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 			StrAllocCopy(escaped1, "Content-Disposition: form-data");
 			HTSprintf(&escaped1, "; name=\"%s\"", name_used);
 			if (MultipartContentType)
-			    StrAllocCat(escaped1, MultipartContentType);
+			    HTSprintf(&escaped1, MultipartContentType, "text/plain");
 			StrAllocCat(escaped1, "\r\n\r\n");
 		    } else {
 			escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
@@ -10617,7 +10638,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 			StrAllocCopy(escaped1, "Content-Disposition: form-data");
 			HTSprintf(&escaped1, "; name=\"%s\"", name_used);
 			if (MultipartContentType)
-			    StrAllocCat(escaped1, MultipartContentType);
+			    HTSprintf(&escaped1, MultipartContentType, "text/plain");
 			StrAllocCat(escaped1, "\r\n\r\n");
 		    } else {
 			escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
@@ -10674,7 +10695,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 			StrAllocCopy(escaped1, "Content-Disposition: form-data");
 			HTSprintf(&escaped1, "; name=\"%s\"", name_used);
 			if (MultipartContentType)
-			    StrAllocCat(escaped1, MultipartContentType);
+			    HTSprintf(&escaped1, MultipartContentType, "text/plain");
 			StrAllocCat(escaped1, "\r\n\r\n");
 		    } else {
 			escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
@@ -10732,7 +10753,7 @@ PUBLIC int HText_SubmitForm ARGS4(
 		    StrAllocCopy(escaped1, "Content-Disposition: form-data");
 		    HTSprintf(&escaped1, "; name=\"%s\"", name_used);
 		    if (MultipartContentType)
-			StrAllocCat(escaped1, MultipartContentType);
+			HTSprintf(&escaped1, MultipartContentType, "text/plain");
 		    StrAllocCat(escaped1, "\r\n\r\n");
 		} else {
 		    escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
@@ -10773,7 +10794,7 @@ PUBLIC int HText_SubmitForm ARGS4(
     }
     FREE(previous_blanks);
 
-    CTRACE((tfp, "QUERY (%d) >> \n%s\n", strlen(query), query));
+    CTRACE((tfp, "QUERY (%d) >> \n%s\n", (int) strlen(query), query));
 
     if (submit_item->submit_method == URL_MAIL_METHOD) {
 	HTUserMsg2(gettext("Submitting %s"), submit_item->submit_action);
@@ -10815,18 +10836,6 @@ PUBLIC int HText_SubmitForm ARGS4(
 	FREE(query);
 	return 1;
     }
-#ifdef EXP_FILE_UPLOAD
-exit_disgracefully:
-    FREE(escaped1);
-    FREE(escaped2);
-    FREE(previous_blanks);
-    FREE(copied_name_used);
-    FREE(copied_val_used);
-    FREE(MultipartContentType);
-    FREE(query);
-    FREE(content_type_out);
-    return 0;
-#endif
 }
 
 PUBLIC void HText_DisableCurrentForm NOARGS
@@ -10842,7 +10851,7 @@ PUBLIC void HText_DisableCurrentForm NOARGS
      */
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 	if (anchor_ptr->link_type == INPUT_ANCHOR &&
 	    anchor_ptr->input_field->number == HTFormNumber) {
 
@@ -10867,7 +10876,7 @@ PUBLIC void HText_ResetForm ARGS1(
      */
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 	if (anchor_ptr->link_type == INPUT_ANCHOR) {
 	    if (anchor_ptr->input_field->number == form->number) {
 
@@ -10916,7 +10925,7 @@ PUBLIC BOOLEAN HText_HaveUserChangedForms NOARGS
      */
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 	if (anchor_ptr->link_type == INPUT_ANCHOR) {
 
 	    if (anchor_ptr->input_field->type == F_RADIO_TYPE ||
@@ -10957,7 +10966,7 @@ PUBLIC void HText_activateRadioButton ARGS1(
 	return;
     for (anchor_ptr = HTMainText->first_anchor;
 	 anchor_ptr != NULL;
-	 anchor_ptr = next_anchor(HTMainText, anchor_ptr)) {
+	 anchor_ptr = anchor_ptr->next) {
 	if (anchor_ptr->link_type == INPUT_ANCHOR &&
 		anchor_ptr->input_field->type == F_RADIO_TYPE) {
 
@@ -11703,9 +11712,10 @@ PRIVATE void insert_new_textarea_anchor ARGS2(
      *  Clone and initialize the struct's needed to add a new TEXTAREA
      *  anchor.
      */
-    if (((a = typecalloc(TextAnchor)) == 0) ||
-	((f = typecalloc(FormInfo)) == 0) ||
-	((l = allocHTLine(MAX_LINE)) == 0))
+    POOLallocHTLine(l, MAX_LINE);
+    POOLtypecalloc(TextAnchor, a);
+    POOLtypecalloc(FormInfo, f);
+    if (a == NULL || l == NULL || f == NULL)
 	outofmem(__FILE__, "insert_new_textarea_anchor");
 
     /*  Init all the fields in the new TextAnchor.                 */
@@ -12526,9 +12536,10 @@ PUBLIC int HText_InsertFile ARGS1(
 	    break;
     }
 
-    if (((a = typecalloc(TextAnchor)) == 0) ||
-	((f = typecalloc(FormInfo)) == 0) ||
-	((l = allocHTLine(MAX_LINE)) == 0))
+    POOLallocHTLine(l, MAX_LINE);
+    POOLtypecalloc(TextAnchor, a);
+    POOLtypecalloc(FormInfo, f);
+    if (a == NULL || l == NULL || f == NULL)
 	outofmem(__FILE__, "HText_InsertFile");
 
     /*  Init all the fields in the new TextAnchor.                 */