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>2003-01-22 10:09:18 -0500
committerThomas E. Dickey <dickey@invisible-island.net>2003-01-22 10:09:18 -0500
commit533c7482785176296637df81cd1a6318a0c29f97 (patch)
treee50cece290409516ee62f08c8912863f5f1ba80a /src/GridText.c
parent490d581c911f53008a7eaaed72b655cf40071b03 (diff)
downloadlynx-snapshots-533c7482785176296637df81cd1a6318a0c29f97.tar.gz
snapshot of project "lynx", label v2-8-5dev_13
Diffstat (limited to 'src/GridText.c')
-rw-r--r--src/GridText.c700
1 files changed, 400 insertions, 300 deletions
diff --git a/src/GridText.c b/src/GridText.c
index e070f164..d5189c46 100644
--- a/src/GridText.c
+++ b/src/GridText.c
@@ -98,8 +98,14 @@ struct _HTStream {			/* only know it as object */
 };
 
 #define TITLE_LINES  1
+
 #define IS_UTF_EXTRA(ch) (text->T.output_utf8 && \
 			  (UCH((ch))&0xc0) == 0x80)
+
+#define IS_UTF8_EXTRA(ch) (!(text && text->T.output_utf8) || \
+			  !is8bits(ch) || \
+			  (UCH(line->data[i] & 0xc0) == 0xc0))
+
 /* a test in compact form: how many extra UTF-8 chars after initial? - kw */
 #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)))
@@ -149,8 +155,30 @@ PUBLIC int LYsb_begin = -1;
 PUBLIC int LYsb_end = -1;
 #endif
 
+#ifndef VMS			/* VMS has a better way - right? - kw */
+#define CHECK_FREE_MEM
+#endif
+
+#ifdef CHECK_FREE_MEM
+PRIVATE void * LY_check_calloc PARAMS((size_t nmemb, size_t size));
+#define LY_CALLOC LY_check_calloc
+#else
+  /* using the regular calloc */
+#define LY_CALLOC calloc
+#endif
+
+/*
+ * The HTPool.data[] array has to align the same as malloc() would, to make the
+ * ALLOC_POOL scheme portable.  For many platforms, that is the same as the
+ * number of bytes in a pointer.  It may be larger, e.g., on machines which
+ * have more stringent requirements for floating point.  32-bits are plenty for
+ * representing styles, but we may need 64-bit or 128-bit alignment.
+ *
+ * The real issue is that performance is degraded if the alignment is not met,
+ * and some platforms such as Tru64 generate lots of warning messages.
+ */
+#define ALIGN_SIZE     8
 
-    /*try to fit in 32bit */
 typedef struct {
 	unsigned int	direction:2;   /* on or off */
 	unsigned int	horizpos:14;   /* horizontal position of this change */
@@ -163,10 +191,12 @@ typedef struct {
 static HTStyleChange stylechanges_buffers[2][MAX_STYLES_ON_LINE];
 #endif
 
-enum { POOL_SIZE = (8192 - 4*sizeof(void*) - sizeof(struct _HTPool*) - sizeof(int)) / sizeof(HTStyleChange) };
+typedef HTStyleChange pool_data;
+
+enum { POOL_SIZE = (8192 - 4*sizeof(void*) - sizeof(struct _HTPool*) - sizeof(int)) / sizeof(pool_data) };
 
 typedef struct _HTPool {
-    HTStyleChange   data[POOL_SIZE];
+    pool_data  data[POOL_SIZE];
     struct _HTPool* prev;
     int used;
 } HTPool;
@@ -203,103 +233,93 @@ to be very efficient.
 lines, anchors, and FormInfo. Arrays of HTStyleChange are stored as is,
 other objects are 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.
- There are 3 macros that deal with pools - POOL_NEW, POOL_FREE and
-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)
-    - 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.
-
-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. Sets ptr to NULL.
+ Pool is referenced by the pointer to the last chunk that contains free slots.
+Functions that allocate memory in pool update that pointer if needed.
+There are 3 functions - POOL_NEW, POOL_FREE, and ALLOC_IN_POOL.
 
       - VH
 
 *************************************************************************/
+
+#define POOLallocstyles(ptr, n)     ptr = ALLOC_IN_POOL(&HTMainText->pool, n * sizeof(pool_data))
+#define POOLallocHTLine(ptr, size)  ptr = (HTLine*) ALLOC_IN_POOL(&HTMainText->pool, LINE_SIZE(size))
+#define POOLallocstring(ptr, len)   ptr = (char*) ALLOC_IN_POOL(&HTMainText->pool, len + 1)
+#define POOLtypecalloc(T,ptr)	    ptr = (T*) ALLOC_IN_POOL(&HTMainText->pool, sizeof(T))
+
+/**************************************************************************/
 /*
- * 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.
+ *   Allocates 'n' items in the pool of type 'HTPool' pointed by 'poolptr'.
+ *   Returns a pointer to the "allocated" memory or NULL if fails.
+ *   Updates 'poolptr' if necessary.
  */
-#define ALLOC_IN_POOL(pool,pool_type,toalloc,ptr) \
-    if (!pool) {					\
-	ptr = NULL;					\
-    } else {						\
-	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;				\
-	   }						\
-	}						\
+PRIVATE pool_data* ALLOC_IN_POOL ARGS2(
+	HTPool**,	ppoolptr,
+	unsigned,	request)
+{
+    HTPool* pool = *ppoolptr;
+    pool_data* ptr;
+    unsigned n;
+    unsigned j;
+
+    if (!pool) {
+	ptr = NULL;
+    } else {
+	n = request;
+	if (n == 0)
+	    n = 1;
+	j = (n % ALIGN_SIZE);
+	if (j != 0)
+	    n += (ALIGN_SIZE - j);
+	n /= sizeof(pool_data);
+	if (POOL_SIZE >= (pool->used + n)) {
+	    ptr = pool->data + pool->used;
+	    pool->used += n;
+	} else {
+	    HTPool* newpool = (HTPool*)LY_CALLOC(1, sizeof(HTPool));
+	    if (!newpool) {
+		ptr = NULL;
+	    } else {
+		newpool->prev = pool;
+		newpool->used = n;
+		ptr = newpool->data;
+		pool = newpool;
+	   }
+	}
     }
+    *ppoolptr = pool;
+    return 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.
+ *   Returns a pointer to initialized pool of type 'HTPool', or 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;				\
-	}						\
+PRIVATE HTPool* POOL_NEW NOARGS
+{
+    HTPool* poolptr = (HTPool*)LY_CALLOC(1, sizeof(HTPool));
+    if (poolptr) {
+	(poolptr)->prev = NULL;
+	(poolptr)->used = 0;
     }
+    return poolptr;
+}
+
 /*
- * void POOL_FREE( pool_type, P*& ptr)
- *     Frees a pool of type 'pool_type' pointed by ptr. Sets ptr to NULL.
+ *   Frees a pool of type 'HTPool' pointed by poolptr.
  */
-#define POOL_FREE(pool_type,ptr)			\
-    {							\
-	pool_type* cur = ptr;				\
-	pool_type* prev;				\
-	while (cur) {					\
-	    prev = cur->prev;				\
-	    FREE(cur);					\
-	    cur = prev;					\
-	}						\
-	ptr = NULL;					\
+PRIVATE void POOL_FREE ARGS1(
+	HTPool*,	poolptr)
+{
+    HTPool* cur = poolptr;
+    HTPool* prev;
+    while (cur) {
+	prev = cur->prev;
+	free(cur);
+	cur = prev;
     }
-/**************************************************************************/
+}
 
-#define _sz_           sizeof(HTStyleChange)      /* 4 */
-#define _round_up_(x)  (x%_sz_ ? x/_sz_ + 1: x/_sz_)
-
-#define POOLallocstyles(ptr, N)     ALLOC_IN_POOL(HTMainText->pool,HTPool,\
-					N,				\
-					ptr)
-#define POOLallocHTLine(ptr, size)  { HTStyleChange* _tmp_;		\
-				      int N = _round_up_(LINE_SIZE(size));  \
-				      ALLOC_IN_POOL(HTMainText->pool,HTPool,\
-					N,      \
-					_tmp_); \
-				      ptr = (HTLine*)_tmp_; \
-				    }
-#define POOLtypecalloc(T,ptr)	    { HTStyleChange* _tmp_;		\
-				      int N = _round_up_(sizeof(T));	\
-				      ALLOC_IN_POOL(HTMainText->pool,HTPool,\
-					N,	\
-					_tmp_);	\
-				      ptr = (T*)_tmp_;	\
-				    }
+/**************************************************************************/
+/**************************************************************************/
 
 typedef struct _line {
 	struct _line    *next;
@@ -315,7 +335,20 @@ typedef struct _line {
 
 
 #define LINE_SIZE(l) (sizeof(HTLine)+(l))	/* Allow for terminator */
-#define allocHTLine(l) (HTLine *)calloc(1, LINE_SIZE(l))
+
+/*
+ * Last line buffer; the second is used in split_line(). Not in pool!
+ * We cannot wrap in middle of multibyte sequences, so allocate 2 extra
+ * for a workspace.  This is stored in the HText, to prevent confusion
+ * between different documents.  Note also that it is declared with an
+ * HTLine at the beginning so pointers will be properly aligned.
+ */
+typedef struct {
+	HTLine	base;
+	char    data[MAX_LINE+2];
+} HTLineTemp;
+
+#define TEMP_LINE(p,n) ((HTLine *)&(p->temp_line[n]))
 
 typedef struct _TextAnchor {
 	struct _TextAnchor *	next;
@@ -349,6 +382,7 @@ struct _HText {
 	HTParentAnchor *	node_anchor;
 
 	HTLine *		last_line;
+	HTLineTemp		temp_line[2];
 	int			Lines;		/* Number of them */
 	TextAnchor *		first_anchor;	/* double-linked on demand */
 	TextAnchor *		last_anchor;
@@ -382,6 +416,7 @@ struct _HText {
 	int			last_lineno_last_disp_partial;
 #endif
 	STable_info *		stbl;
+	HTList *		enclosed_stbl;
 
 	HTkcode			kcode;			/* Kanji code? */
 	HTkcode			specified_kcode;	/* Specified Kanji code */
@@ -493,8 +528,6 @@ PUBLIC void mark_justify_start_position ARGS1(void*,text)
 	HTCJK == NOCJK && !in_DT && \
 	can_justify_here && can_justify_this_line && !form_in_htext )
 
-#else
-#define last_anchor_of_previous_line (TextAnchor*)0
 #endif /* EXP_JUSTIFY_ELTS */
 
 
@@ -535,123 +568,16 @@ PUBLIC  HTList * search_queries = NULL;  /* isindex and whereis queries   */
 #ifdef LY_FIND_LEAKS
 PRIVATE void free_all_texts NOARGS;
 #endif
-PRIVATE int HText_TrueLineSize PARAMS((
+
+PRIVATE BOOL HText_TrueEmptyLine PARAMS((
 	HTLine *	line,
 	HText *		text,
 	BOOL		IgnoreSpaces));
 
-#ifndef VMS			/* VMS has a better way - right? - kw */
-#define CHECK_FREE_MEM
-#endif
-
-/*
- * Set the initial highlight information for a given anchor.
- */
-PRIVATE void LYSetHiText ARGS3(
-	TextAnchor *,	a,
-	char *,		text,
-	int,		len)
-{
-    unsigned have = a->lites.hl_len;
-
-    if (a->lites.hl_info != NULL) {
-	while (have-- != 0) {
-	    if ((int) (have + 1) < a->lites.hl_len) {
-		FREE(a->lites.hl_info[have].hl_text);
-	    }
-	}
-	FREE(a->lites.hl_info);
-    }
-    FREE(a->lites.hl_base.hl_text);
-    a->lites.hl_len = 0;
-
-    if (text != NULL) {
-	StrnAllocCopy(a->lites.hl_base.hl_text, text, len);
-	a->lites.hl_len = 1;
-    }
-}
-
-/*
- * Add highlight information for the next line of a anchor.
- */
-PRIVATE void LYAddHiText ARGS3(
-	TextAnchor *,	a,
-	char *,		text,
-	int,		x)
-{
-    HiliteInfo *have = a->lites.hl_info;
-    unsigned need = (a->lites.hl_len - 1);
-    unsigned want = ++(a->lites.hl_len) * sizeof(HiliteInfo);
-
-    if (have != NULL) {
-	have = (HiliteInfo *) realloc(have, want);
-    } else {
-	have = (HiliteInfo *) malloc(want);
-    }
-    a->lites.hl_info = have;
-
-    have[need].hl_text = NULL;
-    StrAllocCopy(have[need].hl_text, text);
-    have[need].hl_x = x;
-}
-
-/*
- * Get the highlight text, counting from zero.
- */
-PRIVATE char *LYGetHiTextStr ARGS2(
-	TextAnchor *,	a,
-	int,		count)
-{
-    char *result;
-
-    if (count >= a->lites.hl_len)
-	result = NULL;
-    else if (count > 0)
-	result = a->lites.hl_info[count - 1].hl_text;
-    else
-	result = a->lites.hl_base.hl_text;
-    return result;
-}
-
-/*
- * Get the X-ordinate at which to draw the corresponding highlight-text
- */
-PRIVATE int LYGetHiTextPos ARGS2(
-	TextAnchor *,	a,
-	int,		count)
-{
-    int result;
-
-    if (count >= a->lites.hl_len)
-	result = -1;
-    else if (count > 0)
-	result = a->lites.hl_info[count - 1].hl_x;
-    else
-	result = a->line_pos;
-    return result;
-}
-
-/*
- * Copy highlighting information from anchor 'a' to 'b'.
- */
-PRIVATE void LYCopyHiText ARGS2(
-	TextAnchor *,	a,
-	TextAnchor *,	b)
-{
-    int count;
-    char *s;
-
-    LYSetHiText(a, NULL, 0);
-    for (count = 0; ; ++count) {
-	if ((s = LYGetHiTextStr(b, count)) == NULL)
-	    break;
-	if (count == 0) {
-	    LYSetHiText(a, s, strlen(s));
-	} else {
-	    LYAddHiText(a, s, LYGetHiTextPos(b, count));
-	}
-    }
-}
+PRIVATE int HText_TrueLineSize PARAMS((
+	HTLine *	line,
+	HText *		text,
+	BOOL		IgnoreSpaces));
 
 #ifdef CHECK_FREE_MEM
 
@@ -723,7 +649,6 @@ PRIVATE void * LY_check_calloc ARGS2(
 	HText * t = (HText *) HTList_objectAt(loaded_texts, i);
 	if (t == HTMainText)
 	    t = NULL;		/* shouldn't happen */
-	{
 	CTRACE((tfp, "\nBUG *** Emergency freeing document %d/%d for '%s'%s!\n",
 		    i + 1, n,
 		    ((t && t->node_anchor &&
@@ -732,7 +657,6 @@ PRIVATE void * LY_check_calloc ARGS2(
 		    ((t && t->node_anchor &&
 		      t->node_anchor->post_data) ?
 		     " with POST data" : "")));
-	}
 	HTList_removeObjectAt(loaded_texts, i);
 	HText_free(t);
 	if (mem_is_avail(4, nmemb * size)) {
@@ -758,14 +682,119 @@ PRIVATE void * LY_check_calloc ARGS2(
     return (calloc(nmemb, size));
 }
 
-#define LY_CALLOC LY_check_calloc
+#endif /* CHECK_FREE_MEM */
+
+/*
+ * Clear highlight information for a given anchor
+ * (text was allocated in the pool).
+ */
+PRIVATE void LYClearHiText ARGS1(
+	TextAnchor *,	a)
+{
+    FREE(a->lites.hl_info);
 
-#else  /* CHECK_FREE_MEM */
+    a->lites.hl_base.hl_text = NULL;
+    a->lites.hl_len = 0;
+}
 
-  /* using the regular calloc */
-#define LY_CALLOC calloc
+/*
+ * Set the initial highlight information for a given anchor.
+ */
+PRIVATE void LYSetHiText ARGS3(
+	TextAnchor *,	a,
+	char *,		text,
+	int,		len)
+{
+    if (text != NULL) {
+	POOLallocstring(a->lites.hl_base.hl_text, len);
+	memcpy(a->lites.hl_base.hl_text, text, len);
+	*(a->lites.hl_base.hl_text + len) = '\0';
 
-#endif /* CHECK_FREE_MEM */
+	a->lites.hl_len = 1;
+    }
+}
+
+/*
+ * Add highlight information for the next line of a anchor.
+ */
+PRIVATE void LYAddHiText ARGS3(
+	TextAnchor *,	a,
+	char *,		text,
+	int,		x)
+{
+    HiliteInfo *have = a->lites.hl_info;
+    unsigned need = (a->lites.hl_len - 1);
+    unsigned want = (a->lites.hl_len += 1) * sizeof(HiliteInfo);
+
+    if (have != NULL) {
+	have = (HiliteInfo *) realloc(have, want);
+    } else {
+	have = (HiliteInfo *) malloc(want);
+    }
+    a->lites.hl_info = have;
+
+    POOLallocstring(have[need].hl_text, strlen(text));
+    strcpy(have[need].hl_text, text);
+    have[need].hl_x = x;
+}
+
+/*
+ * Get the highlight text, counting from zero.
+ */
+PRIVATE char *LYGetHiTextStr ARGS2(
+	TextAnchor *,	a,
+	int,		count)
+{
+    char *result;
+
+    if (count >= a->lites.hl_len)
+	result = NULL;
+    else if (count > 0)
+	result = a->lites.hl_info[count - 1].hl_text;
+    else
+	result = a->lites.hl_base.hl_text;
+    return result;
+}
+
+/*
+ * Get the X-ordinate at which to draw the corresponding highlight-text
+ */
+PRIVATE int LYGetHiTextPos ARGS2(
+	TextAnchor *,	a,
+	int,		count)
+{
+    int result;
+
+    if (count >= a->lites.hl_len)
+	result = -1;
+    else if (count > 0)
+	result = a->lites.hl_info[count - 1].hl_x;
+    else
+	result = a->line_pos;
+    return result;
+}
+
+/*
+ * Copy highlighting information from anchor 'b' to 'a'.
+ */
+PRIVATE void LYCopyHiText ARGS2(
+	TextAnchor *,	a,
+	TextAnchor *,	b)
+{
+    int count;
+    char *s;
+
+    LYClearHiText(a);
+    for (count = 0; ; ++count) {
+	if ((s = LYGetHiTextStr(b, count)) == NULL)
+	    break;
+	if (count == 0) {
+	    LYSetHiText(a, s, strlen(s));
+	} else {
+	    LYAddHiText(a, s, LYGetHiTextPos(b, count));
+	}
+    }
+}
 
 PRIVATE void HText_getChartransInfo ARGS1(
 	HText *,	me)
@@ -877,14 +906,14 @@ PUBLIC HText *	HText_new ARGS1(
 #endif /* VMS && VAXC && !__DECC */
     }
 
-    POOL_NEW(HTPool, self->pool);
+    self->pool = POOL_NEW();
     if (!self->pool)
 	outofmem(__FILE__, "HText_New");
-    line = self->last_line = allocHTLine(MAX_LINE);
-    if (line == NULL)
-	outofmem(__FILE__, "HText_New");
+
+    line = self->last_line = TEMP_LINE(self, 0);
     line->next = line->prev = line;
     line->offset = line->size = 0;
+    line->data[line->size] = '\0';
 #ifdef USE_COLOR_STYLE
     line->numstyles = 0;
     line->styles = stylechanges_buffers[0];
@@ -1058,24 +1087,6 @@ PUBLIC void HText_free ARGS1(
 
     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 = self->last_line = NULL;
-	    break;
-	}
-    }
-
     while (self->first_anchor) {		/* Free off anchor array */
 	TextAnchor * l = self->first_anchor;
 	self->first_anchor = l->next;
@@ -1126,7 +1137,7 @@ PUBLIC void HText_free ARGS1(
 	    FREE(l->input_field->accept_cs);
 	}
 
-	LYSetHiText(l, NULL, 0);
+	LYClearHiText(l);
     }
     FormList_delete(self->forms);
 
@@ -1184,7 +1195,7 @@ PUBLIC void HText_free ARGS1(
 	    HTMainAnchor = NULL;
     }
 
-    POOL_FREE(HTPool, self->pool);
+    POOL_FREE(self->pool);
     FREE(self);
 }
 
@@ -1323,7 +1334,7 @@ PRIVATE int display_line ARGS4(
 	while (current_style < line->numstyles &&
 	       i >= (int) (CStyle.horizpos + line->offset + 1))
 	{
-	    LynxChangeStyle (CStyle.style,CStyle.direction);
+	    LynxChangeStyle (CStyle.style, CStyle.direction);
 	    current_style++;
 	}
 #endif
@@ -2430,7 +2441,7 @@ PRIVATE int set_style_by_embedded_chars ARGS4(
 PRIVATE void move_anchors_in_region ARGS7(
     HTLine *,		line,
     int,		line_number,
-    TextAnchor **,	prev_anchor,
+    TextAnchor **,	prev_anchor,	/*updates++*/
     int *,		prev_head_processed,
     int,		sbyte,
     int,		ebyte,
@@ -2496,9 +2507,9 @@ PRIVATE void move_anchors_in_region ARGS7(
  *  in appropriate places - so that characters at/after the old
  *  position end up at/after the new position, for each pair, if possible.
  *  Some necessary changes for anchors starting on this line are also done
- *  here if needed.
+ *  here if needed. Updates 'prev_anchor' internally.
  *  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
  */
@@ -2506,7 +2517,7 @@ PRIVATE HTLine * insert_blanks_in_line ARGS7(
     HTLine *,		line,
     int,		line_number,
     HText *,		text,
-    TextAnchor *,	prev_anchor,
+    TextAnchor **,	prev_anchor,	/*updates++*/
     int,		ninserts,
     int *,		oldpos,		/* Measured in cells */
     int *,		newpos)		/* Likewise */
@@ -2533,16 +2544,20 @@ PRIVATE HTLine * insert_blanks_in_line ARGS7(
 	    added_chars = newpos[ip] - oldpos[ip];
     if (line->size + added_chars > MAX_LINE - 2)
 	return NULL;
-    if (line == text->last_line)
-	mod_line = allocHTLine(MAX_LINE);
-    else
-	mod_line = allocHTLine(line->size + added_chars);
+    if (line == text->last_line) {
+	if (line == TEMP_LINE(text, 0))
+	   mod_line = TEMP_LINE(text, 1);
+	else
+	   mod_line = TEMP_LINE(text, 0);
+    } else {
+	POOLallocHTLine(mod_line, line->size + added_chars);
+    }
     if (!mod_line)
 	return NULL;
-    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));
+    if (!*prev_anchor)
+	*prev_anchor = text->first_anchor;
+    head_processed = (*prev_anchor && (*prev_anchor)->line_num < line_number);
+    memcpy(mod_line, line, LINE_SIZE(0));
     t = newdata = mod_line->data;
     ip = 0;
     while (ip <= ninserts) {
@@ -2572,7 +2587,7 @@ PRIVATE HTLine * insert_blanks_in_line ARGS7(
 
 	/* Now s is at the "displayed" char, pre is before the style change */
 	if (ip)				/* Fix anchor positions */
-	    move_anchors_in_region(line, line_number, &prev_anchor,
+	    move_anchors_in_region(line, line_number, prev_anchor /*updates++*/,
 				   &head_processed,
 				   copied - line->data, pre - line->data,
 				   shift);
@@ -2600,8 +2615,8 @@ PRIVATE HTLine * insert_blanks_in_line ARGS7(
     while (pre < s)	/* Copy remaining style-codes */
 	*t++ = *pre++;
     /* Check whether the last anchor continues on the next line */
-    if (head_processed && prev_anchor && prev_anchor->line_num == line_number)
-	prev_anchor->extent += shift;
+    if (head_processed && *prev_anchor && (*prev_anchor)->line_num == line_number)
+	(*prev_anchor)->extent += shift;
     *t = '\0';
     mod_line->size = t - newdata;
     return mod_line;
@@ -2654,18 +2669,24 @@ PRIVATE void split_line ARGS2(
     int TailTrim = 0;
     int s, s_post, s_pre, t_underline = underline_on, t_bold = bold_on;
     char *p;
-    HTLine * previous = text->last_line;
     int ctrl_chars_on_previous_line = 0;
     int utfxtra_on_previous_line = UTFXTRA_ON_THIS_LINE;
     char * cp;
-    /* can't wrap in middle of multibyte sequences, so allocate 2 extra */
-    HTLine * line = (HTLine *)LY_CALLOC(1, LINE_SIZE(MAX_LINE)+2);
+
+    HTLine * previous = text->last_line;
+    HTLine * line;
 
     /*
-     *  Make new line.
+     *  Set new line.
      */
+    if (previous == TEMP_LINE(text, 0))
+	line = TEMP_LINE(text, 1);
+    else
+	line = TEMP_LINE(text, 0);
     if (line == NULL)
-	outofmem(__FILE__, "split_line_1");
+	return;
+    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 = ' ';
@@ -2924,7 +2945,7 @@ PRIVATE void split_line ARGS2(
 		}
 		if ( to < line->styles + MAX_STYLES_ON_LINE - 1
 		     && to[1].direction == STACK_OFF
-		     && to[1].horizpos <= SpecialAttrChars
+		     && to[1].horizpos <= (unsigned) SpecialAttrChars
 		     && to[1].style == scan->style )
 		    to++;
 		else if (to >= line->styles) {
@@ -2956,7 +2977,7 @@ PRIVATE void split_line ARGS2(
 
     {
     HTLine* temp;
-    temp = allocHTLine(previous->size);
+    POOLallocHTLine(temp, previous->size);
     if (!temp)
 	outofmem(__FILE__, "split_line_2");
     memcpy(temp, previous, LINE_SIZE(previous->size));
@@ -2966,7 +2987,6 @@ PRIVATE void split_line ARGS2(
 	outofmem(__FILE__, "split_line_2");
     memcpy(temp->styles, previous->styles, sizeof(HTStyleChange)*previous->numstyles);
 #endif
-    FREE(previous);
     previous = temp;
     }
 
@@ -3243,7 +3263,7 @@ PRIVATE void split_line ARGS2(
 		i++;
 	    }
 	    jline = insert_blanks_in_line(previous, CurLine, text,
-					  last_anchor_of_previous_line,
+					  &last_anchor_of_previous_line /*updates++*/,
 					  ht_num_runs - 1, oldpos, newpos);
 	    free((char*)oldpos);
 	    if (jline == NULL)
@@ -3251,26 +3271,14 @@ PRIVATE void split_line ARGS2(
 	    previous->next->prev = jline;
 	    previous->prev->next = jline;
 
-	    FREE(previous);
-
 	    previous = jline;
 	}
 	{ /* (ht_num_runs==1) */
-	    /* keep maintaining 'last_anchor_of_previous_line' */
-	    TextAnchor* a2 = last_anchor_of_previous_line;
 	    if (justify_start_position) {
 		char* p2 = previous->data;
 		for( ; p2 < previous->data + justify_start_position; ++p2)
 		    *p2 = (*p2 == HT_NON_BREAK_SPACE ? ' ' : *p2);
 	    }
-
-	    if (!a2)
-		a2 = text->first_anchor;
-	    else
-		a2 = a2->next; /* 1st anchor on line we justify */
-
-	    for (; a2 && a2->line_num <= text->Lines-1;
-		last_anchor_of_previous_line = a2, a2 = a2->next);
 	}
     } else {
 	if (REALLY_CAN_JUSTIFY(text) ) {
@@ -3322,9 +3330,7 @@ PRIVATE void blank_lines ARGS2(
 	HText *,	text,
 	int,		newlines)
 {
-    BOOL IgnoreSpaces = FALSE;
-
-    if (!HText_LastLineSize(text, IgnoreSpaces)) { /* No text on current line */
+    if (HText_LastLineEmpty(text, FALSE)) { /* No text on current line */
 	HTLine * line = text->last_line->prev;
 
 #ifdef USE_COLOR_STYLE
@@ -3333,8 +3339,8 @@ PRIVATE void blank_lines ARGS2(
 	    return;			/* Do not add a blank line at start */
 #endif
 
-	while ((line != text->last_line) &&
-	       (HText_TrueLineSize(line, text, IgnoreSpaces) == 0)) {
+	while (line != text->last_line &&
+			HText_TrueEmptyLine(line, text, FALSE)) {
 	    if (newlines == 0)
 		break;
 	    newlines--;		/* Don't bother: already blank */
@@ -4356,7 +4362,7 @@ PUBLIC void _internal_HTC ARGS3(HText *,text, int,style, int,dir)
 
 	if (line->numstyles > 0 && dir == 0 &&
 	    line->styles[line->numstyles-1].direction &&
-	    line->styles[line->numstyles-1].style == style &&
+	    line->styles[line->numstyles-1].style == (unsigned) style &&
 	    (int) line->styles[line->numstyles-1].horizpos
 	    == (int)line->size - ctrl_chars_on_this_line) {
 	    /*
@@ -4488,7 +4494,7 @@ PRIVATE int HText_insertBlanksInStblLines ARGS2(
 	    }
 	}
 	if (line == me->last_line) {
-	    if (line->size == 0 || !HText_TrueLineSize(line, me, FALSE))
+	    if (line->size == 0 || HText_TrueEmptyLine(line, me, FALSE))
 		continue;
 	    /*
 	     *  Last ditch effort to end the table with a line break,
@@ -4517,7 +4523,7 @@ PRIVATE int HText_insertBlanksInStblLines ARGS2(
 	    continue;
 	}
 	mod_line = insert_blanks_in_line(line, lineno, me,
-					 me->last_anchor_before_stbl,
+					 &me->last_anchor_before_stbl /*updates++*/,
 					 ninserts, oldpos, newpos);
 	if (mod_line) {
 	    if (line == me->last_line) {
@@ -4530,7 +4536,6 @@ PRIVATE int HText_insertBlanksInStblLines ARGS2(
 	    lines_changed++;
 	    if (line == first_line)
 		first_line = mod_line;
-	    free(line);
 	    line = mod_line;
 #ifdef DISP_PARTIAL
 	    /*
@@ -4733,6 +4738,25 @@ PUBLIC void HText_startStblTABLE ARGS2(
     }
 }
 
+#ifdef EXP_NESTED_TABLES
+PRIVATE void free_enclosed_stbl ARGS1(
+	HText *,	me)
+{
+    if (me->enclosed_stbl != NULL) {
+	HTList *list = me->enclosed_stbl;
+	STable_info *stbl;
+	while (NULL != (stbl = (STable_info *)HTList_nextObject(list))) {
+	    CTRACE((tfp, "endStblTABLE: finally free %p\n", me->stbl));
+	    Stbl_free(stbl);
+	}
+	HTList_delete(me->enclosed_stbl);
+	me->enclosed_stbl = NULL;
+    }
+}
+#else
+#define free_enclosed_stbl(me) /* nothing */
+#endif
+
 /*	Finish simple table handling
  *	Return TRUE if the table is nested inside another table.
  */
@@ -4744,11 +4768,14 @@ PUBLIC int HText_endStblTABLE ARGS1(
 
     if (!me || !me->stbl) {
 	CTRACE((tfp, "endStblTABLE: ignored.\n"));
+	free_enclosed_stbl(me);
 	return FALSE;
     }
     CTRACE((tfp, "endStblTABLE: ok, will try.\n"));
+
     ncols = Stbl_finishTABLE(me->stbl);
     CTRACE((tfp, "endStblTABLE: ncols = %d.\n", ncols));
+
     if (ncols > 0) {
 	lines_changed = HText_insertBlanksInStblLines(me, ncols);
 	CTRACE((tfp, "endStblTABLE: changed %d lines, done.\n", lines_changed));
@@ -4759,19 +4786,33 @@ PUBLIC int HText_endStblTABLE ARGS1(
 	NumOfLines_partial -= lines_changed;  /* fake */
 #endif  /* DISP_PARTIAL */
     }
+
 #ifdef EXP_NESTED_TABLES
     if (nested_tables) {
 	enclosing = Stbl_get_enclosing(me->stbl);
 	me->last_anchor_before_stbl = Stbl_get_last_anchor_before(me->stbl);
-    } else
-#endif
-    Stbl_free(me->stbl);
-#ifdef EXP_NESTED_TABLES
-    if (nested_tables)
+	if (enclosing == NULL) {
+	    Stbl_free(me->stbl);
+	    free_enclosed_stbl(me);
+	} else {
+	    if (me->enclosed_stbl == NULL)
+		me->enclosed_stbl = HTList_new();
+	    HTList_addObject(me->enclosed_stbl, me->stbl);
+	    CTRACE((tfp, "endStblTABLE: postpone free %p\n", me->stbl));
+	}
 	me->stbl = enclosing;
-    else
-#endif
+    } else {
+	Stbl_free(me->stbl);
+	me->stbl = NULL;
+    }
+#else
+    Stbl_free(me->stbl);
     me->stbl = NULL;
+#endif
+
+    CTRACE((tfp, "endStblTABLE: have%s enclosing table (%p)\n",
+	    enclosing == 0 ? " NO" : "", enclosing));
+
     return enclosing != 0;
 }
 
@@ -5541,7 +5582,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",
@@ -5660,7 +5700,7 @@ re_parse:
 	 *  done the trimming & adjusting for this anchor, so avoid
 	 *  doing it a second time. - kw
 	 */
-	if ((hilite_str = LYGetHiTextStr(anchor_ptr, 0)) != NULL)
+	if (LYGetHiTextStr(anchor_ptr, 0) != NULL)
 	    continue;
 
 	if (anchor_ptr->line_pos > (int) line_ptr->size) {
@@ -5724,10 +5764,12 @@ re_parse:
 	if (line_ptr->data
 	 && anchor_ptr->extent > 0
 	 && anchor_ptr->line_pos >= 0) {
+	    LYClearHiText(anchor_ptr);
 	    LYSetHiText(anchor_ptr,
 			&line_ptr->data[anchor_ptr->line_pos],
 			anchor_ptr->extent);
 	} else {
+	    LYClearHiText(anchor_ptr);
 	    LYSetHiText(anchor_ptr, "", 0);
 	}
 
@@ -5747,7 +5789,7 @@ re_parse:
 
 	    if (!final
 	     && count_line >= stop_before) {
-		LYSetHiText(anchor_ptr, NULL, 0);
+		LYClearHiText(anchor_ptr);
 		break;
 	    } else if (line_ptr2 == text->last_line) {
 		break;
@@ -8440,6 +8482,15 @@ PUBLIC int HText_LastLineSize ARGS2(
     return HText_TrueLineSize(text->last_line, text, IgnoreSpaces);
 }
 
+PUBLIC BOOL HText_LastLineEmpty ARGS2(
+	HText *,	text,
+	BOOL,		IgnoreSpaces)
+{
+    if (!text || !text->last_line || !text->last_line->size)
+	return TRUE;
+    return HText_TrueEmptyLine(text->last_line, text, IgnoreSpaces);
+}
+
 PUBLIC int HText_LastLineOffset ARGS1(
 	HText *,	text)
 {
@@ -8461,6 +8512,22 @@ PUBLIC int HText_PreviousLineSize ARGS2(
     return HText_TrueLineSize(line, text, IgnoreSpaces);
 }
 
+PUBLIC BOOL HText_PreviousLineEmpty ARGS2(
+	HText *,	text,
+	BOOL,		IgnoreSpaces)
+{
+    HTLine * line;
+
+    if (!text || !text->last_line)
+	return TRUE;
+    if (!(line = text->last_line->prev))
+	return TRUE;
+    return HText_TrueEmptyLine(line, text, IgnoreSpaces);
+}
+
+/*
+ * Compute the "true" line size.
+ */
 PRIVATE int HText_TrueLineSize ARGS3(
 	HTLine *,	line,
 	HText *,	text,
@@ -8475,9 +8542,7 @@ PRIVATE int HText_TrueLineSize ARGS3(
     if (IgnoreSpaces) {
 	for (i = 0; i < line->size; i++) {
 	    if (!IsSpecialAttrChar(UCH(line->data[i])) &&
-		(!(text && text->T.output_utf8) ||
-		 !is8bits(line->data[i]) ||
-		 (UCH((line->data[i] & 0xc0)) == 0xc0)) &&
+		IS_UTF8_EXTRA(line->data[i]) &&
 		!isspace(UCH(line->data[i])) &&
 		UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
 		UCH(line->data[i]) != HT_EN_SPACE) {
@@ -8487,9 +8552,7 @@ PRIVATE int HText_TrueLineSize ARGS3(
     } else {
 	for (i = 0; i < line->size; i++) {
 	    if (!IsSpecialAttrChar(line->data[i]) &&
-		(!(text && text->T.output_utf8) ||
-		 !is8bits(line->data[i]) ||
-		 (UCH(line->data[i] & 0xc0) == 0xc0))) {
+		IS_UTF8_EXTRA(line->data[i])) {
 		true_size++;
 	    }
 	}
@@ -8497,6 +8560,42 @@ PRIVATE int HText_TrueLineSize ARGS3(
     return true_size;
 }
 
+/*
+ * Tell if the line is really empty.  This is invoked much more often than
+ * HText_TrueLineSize(), and most lines are not empty.  So it is faster to
+ * do this check than to check if the line size happens to be zero.
+ */
+PRIVATE BOOL HText_TrueEmptyLine ARGS3(
+	HTLine *,	line,
+	HText *,	text,
+	BOOL,		IgnoreSpaces)
+{
+    size_t i;
+
+    if (!(line && line->size))
+	return TRUE;
+
+    if (IgnoreSpaces) {
+	for (i = 0; i < line->size; i++) {
+	    if (!IsSpecialAttrChar(UCH(line->data[i])) &&
+		IS_UTF8_EXTRA(line->data[i]) &&
+		!isspace(UCH(line->data[i])) &&
+		UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
+		UCH(line->data[i]) != HT_EN_SPACE) {
+		return FALSE;
+	    }
+	}
+    } else {
+	for (i = 0; i < line->size; i++) {
+	    if (!IsSpecialAttrChar(line->data[i]) &&
+		IS_UTF8_EXTRA(line->data[i])) {
+		return FALSE;
+	    }
+	}
+    }
+    return TRUE;
+}
+
 PUBLIC void HText_NegateLineOne ARGS1(
 	HText *,	text)
 {
@@ -8538,7 +8637,6 @@ PUBLIC void HText_RemovePreviousLine ARGS1(
     previous->next = text->last_line;
     text->last_line->prev = previous;
     text->Lines--;
-    FREE(line);
 }
 
 /*
@@ -8587,7 +8685,7 @@ PUBLIC void HText_setTabID ARGS2(
     HTList * cur = text->tabs;
     HTList * last = NULL;
 
-    if (!text || !name || !*name)
+    if (!text || isEmpty(name))
 	return;
 
     if (!cur) {
@@ -9337,7 +9435,7 @@ PUBLIC int HText_beginInput ARGS3(
     a->link_type = INPUT_ANCHOR;
     a->show_anchor = YES;
 
-    LYSetHiText(a, NULL, 0);
+    LYClearHiText(a);
     a->extent = 2;
 
     a->input_field = f;
@@ -9981,6 +10079,7 @@ PRIVATE unsigned check_form_specialchars ARGS1(
     return result;
 }
 
+#ifdef EXP_FILE_UPLOAD
 PRIVATE CONST char *guess_content_type ARGS1(CONST char *, filename)
 {
     HTAtom *encoding;
@@ -9990,6 +10089,7 @@ PRIVATE CONST char *guess_content_type ARGS1(CONST char *, filename)
 	    ? format->name
 	    : "text/plain";
 }
+#endif
 
 /*
  *  HText_SubmitForm - generate submit data from form fields.
@@ -11726,7 +11826,7 @@ PRIVATE void insert_new_textarea_anchor ARGS2(
      *  Clone and initialize the struct's needed to add a new TEXTAREA
      *  anchor.
      */
-    l = allocHTLine(MAX_LINE);
+    POOLallocHTLine(l, MAX_LINE);
     POOLtypecalloc(TextAnchor, a);
     POOLtypecalloc(FormInfo, f);
     if (a == NULL || l == NULL || f == NULL)
@@ -12550,7 +12650,7 @@ PUBLIC int HText_InsertFile ARGS1(
 	    break;
     }
 
-    l = allocHTLine(MAX_LINE);
+    POOLallocHTLine(l, MAX_LINE);
     POOLtypecalloc(TextAnchor, a);
     POOLtypecalloc(FormInfo, f);
     if (a == NULL || l == NULL || f == NULL)